pax_global_header 0000666 0000000 0000000 00000000064 13565763364 0014534 g ustar 00root root 0000000 0000000 52 comment=4057c6564e7a2356e92d1ea37047c86d58042350
form-master/ 0000775 0000000 0000000 00000000000 13565763364 0013334 5 ustar 00root root 0000000 0000000 form-master/.gitignore 0000664 0000000 0000000 00000001551 13565763364 0015326 0 ustar 00root root 0000000 0000000 *.o
*.exe
*.gcno
*.gcda
*.gcov
gmon.out
.deps
Makefile
Makefile.in
aclocal.m4
autom4te.cache/
build-aux/
config.cache
config.h
config.h.in
config.log
config.status
configure
stamp-h1
sources/version.h
sources/form
sources/tform
sources/parform
sources/vorm
sources/tvorm
sources/parvorm
form-*/
form-*.tar.bz2
form-*.tar.gz
doc/devref/devref.tex
doc/doxygen/DoxyfileHTML
doc/doxygen/DoxyfileLATEX
doc/doxygen/DoxyfilePDFLATEX
doc/manual/manual.tex
doc/*/*.4ct
doc/*/*.4dx
doc/*/*.4ix
doc/*/*.4tc
doc/*/*.aux
doc/*/*.css
doc/*/*.dvi
doc/*/*.html
doc/*/*.idv
doc/*/*.idx
doc/*/*.ilg
doc/*/*.ind
doc/*/*.lg
doc/*/*.log
doc/*/*.out
doc/*/*.pdf
doc/*/*.ps
doc/*/*.tmp
doc/*/*.toc
doc/*/*.xref
doc/devref/version.tex
doc/devref/html/
doc/doxygen/html/
doc/doxygen/latex/
doc/doxygen/pdflatex/
doc/manual/html/
doc/manual/version.tex
doc/devref/devref/
doc/manual/manual/
form-master/.travis.yml 0000664 0000000 0000000 00000011632 13565763364 0015450 0 ustar 00root root 0000000 0000000 dist: trusty
sudo: false
language: cpp
git:
depth: 10000
env:
global:
MAKEFLAGS='-j 4'
addons:
apt:
packages:
- libgmp-dev
- zlib1g-dev
before_install:
- |
type gcc-7 >/dev/null 2>&1 && type g++-7 >/dev/null && {
export CC=gcc-7
export CXX=g++-7
} || :
install:
- ./scripts/travis-install.sh
script:
- ./scripts/travis-script.sh
after_success:
- ./scripts/travis-after_success.sh
after_script:
- sleep 2 # avoids the bug of travis-ci/travis-ci#6018
# NOTE:
# - Travis CI doesn't support 32-bit environments (travis-ci/travis-ci#5770).
# For testing 32-bit builds, we use a 32-bit Docker image.
# - Builds with sanitizers require recent versions of compilers.
# - openmpi + gcov on precise occasionally crashes. We measure the code
# coverage of parform with mpich.
# - The following combinations give many false positives on the valgrind check:
# - openmpi-bin + valgrind on precise, trusty,
# - mpich-3.2 + valgrind-3.12.0 on xcode7.3.
# The Valgrind-suppression file in openmpi is almost useless.
# The APT whitelist request for mpich seems to be continuously ignored,
# travis-ci/apt-package-whitelist#406, so on linux we need to build/brew it.
# - It is best to cache ./mpich or ./texlive when adequate.
matrix:
include:
- os: linux
compiler: gcc
env: CI_TARGET=form
- os: linux
compiler: gcc
env: CI_TARGET=tform
- os: linux
compiler: gcc
env: CI_TARGET=parform
addons: { apt: { packages: [ libgmp-dev, libopenmpi-dev, openmpi-bin, zlib1g-dev ] } }
sudo: required
- os: linux
compiler: gcc
env: CI_TARGET=form-i386
addons: { apt: { packages: [] } }
sudo: required
services: [ docker ]
- os: linux
compiler: gcc
env: CI_TARGET=tform-i386
addons: { apt: { packages: [] } }
sudo: required
services: [ docker ]
- os: linux
compiler: gcc
env: CI_TARGET=sanitize-vorm
addons: { apt: { sources: [ ubuntu-toolchain-r-test ], packages: [ g++-7, libgmp-dev, zlib1g-dev ] } }
sudo: required
- os: linux
compiler: gcc
env: CI_TARGET=sanitize-tvorm
addons: { apt: { sources: [ ubuntu-toolchain-r-test ], packages: [ g++-7, libgmp-dev, zlib1g-dev ] } }
sudo: required
- os: linux
compiler: gcc
env: CI_TARGET=coverage-vorm
- os: linux
compiler: gcc
env: CI_TARGET=coverage-tvorm
- os: linux
compiler: gcc
env: CI_TARGET=coverage-parvorm
cache: { directories: [ mpich ] }
- os: linux
compiler: gcc
env: CI_TARGET=valgrind-vorm TEST=examples.frm
addons: { apt: { packages: [ libgmp-dev, valgrind, zlib1g-dev ] } }
- os: linux
compiler: gcc
env: CI_TARGET=valgrind-vorm TEST=features.frm
addons: { apt: { packages: [ libgmp-dev, valgrind, zlib1g-dev ] } }
- os: linux
compiler: gcc
env: CI_TARGET=valgrind-vorm TEST=fixes.frm
addons: { apt: { packages: [ libgmp-dev, valgrind, zlib1g-dev ] } }
- os: linux
compiler: gcc
env: CI_TARGET=valgrind-tvorm TEST=examples.frm
addons: { apt: { packages: [ libgmp-dev, valgrind, zlib1g-dev ] } }
- os: linux
compiler: gcc
env: CI_TARGET=valgrind-tvorm TEST=features.frm
addons: { apt: { packages: [ libgmp-dev, valgrind, zlib1g-dev ] } }
- os: linux
compiler: gcc
env: CI_TARGET=valgrind-tvorm TEST=fixes.frm
addons: { apt: { packages: [ libgmp-dev, valgrind, zlib1g-dev ] } }
- os: linux
compiler: gcc
env: CI_TARGET=valgrind-parvorm TEST=examples.frm
addons: { apt: { packages: [ libgmp-dev, valgrind, zlib1g-dev ] } }
cache: { directories: [ mpich ] }
- os: linux
compiler: gcc
env: CI_TARGET=valgrind-parvorm TEST=features.frm
addons: { apt: { packages: [ libgmp-dev, valgrind, zlib1g-dev ] } }
cache: { directories: [ mpich ] }
- os: linux
compiler: gcc
env: CI_TARGET=valgrind-parvorm TEST=fixes.frm
addons: { apt: { packages: [ libgmp-dev, valgrind, zlib1g-dev ] } }
cache: { directories: [ mpich ] }
- os: linux
env: CI_TARGET=src-release
- os: linux
env: CI_TARGET=doc-pdf-release
addons: { apt: { packages: [] } }
cache: { directories: [ texlive ] }
- os: linux
env: CI_TARGET=doc-html-release
addons: { apt: { packages: [ ghostscript, netpbm ] } }
cache: { directories: [ texlive ] }
- os: linux
compiler: gcc
env: CI_TARGET=bin-release
- os: osx
compiler: clang
env: CI_TARGET=bin-release
# NOTE: $GITHUB_TOKEN is given as an encrypted environment variable.
deploy:
provider: releases
api_key: "$GITHUB_TOKEN"
file_glob: true
file:
- "form-*.tar.gz"
- "form-*.pdf"
skip_cleanup: true
overwrite: true
on:
tags: true
condition: "( $TRAVIS_SECURE_ENV_VARS == true ) && ( $CI_TARGET == *release )"
notifications:
email: false
form-master/AUTHORS 0000664 0000000 0000000 00000000773 13565763364 0014413 0 ustar 00root root 0000000 0000000 Over the years the following people have made contributions to the code of FORM
(in alphabetical order)
Denny Fliegner
Markus Frank
Jan Kuipers
Andrei Onyshenko
Irina Pushkina
Thomas Reiter
Albert Retey
Ben Ruijl
Misha Tentyukov
Takahiro Ueda
Jos Vermaseren
Jens Vollinga
Much support has come from Hans Staudenmaier and Hans Kuehn in Karlsruhe,
the late Eric Wassenaar and Ton Damen at Nikhef and all the directors of
Nikhef during the period that FORM was developed.
form-master/COPYING 0000664 0000000 0000000 00000104513 13565763364 0014373 0 ustar 00root root 0000000 0000000 GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc.
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. 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
them 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 prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. 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.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey 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;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If 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 convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU 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 that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
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.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
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.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
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
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
Copyright (C)
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 .
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
Copyright (C)
This program 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, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
.
The GNU 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. But first, please read
.
form-master/INSTALL 0000664 0000000 0000000 00000014630 13565763364 0014371 0 ustar 00root root 0000000 0000000 Overview
========
FORM uses the GNU autoconf tools to configure and install. In principle, the
three-step invocation
./configure
make
make install
should be enough to configure, compile, and install FORM with default settings
into the default path "/usr/bin". Nevertheless, you are strongly advised to
carefully read the following sections in order to prevent common mistakes and to
be able to choose the best configuration settings for your system.
Prerequisites
=============
In case you need to (re-)generate the script "configure" (see next section), you
have to have the GNU autoconf/automake programs installed on your system. You
should have at least autoconf version >= 2.59 and automake version >= 1.7.
For generating the configure script from a GIT repository, you also need Git.
To compile the sources you need reasonably modern C and C++ compilers, like for
example the GNU compiler collection (GCC) or the Intel C compiler. The
facilities in FORM for external communication need a POSIX compliant C library,
like the GNU glibc.
The threaded version of FORM needs a Posix compliant implementation of threads
(pthreads). The parallel version ParFORM needs an MPI implementation.
The automated test suite of FORM requires Ruby version >= 1.8 and the testing
framework "test/unit". For the latter, you may need to install test-unit gem
separately.
The manual needs a LaTeX installation with the commands "latex" and "dvips" or
"pdflatex" available. For the html format the command "htlatex" is needed.
The source code documentation needs Doxygen, at least in version 1.3.
As a default, FORM tries to use the GMP library and the zlib library for fast
numerics and compression, respectively. If any of these libraries is not
available, the corresponding feature will be deactivated. GMP should be at least
version 4.2. The zlib library should be a recent version, >= 1.2.
Preparations
============
If you have acquired the FORM sources via GIT, several files will be missing,
especially the script "configure". To generate these files you have to issue
the command
autoreconf -i
If you have downloaded and extracted the tar-file distribution, these files are
already there and the above step is not necessary. But in case you experience
problems related to the GNU autoconf files, it can be a good idea to recreate
all these files with the command "autoreconf", maybe with the option "-f" to
force a recreation.
Configuration
=============
Running
./configure
will check your system and activate the available default settings. The chosen
configuration will be printed at the end of the running. To change the default
installation path use the "--prefix" option:
./configure --prefix=
The FORM executables will then be installed into the directory "/bin".
As a default, the sequential version (form) and the threaded version (tform) of
FORM will be selected for compilation. To prevent a flavor from being build, use
one of the following options:
./configure --disable-scalar
./configure --disable-threaded
If you want to build the parallel version ParFORM, then add the option:
./configure --enable-parform
If you want to build the debugging versions of these flavors, then add the
option:
./configure --enable-debug
Use one of the following options
./configure --without-gmp
./configure --without-zlib
to prevent FORM from using one of these libraries. The executable will not be
linked against this library then and the functionality will be provided by
internal code. Usually, you don't need to care about these options.
The option
./configure --disable-largefile
forces FORM not to use large file support, i.e. to use _FILE_OFFSET_BITS==32 and
thereby restrict files to be less than 4GB in size on 32bit machines. Usually,
you don't need to care about this option.
To choose a compiler that is different from the one "configure" automatically
determines, you can set the environment variables "CC" and "CXX" on the command
line:
./configure CC=icc CXX=icpc CFLAGS=-Werror CXXFLAGS=-Werror
The above example shows also how to set additional compiler flags.
The detailed compiler/linker options for the release versions and the
debugging versions can be specified by the environment variables
COMPILEFLAGS
LINKFLAGS
DEBUGCOMPILEFLAGS
DEBUGLINKFLAGS
If they are not set, they will be chosen for the local machine where you are
compiling executables, which may contain optimization flags that makes
executables incompatible with other machines. If you plan to move the
executables to other machines and want to avoid such incompatibility, use the
following option:
./configure --disable-native
The configure script creates a file "config.h" in which several options and
settings are passed on to the source code files via preprocessor definitions.
For short-term adjustments you can alter these settings manually, but beware
that they will be overwritten the next time "configure" runs.
Finally, the option "--help" shows the available options together with a short
explanation:
./configure --help
Compilation
===========
Issue the command
make
to build all activated FORM flavors (form, tform, ...). The compilation will
result in the executables sitting in the sources directory of the distribution.
To compile only a specific flavor of FORM, name it as a parameter:
make form
make tform
make vorm
make tvorm
make parform
make parvorm
Additional flags for the compiler or linker can be given at the command line,
for example:
make vorm CFLAGS=-O1 CXXFLAGS=-O1
To cleanup the distribution directory, the command
make clean
can be used.
Testing
=======
If Ruby version >= 1.8 and "test/unit" are installed on your system, the
configure script enables the automated test suite. Then you can run it by
make check
Documentation
=============
You need to change into the directory "doc" to build the documentation. There
you can choose to run one of the commands
make dvi
make ps
make pdf
make html
to build all documentation in the specified format. If you want to build only
the manual or the source code documentation, you need to change directory into
"doc/manual" or "doc/doxygen" before you issue the make commands.
Installation
============
With the command
make install
the compiled executables will be copied into the configured path ("/usr/bin" as
the default).
Troubleshooting
===============
No troubles to be shot, yet.
form-master/Makefile.am 0000664 0000000 0000000 00000000457 13565763364 0015376 0 ustar 00root root 0000000 0000000 SUBDIRS = doc sources check
EXTRA_DIST = README.md
dist-hook:
$(DISTHOOK_VERSION)
if FIXED_VERSION
DISTHOOK_VERSION = \
cp "$(srcdir)/.version" "$(distdir)/.version"
else
DISTHOOK_VERSION = \
$(SHELL) "$(top_srcdir)/scripts/git-version-gen.sh" -C "$(srcdir)" -v -o "$(distdir)/.version"
endif
form-master/README.md 0000664 0000000 0000000 00000003546 13565763364 0014623 0 ustar 00root root 0000000 0000000 FORM
====
[](https://travis-ci.org/vermaseren/form)
[](https://coveralls.io/github/vermaseren/form?branch=master)
FORM is a Symbolic Manipulation System. It reads symbolic expressions from files
and executes symbolic/algebraic transformations upon them. The answers are
returned in a textual mathematical representation. As its landmark feature, the
size of the considered expressions in FORM is only limited by the available
disk space and not by the available RAM.
FORM's original author is Jos Vermaseren of NIKHEF, the Dutch institute for
subatomic physics. Other people that have made contributions can be found in the
file "[AUTHORS](AUTHORS)".
Build instructions
------------
Before building FORM, it is advised to install the optional dependencies `gmp`
and `zlib` for better performance. To quickly build FORM, install the `autoconf`
and `automake` packages. Then, after cloning the repository, run:
autoreconf -i
./configure
make
make install
For more advanced build options, see the file "[INSTALL](INSTALL)".
Additional Information
----------------------
Information about copying and licencing of this software can be found in the
file "[COPYING](COPYING)".
More background information a collection of FORM
programs a number of courses and an online version of the manual can be
found on the official FORM website: http://www.nikhef.nl/~form.
Bugs and remarks
----------------
Bugs can be reported via the
[Issue Tracker](https://github.com/vermaseren/form/issues) of Github.
The issue tracker can also be used for questions, remarks and suggestions.
In the past the FORM [forum](http://www.nikhef.nl/~form/forum/) was used
for this but we will discontinue the forum in the future.
form-master/check/ 0000775 0000000 0000000 00000000000 13565763364 0014411 5 ustar 00root root 0000000 0000000 form-master/check/.rubocop.yml 0000664 0000000 0000000 00000002671 13565763364 0016671 0 ustar 00root root 0000000 0000000 # AllCops:
# TargetRubyVersion: 2.1
# Stop ridiculous use of freeze.
Style/MutableConstant:
Enabled: false
# Allow several expressions on the same line.
Style/Semicolon:
AllowAsExpressionSeparator: true
# Prefer double quotes.
Style/StringLiterals:
EnforcedStyle: double_quotes
# Math is more intuitive than words.
Style/NumericPredicate:
EnforcedStyle: comparison
####
# Modifiers. Favor the traditional way.
Style/IfUnlessModifier:
Enabled: false
Style/WhileUntilModifier:
Enabled: false
####
# Negating conditions. Forcing them may leads to confusing constructs.
Style/NegatedIf:
Enabled: false
Style/NegatedWhile:
Enabled: false
Style/Next:
Enabled: false
####
# Some Ruby features that personally I never get used to.
Style/ConditionalAssignment:
Enabled: false
####
# Disable metrics checks. We are not smart enough to think in simple terms.
Metrics/AbcSize:
Enabled: false
Metrics/BlockNesting:
Enabled: false
Metrics/ClassLength:
Enabled: false
Metrics/CyclomaticComplexity:
Enabled: false
Metrics/MethodLength:
Enabled: false
Metrics/BlockLength:
Enabled: false
Metrics/ModuleLength:
Enabled: false
Metrics/LineLength:
Enabled: false
Metrics/ParameterLists:
Enabled: false
Metrics/PerceivedComplexity:
Enabled: false
####
# For now.
Style/FormatStringToken:
EnforcedStyle: unannotated
Style/PerlBackrefs:
Enabled: false
Style/SpecialGlobalVars:
EnforcedStyle: use_perl_names
form-master/check/Makefile.am 0000664 0000000 0000000 00000001147 13565763364 0016450 0 ustar 00root root 0000000 0000000 TEST_BINS =
if BUILD_FORM
TEST_BINS += $(top_builddir)/sources/form
endif
if BUILD_TFORM
TEST_BINS += $(top_builddir)/sources/tform
endif
if BUILD_PARFORM
TEST_BINS += $(top_builddir)/sources/parform
endif
TEST_OPTS =
TESTS =
if CONFIG_RUBY
TESTS_ENVIRONMENT = \
RUBY="$(RUBY)" \
TEST_BINS="$(TEST_BINS)" \
TEST_OPTS="$(TEST_OPTS)" \
$(SHELL)
TESTS += check-help.sh
else
TESTS_ENVIRONMENT = \
TEST_BINS="$(TEST_BINS)" \
$(SHELL)
endif
TESTS += benchmark-fu.sh
EXTRA_DIST = \
check-help.sh \
benchmark-fu.sh \
check.rb \
examples.frm \
features.frm \
fixes.frm \
forcer/forcer.frm \
formunit/fu.frm
form-master/check/README.md 0000664 0000000 0000000 00000010011 13565763364 0015661 0 ustar 00root root 0000000 0000000 FORM Test Suite
===============
This directory contains a collection of test cases that can be used for
verifying the behaviour of FORM. It also has a script to run the test cases and
check the results.
Prerequisites
-------------
The test runner script is written in [Ruby](https://www.ruby-lang.org/)
and requires Ruby 1.8 or later. The script uses the so-called `test/unit`
library. In some Linux distributions the library is installed together with
Ruby, while some distributions may have the library as an optional package,
or one may need to manually install
[test-unit](http://test-unit.github.io/test-unit/en/) via the `gem` command.
Currently, the script runs only on Unix-like systems.
Usage
-----
### From the build system
To use the test suite from the automatic build system
(see also the [INSTALL](../INSTALL) file),
run
```
# in the root build directory
make check
```
which tests the executables (release versions) compiled by the build system.
### Testing in the standalone mode
Alternatively, one can run the test runner script directly:
```
# in the "check" directory
./check.rb
```
By default, it tests `form` found in $PATH.
To check another executable, give the path as a command line option:
```
./check.rb /path/to/form
```
One can specify a TFORM (or ParFORM) executable in this way.
TFORM and ParFORM will be run with 4 CPUs (can be changed by the `--cpu N`
option).
By default, all test cases in all FORM files (`*.frm`) found in the `check`
directory (not in subdirectories) are used. To select test cases or FORM files
to be run, give their names as command line options, for example,
```
./check.rb examples.frm
./check.rb Issue8
```
For more advanced options, see the help message shown by the `--help` option.
Writing tests
-------------
### Where to add test cases?
Currently, the standard test set (run by default) consists of 3 files:
- `examples.frm`: Examples found in the manual.
- `features.frm`: Test cases for newly added features.
- `fixes.frm`: Test cases for bug fixes.
Each test case in these files should finish in a short time: the timeout is set
to 10 seconds. Bigger tests that take more time are put in subdirectories
(e.g., forcer) and should be specified by command-line options when the test
suite is invoked.
### Structure of a test case
A test case is given as a fold in a FORM file. A simple example is:
```
*--#[ Test1 :
S x;
L F = (1+x)^2;
P;
.end
assert succeeded?
assert result("F") =~ expr("1 + 2*x + x^2")
*--#] Test1 :
```
The fold name `Test1` gives the name of the test case, which should be unique.
The part before `.end` is a normal FORM program. After `.end`, one can write
a Ruby program to check the results. In this example, `assert` method (which is
provided by some unit test class) is used for checking whether its argument is
`true`. The first assertion checks `succeeded?`, which gives `true` if the FORM
successfully finishes. The second assertion checks the printed result of the
expression `F` by a regular expression matching (`=~`). In the left-hand side,
`result("F")` returns the (lastly) printed output for the expression `F` as
a string. In the right-hand side, `expr("...")` makes a regular expression with
removing white spaces in its argument. Since `expr()` removes all white spaces,
one can also put new lines, for example,
```
*--#[ Test2 :
S x;
L F = (1+x)^2;
P +s;
.end
assert succeeded?
assert result("F") =~ expr("
+ 1
+ 2*x
+ x^2
")
*--#] Test2 :
```
which is convenient to copy and paste a long output from a terminal.
### Tips
- To verify that FORM finishes with a certain error, one can use
`assert compile_error?` or `assert runtime_error?`.
- Two or more FORM programs, separated by `.end`, can be put in a test case.
Then the part after the last `.end` is for Ruby.
- To skip a test case for some condition, one can specify it by `#pend_if`.
(See the result of grepping `pend_if` in the existing files.)
- When a test case requires other text files, one can use `#prepare write`.
(See the result of grepping `prepare` in the existing files.)
form-master/check/benchmark-fu.sh 0000664 0000000 0000000 00000000552 13565763364 0017311 0 ustar 00root root 0000000 0000000 #!/bin/sh
# This is intended to be run from "make check".
trap 'exit 1' 1 2 13 15
status=0
for form in $TEST_BINS; do
case $form in
*tform*|*parform*|*tvorm*|*parvorm*)
;;
*)
# For now, only the sequential version.
"$form" -v | head -1
"$form" -q -D QUIET "$srcdir/formunit/fu.frm" || status=1
;;
esac
done
exit $status
form-master/check/check-help.sh 0000664 0000000 0000000 00000000304 13565763364 0016745 0 ustar 00root root 0000000 0000000 #!/bin/sh
# This is intended to be run from "make check".
trap 'exit 1' 1 2 13 15
status=0
for form in $TEST_BINS; do
"$RUBY" "$srcdir/check.rb" "$form" $TEST_OPTS || status=1
done
exit $status
form-master/check/check.rb 0000775 0000000 0000000 00000107634 13565763364 0016031 0 ustar 00root root 0000000 0000000 #!/bin/sh
# See bbatsov/rubocop#3326
# rubocop:disable all
exec ruby "-S" "-x" "$0" "$@"
#! ruby
# rubocop:enable all
# The default prefix for the root temporary directory. See TempDir.root.
TMPDIR_PREFIX = "form_check_"
# The default maximal running time in seconds of FORM jobs before they get
# terminated.
TIMEOUT = 10
# The default directory for searching test cases.
TESTDIR = File.dirname(__FILE__)
# Check the Ruby version.
if RUBY_VERSION < "1.8.0"
warn("ruby 1.8 required for the test suite")
exit(1)
end
require "fileutils"
require "open3"
require "ostruct"
require "optparse"
require "set"
require "tmpdir"
# Show an error message and exit.
def fatal(message, file = nil, lineno = nil)
if !file.nil? && !lineno.nil?
STDERR.puts("#{file}:#{lineno}: error: #{message}")
elsif !file.nil?
STDERR.puts("#{file}: error: #{message}")
else
STDERR.puts("error: #{message}")
end
exit(1)
end
# Show a warning message.
def warn(message, file = nil, lineno = nil)
if !file.nil? && !lineno.nil?
STDERR.puts("#{file}:#{lineno}: warning: #{message}")
elsif !file.nil?
STDERR.puts("#{file}: warning: #{message}")
else
STDERR.puts("warning: #{message}")
end
end
# Routines for temporary directories.
class TempDir
@root = nil
# Return the root temporary directory name.
def self.root
if @root.nil?
@root = Dir.mktmpdir(TMPDIR_PREFIX)
end
@root
end
# Create a temporary directory under the root temporary directory, and return
# the directory name.
def self.mktmpdir(prefix)
Dir.mktmpdir(prefix, root)
end
# Clean up the all temporary directory.
def self.cleanup
return if @root.nil?
# The first try.
FileUtils.rm_rf(@root)
# Wait up to 5 seconds.
50.times do
# If the directory still remains, try to remove it after 0.1 seconds.
if !FileTest.directory?(@root)
return
end
sleep(0.1)
FileUtils.rm_rf(@root)
end
# Failed.
if FileTest.directory?(@root)
warn("failed to delete the temporary directory '#{@root}'")
end
@root = nil
end
# We need to register the cleanup function before loading test/unit.
at_exit { TempDir.cleanup }
end
# Register a finalization function before loading test/unit.
at_exit { defined?(finalize) && finalize }
# We use test/unit, which is now not in the standard library.
begin
require "test/unit"
rescue LoadError
warn("test/unit required for the test suite")
exit(1)
end
# Find the path to a program.
def which(name)
result = nil
if name != File.basename(name)
# Convert the relative path to the absolute path.
result = File.expand_path(name)
else
# Search from $PATH.
ENV["PATH"].split(":").each do |path|
candidate = File.join(path, name)
if File.executable?(candidate)
result = File.expand_path(candidate)
break
end
end
end
result = name if result.nil? # Fallback.
result
end
# To be mixed-in all FORM tests.
module FormTest
# Interplay with globals.
@cfg = nil
@tests = nil
def self.cfg=(val)
@cfg = val
end
def self.cfg
@cfg
end
def self.tests=(val)
@tests = val
end
def self.tests
@tests
end
def info
FormTest.tests.classes_info[self.class.name]
end
# Accessors to the configuration.
def timeout
FormTest.cfg.timeout
end
def ncpu
FormTest.cfg.ncpu
end
def serial?
FormTest.cfg.serial?
end
def threaded?
FormTest.cfg.threaded?
end
def mpi?
FormTest.cfg.mpi?
end
def valgrind?
!FormTest.cfg.valgrind.nil?
end
def wordsize
FormTest.cfg.wordsize
end
def cygwin?
RUBY_PLATFORM =~ /cygwin/i
end
def mac?
RUBY_PLATFORM =~ /darwin/i
end
def linux?
RUBY_PLATFORM =~ /linux/i
end
def travis?
ENV["TRAVIS"] == "true"
end
# Override methods in Test::Unit::TestCase.
def setup
super
@tmpdir = nil
@filename = nil
end
def teardown
cleanup_files
super
end
# Set up the working directory and put FORM files.
def setup_files
cleanup_files
@tmpdir = TempDir.mktmpdir(self.class.name + "_")
nfiles.times do |i|
File.open(File.join(@tmpdir, "#{i + 1}.frm"), "w") do |file|
file.write(info.sources[i])
end
end
end
# Delete the working directory.
def cleanup_files
if !@tmpdir.nil?
FileUtils.rm_rf(@tmpdir)
end
@tmpdir = nil
end
# Called from derived classes' test_* methods.
def do_test
if !requires
info.status = "SKIPPED"
if defined?(omit)
omit(requires_str) do
yield
end
elsif defined?(skip)
skip(requires_str)
end
return
end
if !FormTest.cfg.full && pendings
info.status = "SKIPPED"
if defined?(pend)
pend(pendings_str) do
assert(false)
yield
end
elsif defined?(skip)
skip(requires_str)
end
return
end
setup_files
prepare
@stdout = ""
@stderr = ""
begin
nfiles.times do |i|
@filename = "#{i + 1}.frm"
execute("#{FormTest.cfg.form_cmd} #{@filename}")
if !finished?
info.status = "TIMEOUT"
assert(false, "timeout (= #{timeout} sec) in #{@filename} of #{info.desc}")
end
if return_value != 0
break
end
end
yield
# NOTE: Here we catch all exceptions, though it is a very bad style. This
# is because, in Ruby 1.9, test/unit is implemented based on
# minitest and MiniTest::Assertion is not a subclass of
# StandardError.
rescue Exception => e # rubocop:disable Lint/RescueException
STDERR.puts
STDERR.puts("=" * 79)
STDERR.puts("#{info.desc} FAILED")
STDERR.puts("=" * 79)
STDERR.puts(@stdout)
STDERR.puts("=" * 79)
STDERR.puts
if info.status.nil?
if (defined?(MiniTest::Assertion) && e.is_a?(MiniTest::Assertion)) ||
(defined?(Test::Unit::AssertionFailedError) && e.is_a?(Test::Unit::AssertionFailedError))
info.status = "FAILED"
else
info.status = "ERROR"
end
end
raise e
else
if FormTest.cfg.verbose
STDERR.puts
STDERR.puts("=" * 79)
STDERR.puts("#{info.desc} SUCCEEDED")
STDERR.puts("=" * 79)
STDERR.puts(@stdout)
STDERR.puts("=" * 79)
STDERR.puts
end
info.status = "OK"
end
end
# Execute a FORM job.
def execute(cmdline)
@finished = false
@exit_status = nil
t0 = Time.now
begin
execute_popen3(cmdline, timeout)
ensure
t1 = Time.now
dt = t1 - t0
if info.times.nil?
info.times = []
end
info.times.push(dt)
end
end
# An implementation by popen3. Should work with Ruby 1.8 on Unix.
#
# tested on:
# ruby 1.8.5 (2006-08-25) [x86_64-linux]
# ruby 1.8.7 (2013-12-22 patchlevel 375) [x86_64-linux]
# ruby 1.9.3p484 (2013-11-22 revision 43786) [x86_64-linux]
# ruby 1.9.3p545 (2014-02-24) [i386-cygwin]
# ruby 1.9.3p545 (2014-02-24) [x86_64-cygwin]
# ruby 2.0.0p247 (2013-06-27) [x86_64-linux]
# ruby 2.0.0p481 (2014-05-08 revision 45883) [x86_64-linux]
# ruby 2.1.4p265 (2014-10-27 revision 48166) [x86_64-linux]
#
# segfault at IO#gets
# ruby 1.8.6 (2010-09-02 patchlevel 420) [x86_64-linux]
# ruby 1.8.7 (2012-02-08 patchlevel 358) [x86_64-linux]
#
def execute_popen3(cmdline, timeout)
cmdline = "echo pid=$$;cd #{@tmpdir};#{cmdline};echo exit_status=$?"
stdout = []
stderr = []
Open3.popen3(cmdline) do |stdinstream, stdoutstream, stderrstream|
stdinstream.close
out = Thread.new do
while (line = stdoutstream.gets)
stdout << line
end
end
err = Thread.new do
while (line = stderrstream.gets)
stderr << line
# We print both stdout and stderr when a test fails. An easy way to
# implement this is to copy messages in stderr to those in stdout.
# Unfortunately their orders are not preserved.
stdout << line
end
end
begin
runner = Thread.current
killer = Thread.new(timeout) do |timeout1|
sleep(timeout1)
runner.raise
end
out.join
err.join
killer.kill
rescue StandardError
while out.alive? && stdout.empty?
sleep(0.01)
end
if !stdout.empty? && stdout[0] =~ /pid=([0-9]+)/
pid = $1.to_i
Process.kill("KILL", pid)
else
warn("failed to kill FORM job at timeout (unknown pid)")
end
else
@finished = true
ensure
out.kill # avoid SEGFAULT at IO#close in some old versions
err.kill
end
end
if !stdout.empty? && stdout[0] =~ /pid=([0-9]+)/
stdout.shift
end
if !FormTest.cfg.valgrind.nil?
# The exit status may be in the middle of the output (sometimes annoyingly
# happened on Travis CI).
if @finished && !stdout.empty? && !stdout[-1].start_with?("exit_status=")
i = stdout.map { |x| x.start_with?("exit_status") }.rindex(true)
if !i.nil?
s = stdout[i]
stdout.delete_at(i)
stdout << s
end
end
end
if @finished && !stdout.empty? && stdout[-1] =~ /exit_status=([0-9]+)/
@exit_status = $1.to_i
stdout.pop
end
# We exclude the Valgrind warnings "Warning: set address range perms: ..."
# from the standard output, which can happen when the program allocates
# big memory chunks.
stdout = stdout.reject { |l| l =~ /Warning: set address range perms/ }
@stdout += stdout.join
@stderr += stderr.join
end
# Default assertions.
def default_check
if return_value != 0
assert(false, "nonzero return value (= #{return_value}) from #{@filename} of #{info.desc}")
elsif warning?
assert(false, "warning in #{@filename} of #{info.desc}")
else
assert(true)
end
end
# Methods to be overridden in derived classes.
# The number of FORM files attached to the test.
def nfiles
1
end
# The required condition. The test will be skipped if the condition does not
# hold.
def requires
true
end
# The string representation for the required condition.
def requires_str
"true"
end
# The pending condition. The test will be skipped if the condition holds.
def pendings
false
end
# The string representation for the pending condition.
def pendings_str
"false"
end
# The method to be called before the test.
def prepare
# Can be overridden in child classes.
end
# Test-result functions.
# The exit status as a number
def return_value
@exit_status
end
# The verbatim result keeping line breaks and whitespaces.
# Must be in the default output format.
def exact_result(exprname, index = -1)
matches = @stdout.scan(/^[ \t]+#{Regexp.escape(exprname)}\s*=(.+?);/m)
return matches[index].first if !matches.empty? && !matches[index].nil?
""
end
# The result on one line with multiple whitespaces reduced to one.
# Must be in the default output format.
def result(exprname, index = -1)
r = exact_result(exprname, index)
return r.gsub(/\s+/, "") if !r.nil?
""
end
# The number of terms in the given expression.
# Must be in the default statistics format.
def nterms(exprname, index = -1)
matches = @stdout.scan(/^[ \t]+#{exprname}\s*Terms in output\s*=\s*(\d+)\s*Bytes used\s*=\s*\d+/m)
return matches[index].first.to_i if !matches.empty? && !matches[index].nil?
-1
end
# The size in byte.
# Must be in the default statistics format.
def bytesize(exprname, index = -1)
matches = @stdout.scan(/^[ \t]+#{exprname}\s*Terms in output\s*=\s*\d+\s*Bytes used\s*=\s*(\d+)/m)
return matches[index].first.to_i if !matches.empty? && !matches[index].nil?
-1
end
# The file contents as a string (in the working directory).
def file(filename)
begin
File.open(File.join(@tmpdir, filename), "r") do |f|
return f.read
end
rescue StandardError
STDERR.puts("warning: failed to read '#{filename}'")
end
""
end
# Same as file(filename).
def read(filename)
file filename
end
# Write to a file (in the working directory).
def write(filename, text)
fname = File.join(@tmpdir, filename)
FileUtils.mkdir_p(File.dirname(fname))
File.open(fname, "w") do |f|
f.write(text)
end
end
# The working directory for the test.
def workdir
@tmpdir
end
# The standard output of the FORM job as a string.
def stdout
@stdout
end
# The standard error of the FORM job as a string.
def stderr
@stderr
end
# Test-result functions to be used in assertions.
# true if the FORM job finished in timeout.
def finished?
@finished
end
# true if the FORM job put warning messages.
def warning?
@stdout =~ /Warning/
end
# true if the FORM job put compile time errors.
def compile_error?
@stdout =~ /#{@filename} Line \d+ -->/
end
# true if the FORM job put run time errors.
def runtime_error?
if serial?
@stdout =~ /Program terminating at #{@filename} Line \d+ -->/
elsif threaded?
@stdout =~ /Program terminating in thread \d+ at #{@filename} Line \d+ -->/
elsif mpi?
@stdout =~ /Program terminating in process \d+ at #{@filename} Line \d+ -->/
end
end
# true if the FORM job completed without any warnings/errors and
# the exit code was 0.
def succeeded?
if finished? && !warning? && !compile_error? && !runtime_error? && return_value == 0
if FormTest.cfg.valgrind.nil?
if @stderr.empty?
return true
end
@stdout += "!!! stderr is not empty"
return false
end
# Check for Valgrind errors.
ok = !@stderr.include?("Invalid read") &&
!@stderr.include?("Invalid write") &&
!@stderr.include?("Invalid free") &&
!@stderr.include?("Mismatched free") &&
!@stderr.include?("Use of uninitialised value") &&
!@stderr.include?("Conditional jump or move depends on uninitialised value") &&
!@stderr.include?("points to unaddressable byte") &&
!@stderr.include?("points to uninitialised byte") &&
!@stderr.include?("contains uninitialised byte") &&
!@stderr.include?("Source and destination overlap in memcpy") &&
!@stderr.include?("has a fishy") &&
@stderr !~ /definitely lost: [1-9]/ &&
@stderr !~ /indirectly lost: [1-9]/ &&
@stderr !~ /possibly lost: [1-9]/
if !ok
@stdout += "!!! Valgrind test failed"
end
return ok
end
false
end
# Utility functions for pattern matching.
# A pattern from the given string with escaping any special characters.
def exact_pattern(str)
san_str = Regexp.quote(str)
Regexp.new(san_str)
end
# The same as #exact_pattern but ignores whitespaces.
def pattern(str)
san_str = Regexp.quote(str.gsub(/\s+/, ""))
Regexp.new(san_str)
end
# Same as #pattern but matches only with the whole expression.
# Assumes the default output format.
def expr(str)
san_str = Regexp.quote(str.gsub(/\s+/, ""))
Regexp.new("^" + san_str + "$")
end
end
# Information of a test case.
class TestInfo
def initialize
@where = nil # where the test is defined
@foldname = nil # fold name of the test
@enabled = nil # enabled or not
@sources = [] # FORM sources
@status = nil # status
@times = nil # elapsed time (array)
end
attr_accessor :where, :foldname, :enabled, :sources, :status, :times
# Return the description of the test.
def desc
"#{@foldname} (#{@where})"
end
end
# List of test cases.
class TestCases
def initialize
@files = [] # Ruby files
@classes = [] # test class names (unsorted)
@classes_set = Set.new # set of test class names
@classes_info = {} # TestInfo objects, key: Ruby class name
@name_patterns = []
@exclude_patterns = []
end
attr_reader :classes_info
attr_accessor :name_patterns, :exclude_patterns
# Return a list containing info objects for enabled tests.
def classes_info_list
infos = []
@classes.each do |c|
info = @classes_info["Test_" + c]
if info.enabled
infos.push(info)
end
end
infos
end
# Convert a .frm file to a .rb file and load it.
def make_ruby_file(filename)
# Check existing files.
inname = File.basename(filename)
outname = File.basename(filename, ".frm") + ".rb"
if @files.include?(outname)
fatal("duplicate output file name", inname)
end
@files.push(outname)
outname = File.join(TempDir.root, outname)
File.open(filename, "r") do |infile|
File.open(outname, "w") do |outfile|
lineno = 0
level = 0
classname = nil
info = nil
block = nil
blockno = 0
fileno = 0
skipping = false
heredoc = nil
requires = nil
pendings = nil
prepares = nil
infile.each_line do |line|
line.chop!
lineno += 1
if level == 0
if line =~ /^\*..#\[\s*([^:]*)/
# fold open: start a class
fold = $1.strip
if fold.empty?
fatal("empty fold", inname, lineno)
end
classname = canonical_name(fold)
info = TestInfo.new
@classes.push(classname)
@classes_set.add(classname)
@classes_info["Test_#{classname}"] = info
info.where = "#{inname}:#{lineno}"
info.foldname = fold
info.enabled = test_enabled?(classname)
level += 1
block = ""
blockno = 0
fileno = 0
skipping = !info.enabled
heredoc = nil
requires = nil
pendings = nil
prepares = nil
if skipping
line = ""
else
line = "class Test_#{classname} < Test::Unit::TestCase; include FormTest"
end
elsif line =~ /^\*..#\]/
# unexpected fold close
fatal("unexpected fold close", inname, lineno)
else
# as commentary
line = ""
end
elsif heredoc.nil? && line =~ /^\*..#\]\s*([^:]*)/ && level == 1
# fold close: end of the class
fold = $1.strip
foldname = info.foldname
if !fold.empty? && fold != foldname
warn("unmatched fold '#{fold}', which should be '#{foldname}'", inname, lineno)
end
line = ""
if !skipping
if fileno == 0
# no .end
blockno.times do
outfile.write("\n")
end
block += ".end\n"
fileno += 1
info.sources.push(block)
line += "def test_#{classname}; do_test { default_check } end; "
else
outfile.write("def test_#{classname}; do_test {\n" + block)
line = "} end; "
end
line += "def nfiles; #{fileno} end; " if fileno != 1
if !requires.nil?
requires = requires.map { |s| "(" + s + ")" }.join(" && ")
line += "def requires; #{requires} end; "
line += "def requires_str; %(#{requires}) end; "
end
if !pendings.nil?
pendings = pendings.map { |s| "(" + s + ")" }.join(" || ")
line += "def pendings; #{pendings} end; "
line += "def pendings_str; %(#{pendings}) end; "
end
if !prepares.nil?
prepares = prepares.join("; ")
line += "def prepare; #{prepares} end; "
end
line += "end"
end
level = 0
classname = nil
info = nil
elsif heredoc.nil? && line =~ /^\s*\.end/
# .end
if skipping
line = ""
else
blockno += 1 if fileno > 0 # previous .end
blockno.times do
outfile.write("\n")
end
block += line + "\n"
fileno += 1
info.sources.push(block)
block = ""
blockno = 0
line = nil # later
end
elsif heredoc.nil? && line =~ /^\s*#\s*require\s+(.*)/
# #require
line = ""
if requires.nil?
requires = []
end
requires << $1
elsif heredoc.nil? && line =~ /^\s*#\s*pend_if\s+(.*)/
# #pend_if
line = ""
if pendings.nil?
pendings = []
end
pendings << $1
elsif heredoc.nil? && line =~ /^\s*#\s*prepare\s+(.*)/
# #prepare
line = ""
if prepares.nil?
prepares = []
end
prepares << $1
elsif heredoc.nil? && line =~ /^\*\s*#\s*(require|prepare|pend_if)\s+(.*)/
# *#require/prepare/pend_if, commented out in the FORM way
line = ""
else
if heredoc.nil?
if line =~ /^\*..#\[/
# fold open
level += 1
elsif line =~ /^\*..#\]\s*([^:]*)/
# fold close
level -= 1
elsif line =~ /< && (line =~ /<<-?(\w+)/ ||
line =~ /<<-?"(\w+)"/ ||
line =~ /<<-?'(\w+)'/ ||
line =~ /<<-?`(\w+)`/)
# start here document
heredoc = Regexp.new($1)
# NOTE: Currently, we don't support more than one << operators
# in the same line.
end
elsif line =~ heredoc
# end here document
heredoc = nil
end
if skipping
line = ""
else
# some typical assertions
if line =~ /^\s*assert\s+(succeeded\?|finished\?)\s*$/
line = "assert(#{$1}, 'Failed for #{$1}')"
end
block += line + "\n"
blockno += 1
line = nil
end
end
if !line.nil?
outfile.write(line + "\n")
end
end
if level >= 1
fatal("expected fold close", inname, lineno)
end
end
end
require outname
end
# true if the test is enabled
def test_enabled?(name)
# construct regular expressions (wildcards: '*' and '?')
@name_patterns.length.times do |i|
if !@name_patterns[i].is_a?(Regexp)
s = @name_patterns[i].to_s.gsub("\*", ".*").tr("\?", ".")
s = "^" + s + "$"
@name_patterns[i] = Regexp.new(s)
end
end
@exclude_patterns.length.times do |i|
if !@exclude_patterns[i].is_a?(Regexp)
s = @exclude_patterns[i].to_s.gsub("\*", ".*").tr("\?", ".")
s = "^" + s + "$"
@exclude_patterns[i] = Regexp.new(s)
end
end
# check --name NAME
ok = true
if !@name_patterns.empty?
ok = false
@name_patterns.each do |pat|
if name =~ pat
ok = true
break
end
end
end
if !ok
return false
end
# check --execlude NAME
if !@exclude_patterns.empty?
@exclude_patterns.each do |pat|
if name =~ pat
ok = false
break
end
end
end
ok
end
# Return a class name that is valid and unique.
def canonical_name(name)
prefix = name.gsub(/[^a-zA-Z0-9_]/, "_")
s = prefix
i = 0
loop do
if !@classes.include?(s)
break
end
i += 1
s = prefix + "_" + i.to_s
end
s
end
end
# FORM configuration.
class FormConfig
def initialize(form, mpirun, valgrind, ncpu, timeout, stat, full, verbose)
@form = form
@mpirun = mpirun
@valgrind = valgrind
@ncpu = ncpu
@timeout = timeout
@stat = stat
@full = full
@verbose = verbose
@form_bin = nil
@mpirun_bin = nil
@valgrind_bin = nil
@valgrind_supp = nil
@head = nil
@is_serial = nil
@is_threaded = nil
@is_mpi = nil
@wordsize = nil
@form_cmd = nil
end
attr_reader :form, :mpirun, :valgrind, :ncpu, :timeout, :stat, :full, :verbose
attr_reader :form_bin, :mpirun_bin, :valgrind_bin, :valgrind_supp
attr_reader :head, :wordsize, :form_cmd
def serial?
@is_serial
end
def threaded?
@is_threaded
end
def mpi?
@is_mpi
end
def check_bin(name, bin)
# Check if the executable is available.
system("cd #{TempDir.root}; type #{bin} >/dev/null 2>&1")
if $? == 0
# OK.
return
end
if name == bin
fatal("executable '#{name}' not found")
else
fatal("executable '#{name}' ('#{bin}') not found")
end
end
def check
# Check if FORM is available.
@form_bin = which(@form)
check_bin(@form, @form_bin)
# Check if Valgrind is available.
if !@valgrind.nil?
@valgrind_bin = which(@valgrind)
check_bin(@valgrind, @valgrind_bin)
end
# Check the FORM version.
tmpdir = TempDir.mktmpdir("ver_")
begin
frmname = File.join(tmpdir, "ver.frm")
File.open(frmname, "w") do |f|
f.write(<<-'TEST_FRM')
#-
Off finalstats;
.end
TEST_FRM
end
@head = `#{@form_bin} #{frmname} 2>/dev/null`.split("\n").first
@is_serial = false
if @head =~ /^FORM/
@is_serial = true
@is_threaded = false
@is_mpi = false
elsif @head =~ /^TFORM/
@is_serial = false
@is_threaded = true
@is_mpi = false
elsif @head =~ /^ParFORM/
@is_serial = false
@is_threaded = false
@is_mpi = true
else
system("#{form_bin} #{frmname}")
fatal("failed to get the version of '#{@form}'")
end
if @head =~ /FORM[^(]*\([^)]*\)\s*(\d+)-bits/
@wordsize = $1.to_i / 16
else
system("#{form_bin} #{frmname}")
fatal("failed to get the wordsize of '#{@form}'")
end
# Prepare for mpirun
if @is_mpi
@mpirun_bin = which(@mpirun)
check_bin(@mpirun, @mpirun_bin)
# Open MPI is known to be not Valgrind-clean. Try to suppress some
# errors. Unfortunately, it would be insufficient.
supp = File.expand_path(File.join(File.dirname(@mpirun_bin),
"..", "share", "openmpi",
"openmpi-valgrind.supp"))
if File.exist?(supp)
@valgrind_supp = supp
end
end
# Construct the command.
cmdlist = []
if @is_mpi
cmdlist << @mpirun_bin << "-np" << @ncpu.to_s
end
if !@valgrind_bin.nil?
cmdlist << @valgrind_bin
cmdlist << "--leak-check=full"
if !@valgrind_supp.nil?
cmdlist << "--suppressions=#{@valgrind_supp}"
end
end
cmdlist << @form_bin
if @is_threaded
cmdlist << "-w#{@ncpu}"
end
@form_cmd = cmdlist.join(" ")
# Check the output header.
@head = `#{@form_cmd} #{frmname} 2>/dev/null`.split("\n").first
if $? != 0
system("#{form_cmd} #{frmname}")
fatal("failed to execute '#{@form_cmd}'")
end
if !@valgrind.nil?
# Include valgrind version information.
@head += "\n" + `#{@form_cmd} @{frmname} 2>&1 >/dev/null | grep Valgrind`.split("\n")[0]
end
ensure
FileUtils.rm_rf(tmpdir)
end
end
end
# Return paths obtained by `oldpath` + `newpath`.
def add_path(oldpath, newpath)
newpath = File.expand_path(newpath)
if oldpath.nil?
return newpath
end
newpath + ":" + oldpath
end
# Parse `TEST=...`.
def parse_def(pat)
if pat =~ /^TEST=(.*)/
return $1
end
nil
end
# Search for the `file`.
def search_file(file, opts)
f = file
return f if File.exist?(f)
if !opts.dir.nil?
f = File.join(opts.dir, file)
return f if File.exist?(f)
end
if !TESTDIR.nil?
f = File.join(TESTDIR, file)
return f if File.exist?(f)
end
fatal("file '#{file}' not found")
end
# Search for the `dir`.
def search_dir(dir, opts)
d = dir
return d if File.directory?(d)
if !opts.dir.nil?
d = File.join(opts.dir, dir)
return d if File.directory?(d)
end
if !TESTDIR.nil?
d = File.join(TESTDIR, dir)
return d if File.directory?(d)
end
fatal("directory '#{dir}' not found")
end
def main
# Parse options.
opts = OpenStruct.new
opts.list = false
opts.path = nil
opts.form = "form"
opts.mpirun = "mpirun"
opts.ncpu = 4
opts.timeout = nil
opts.stat = false
opts.full = false
opts.enable_valgrind = false
opts.valgrind = "valgrind"
opts.dir = nil
opts.name_patterns = []
opts.exclude_patterns = []
opts.files = []
opts.verbose = false
parser = OptionParser.new
parser.banner = "Usage: #{File.basename($0)} [options] [--] [binname] [files|tests..]"
parser.on("-h", "--help", "Show this help and exit") { puts(parser); exit }
parser.on("-l", "--list", "List all tests and exit") { opts.list = true }
parser.on("--path PATH", "Use PATH for executables") { |path| opts.path = add_path(opts.path, path) }
parser.on("--form BIN", "Use BIN as FORM executable") { |bin| opts.form = bin }
parser.on("--mpirun BIN", "Use BIN as mpirun executable") { |bin| opts.mpirun = bin }
parser.on("-w", "--ncpu N", "Use N CPUs") { |n| opts.ncpu = n.to_i }
parser.on("-t", "--timeout N", "Timeout N in seconds") { |n| opts.timeout = n.to_i }
parser.on("--stat", "Print detailed statistics") { opts.stat = true }
parser.on("--full", "Full test, ignoring pending") { opts.full = true }
parser.on("--enable-valgrind", "Enable Valgrind") { opts.enable_valgrind = true }
parser.on("--valgrind BIN", "Use BIN as Valgrind executable") { |bin| opts.enable_valgrind = true; opts.valgrind = bin }
parser.on("-C", "--directory DIR", "Directory for test cases") { |dir| opts.dir = search_dir(dir, opts) }
parser.on("-n", "--name NAME", "Run tests matching NAME") { |pat| opts.name_patterns << pat }
parser.on("-x", "--exclude NAME", "Do not run tests matching NAME") { |pat| opts.exclude_patterns << pat }
parser.on("-v", "--verbose", "Do not suppress the test output") { opts.verbose = true }
parser.on("-D TEST=NAME", "Alternative way to run tests NAME") { |pat| opts.name_patterns << parse_def(pat) }
begin
parser.parse!(ARGV)
rescue OptionParser::ParseError => e
STDERR.puts(e.backtrace.first + ": #{e.message} (#{e.class})")
e.backtrace[1..-1].each { |m| STDERR.puts("\tfrom #{m}") }
puts(parser)
exit(1)
end
# Parse other arguments.
while !ARGV.empty?
if ARGV[0] =~ /\.frm$/
opts.files << search_file(ARGV[0], opts)
elsif ARGV[0] =~ /valgrind/
opts.enable_valgrind = true
opts.valgrind = ARGV[0]
elsif ARGV[0] =~ /mpirun/ || ARGV[0] =~ /mpiexec/
opts.mpirun = ARGV[0]
elsif ARGV[0] =~ /form/ || ARGV[0] =~ /vorm/ || File.executable?(ARGV[0])
opts.form = ARGV[0]
elsif File.exist?(ARGV[0])
opts.files << ARGV[0]
else
opts.name_patterns << ARGV[0]
end
ARGV.shift
end
# Make test cases.
FormTest.tests = TestCases.new
FormTest.tests.name_patterns = opts.name_patterns
FormTest.tests.exclude_patterns = opts.exclude_patterns
if opts.files.empty?
Dir.glob(File.join(opts.dir.nil? ? TESTDIR : opts.dir, "*.frm")).sort.each do |file|
opts.files << search_file(file, opts)
end
end
opts.files.uniq.sort.each do |file|
FormTest.tests.make_ruby_file(file)
end
# --list option.
if opts.list
infos = FormTest.tests.classes_info_list
infos.each do |info|
puts("#{info.foldname} (#{info.where})")
end
puts("#{infos.length} tests")
exit
end
# --path option.
if !opts.path.nil?
ENV["PATH"] = opts.path + ":" + ENV["PATH"]
end
# Set FORMPATH
ENV["FORMPATH"] = File.expand_path(opts.dir.nil? ? TESTDIR : opts.dir) +
(ENV["FORMPATH"].nil? ? "" : ":" + ENV["FORMPATH"])
# Default timeout.
if opts.timeout.nil?
opts.timeout = TIMEOUT
# Running Valgrind can be really slow.
if opts.enable_valgrind
opts.timeout *= 30
end
end
# Initialize the FORM configuration.
FormTest.cfg = FormConfig.new(opts.form,
opts.mpirun,
opts.enable_valgrind ? opts.valgrind : nil,
opts.ncpu,
opts.timeout > 1 ? opts.timeout : 1,
opts.stat,
opts.full,
opts.verbose)
FormTest.cfg.check
puts("Check #{FormTest.cfg.form_bin}")
puts(FormTest.cfg.head)
end
def finalize
return if FormTest.cfg.nil? || !FormTest.cfg.stat
infos = FormTest.tests.classes_info_list
return if infos.empty?
# Print detailed statistics.
term_width = guess_term_width
max_foldname_width = infos.map { |info| info.foldname.length }.max
max_where_width = infos.map { |info| info.where.length }.max + 2
status_width = 7
time_width = 13
bar_width = term_width - max_foldname_width - max_where_width - status_width -
time_width - 5
if bar_width < 12
bar_width = 12
elsif bar_width > 40
bar_width = 40
end
puts("timeout: #{FormTest.cfg.timeout}s")
infos.each do |info|
(0..info.sources.length - 1).each do |i|
t = 0
if !info.times.nil? && i < info.times.length
t = info.times[i]
end
if i == 0
puts(format("%s %s %s %s%s",
lpad(info.foldname, max_foldname_width),
lpad("(" + info.where + ")", max_where_width),
lpad(info.status.nil? ? "UNKNOWN" : info.status, status_width),
bar_str(t, FormTest.cfg.timeout, bar_width),
format_time(t, FormTest.cfg.timeout)))
else
puts(format("%s %s %s %s%s",
lpad("", max_foldname_width),
lpad("", max_where_width),
lpad("", status_width),
bar_str(t, FormTest.cfg.timeout, bar_width),
format_time(t, FormTest.cfg.timeout)))
end
end
end
end
# Return the string with padding to left.
def lpad(str, len)
if str.length > len
str[0..len - 1]
elsif str.length < len
str + " " * (len - str.length)
else
str
end
end
# Return a string for a bar chart.
def bar_str(value, max_value, bar_width)
bar_body_width = bar_width - 2
bar = " " * bar_width
bar[0] = "|"
bar[bar_width - 1] = "|"
pos = (Float(value) / max_value * bar_body_width).round
if pos < 0
pos = 0
elsif pos > bar_body_width
pos = bar_body_width
end
if pos >= 1
(1..pos).each do |i|
bar[i] = "#"
end
end
bar
end
# Format an elapsed time.
def format_time(time, max_time)
overflow = time > max_time
if overflow
t = max_time
else
t = time
end
t = Float(t)
h = Integer(t / 3600)
t = t % 3600
m = Integer(t / 60)
t = t % 60
s = Integer(t)
t = t % 1
ms = Integer(t * 1000)
format("%s%02d:%02d:%02d.%03d", overflow ? ">" : " ", h, m, s, ms)
end
# Return a guessed terminal width.
def guess_term_width
require "io/console"
IO.console.winsize[1]
rescue LoadError, NoMethodError
system("type tput >/dev/null 2>&1")
if $? == 0
cols = `tput cols`
else
cols = ENV["COLUMNS"] || ENV["TERM_WIDTH"]
end
begin
Integer(cols)
rescue ArgumentError, TypeError
80
end
end
if $0 == __FILE__
main
end
form-master/check/examples.frm 0000664 0000000 0000000 00000155241 13565763364 0016745 0 ustar 00root root 0000000 0000000 * Tests using the examples in the manual
*
* Some assertions here check for trivial, secondary things like runtime
* information or layout. Usually, this should be avoided. But since we are here
* not only testing FORM but also the code examples in the manual, this extra
* strictness makes sometimes sense.
* In the manual the example code has been given a comment that says it is used
* here. Therefore, if you change something here, consider applying the
* appropriate changes also in the manual.
#ifndef `TEST'
#message Use -D TEST=XXX
#terminate
#else
#include `NAME_' # `TEST'
#endif
.end
*--#[ Var_Symbols_1 :
s x(:10),y;
L F=y^7;
id y=x+x^2;
print;
.end
assert succeeded?
assert bytesize("F") == 27 * wordsize
assert result("F") =~ expr("x^7 + 7*x^8 + 21*x^9 + 35*x^10")
*--#] Var_Symbols_1 :
*--#[ Var_Sets_1 :
Symbols a1,a2,a3,b1,b2,b3,x,n;
CFunctions g1,g2,g3,g;
Local expr =
g(a1)+g(a2)+g(a3)+g(x);
id,g(x?{a1,a2,a3}[n]) = {g1,g2,g3}[n]({b1,b2,b3}[n]);
print;
.end
assert succeeded?
assert result("expr") =~ expr("g1(b1) + g2(b2) + g3(b3) + g(x)")
*--#] Var_Sets_1 :
*--#[ Var_Dummy_indices_1 :
i mu,nu;
f f1,f2;
L F=f1(mu)*f2(mu)+f1(nu)*f2(nu);
sum mu;
sum nu;
print;
.end
assert succeeded?
assert result("F") =~ expr("2*f1(N1_?)*f2(N1_?)")
*--#] Var_Dummy_indices_1 :
*--#[ Var_Dummy_indices_2 :
Index mu,nu;
CFunctions f,g;
Vectors p,q;
Local F = (f(mu)*g(mu))^2;
sum mu;
id f(nu?) = p(nu);
id g(nu?) = q(nu);
print;
.end
assert succeeded?
assert result("F") =~ expr("p.p*q.q")
*--#] Var_Dummy_indices_2 :
*--#[ Var_Dummy_indices_3 :
Index mu,nu;
Symbol x;
CFunctions f,g;
Vectors p,q;
Local F = x^2;
repeat;
id,once,x = f(mu)*g(mu);
sum mu;
endrepeat;
id f(nu?) = p(nu);
id g(nu?) = q(nu);
print;
.end
assert succeeded?
assert result("F") =~ expr("p.q^2")
*--#] Var_Dummy_indices_3 :
*--#[ Var_Dummy_indices_4 :
Indices mu,nu;
CFunctions f;
L F = f(mu,nu)*f(nu,mu);
sum mu, nu;
Print;
.sort
Indices rho,si;
Vectors p1,p2,p3,v;
Tensor g;
Local G = e_(mu,nu,rho,si)*g(mu,nu,p1,v)*g(rho,si,p2,v);
sum mu,nu,rho,si;
Multiply F^3;
id v = e_(p1,p2,p3,?);
print;
.end
assert succeeded?
assert result("G") =~ expr("f(N1_?,N2_?)*f(N2_?,N1_?)*f(N3_?,N4_?)*f(N4_?,N3_?)*f(N5_?,N6_?)*f(N6_?,N5_?)
*g(N7_?,N8_?,p1,N9_?)*g(N10_?,N11_?,p2,N12_?)*e_(p1,p2,p3,N9_?)*e_(p1,p2,p3,N12_?)*e_(N7_?,N8_?,N10_?,N11_?)")
*--#] Var_Dummy_indices_4 :
*--#[ Var_Extra_Symbols_1 :
* NOTE: removed "Generated on `DATE_'"
Vector p,q,p1,p2;
CFunction f;
CFunction Dot,InvDot;
Symbol x,x1,x2;
Set pdot:p,q;
Off Statistics;
Local F = x+x^2+1/x+1/x^2+f(x1)+f(x2)*p.q*x+f(x2)/p.q^2;
id p1?pdot.p2?pdot = Dot(p1,p2);
id 1/p1?pdot.p2?pdot = InvDot(p1,p2);
Print;
.sort
ExtraSymbols,array,Y;
Format DOUBLEFORTRAN;
ToPolynomial;
Print;
.sort
#write " SUBROUTINE sub(Y)"
#write "*"
#write "* Compute the extra symbols."
#write "*"
#write " REAL*8 Y(`EXTRASYMBOLS_')"
#write " REAL*8 Dot,InvDot"
#write " Dot(p1,p2)=p1(1)*p2(1)-p1(2)*p2(2)-p1(3)*p2(3)\
-p1(4)*p2(4)"
#write " InvDot(p1,p2)=1.D0/(Dot(p1,p2))"
#write "*"
#write "* We still have to add definitions here."
#write "* And we have to import all the variables."
#write "*"
#write "%X"
#write "*"
#write " RETURN"
#write " END"
ExtraSymbols,underscore,Z;
Format Normal;
Format 80;
Print;
.sort
FromPolynomial;
Print;
.end
assert succeeded?
if !threaded?
# In TFORM, the output can differ.
assert stdout =~ exact_pattern(<<'EOF')
F =
x^-2 + x^-1 + x + x^2 + f(x1) + f(x2)*Dot(p,q)*x + f(x2)*InvDot(p,q)^2;
ExtraSymbols,array,Y;
Format DOUBLEFORTRAN;
ToPolynomial;
Print;
.sort
F =
& Y(1) + Y(1)**2 + Y(2) + Y(5)**2*Y(3) + x + x*Y(4)*Y(3) + x**2
#write " SUBROUTINE sub(Y)"
#write "*"
#write "* Compute the extra symbols."
#write "*"
#write " REAL*8 Y(`EXTRASYMBOLS_')"
#write " REAL*8 Dot,InvDot"
#write " Dot(p1,p2)=p1(1)*p2(1)-p1(2)*p2(2)-p1(3)*p2(3)\
-p1(4)*p2(4)"
#write " InvDot(p1,p2)=1.D0/(Dot(p1,p2))"
#write "*"
#write "* We still have to add definitions here."
#write "* And we have to import all the variables."
#write "*"
#write "%X"
#write "*"
#write " RETURN"
#write " END"
ExtraSymbols,underscore,Z;
Format Normal;
Format 80;
Print;
.sort
F =
Z1_ + Z1_^2 + Z2_ + Z5_^2*Z3_ + x + x*Z4_*Z3_ + x^2;
FromPolynomial;
Print;
.end
F =
x^-2 + x^-1 + x + x^2 + f(x1) + f(x2)*Dot(p,q)*x + f(x2)*InvDot(p,q)^2;
EOF
assert file("sub.f") == <<-'EOF'
SUBROUTINE sub(Y)
*
* Compute the extra symbols.
*
REAL*8 Y(5)
REAL*8 Dot,InvDot
Dot(p1,p2)=p1(1)*p2(1)-p1(2)*p2(2)-p1(3)*p2(3)-p1(4)*p2(4)
InvDot(p1,p2)=1.D0/(Dot(p1,p2))
*
* We still have to add definitions here.
* And we have to import all the variables.
*
Y(1)=x**(-1)
Y(2)=f(x1)
Y(3)=f(x2)
Y(4)=Dot(p,q)
Y(5)=InvDot(p,q)
*
RETURN
END
EOF
end
*--#] Var_Extra_Symbols_1 :
*--#[ Pre_call_1 :
#define a "1"
#define bc2 "x"
#define bc3 "y"
#define b "c`~a'"
#procedure hop(c,?d);
#redefine a "3"
#message This is the call: `c',`?d'
#endprocedure
#redefine a "2"
#message This is b: `b'
#call hop(`b`!b''`!b'`b'`!b'`b',`~a',`b',`a')
.end
assert succeeded?
assert stdout =~ /#message This is b: `b'\n~~~This is b: c2\n/
assert stdout =~ /#call hop\(`b`!b''`!b'`b'`!b'`b',`~a',`b',`a'\)\n~~~This is the call: xc2c3c2c3,3,c3,2\n/
*--#] Pre_call_1 :
*--#[ Pre_define_1 :
#define c "3"
#define var1(a,b) "(`~a'+`~b'+`c')"
#define var2(a,b) "(`~a'+`~b'+`~c')"
#redefine c "4"
Local F1 = `var1(1,2)';
Local F2 = `var2(1,2)';
Print;
.end
assert succeeded?
assert result("F1") =~ expr("6")
assert result("F2") =~ expr("7")
*--#] Pre_define_1 :
*--#[ Pre_preout_1 :
#PreOut ON
S a1,...,a4;
L F = (a1+...+a4)^2;
id a4 = -a1;
.end
assert succeeded?
assert stdout =~ exact_pattern(<<-EOF
#PreOut ON
S a1,...,a4;
S,a1,a2,a3,a4
L F = (a1+...+a4)^2;
L,F=(a1+a2+a3+a4)^2
id a4 = -a1;
id,a4=-a1
.end
EOF
)
*--#] Pre_preout_1 :
*--#[ Pre_write_1 :
Symbols a,b;
L F = a+b;
#$a1 = a+b;
#$a2 = (a+b)^2;
#$a3 = $a1^3;
#write " One power: %$\\n Two powers: %$\\n Three powers: %$\n%s"\
,$a1,$a2,$a3," The end"
.end
assert succeeded?
assert stdout =~ exact_pattern(<<-EOF
One power: b+a
Two powers: b^2+2*a*b+a^2
Three powers: b^3+3*a*b^2+3*a^2*b+a^3
The end
.end
EOF
)
*--#] Pre_write_1 :
*--#[ Pre_write_2 :
* TODO: change the result in the manual.
S x1,...,x10;
L MyExpression = (x1+...+x10)^4;
.sort
Format Fortran;
#write " FUNCTION fun(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10)"
#write " REAL x1,x2,x3,x4,x5,x6,x7,x8,x9,x10"
#write " fun = %e",MyExpression(fun)
#write " RETURN"
#write " END"
.end
assert succeeded?
assert file("fun.f") == <<-EOF
FUNCTION fun(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10)
REAL x1,x2,x3,x4,x5,x6,x7,x8,x9,x10
fun = x10**4 + 4*x9*x10**3 + 6*x9**2*x10**2 + 4*x9**3*x10 + x9**4
& + 4*x8*x10**3 + 12*x8*x9*x10**2 + 12*x8*x9**2*x10 + 4*x8*x9**3
& + 6*x8**2*x10**2 + 12*x8**2*x9*x10 + 6*x8**2*x9**2 + 4*x8**3*
& x10 + 4*x8**3*x9 + x8**4 + 4*x7*x10**3 + 12*x7*x9*x10**2 + 12*x7
& *x9**2*x10 + 4*x7*x9**3 + 12*x7*x8*x10**2 + 24*x7*x8*x9*x10 + 12
& *x7*x8*x9**2 + 12*x7*x8**2*x10 + 12*x7*x8**2*x9 + 4*x7*x8**3 + 6
& *x7**2*x10**2 + 12*x7**2*x9*x10 + 6*x7**2*x9**2 + 12*x7**2*x8*
& x10 + 12*x7**2*x8*x9 + 6*x7**2*x8**2 + 4*x7**3*x10 + 4*x7**3*x9
& + 4*x7**3*x8 + x7**4 + 4*x6*x10**3 + 12*x6*x9*x10**2 + 12*x6*
& x9**2*x10 + 4*x6*x9**3 + 12*x6*x8*x10**2 + 24*x6*x8*x9*x10 + 12*
& x6*x8*x9**2 + 12*x6*x8**2*x10 + 12*x6*x8**2*x9 + 4*x6*x8**3 + 12
& *x6*x7*x10**2 + 24*x6*x7*x9*x10 + 12*x6*x7*x9**2 + 24*x6*x7*x8*
& x10 + 24*x6*x7*x8*x9 + 12*x6*x7*x8**2 + 12*x6*x7**2*x10 + 12*x6*
& x7**2*x9 + 12*x6*x7**2*x8 + 4*x6*x7**3 + 6*x6**2*x10**2 + 12*
& x6**2*x9*x10 + 6*x6**2*x9**2 + 12*x6**2*x8*x10 + 12*x6**2*x8*x9
& + 6*x6**2*x8**2
fun = fun + 12*x6**2*x7*x10 + 12*x6**2*x7*x9 + 12*x6**2*x7*x8 + 6
& *x6**2*x7**2 + 4*x6**3*x10 + 4*x6**3*x9 + 4*x6**3*x8 + 4*x6**3*
& x7 + x6**4 + 4*x5*x10**3 + 12*x5*x9*x10**2 + 12*x5*x9**2*x10 + 4
& *x5*x9**3 + 12*x5*x8*x10**2 + 24*x5*x8*x9*x10 + 12*x5*x8*x9**2
& + 12*x5*x8**2*x10 + 12*x5*x8**2*x9 + 4*x5*x8**3 + 12*x5*x7*
& x10**2 + 24*x5*x7*x9*x10 + 12*x5*x7*x9**2 + 24*x5*x7*x8*x10 + 24
& *x5*x7*x8*x9 + 12*x5*x7*x8**2 + 12*x5*x7**2*x10 + 12*x5*x7**2*x9
& + 12*x5*x7**2*x8 + 4*x5*x7**3 + 12*x5*x6*x10**2 + 24*x5*x6*x9*
& x10 + 12*x5*x6*x9**2 + 24*x5*x6*x8*x10 + 24*x5*x6*x8*x9 + 12*x5*
& x6*x8**2 + 24*x5*x6*x7*x10 + 24*x5*x6*x7*x9 + 24*x5*x6*x7*x8 +
& 12*x5*x6*x7**2 + 12*x5*x6**2*x10 + 12*x5*x6**2*x9 + 12*x5*x6**2*
& x8 + 12*x5*x6**2*x7 + 4*x5*x6**3 + 6*x5**2*x10**2 + 12*x5**2*x9*
& x10 + 6*x5**2*x9**2 + 12*x5**2*x8*x10 + 12*x5**2*x8*x9 + 6*x5**2
& *x8**2 + 12*x5**2*x7*x10 + 12*x5**2*x7*x9 + 12*x5**2*x7*x8 + 6*
& x5**2*x7**2 + 12*x5**2*x6*x10 + 12*x5**2*x6*x9 + 12*x5**2*x6*x8
& + 12*x5**2*x6*x7
fun = fun + 6*x5**2*x6**2 + 4*x5**3*x10 + 4*x5**3*x9 + 4*x5**3*x8
& + 4*x5**3*x7 + 4*x5**3*x6 + x5**4 + 4*x4*x10**3 + 12*x4*x9*
& x10**2 + 12*x4*x9**2*x10 + 4*x4*x9**3 + 12*x4*x8*x10**2 + 24*x4*
& x8*x9*x10 + 12*x4*x8*x9**2 + 12*x4*x8**2*x10 + 12*x4*x8**2*x9 +
& 4*x4*x8**3 + 12*x4*x7*x10**2 + 24*x4*x7*x9*x10 + 12*x4*x7*x9**2
& + 24*x4*x7*x8*x10 + 24*x4*x7*x8*x9 + 12*x4*x7*x8**2 + 12*x4*
& x7**2*x10 + 12*x4*x7**2*x9 + 12*x4*x7**2*x8 + 4*x4*x7**3 + 12*x4
& *x6*x10**2 + 24*x4*x6*x9*x10 + 12*x4*x6*x9**2 + 24*x4*x6*x8*x10
& + 24*x4*x6*x8*x9 + 12*x4*x6*x8**2 + 24*x4*x6*x7*x10 + 24*x4*x6*
& x7*x9 + 24*x4*x6*x7*x8 + 12*x4*x6*x7**2 + 12*x4*x6**2*x10 + 12*
& x4*x6**2*x9 + 12*x4*x6**2*x8 + 12*x4*x6**2*x7 + 4*x4*x6**3 + 12*
& x4*x5*x10**2 + 24*x4*x5*x9*x10 + 12*x4*x5*x9**2 + 24*x4*x5*x8*
& x10 + 24*x4*x5*x8*x9 + 12*x4*x5*x8**2 + 24*x4*x5*x7*x10 + 24*x4*
& x5*x7*x9 + 24*x4*x5*x7*x8 + 12*x4*x5*x7**2 + 24*x4*x5*x6*x10 +
& 24*x4*x5*x6*x9 + 24*x4*x5*x6*x8 + 24*x4*x5*x6*x7 + 12*x4*x5*
& x6**2
fun = fun + 12*x4*x5**2*x10 + 12*x4*x5**2*x9 + 12*x4*x5**2*x8 +
& 12*x4*x5**2*x7 + 12*x4*x5**2*x6 + 4*x4*x5**3 + 6*x4**2*x10**2 +
& 12*x4**2*x9*x10 + 6*x4**2*x9**2 + 12*x4**2*x8*x10 + 12*x4**2*x8*
& x9 + 6*x4**2*x8**2 + 12*x4**2*x7*x10 + 12*x4**2*x7*x9 + 12*x4**2
& *x7*x8 + 6*x4**2*x7**2 + 12*x4**2*x6*x10 + 12*x4**2*x6*x9 + 12*
& x4**2*x6*x8 + 12*x4**2*x6*x7 + 6*x4**2*x6**2 + 12*x4**2*x5*x10
& + 12*x4**2*x5*x9 + 12*x4**2*x5*x8 + 12*x4**2*x5*x7 + 12*x4**2*
& x5*x6 + 6*x4**2*x5**2 + 4*x4**3*x10 + 4*x4**3*x9 + 4*x4**3*x8 +
& 4*x4**3*x7 + 4*x4**3*x6 + 4*x4**3*x5 + x4**4 + 4*x3*x10**3 + 12*
& x3*x9*x10**2 + 12*x3*x9**2*x10 + 4*x3*x9**3 + 12*x3*x8*x10**2 +
& 24*x3*x8*x9*x10 + 12*x3*x8*x9**2 + 12*x3*x8**2*x10 + 12*x3*x8**2
& *x9 + 4*x3*x8**3 + 12*x3*x7*x10**2 + 24*x3*x7*x9*x10 + 12*x3*x7*
& x9**2 + 24*x3*x7*x8*x10 + 24*x3*x7*x8*x9 + 12*x3*x7*x8**2 + 12*
& x3*x7**2*x10 + 12*x3*x7**2*x9 + 12*x3*x7**2*x8 + 4*x3*x7**3 + 12
& *x3*x6*x10**2 + 24*x3*x6*x9*x10 + 12*x3*x6*x9**2 + 24*x3*x6*x8*
& x10
fun = fun + 24*x3*x6*x8*x9 + 12*x3*x6*x8**2 + 24*x3*x6*x7*x10 +
& 24*x3*x6*x7*x9 + 24*x3*x6*x7*x8 + 12*x3*x6*x7**2 + 12*x3*x6**2*
& x10 + 12*x3*x6**2*x9 + 12*x3*x6**2*x8 + 12*x3*x6**2*x7 + 4*x3*
& x6**3 + 12*x3*x5*x10**2 + 24*x3*x5*x9*x10 + 12*x3*x5*x9**2 + 24*
& x3*x5*x8*x10 + 24*x3*x5*x8*x9 + 12*x3*x5*x8**2 + 24*x3*x5*x7*x10
& + 24*x3*x5*x7*x9 + 24*x3*x5*x7*x8 + 12*x3*x5*x7**2 + 24*x3*x5*
& x6*x10 + 24*x3*x5*x6*x9 + 24*x3*x5*x6*x8 + 24*x3*x5*x6*x7 + 12*
& x3*x5*x6**2 + 12*x3*x5**2*x10 + 12*x3*x5**2*x9 + 12*x3*x5**2*x8
& + 12*x3*x5**2*x7 + 12*x3*x5**2*x6 + 4*x3*x5**3 + 12*x3*x4*
& x10**2 + 24*x3*x4*x9*x10 + 12*x3*x4*x9**2 + 24*x3*x4*x8*x10 + 24
& *x3*x4*x8*x9 + 12*x3*x4*x8**2 + 24*x3*x4*x7*x10 + 24*x3*x4*x7*x9
& + 24*x3*x4*x7*x8 + 12*x3*x4*x7**2 + 24*x3*x4*x6*x10 + 24*x3*x4*
& x6*x9 + 24*x3*x4*x6*x8 + 24*x3*x4*x6*x7 + 12*x3*x4*x6**2 + 24*x3
& *x4*x5*x10 + 24*x3*x4*x5*x9 + 24*x3*x4*x5*x8 + 24*x3*x4*x5*x7 +
& 24*x3*x4*x5*x6 + 12*x3*x4*x5**2 + 12*x3*x4**2*x10 + 12*x3*x4**2*
& x9
fun = fun + 12*x3*x4**2*x8 + 12*x3*x4**2*x7 + 12*x3*x4**2*x6 + 12
& *x3*x4**2*x5 + 4*x3*x4**3 + 6*x3**2*x10**2 + 12*x3**2*x9*x10 + 6
& *x3**2*x9**2 + 12*x3**2*x8*x10 + 12*x3**2*x8*x9 + 6*x3**2*x8**2
& + 12*x3**2*x7*x10 + 12*x3**2*x7*x9 + 12*x3**2*x7*x8 + 6*x3**2*
& x7**2 + 12*x3**2*x6*x10 + 12*x3**2*x6*x9 + 12*x3**2*x6*x8 + 12*
& x3**2*x6*x7 + 6*x3**2*x6**2 + 12*x3**2*x5*x10 + 12*x3**2*x5*x9
& + 12*x3**2*x5*x8 + 12*x3**2*x5*x7 + 12*x3**2*x5*x6 + 6*x3**2*
& x5**2 + 12*x3**2*x4*x10 + 12*x3**2*x4*x9 + 12*x3**2*x4*x8 + 12*
& x3**2*x4*x7 + 12*x3**2*x4*x6 + 12*x3**2*x4*x5 + 6*x3**2*x4**2 +
& 4*x3**3*x10 + 4*x3**3*x9 + 4*x3**3*x8 + 4*x3**3*x7 + 4*x3**3*x6
& + 4*x3**3*x5 + 4*x3**3*x4 + x3**4 + 4*x2*x10**3 + 12*x2*x9*
& x10**2 + 12*x2*x9**2*x10 + 4*x2*x9**3 + 12*x2*x8*x10**2 + 24*x2*
& x8*x9*x10 + 12*x2*x8*x9**2 + 12*x2*x8**2*x10 + 12*x2*x8**2*x9 +
& 4*x2*x8**3 + 12*x2*x7*x10**2 + 24*x2*x7*x9*x10 + 12*x2*x7*x9**2
& + 24*x2*x7*x8*x10 + 24*x2*x7*x8*x9 + 12*x2*x7*x8**2 + 12*x2*
& x7**2*x10
fun = fun + 12*x2*x7**2*x9 + 12*x2*x7**2*x8 + 4*x2*x7**3 + 12*x2*
& x6*x10**2 + 24*x2*x6*x9*x10 + 12*x2*x6*x9**2 + 24*x2*x6*x8*x10
& + 24*x2*x6*x8*x9 + 12*x2*x6*x8**2 + 24*x2*x6*x7*x10 + 24*x2*x6*
& x7*x9 + 24*x2*x6*x7*x8 + 12*x2*x6*x7**2 + 12*x2*x6**2*x10 + 12*
& x2*x6**2*x9 + 12*x2*x6**2*x8 + 12*x2*x6**2*x7 + 4*x2*x6**3 + 12*
& x2*x5*x10**2 + 24*x2*x5*x9*x10 + 12*x2*x5*x9**2 + 24*x2*x5*x8*
& x10 + 24*x2*x5*x8*x9 + 12*x2*x5*x8**2 + 24*x2*x5*x7*x10 + 24*x2*
& x5*x7*x9 + 24*x2*x5*x7*x8 + 12*x2*x5*x7**2 + 24*x2*x5*x6*x10 +
& 24*x2*x5*x6*x9 + 24*x2*x5*x6*x8 + 24*x2*x5*x6*x7 + 12*x2*x5*
& x6**2 + 12*x2*x5**2*x10 + 12*x2*x5**2*x9 + 12*x2*x5**2*x8 + 12*
& x2*x5**2*x7 + 12*x2*x5**2*x6 + 4*x2*x5**3 + 12*x2*x4*x10**2 + 24
& *x2*x4*x9*x10 + 12*x2*x4*x9**2 + 24*x2*x4*x8*x10 + 24*x2*x4*x8*
& x9 + 12*x2*x4*x8**2 + 24*x2*x4*x7*x10 + 24*x2*x4*x7*x9 + 24*x2*
& x4*x7*x8 + 12*x2*x4*x7**2 + 24*x2*x4*x6*x10 + 24*x2*x4*x6*x9 +
& 24*x2*x4*x6*x8 + 24*x2*x4*x6*x7 + 12*x2*x4*x6**2 + 24*x2*x4*x5*
& x10
fun = fun + 24*x2*x4*x5*x9 + 24*x2*x4*x5*x8 + 24*x2*x4*x5*x7 + 24
& *x2*x4*x5*x6 + 12*x2*x4*x5**2 + 12*x2*x4**2*x10 + 12*x2*x4**2*x9
& + 12*x2*x4**2*x8 + 12*x2*x4**2*x7 + 12*x2*x4**2*x6 + 12*x2*
& x4**2*x5 + 4*x2*x4**3 + 12*x2*x3*x10**2 + 24*x2*x3*x9*x10 + 12*
& x2*x3*x9**2 + 24*x2*x3*x8*x10 + 24*x2*x3*x8*x9 + 12*x2*x3*x8**2
& + 24*x2*x3*x7*x10 + 24*x2*x3*x7*x9 + 24*x2*x3*x7*x8 + 12*x2*x3*
& x7**2 + 24*x2*x3*x6*x10 + 24*x2*x3*x6*x9 + 24*x2*x3*x6*x8 + 24*
& x2*x3*x6*x7 + 12*x2*x3*x6**2 + 24*x2*x3*x5*x10 + 24*x2*x3*x5*x9
& + 24*x2*x3*x5*x8 + 24*x2*x3*x5*x7 + 24*x2*x3*x5*x6 + 12*x2*x3*
& x5**2 + 24*x2*x3*x4*x10 + 24*x2*x3*x4*x9 + 24*x2*x3*x4*x8 + 24*
& x2*x3*x4*x7 + 24*x2*x3*x4*x6 + 24*x2*x3*x4*x5 + 12*x2*x3*x4**2
& + 12*x2*x3**2*x10 + 12*x2*x3**2*x9 + 12*x2*x3**2*x8 + 12*x2*
& x3**2*x7 + 12*x2*x3**2*x6 + 12*x2*x3**2*x5 + 12*x2*x3**2*x4 + 4*
& x2*x3**3 + 6*x2**2*x10**2 + 12*x2**2*x9*x10 + 6*x2**2*x9**2 + 12
& *x2**2*x8*x10 + 12*x2**2*x8*x9 + 6*x2**2*x8**2 + 12*x2**2*x7*x10
& + 12*x2**2*x7*x9
fun = fun + 12*x2**2*x7*x8 + 6*x2**2*x7**2 + 12*x2**2*x6*x10 + 12
& *x2**2*x6*x9 + 12*x2**2*x6*x8 + 12*x2**2*x6*x7 + 6*x2**2*x6**2
& + 12*x2**2*x5*x10 + 12*x2**2*x5*x9 + 12*x2**2*x5*x8 + 12*x2**2*
& x5*x7 + 12*x2**2*x5*x6 + 6*x2**2*x5**2 + 12*x2**2*x4*x10 + 12*
& x2**2*x4*x9 + 12*x2**2*x4*x8 + 12*x2**2*x4*x7 + 12*x2**2*x4*x6
& + 12*x2**2*x4*x5 + 6*x2**2*x4**2 + 12*x2**2*x3*x10 + 12*x2**2*
& x3*x9 + 12*x2**2*x3*x8 + 12*x2**2*x3*x7 + 12*x2**2*x3*x6 + 12*
& x2**2*x3*x5 + 12*x2**2*x3*x4 + 6*x2**2*x3**2 + 4*x2**3*x10 + 4*
& x2**3*x9 + 4*x2**3*x8 + 4*x2**3*x7 + 4*x2**3*x6 + 4*x2**3*x5 + 4
& *x2**3*x4 + 4*x2**3*x3 + x2**4 + 4*x1*x10**3 + 12*x1*x9*x10**2
& + 12*x1*x9**2*x10 + 4*x1*x9**3 + 12*x1*x8*x10**2 + 24*x1*x8*x9*
& x10 + 12*x1*x8*x9**2 + 12*x1*x8**2*x10 + 12*x1*x8**2*x9 + 4*x1*
& x8**3 + 12*x1*x7*x10**2 + 24*x1*x7*x9*x10 + 12*x1*x7*x9**2 + 24*
& x1*x7*x8*x10 + 24*x1*x7*x8*x9 + 12*x1*x7*x8**2 + 12*x1*x7**2*x10
& + 12*x1*x7**2*x9 + 12*x1*x7**2*x8 + 4*x1*x7**3 + 12*x1*x6*
& x10**2
fun = fun + 24*x1*x6*x9*x10 + 12*x1*x6*x9**2 + 24*x1*x6*x8*x10 +
& 24*x1*x6*x8*x9 + 12*x1*x6*x8**2 + 24*x1*x6*x7*x10 + 24*x1*x6*x7*
& x9 + 24*x1*x6*x7*x8 + 12*x1*x6*x7**2 + 12*x1*x6**2*x10 + 12*x1*
& x6**2*x9 + 12*x1*x6**2*x8 + 12*x1*x6**2*x7 + 4*x1*x6**3 + 12*x1*
& x5*x10**2 + 24*x1*x5*x9*x10 + 12*x1*x5*x9**2 + 24*x1*x5*x8*x10
& + 24*x1*x5*x8*x9 + 12*x1*x5*x8**2 + 24*x1*x5*x7*x10 + 24*x1*x5*
& x7*x9 + 24*x1*x5*x7*x8 + 12*x1*x5*x7**2 + 24*x1*x5*x6*x10 + 24*
& x1*x5*x6*x9 + 24*x1*x5*x6*x8 + 24*x1*x5*x6*x7 + 12*x1*x5*x6**2
& + 12*x1*x5**2*x10 + 12*x1*x5**2*x9 + 12*x1*x5**2*x8 + 12*x1*
& x5**2*x7 + 12*x1*x5**2*x6 + 4*x1*x5**3 + 12*x1*x4*x10**2 + 24*x1
& *x4*x9*x10 + 12*x1*x4*x9**2 + 24*x1*x4*x8*x10 + 24*x1*x4*x8*x9
& + 12*x1*x4*x8**2 + 24*x1*x4*x7*x10 + 24*x1*x4*x7*x9 + 24*x1*x4*
& x7*x8 + 12*x1*x4*x7**2 + 24*x1*x4*x6*x10 + 24*x1*x4*x6*x9 + 24*
& x1*x4*x6*x8 + 24*x1*x4*x6*x7 + 12*x1*x4*x6**2 + 24*x1*x4*x5*x10
& + 24*x1*x4*x5*x9 + 24*x1*x4*x5*x8 + 24*x1*x4*x5*x7 + 24*x1*x4*
& x5*x6
fun = fun + 12*x1*x4*x5**2 + 12*x1*x4**2*x10 + 12*x1*x4**2*x9 +
& 12*x1*x4**2*x8 + 12*x1*x4**2*x7 + 12*x1*x4**2*x6 + 12*x1*x4**2*
& x5 + 4*x1*x4**3 + 12*x1*x3*x10**2 + 24*x1*x3*x9*x10 + 12*x1*x3*
& x9**2 + 24*x1*x3*x8*x10 + 24*x1*x3*x8*x9 + 12*x1*x3*x8**2 + 24*
& x1*x3*x7*x10 + 24*x1*x3*x7*x9 + 24*x1*x3*x7*x8 + 12*x1*x3*x7**2
& + 24*x1*x3*x6*x10 + 24*x1*x3*x6*x9 + 24*x1*x3*x6*x8 + 24*x1*x3*
& x6*x7 + 12*x1*x3*x6**2 + 24*x1*x3*x5*x10 + 24*x1*x3*x5*x9 + 24*
& x1*x3*x5*x8 + 24*x1*x3*x5*x7 + 24*x1*x3*x5*x6 + 12*x1*x3*x5**2
& + 24*x1*x3*x4*x10 + 24*x1*x3*x4*x9 + 24*x1*x3*x4*x8 + 24*x1*x3*
& x4*x7 + 24*x1*x3*x4*x6 + 24*x1*x3*x4*x5 + 12*x1*x3*x4**2 + 12*x1
& *x3**2*x10 + 12*x1*x3**2*x9 + 12*x1*x3**2*x8 + 12*x1*x3**2*x7 +
& 12*x1*x3**2*x6 + 12*x1*x3**2*x5 + 12*x1*x3**2*x4 + 4*x1*x3**3 +
& 12*x1*x2*x10**2 + 24*x1*x2*x9*x10 + 12*x1*x2*x9**2 + 24*x1*x2*x8
& *x10 + 24*x1*x2*x8*x9 + 12*x1*x2*x8**2 + 24*x1*x2*x7*x10 + 24*x1
& *x2*x7*x9 + 24*x1*x2*x7*x8 + 12*x1*x2*x7**2 + 24*x1*x2*x6*x10 +
& 24*x1*x2*x6*x9
fun = fun + 24*x1*x2*x6*x8 + 24*x1*x2*x6*x7 + 12*x1*x2*x6**2 + 24
& *x1*x2*x5*x10 + 24*x1*x2*x5*x9 + 24*x1*x2*x5*x8 + 24*x1*x2*x5*x7
& + 24*x1*x2*x5*x6 + 12*x1*x2*x5**2 + 24*x1*x2*x4*x10 + 24*x1*x2*
& x4*x9 + 24*x1*x2*x4*x8 + 24*x1*x2*x4*x7 + 24*x1*x2*x4*x6 + 24*x1
& *x2*x4*x5 + 12*x1*x2*x4**2 + 24*x1*x2*x3*x10 + 24*x1*x2*x3*x9 +
& 24*x1*x2*x3*x8 + 24*x1*x2*x3*x7 + 24*x1*x2*x3*x6 + 24*x1*x2*x3*
& x5 + 24*x1*x2*x3*x4 + 12*x1*x2*x3**2 + 12*x1*x2**2*x10 + 12*x1*
& x2**2*x9 + 12*x1*x2**2*x8 + 12*x1*x2**2*x7 + 12*x1*x2**2*x6 + 12
& *x1*x2**2*x5 + 12*x1*x2**2*x4 + 12*x1*x2**2*x3 + 4*x1*x2**3 + 6*
& x1**2*x10**2 + 12*x1**2*x9*x10 + 6*x1**2*x9**2 + 12*x1**2*x8*x10
& + 12*x1**2*x8*x9 + 6*x1**2*x8**2 + 12*x1**2*x7*x10 + 12*x1**2*
& x7*x9 + 12*x1**2*x7*x8 + 6*x1**2*x7**2 + 12*x1**2*x6*x10 + 12*
& x1**2*x6*x9 + 12*x1**2*x6*x8 + 12*x1**2*x6*x7 + 6*x1**2*x6**2 +
& 12*x1**2*x5*x10 + 12*x1**2*x5*x9 + 12*x1**2*x5*x8 + 12*x1**2*x5*
& x7 + 12*x1**2*x5*x6 + 6*x1**2*x5**2 + 12*x1**2*x4*x10 + 12*x1**2
& *x4*x9
fun = fun + 12*x1**2*x4*x8 + 12*x1**2*x4*x7 + 12*x1**2*x4*x6 + 12
& *x1**2*x4*x5 + 6*x1**2*x4**2 + 12*x1**2*x3*x10 + 12*x1**2*x3*x9
& + 12*x1**2*x3*x8 + 12*x1**2*x3*x7 + 12*x1**2*x3*x6 + 12*x1**2*
& x3*x5 + 12*x1**2*x3*x4 + 6*x1**2*x3**2 + 12*x1**2*x2*x10 + 12*
& x1**2*x2*x9 + 12*x1**2*x2*x8 + 12*x1**2*x2*x7 + 12*x1**2*x2*x6
& + 12*x1**2*x2*x5 + 12*x1**2*x2*x4 + 12*x1**2*x2*x3 + 6*x1**2*
& x2**2 + 4*x1**3*x10 + 4*x1**3*x9 + 4*x1**3*x8 + 4*x1**3*x7 + 4*
& x1**3*x6 + 4*x1**3*x5 + 4*x1**3*x4 + 4*x1**3*x3 + 4*x1**3*x2 +
& x1**4
RETURN
END
EOF
*--#] Pre_write_2 :
*--#[ DolVars_1 :
S x,a,b;
Off statistics;
L F = (a+b)^4+a*(a+x)^3;
.sort
#$a = 0;
if ( count(x,1) > $a ) $a = count_(x,1);
Print " >> After %t the maximum power of x is %$",$a;
#write " ># $a = `$a'"
.sort
#write " ># $a = `$a'"
.end
assert succeeded?
assert stdout =~ Regexp.new(<<-EOF
># \\$a = 0
\.sort
>> .+0
>> .+0
>> .+0
>> .+0
>> .+0
>> .+1
>> .+2
>> .+3
#write " ># \\$a = `\\$a'"
># \\$a = 3
EOF
)
*--#] DolVars_1 :
*--#[ DolVarsParallel_1 :
S a1,...,a10;
L F = (a1+...+a10)^3;
.sort
#$c = 0;
Print +f "<%w> %t";
Multiply,(a1+...+a10);
$c = $c+1;
ModuleOption,sum,$c;
.sort
#message $c = `$c'
#$max = 0;
#$min = 10;
if ( count(a1,1) > $max ) $max = count_(a1,1);
if ( count(a4,1) < $min ) $min = count_(a4,1);
ModuleOption,maximum,$max;
ModuleOption,minimum,$min;
.sort
#message $max = `$max'
#message $min = `$min'
.end
assert succeeded?
if serial?
assert stdout =~ /\s+\.sort\n(<>\ \ \+\ \S+\n){220}\n/
else
assert stdout =~ /\s+\.sort\n(<\d+>\ \ \+\ \S+\n){220}\n/
end
assert stdout =~ /~~~\$c = 2200/
assert stdout =~ /~~~\$max = 4/
assert stdout =~ /~~~\$min = 0/
*--#] DolVarsParallel_1 :
*--#[ Sta_ArgImplode_1 :
CF Z1, ..., Z4;
S x, a, b;
L s = a * (Z1(0,0,0,1,0,0,-1) - Z2(0,0,0,1,0,0,-1))
+ b * (Z3(-2,8,-1,-1) - Z4(-2,8,-1,-1));
ArgImplode Z1;
repeat id Z2(?a,0,x?!{0,0},?b) = Z2(?a,x+sig_(x),?b);
ArgExplode Z3;
repeat id Z4(?a,x?!{1,0,-1},?b) = Z4(?a,0,x-sig_(x),?b);
id Z2(?a) = Z1(?a);
id Z4(?a) = Z3(?a);
Print;
.end
assert succeeded?
assert result("s") =~ expr("0")
*--#] Sta_ArgImplode_1 :
*--#[ Sta_Collect_1 :
* TODO: change the result in the manual.
S a,b,c;
CF cfun;
L F =
a*(b^2+c)
+ a^2*(b+6)
+ b^3 + c*b + 12;
B a;
.sort
Collect cfun;
P;
.end
assert succeeded?
assert result("F") =~ expr("
cfun(6 + b)*a^2 + cfun(12 + b*c + b^3) + cfun(c + b^2)*a
")
*--#] Sta_Collect_1 :
*--#[ Sta_CommuteInSet_1 :
I i1,...,i10;
F A1,...,A10;
CommuteInSet{A1,A3,A5},{A1,g_},{A1,A1};
L F = A5*A1*A5*A1*A5*A2*A3*A5*A1*A5*A3*A1;
L G = g_(2,i1)*g_(2,i2,i3)*A1(i2)*g_(1,i4)*g_(1,5_,i5,i6)
*A1(i1)*A1(i3)*g5_(1)*A3(i5)*A3(i4)*g5_(1);
Print +f +s;
.end
assert succeeded?
assert result("F") =~ expr("
+ A1*A1*A5*A5*A5*A2*A1*A1*A3*A3*A5*A5
")
assert result("G") =~ expr("
+ g_(1,i4,i5,i6)*g_(2,i1,i2,i3)*A1(i1)*A1(i2)*A1(i3)*
A3(i5)*A3(i4)*g_(1,5_)
")
*--#] Sta_CommuteInSet_1 :
*--#[ Sta_FactArg_1 :
*TODO: OldFactArg is needed for the result in the manual.
On OldFactArg;
Symbols a,b,c;
CFunctions f,f1,f2,f3;
Local F = f(-3*a*b)+f(3*a*b)
+f1(-3*a*b)+f1(3*a*b)
+f2(-3*a*b)+f2(3*a*b)
+f3(-3*a*b)+f3(3*a*b);
FactArg,f;
Factarg,(0),f1;
Factarg,(1),f2;
Factarg,(-1),f3;
Print;
.end
assert succeeded?
assert result("F") =~ expr("
f(a,b,-1,3) + f(a,b,3) + 2*f1(a*b) + f2(a*b,-1,3) + f2(a*b,3)
+ f3(a*b,-3) + f3(a*b,3)
")
*--#] Sta_FactArg_1 :
*--#[ Sta_Fill_1 :
Table B(1:1);
Local dummy = 1;
.sort
Fill B(1) = dummy;
Drop dummy;
.sort
Local F = B(1);
Print;
.end
assert finished?
assert warning?
*--#] Sta_Fill_1 :
*--#[ Sta_Fill_2 :
Table B(1:1);
Local dummy = 1;
.sort
Fill B(1) = dummy;
.sort
Local F = B(1);
Print;
.sort
Drop;
.sort
Local dummy = 2;
.sort
Local F = B(1);
Print;
.end
# RHS expressions in Fill doesn't work in ParFORM. (#17)
# Anyway, the user is warned even in the sequential FORM, and should avoid it.
#pend_if mpi?
assert finished?
assert warning?
assert return_value == 0
assert result("F", 0) =~ expr("1")
assert result("F", 1) =~ expr("2")
*--#] Sta_Fill_2 :
*--#[ Sta_Fill_3 :
Table B(1:1);
Local dummy = 1;
.sort
#$value = dummy;
Fill B(1) = `$value';
Drop dummy;
.sort
Local F = B(1);
Print;
.end
assert succeeded?
assert result("F") =~ expr("1")
*--#] Sta_Fill_3 :
*--#[ Sta_Fill_4 :
Table B(1:1);
Local u = 2;
Local dummy = 1;
.sort
Fill B(1) = dummy;
Drop dummy;
.sort
Local v = 5;
Local F = B(1);
Print;
.end
# RHS expressions in Fill doesn't work in ParFORM. (#17)
# Anyway, the user is warned even in the sequential FORM, and should avoid it.
#pend_if mpi?
assert finished?
assert warning?
assert return_value == 0
assert result("F") =~ expr("5")
*--#] Sta_Fill_4 :
*--#[ Sta_Identify_1 :
Vector Q,p1,...,p5,q1,...,q5;
Cfunction V(s),replace;
Format 60;
* This is a t1 topology:
L F = V(Q,p1,p4)*V(p1,p2,p5)*
V(p2,p3,Q)*V(p3,p4,p5);
$t = term_;
id,all,$t*replace_(,...,) =
$t*replace(,...,);
Print +s;
.end
assert succeeded?
assert result("F") =~ expr("
+ V(Q,p1,p4)*V(Q,p2,p3)*V(p1,p2,p5)*V(p3,p4,p5)*
replace(p1,q1,p2,q2,p3,q3,p4,q4,p5,q5)
+ V(Q,p1,p4)*V(Q,p2,p3)*V(p1,p2,p5)*V(p3,p4,p5)*
replace(p2,q1,p1,q2,p4,q3,p3,q4,p5,q5)
+ V(Q,p1,p4)*V(Q,p2,p3)*V(p1,p2,p5)*V(p3,p4,p5)*
replace(p3,q1,p4,q2,p1,q3,p2,q4,p5,q5)
+ V(Q,p1,p4)*V(Q,p2,p3)*V(p1,p2,p5)*V(p3,p4,p5)*
replace(p4,q1,p3,q2,p2,q3,p1,q4,p5,q5)
")
*--#] Sta_Identify_1 :
*--#[ Sta_Keep_1 :
CF f,g;
I i1;
S x,y,z;
L F = f(i1,x)*(g(i1,y)+g(i1,z));
B f;
.sort
Keep Brackets;
sum i1;
Print;
.end
assert succeeded?
assert result("F") =~ expr("f(N1_?,x)*g(i1,y)+f(N1_?,x)*g(i1,z)")
*--#] Sta_Keep_1 :
*--#[ Sta_LFactorized_1 :
Symbols x,y,z;
LocalFactorized F1 = 3*(x+y)*(y+z)*((x+z)*(2*x+1));
LocalFactorized F2 = 3*(x+y)*(y+z)+((x+z)*(2*x+1));
Print;
.end
assert succeeded?
assert result("F1") =~ expr("
( 3 )
* ( y + x )
* ( z + y )
* ( z + x + 2*x*z + 2*x^2 )
")
assert result("F2") =~ expr("
( z + 3*y*z + 3*y^2 + x + 5*x*z + 3*x*y + 2*x^2 )
")
*--#] Sta_LFactorized_1 :
*--#[ Sta_MakeInteger_1 :
S a,b,c;
CF f;
L F = f(22/3*a+14/5*b+18/7*c);
MakeInteger,f;
Print +f;
.end
assert succeeded?
assert result("F") =~ expr("2/105*f(135*c + 147*b + 385*a)")
*--#] Sta_MakeInteger_1 :
*--#[ Sta_PolyFun_1 :
Symbol x,y;
CF acc;
PolyFun acc;
Local F = 3*x^2*acc(1+y+y^2)+2*x^2*acc(1-y+y^2);
Print;
.end
assert succeeded?
assert result("F") =~ expr("x^2 * acc(5 + y + 5*y^2)")
*--#] Sta_PolyFun_1 :
*--#[ Sta_PolyRatFun_1 :
S x,y;
CF acc;
PolyRatFun acc;
Local F = 3*x^2*acc(1+y+y^2,1-y)+2*x^2*acc(1-y+y^2,1+y);
P;
.end
assert succeeded?
assert result("F") =~ expr("x^2*acc(-y^3-10*y^2-2*y-5,y^2-1)")
*--#] Sta_PolyRatFun_1 :
*--#[ Sta_Print_1 :
Symbols a,b,c;
Local F = 3*a+2*b;
Print "> %T";
id a = b+c;
Print ">> %t";
Print;
.end
assert succeeded?
assert stdout =~ exact_pattern(<<-'EOF'
> 3*a
>> + 3*b
>> + 3*c
> 2*b
>> + 2*b
EOF
)
assert result("F") =~ expr("3*c + 5*b")
*--#] Sta_Print_1 :
*--#[ Sta_ReplaceLoop_1 :
*TODO: change the result in the manual.
Functions f(antisymmetric),ff(cyclesymmetric);
Indices i1,...,i8;
Local F = f(i1,i4,i2)*f(i5,i2,i3)*f(i3,i1,i6)*f(i4,i7,i8);
ReplaceLoop f,arg=3,loop=3,out=ff;
P;
.end
assert succeeded?
assert result("F") =~ expr("- ff(i4,i5,i6)*f(i4,i7,i8)")
*--#] Sta_ReplaceLoop_1 :
*--#[ Sta_ReplaceLoop_2 :
*TODO: change the result in the manual.
Functions f(antisymmetric),ff(cyclesymmetric);
Indices i1,...,i9;
Local F = f(i1,i4,i2)*f(i5,i2,i3)*f(i3,i1,i6)*f(i4,i7,i8)
*f(i6,i7,i8);
ReplaceLoop f,arg=3,loop=all,out=ff;
P;
.end
assert succeeded?
assert result("F") =~ expr("- f(i1,i2,i4)*f(i2,i3,i5)*f(i1,i3,i6)*ff(i4,i6)")
*--#] Sta_ReplaceLoop_2 :
*--#[ Sta_Shuffle_1 :
CF f,ff,g;
S a,b,c,d,x1,x2;
Local F1 = ff*f(a,b)*f(c,d);
Local F2 = g(a,b)*g(c,d);
repeat id f(x1?,?a)*f(x2?,?b)*ff(?c) =
+f(?a)*f(x2,?b)*ff(?c,x1)
+f(x1,?a)*f(?b)*ff(?c,x2);
id f(?a)*f(?b)*ff(?c) = f(?c,?a,?b);
shuffle,g;
Print;
.end
assert succeeded?
assert result("F1") =~ expr("f(a,b,c,d)+f(a,c,b,d)+f(a,c,d,b)+f(c,a,b,d)+f(c,a,d,b)+f(c,d,a,b)")
assert result("F2") =~ expr("g(a,b,c,d)+g(a,c,b,d)+g(a,c,d,b)+g(c,a,b,d)+g(c,a,d,b)+g(c,d,a,b)")
*--#] Sta_Shuffle_1 :
*--#[ Sta_Stuffle_1 :
CF S,R;
Symbols N,n;
L F = S(R(1,-3),N)*S(R(-5,1),N);
id S(R(?a),n?)*S(R(?b),n?) = S(?a)*S(?b)*R(n);
Stuffle,S-;
id S(?a)*R(n?) = S(R(?a),n);
Print +s;
.end
assert succeeded?
assert result("F") =~ expr(<<-'EOF'
+ S(R(-6,-4),N)
- S(R(-6,-3,1),N)
- S(R(-6,1,-3),N)
- S(R(-5,1,-4),N)
+ S(R(-5,1,-3,1),N)
+ 2*S(R(-5,1,1,-3),N)
- S(R(-5,2,-3),N)
- S(R(1,-5,-4),N)
+ S(R(1,-5,-3,1),N)
+ S(R(1,-5,1,-3),N)
+ S(R(1,-3,-5,1),N)
- S(R(1,8,1),N)
EOF
)
*--#] Sta_Stuffle_1 :
*--#[ Sta_ToTensor_1 :
*NOTE: "functions" option is needed.
V p,p1,p2;
F f;
I mu;
T tt,t;
L F = p.p1^2*f(p,p1)*p(mu)*tt(p1,p,p2,p);
totensor functions,p,t;
P;
.end
assert succeeded?
assert result("F") =~ expr("
f(N1_?,p1)*tt(p1,N2_?,p2,N3_?)*t(p1,p1,mu,N1_?,N2_?,N3_?)
")
*--#] Sta_ToTensor_1 :
*--#[ Sta_Transform_1 :
Symbol x,x1,x2;
CF H,H1;
Off Statistics;
L F = H(3,4,2,6,1,1,1,2);
Transform,H,explode(1,last),
replace(1,last)=(0,1,1,0),
encode(1,last):base=2;
Print;
.end
assert succeeded?
assert result("F") =~ expr("H(907202)")
*--#] Sta_Transform_1 :
*--#[ Fun_distrib_1 :
Symbols x1,...,x4;
CFunctions f,f1,f2;
Local F = f(x1,...,x4);
id f(?a) = distrib_(-1,2,f1,f2,?a);
Print +s;
.end
assert succeeded?
assert result("F") =~ expr("
+ f1(x1,x2)*f2(x3,x4)
- f1(x1,x3)*f2(x2,x4)
+ f1(x1,x4)*f2(x2,x3)
+ f1(x2,x3)*f2(x1,x4)
- f1(x2,x4)*f2(x1,x3)
+ f1(x3,x4)*f2(x1,x2)
")
*--#] Fun_distrib_1 :
*--#[ Fun_exteuclidean_1 :
Symbols x1,x2,x3,x4;
Local F = exteuclidean_(54,84);
Print;
.sort
id exteuclidean_(x1?,x2?,x3?,x4?) = x1*x3+x2*x4;
Print;
.end
assert succeeded?
assert result("F", 0) =~ expr("exteuclidean_(54,84,-3,2)")
assert result("F", 1) =~ expr("6")
*--#] Fun_exteuclidean_1 :
*--#[ Fun_exteuclidean_2 :
Symbols x1,x2,x3,x4,a,b;
Local F = exteuclidean_(97,101);
Print;
.sort
id exteuclidean_(x1?,x2?,x3?,x4?) = x1*x3+x2*x4
+a*mod2_(1/97,101)+b*mod2_(1/101,97);
Print;
.end
assert succeeded?
assert result("F", 0) =~ expr("exteuclidean_(97,101,25,-24)")
assert result("F", 1) =~ expr("1 - 24*b + 25*a")
*--#] Fun_exteuclidean_2 :
*--#[ Fun_makerational_1 :
#$m = prime_(1);
#write <> "The prime number is %$",$m
L F = MakeRational_(12345678,$m);
Print;
.sort
Modulus `$m';
Print;
.end
assert succeeded?
if wordsize == 2
assert stdout =~ /The prime number is 32719/
assert result("F", 0) =~ expr("127/37")
assert result("F", 1) =~ expr("10615")
elsif wordsize == 4
assert stdout =~ /The prime number is 2147483587/
assert result("F", 0) =~ expr("9719/38790")
assert result("F", 1) =~ expr("12345678")
end
*--#] Fun_makerational_1 :
*--#[ Fun_perm_1 :
CFunction f;
Symbols x1,...,x3;
Local F = perm_(f,x1,x2,x3);
Print +s;
.end
assert succeeded?
assert result("F") =~ expr("""
+ f(x1,x2,x3)
+ f(x1,x3,x2)
+ f(x2,x1,x3)
+ f(x2,x3,x1)
+ f(x3,x1,x2)
+ f(x3,x2,x1)
""")
*--#] Fun_perm_1 :
*--#[ Fun_prime_1 :
Symbols x1,x2,x3,x4;
ON highfirst;
Local F = x1*prime_(1)+x2*prime_(2)
+x3*prime_(3)+x4*prime_(4);
Print;
.end
assert succeeded?
if wordsize == 2
assert result("F") =~ expr("32719*x1 + 32717*x2 + 32713*x3 + 32707*x4")
elsif wordsize == 4
assert result("F") =~ expr("2147483587*x1 + 2147483579*x2 + 2147483563*x3 + 2147483549*x4")
end
*--#] Fun_prime_1 :
*--#[ Fun_putfirst_1 :
S a,a1,...,a10;
CF f,g;
L F = g(a,a1,...,a10);
id g(?a) = putfirst_(f,4,?a);
Print;
.end
assert succeeded?
assert result("F") =~ expr("
f(a3,a,a1,a2,a4,a5,a6,a7,a8,a9,a10)
")
*--#] Fun_putfirst_1 :
*--#[ Fun_ranperm_1 :
Function f;
Symbols x1,...,x5;
Local F = ranperm_(f,1,2,3,4,5,6)
+ranperm_(f,x1,x2,x3+x1,x4,x5);
Print +s;
.end
assert succeeded?
# We can't predict the results!
*--#] Fun_ranperm_1 :
*--#[ Fun_sump_1 :
Symbol i,x;
Local F = sump_(i,0,5,x/i);
Print;
.end
assert succeeded?
assert result("F") =~ expr("1 + x + 1/2*x^2 + 1/6*x^3 + 1/24*x^4 + 1/120*x^5")
*--#] Fun_sump_1 :
*--#[ Brackets_1 :
Symbols a,b,c,x;
L F = a*x^2+b*x+c;
B x;
.sort
L Discriminant = F[x]^2-4*F[x^2]*F[1];
Print;
.end
assert succeeded?
assert result("Discriminant") =~ pattern("b^2-4*a*c");
*--#] Brackets_1 :
*--#[ PolyandFact_1 :
Symbol x,y;
CFunction rat;
PolyRatFun rat;
L F = rat(x+y,x-y)+rat(x-y,x+y);
Print;
.end
assert succeeded?
assert result("F") =~ expr("rat(2*x^2 + 2*y^2,x^2 - y^2)")
*--#] PolyandFact_1 :
*--#[ PolyandFact_2 :
* TODO: change the result in the manual.
Symbol x,y;
CFunction f1,f2;
Local F = f1(x^4-y^4)+f2(3*y^4-3*x^4);
FactArg,f1,f2;
Print;
.end
assert succeeded?
assert result("F") =~ expr("f1(-1,y - x,y + x,y^2 + x^2) + f2(3,y - x,y + x,y^2 + x^2)")
*--#] PolyandFact_2 :
*--#[ PolyandFact_3 :
* TODO: change the the first result in the manual.
Symbol x,y;
CFunction f1,f2;
Local F = f2(3*y^4-3*x^4);
FactArg,f2;
Print;
.sort
ChainOut,f2;
id f2(x?number_) = x;
Print;
.end
assert succeeded?
assert result("F", 0) =~ expr("f2(3,y - x,y + x,y^2 + x^2)")
assert result("F", 1) =~ expr("3*f2(y-x)*f2(y+x)*f2(y^2+x^2)")
*--#] PolyandFact_3 :
*--#[ PolyandFact_4 :
Symbol x,y;
Local F = x^4-y^4;
Print;
.sort
Print;
Factorize F;
.end
assert succeeded?
assert result("F", 0) =~ expr("-y^4+x^4")
assert result("F", 1) =~ expr("(-1)*(y-x)*(y+x)*(y^2+x^2)")
*--#] PolyandFact_4 :
*--#[ PolyandFact_5 :
* TODO: change the the result of F2 in the manual.
Symbol x,y;
Local F1 = x^4-y^4;
Local F2 = 0;
Local F3 = 1;
Local F4 = x^4-y^4;
Print;
Factorize F1,F2,F3;
.sort
#do i = 1,4
#$n`i' = numfactors_(F`i');
#message expression F`i' has `$n`i'' factors
#enddo
.end
assert succeeded?
assert result("F1") =~ expr("(-1)*(y-x)*(y+x)*(y^2+x^2)")
assert result("F2") =~ expr("(0)")
assert result("F3") =~ expr("(1)")
assert result("F4") =~ expr("-y^4+x^4")
assert stdout =~ /~~~expression F1 has 4 factors/
assert stdout =~ /~~~expression F2 has 1 factors/
assert stdout =~ /~~~expression F3 has 1 factors/
assert stdout =~ /~~~expression F4 has 0 factors/
*--#] PolyandFact_5 :
*--#[ PolyandFact_6 :
Symbol x,y;
Local F = x^4-y^4;
Factorize F;
.sort
#$n = numfactors_(F);
#do i = 1,`$n'
Local F`i' = F[factor_^`i'];
#enddo
Print;
.end
assert succeeded?
assert result("F") =~ expr("(-1)*(y-x)*(y+x)*(y^2+x^2)")
assert result("F1") =~ expr("-1")
assert result("F2") =~ expr("y-x")
assert result("F3") =~ expr("y+x")
assert result("F4") =~ expr("y^2+x^2")
*--#] PolyandFact_6 :
*--#[ PolyandFact_7 :
Symbol x,y;
LocalFactorize E = -(x+1)*(x+2)*((x+3)*(x+4));
Print;
.end
assert succeeded?
assert result("E") =~ expr("
( - 1 )
* ( 1 + x )
* ( 2 + x )
* ( 12 + 7*x + x^2 )
")
*--#] PolyandFact_7 :
*--#[ PolyandFact_8 :
Symbol x,y;
LocalFactorize E = -(x+1)*(x+2)*((x+3)*(x+4));
Local F = -(x+1)*(x+2)*((x+3)*(x+4));
Print;
.sort
LF G = (x-1)*(x+2)^2*E^2*F^2;
Print G;
.end
assert succeeded?
assert result("E") =~ expr("
(-1)
*(1+x)
*(2+x)
*(12+7*x+x^2)
")
assert result("F") =~ expr("
-24-50*x-35*x^2-10*x^3-x^4
")
assert result("G") =~ expr("
(-1+x)
*(2+x)
*(2+x)
*(-1)
*(1+x)
*(2+x)
*(12+7*x+x^2)
*(-1)
*(1+x)
*(2+x)
*(12+7*x+x^2)
*(-24-50*x-35*x^2-10*x^3-x^4)
*(-24-50*x-35*x^2-10*x^3-x^4)
")
*--#] PolyandFact_8 :
*--#[ PolyandFact_9 :
Symbol x,y;
LocalFactorize E = -(x+1)*(x+2)*((x+3)*(x+4));
Local F = -(x+1)*(x+2)*((x+3)*(x+4));
.sort
LF G = (x-1)*(x+2)^2*E^2*F^2;
Print G;
Factorize G;
.end
assert succeeded?
assert result("G") =~ expr("
(-1+x)
*(1+x)
*(1+x)
*(1+x)
*(1+x)
*(2+x)
*(2+x)
*(2+x)
*(2+x)
*(2+x)
*(2+x)
*(3+x)
*(3+x)
*(3+x)
*(3+x)
*(4+x)
*(4+x)
*(4+x)
*(4+x)
")
*--#] PolyandFact_9 :
*--#[ PolyandFact_10 :
Symbol x,y;
LocalFactorize E = -0*(x+1)*(x+2)*0*((x+3)*(x+4));
Print;
.end
assert succeeded?
assert result("E") =~ expr("
(-1)
*(0)
*(1+x)
*(2+x)
*(0)
*(12+7*x+x^2)
")
*--#] PolyandFact_10 :
*--#[ PolyandFact_11 :
Symbol x,y;
Format Nospaces;
LocalFactorize E = -0*3*(x+1)*(x+2)/2*0*((x+3)*(x+4));
Print;
.sort
Print;
Factorize(keepzero) E;
.end
assert succeeded?
assert result("E", 0) =~ expr("
(-1)
*(0)
*(3)
*(1+x)
*(2+x)
*(1/2)
*(0)
*(12+7*x+x^2)
")
assert result("E", 1) =~ expr("
(0)
*(-3/2)
*(1+x)
*(2+x)
*(3+x)
*(4+x)
")
*--#] PolyandFact_11 :
*--#[ PolyandFact_12 :
Symbol x,y;
LFactorized F = (x+1)*(x+y)*(y+1);
Print;
.sort
Print;
Bracket x;
UnFactorize F;
.end
assert succeeded?
assert result("F", 0) =~ expr("
(1+x)
*(y+x)
*(1+y)
")
assert result("F", 1) =~ expr("
+x*(1+2*y+y^2)
+x^2*(1+y)
+y+y^2
")
*--#] PolyandFact_12 :
*--#[ PolyandFact_13 :
Symbol x,y;
LFactorized F = (x+1)*(x+y)*(y+1);
Print;
.sort
#$num = numfactors_(F);
Local G = *...*;
Bracket x;
Print;
.end
assert succeeded?
assert result("F", 0) =~ expr("
(1+x)
*(y+x)
*(1+y)
")
assert result("F", 1) =~ expr("
(1+x)
*(y+x)
*(1+y)
")
assert result("G") =~ expr("
+x*(1+2*y+y^2)
+x^2*(1+y)
+y+y^2
")
*--#] PolyandFact_13 :
*--#[ PolyandFact_14 :
Symbol x,y;
CFunction f;
Off Statistics;
#$a = x^4-y^4;
Local F = f(x^4-y^4)+f(x^6-y^6);
Print;
.sort
#factdollar $a;
#do i = 1,`$a[0]'
#write <> "Factor `i' of `$a' is `$a[`i']'"
#enddo
id f(x?$b) = f(x);
FactDollar $b;
do $i = 1,$b[0];
Print "Factor %$ of %$ is %$",$i,$b,$b[$i];
enddo;
Print;
.end
assert succeeded?
assert result("F", 0) =~ expr("
f(-y^4+x^4)+f(-y^6+x^6)
")
assert result("F", 1) =~ expr("
f(-y^4+x^4)+f(-y^6+x^6)
")
assert stdout =~ exact_pattern("
Factor 1 of -y^4+x^4 is -1
#enddo
Factor 2 of -y^4+x^4 is y-x
Factor 3 of -y^4+x^4 is y+x
Factor 4 of -y^4+x^4 is y^2+x^2
id f(x?$b) = f(x);
")
assert stdout =~ exact_pattern("
Factor 1 of - y^4 + x^4 is - 1
Factor 2 of - y^4 + x^4 is y - x
Factor 3 of - y^4 + x^4 is y + x
Factor 4 of - y^4 + x^4 is y^2 + x^2
Factor 1 of - y^6 + x^6 is - 1
Factor 2 of - y^6 + x^6 is y - x
Factor 3 of - y^6 + x^6 is y + x
Factor 4 of - y^6 + x^6 is y^2 - x*y + x^2
Factor 5 of - y^6 + x^6 is y^2 + x*y + x^2
")
*--#] PolyandFact_14 :
*--#[ PolyandFact_15 :
Symbol x,y;
CFunction f;
Format Nospaces;
#$a = x^4-y^4;
#factdollar $a;
Local F = f(numfactors_($a))
+f(<$a[1]>,...,<$a[`$a[0]']>);
Print;
.end
assert succeeded?
assert result("F", 0) =~ expr("
f(-1,y-x,y+x,y^2+x^2)+f(4)
")
*--#] PolyandFact_15 :
*--#[ PolyandFact_16 :
Symbol x,y;
Format NoSpaces;
On ShortStats;
Local F1 = x^60-1;
Local F2 = y^60-x^60;
Factorize F1,F2;
Print;
.end
if wordsize < 4
# ERROR: polynomials too large (> WORDSIZE)
else
assert succeeded?
assert result("F1") =~ expr("
(-1+x)
*(1-x+x^2)
*(1-x+x^2-x^3+x^4)
*(1-x+x^3-x^4+x^5-x^7+x^8)
*(1+x)
*(1+x+x^2)
*(1+x+x^2+x^3+x^4)
*(1+x-x^3-x^4-x^5+x^7+x^8)
*(1-x^2+x^4)
*(1-x^2+x^4-x^6+x^8)
*(1+x^2)
*(1+x^2-x^6-x^8-x^10+x^14+x^16)
")
assert result("F2") =~ expr("
(y-x)
*(y+x)
*(y^2-x*y+x^2)
*(y^4-x*y^3+x^2*y^2-x^3*y+x^4)
*(y^4+x*y^3+x^2*y^2+x^3*y+x^4)
*(y^2+x*y+x^2)
*(y^2+x^2)
*(y^8-x*y^7+x^3*y^5-x^4*y^4+x^5*y^3-x^7*y+x^8)
*(y^8+x*y^7-x^3*y^5-x^4*y^4-x^5*y^3+x^7*y+x^8)
*(y^8-x^2*y^6+x^4*y^4-x^6*y^2+x^8)
*(y^4-x^2*y^2+x^4)
*(y^16+x^2*y^14-x^6*y^10-x^8*y^8-x^10*y^6+x^14*y^2+x^16)
")
end
*--#] PolyandFact_16 :
*--#[ PolyandFact_17 :
Symbols a,b;
LF F = (a+b)^2;
multiply 2;
Print;
.end
assert succeeded?
assert result("F") =~ expr("
( 2*b + 2*a )
* ( 2*b + 2*a )
")
*--#] PolyandFact_17 :
*--#[ PolyandFact_18 :
Symbols a,b;
LF F = (a+b)^2;
.sort
LF F = 2*F;
Print;
.end
assert succeeded?
assert result("F") =~ expr("
( 2 )
* ( b + a )
* ( b + a )
")
*--#] PolyandFact_18 :
*--#[ OutputOptimization_1 :
CF f;
S a,b,c;
L H = f(a)+f(b)+(a+b+c)^2;
L G = f(c)+(a+b+c)^3;
Format O2;
Print +f;
.sort
ExtraSymbols,array,w;
Format Fortran;
#optimize G
#write " REAL*8 w(`optimmaxvar_')"
#write "%O"
#write " G = %e",G
#clearoptimize
.sort
#optimize H
#write " REAL*8 w(`optimmaxvar_')"
#write "%O"
#write " H = %e",H
.end
assert succeeded?
if serial?
# TFORM may optimize the expressions in a different way.
assert file("outg.f") == <<-'EOF'
REAL*8 w(4)
w(1)=f(c)
w(2)=c**2
w(3)=3*c + b
w(3)=b*w(3)
w(3)=3*w(2) + w(3)
w(3)=b*w(3)
w(4)=2*c + b
w(4)=b*w(4)
w(2)=w(2) + w(4)
w(4)=c + b
w(4)=3*w(4) + a
w(4)=a*w(4)
w(2)=3*w(2) + w(4)
w(2)=a*w(2)
w(4)=c**3
G = w(1) + w(2) + w(3) + w(4)
EOF
assert file("outh.f") == <<-'EOF'
REAL*8 w(5)
w(1)=f(a)
w(2)=f(b)
w(3)=c**2
w(4)=2*c + b
w(4)=b*w(4)
w(5)=c + b
w(5)=2*w(5) + a
w(5)=a*w(5)
H = w(1) + w(2) + w(3) + w(4) + w(5)
EOF
end
*--#] OutputOptimization_1 :
*--#[ Dictionaries_1 :
Symbols x1,y2,z3,N;
Indices mu,nu,ro,si;
Tensor tens;
CFunction S,R,f;
ExtraSymbols array w;
#OpenDictionary test
#add x1: "x_1"
#add y2: "y^{(2)}"
#add z3: "{\cal Z}"
#add *: " "
#add S(R(1),N): "S_1(N)"
#add S(R(2),N): "S_2(N)"
#add S(R(1,1),N): "S_{1,1}(N)"
#add f: "\ln"
#add mu: "\mu"
#add nu: "\nu"
#add ro: "\rho"
#add si: "\sigma"
#add tens: "T"
#CloseDictionary
Local F = x1*y2*z3
+ S(R(1),N) + S(R(1,1),N) + S(R(2),N)
+ tens(mu,nu,ro,si) + f(x1+1);
#usedictionary test
Print +s;
.end
assert succeeded?
assert result("F") =~ expr("
+ x_1 y^2 {\\cal Z}
+ T(\\mu,\\nu,\\rho,\\sigma)
+ S_1(N)
+ S_{1,1}(N)
+ S_2(N)
+ \\ln(1 + x_1)
")
*--#] Dictionaries_1 :
*--#[ Dictionaries_2 :
Symbol x,n;
Format DoubleFortran;
#OpenDictionary numbers
#add 2: "TWO"
#add 5: "FIVE"
#add 7: "SEVEN"
#CloseDictionary
Local F = (1+x)^7/7;
id x^n? = x*x^n/(n+1);
#UseDictionary numbers
Print;
.end
assert succeeded?
assert stdout =~ exact_pattern("
F =
& 1/SEVEN*x + 1/TWO*x**2 + x**3 + FIVE/4*x**4 + x**5 + 1/TWO*x**6
& + 1/SEVEN*x**7 + 1.D0/56.D0*x**8
")
*--#] Dictionaries_2 :
*--#[ Dictionaries_3 :
Symbol x,n;
Format DoubleFortran;
#OpenDictionary numbers
#add 2: "TWO"
#add 5: "FIVE"
#add 7: "SEVEN"
#add 1/2: "HALF"
#CloseDictionary
Local F = (1+x)^7/7;
id x^n? = x*x^n/(n+1);
#UseDictionary numbers
Print;
.end
assert succeeded?
assert stdout =~ exact_pattern("
F =
& 1/SEVEN*x + HALF*x**2 + x**3 + FIVE/4*x**4 + x**5 + HALF*x**6 +
& 1/SEVEN*x**7 + 1.D0/56.D0*x**8
")
*--#] Dictionaries_3 :
*--#[ Dictionaries_4 :
Symbol x,n;
Format DoubleFortran;
#OpenDictionary numbers
#add 2: "TWO"
#add 5: "FIVE"
#add 7: "SEVEN"
#add 1/2: "HALF"
#CloseDictionary
Local F = (1+x)^7/7;
id x^n? = x*x^n/(n+1);
#UseDictionary numbers (warnings)
Print;
.end
assert succeeded?
assert stdout =~ exact_pattern("
F =
& 1/SEVEN*x + HALF*x**2 + x**3 + FIVE/4*x**4 + x**5 + HALF*x**6 +
>>>>>>>>Could not translate coefficient with dictionary numbers<<<<<<<<<
<<<
& 1/SEVEN*x**7 + 1.D0/56.D0*x**8
")
*--#] Dictionaries_4 :
*--#[ Dictionaries_5 :
Symbol x,n;
Format DoubleFortran;
#OpenDictionary numbers
#add 2: "cd2"
#add 5: "cd5"
#add 7: "cd7"
#add 56: "cd56"
#add 1/2: "c1d2"
#add 5/4: "c5d4"
#CloseDictionary
Local F = (1+x)^7/7;
id x^n? = x*x^n/(n+1);
#UseDictionary numbers (warnings)
Print;
.end
assert succeeded?
assert stdout =~ exact_pattern("
F =
& 1/cd7*x + c1d2*x**2 + x**3 + c5d4*x**4 + x**5 + c1d2*x**6 + 1/
& cd7*x**7 + 1/cd56*x**8
")
*--#] Dictionaries_5 :
*--#[ Dictionaries_6 :
Symbol x;
CFunction f;
#OpenDictionary ranges
#add (1,2): "w(%#)"
#add (3): "ww(%#)"
#add (4,6): "www(%@)"
#CloseDictionary
Local F = +...+;
ToPolynomial;
Print;
.sort
#UseDictionary ranges
Print;
.end
assert succeeded?
assert result("F", 0) =~ expr("
x*Z1_ + x^2*Z2_ + x^3*Z3_ + x^4*Z4_ + x^5*Z5_ + x^6*Z6_
")
assert result("F", 1) =~ expr("
x*w(1) + x^2*w(2) + x^3*ww(3) + x^4*www(1) + x^5*www(2) + x^6*www(3)
")
*--#] Dictionaries_6 :
*--#[ DiracAlgebla_1 :
*
* Symmetric trace of a gamma5 and 12 regular matrices
*
I m1,...,m12;
F G5,g1,g2;
L F = G5(m1,...,m12);
id G5(?a) = distrib_(-1,4,g1,g2,?a);
id g1(?a) = e_(?a);
id g2(?a) = g_(1,?a);
tracen,1;
.end
assert succeeded?
assert stdout =~ /Generated terms = 51975$/
assert stdout =~ /Terms in output = 51975$/
assert bytesize("F") == 459582 * wordsize
*--#] DiracAlgebla_1 :
*--#[ DiracAlgebla_2 :
*
* Regular trace of a gamma5 and 12 regular matrices
*
I m1,...,m12;
L F = g_(1,5_,m1,...,m12);
trace4,1;
.end
assert succeeded?
assert @stdout =~ /Generated terms = 1053$/
assert @stdout =~ /Terms in output = 1029$/
assert bytesize("F") == 10142 * wordsize
*--#] DiracAlgebla_2 :
*--#[ NotesMetric_1 :
Indices m1,m2,m3,n1,n2,n3,i1,i2,i3;
Cfunction eta(symmetric),e(antisymmetric);
Off Statistics;
*
* We have our own Levi-Civita tensor e
*
Local F = e(m1,m2,m3)*e(m1,m2,m3);
*
* We write the contraction as
*
id e(m1?,m2?,m3?)*e(n1?,n2?,n3?) =
e_(m1,m2,m3)*e_(i1,i2,i3)*
eta(n1,i1)*eta(n2,i2)*eta(n3,i3);
*
* Now we can use the internal workings of the contract:
*
Contract;
Print +s;
.sort;
*
* For specifying a metric we need individual components:
*
Sum i1,1,2,3;
Sum i2,1,2,3;
Sum i3,1,2,3;
Print +s;
.sort;
*
* And now we can provide the metric tensor
*
id eta(1,1) = 1;
id eta(2,2) = 1;
id eta(3,3) = -1;
id eta(1,2) = 0;
id eta(1,3) = 0;
id eta(2,3) = 0;
Print +s;
.end
assert succeeded?
assert result("F",0) =~ expr(<<-EOF
+ eta(i1,i1)*eta(i2,i2)*eta(i3,i3)
- eta(i1,i1)*eta(i2,i3)^2
- eta(i1,i2)^2*eta(i3,i3)
+ 2*eta(i1,i2)*eta(i1,i3)*eta(i2,i3)
- eta(i1,i3)^2*eta(i2,i2)
EOF
)
assert result("F",1) =~ expr(<<-EOF
+ 6*eta(1,1)*eta(2,2)*eta(3,3)
- 6*eta(1,1)*eta(2,3)^2
- 6*eta(1,2)^2*eta(3,3)
+ 12*eta(1,2)*eta(1,3)*eta(2,3)
- 6*eta(1,3)^2*eta(2,2)
EOF
)
assert result("F") =~ expr("-6")
*--#] NotesMetric_1 :
*--#[ NotesMetric_2 :
Indices i1,i2,i3;
FixIndex 1:1,2:1,3:-1;
Off Statistics;
*
Local F = e_(i1,i2,i3)*e_(i1,i2,i3);
Sum i1,1,2,3;
Sum i2,1,2,3;
Sum i3,1,2,3;
Print +s;
.sort
Contract;
Print +s;
.end
assert succeeded?
assert result("F",0) =~ expr("+6*e_(1,2,3)*e_(1,2,3)")
assert result("F") =~ expr("-6")
*--#] NotesMetric_2 :
*--#[ NotesMetric_3 :
Indices i1=0,i2=0,i3=0;
FixIndex 1:1,2:1,3:-1;
Off Statistics;
*
Local F = e_(i1,i2,i3)*e_(i1,i2,i3);
Contract;
Print +s;
.sort
Sum i1,1,2,3;
Sum i2,1,2,3;
Sum i3,1,2,3;
Print +s;
.end
assert succeeded?
assert result("F",0) =~ expr(<<-EOF
+ d_(i1,i1)*d_(i2,i2)*d_(i3,i3)
- d_(i1,i1)*d_(i2,i3)*d_(i2,i3)
- d_(i1,i2)*d_(i1,i2)*d_(i3,i3)
+ 2*d_(i1,i2)*d_(i1,i3)*d_(i2,i3)
- d_(i1,i3)*d_(i1,i3)*d_(i2,i2)
EOF
)
assert result("F") =~ expr("-6")
*--#] NotesMetric_3 :
*--#[ ExtComm_1 :
symbol a,b;
#external "n1" cat -u
#external "n2" cat -u
* cat simply repeats its input. The default prompt is an
* empty line. So we use "\n\n" here -- one "\n" is to finish
* the line, and the next "\n" is the prompt:
#toexternal "(a+b)^2\n\n"
#setexternal `n1'
* For this channel the prompt will be "READY\n":
#toexternal "(a+b)^3\nREADY\n"
#setexternal `n2'
* Set the default prompt:
#prompt
Local aPLUSbTO2=
#fromexternal
;
#setexternal `n1'
#prompt READY
Local aPLUSbTO3=
#fromexternal
;
#rmexternal `n1'
#rmexternal `n2'
Print;
.end
# This gives Valgrind errors (3 memory leaks) on Travis CI
# (osx-gcc-valgrind-parvorm), but cleanly works on Linux with mpich 3.2.
# Might be an OS- or implementation-specific bug.
# Update (22 Sep 2017): Now I see even for Linux (both on Travis CI and
# a desktop PC) each child process leads to 1 memory leak. Best to skip this
# test for Valgrind.
#pend_if valgrind?
assert succeeded?
assert result("aPLUSbTO2") =~ expr("b^2 + 2*a*b + a^2")
assert result("aPLUSbTO3") =~ expr("b^3 + 3*a*b^2 + 3*a^2*b + a^3")
*--#] ExtComm_1 :
*--#[ Diagrams_1 :
Vectors Q1,Q2,p1,...,p8;
Set QQ:Q1,Q2;
Set PP:p1,...,p8;
#define LOOPS "2"
Local F = topologies_(`LOOPS',2,{3,},QQ,PP);
Print +f +s;
.end
assert succeeded?
assert result("F") =~ expr("
+ node_(0,-Q1)*node_(1,-Q2)*node_(2,Q1,-p1,-p2)*node_(3,Q2,p1,-p3)*
node_(4,p2,-p4,-p5)*node_(5,p3,p4,p5)
+ node_(0,-Q1)*node_(1,-Q2)*node_(2,Q1,-p1,-p2)*node_(3,p1,-p3,-p4)*
node_(4,p2,p3,-p5)*node_(5,Q2,p4,p5)
")
*--#] Diagrams_1 :
*--#[ Diagrams_2 :
Vectors Q1,Q2,p1,...,p8;
Set QQ:Q1,Q2;
Set PP:p1,...,p8;
#define LOOPS "2"
Local F = topologies_(`LOOPS',2,{3,4},QQ,PP);
Print +f +s;
.end
assert succeeded?
assert result("F") =~ expr("
+ node_(0,-Q1)*node_(1,-Q2)*node_(2,Q1,Q2,-p1,-p2)*node_(3,p1,p2,-p3,p3
)
+ node_(0,-Q1)*node_(1,-Q2)*node_(2,Q1,-p1,-p2)*node_(3,Q2,p1,-p3)*
node_(4,p2,p3,-p4,p4)
+ node_(0,-Q1)*node_(1,-Q2)*node_(2,Q1,-p1,-p2)*node_(3,Q2,p1,-p3)*
node_(4,p2,-p4,-p5)*node_(5,p3,p4,p5)
+ node_(0,-Q1)*node_(1,-Q2)*node_(2,Q1,-p1,-p2)*node_(3,Q2,-p3,-p4)*
node_(4,p1,p2,p3,p4)
+ node_(0,-Q1)*node_(1,-Q2)*node_(2,Q1,-p1,-p2)*node_(3,p1,-p3,-p4)*
node_(4,Q2,p2,p3,p4)
+ node_(0,-Q1)*node_(1,-Q2)*node_(2,Q1,-p1,-p2)*node_(3,p1,-p3,-p4)*
node_(4,p2,p3,-p5)*node_(5,Q2,p4,p5)
+ node_(0,-Q1)*node_(1,-Q2)*node_(2,Q1,-p1,-p2,-p3)*node_(3,Q2,p1,p2,p3
)
+ node_(0,-Q1)*node_(1,-Q2)*node_(2,-p1,-p2,-p3)*node_(3,Q2,p1,-p4)*
node_(4,Q1,p2,p3,p4)
+ node_(0,-Q1)*node_(1,-Q2)*node_(2,-p1,-p2,-p3)*node_(3,p1,p2,-p4)*
node_(4,Q1,Q2,p3,p4)
")
*--#] Diagrams_2 :
*--#[ Diagrams_3 :
Vectors Q1,Q2,p1,...,p8;
Set QQ:Q1,Q2;
Set PP:p1,...,p8;
#define LOOPS "2"
Local F = topologies_(`LOOPS',-2,{3,4},QQ,PP);
Print +f +s;
.end
assert succeeded?
assert result("F") =~ expr("
+ node_(0,-Q1)*node_(1,-Q2)*node_(2,Q1,Q2,-p1,-p2)*node_(3,p1,p2,-p3,p3
)
+ node_(0,-Q1)*node_(1,-Q2)*node_(2,Q1,-p1,-p2)*node_(3,Q2,p1,-p3)*
node_(4,p2,p3,-p4,p4)
+ node_(0,-Q1)*node_(1,-Q2)*node_(2,Q1,-p1,-p2)*node_(3,Q2,p1,-p3)*
node_(4,p2,-p4,-p5)*node_(5,p3,p4,p5)
+ node_(0,-Q1)*node_(1,-Q2)*node_(2,Q1,-p1,-p2)*node_(3,Q2,-p3,-p4)*
node_(4,p1,p2,p3,p4)
+ node_(0,-Q1)*node_(1,-Q2)*node_(2,Q1,-p1,-p2)*node_(3,Q2,-p3,-p4)*
node_(4,p1,p3,-p5)*node_(5,p2,p4,p5)
+ node_(0,-Q1)*node_(1,-Q2)*node_(2,Q1,-p1,-p2)*node_(3,p1,-p3,-p4)*
node_(4,Q2,p2,p3,p4)
+ node_(0,-Q1)*node_(1,-Q2)*node_(2,Q1,-p1,-p2,-p3)*node_(3,Q2,p1,p2,p3
)
+ node_(0,-Q1)*node_(1,-Q2)*node_(2,-p1,-p2,-p3)*node_(3,p1,p2,-p4)*
node_(4,Q1,Q2,p3,p4)
")
*--#] Diagrams_3 :
*--#[ Diagrams_4 :
Vectors Q1,Q2,p1,...,p17;
Set QQ:Q1,Q2;
Set PP:p1,...,p17;
#define LOOPS "6"
Local F = topologies_(`LOOPS',-2,{3,},QQ,PP);
.end
assert succeeded?
assert nterms("F") == 2793
*--#] Diagrams_4 :
*--#[ Diagrams_5 :
Vectors Q1,Q2,p1,...,p17;
Set QQ:Q1,Q2;
Set PP:p1,...,p17;
#define LOOPS "6"
Local F = topologies_(`LOOPS',2,{3,},QQ,PP);
.end
assert succeeded?
assert nterms("F") == 4999
*--#] Diagrams_5 :
form-master/check/features.frm 0000664 0000000 0000000 00000046243 13565763364 0016746 0 ustar 00root root 0000000 0000000 #ifndef `TEST'
#message Use -D TEST=XXX
#terminate
#else
#include `NAME_' # `TEST'
#endif
.end
*--#[ divmod_1 :
* Test div_, rem_ functions for monic univariate polynomials
#-
S x1;
L F1 = (x1+1)^2;
L F2 = (x1+1);
.sort
L F3 = div_(F1,F2);
L F4 = rem_(F1,F2);
P;
.end
assert succeeded?
assert result("F3") =~ expr("1 + x1")
assert result("F4") =~ expr("0")
*--#] divmod_1 :
*--#[ divmod_2 :
* Test div_, rem_ functions for non-monic univariate polynomials with remainder
#-
S x1;
L F1 = (2*x1+1)^2+3*x1+5;
L F2 = (2*x1+1);
.sort
L F3 = div_(F1,F2);
L F4 = rem_(F1,F2);
P;
.end
assert succeeded?
assert result("F3") =~ expr("5/2 + 2*x1")
assert result("F4") =~ expr("7/2")
*--#] divmod_2 :
*--#[ divmod_3 :
* Test div_, rem_ functions for non-monic multivariate polynomials without remainder
#-
S x1,x2;
L F1 = (2*x1*x2+1*x1)^2;
L F2 = (2*x1*x2+1*x1);
.sort
L F3 = div_(F1,F2);
L F4 = rem_(F1,F2);
P;
.end
assert succeeded?
assert result("F3") =~ expr("x1 + 2*x1*x2")
assert result("F4") =~ expr("0")
*--#] divmod_3 :
*--#[ divmod_4 :
* Test div_, rem_ functions for non-monic multivariate polynomials with remainder
#-
S x1,x2,x3;
L F1 = -7*x1*x2^9*x3+5*x1*x2^10*x3-3*x1^2*x2^3*x3^2+35*x1^2*x2^4*x3^4
-42*x1^2*x2^5*x3^2-25*x1^2*x2^5*x3^4+30*x1^2*x2^6*x3^2-8*x1^4*x2^5*x3^3+40*x1^5*x3^6
-48*x1^5*x2*x3^4+3*x1^6*x2^2*x3;
L F2 = x1*x2^5-5*x1^2*x3^3+6*x1^2*x2*x3;
L F3 = div_(F1,F2);
L F4 = rem_(F1,F2);
P;
.end
assert succeeded?
assert result("F3") =~ expr("
- 25/72*x3^5 + 12969970703125/2742118830047232*x3^30 - 5/12*x2*x3^3 +
2593994140625/457019805007872*x2*x3^28 - 1/2*x2^2*x3 + 518798828125/
76169967501312*x2^2*x3^26 + 103759765625/12694994583552*x2^3*x3^24 - 7*
x2^4*x3 + 20751953125/2115832430592*x2^4*x3^22 + 5*x2^5*x3 + 29052734375/
2821109907456*x2^5*x3^20 + 830078125/78364164096*x2^6*x3^18 + 830078125/
78364164096*x2^7*x3^16 + 33203125/3265173504*x2^8*x3^14 + 6640625/
725594112*x2^9*x3^12 + 5703125/725594112*x2^10*x3^10 + 765625/120932352*
x2^11*x3^8 + 15625/3359232*x2^12*x3^6 + 625/209952*x2^13*x3^4 + 875/
559872*x2^14*x3^2 + 25/46656*x2^15 - 20751953125/2821109907456*x1*x3^23
- 4150390625/470184984576*x1*x2*x3^21 - 830078125/78364164096*x1*x2^2*
x3^19 - 166015625/13060694016*x1*x2^3*x3^17 - 33203125/2176782336*x1*
x2^4*x3^15 - 11328125/725594112*x1*x2^5*x3^13 - 78125/5038848*x1*x2^6*
x3^11 - 296875/20155392*x1*x2^7*x3^9 - 21875/1679616*x1*x2^8*x3^7 - 625/
62208*x1*x2^9*x3^5 - 625/93312*x1*x2^10*x3^3 - 25/7776*x1*x2^11*x3 +
9765625/725594112*x1^2*x3^16 + 1953125/120932352*x1^2*x2*x3^14 + 390625/
20155392*x1^2*x2^2*x3^12 + 78125/3359232*x1^2*x2^3*x3^10 + 15625/559872*
x1^2*x2^4*x3^8 + 625/23328*x1^2*x2^5*x3^6 + 125/5184*x1^2*x2^6*x3^4 + 25/
1296*x1^2*x2^7*x3^2 + 5/432*x1^2*x2^8 - 8*x1^3*x3^3 - 3125/93312*x1^3*
x3^9 - 625/15552*x1^3*x2*x3^7 - 125/2592*x1^3*x2^2*x3^5 - 25/432*x1^3*
x2^3*x3^3 - 5/72*x1^3*x2^4*x3 + 5/12*x1^4*x3^2 + 1/2*x1^4*x2
")
assert result("F4") =~ expr("
25/72*x1*x2^5*x3^5 - 12969970703125/2742118830047232*x1*x2^5*x3^30 + 5/
12*x1*x2^6*x3^3 - 2593994140625/457019805007872*x1*x2^6*x3^28 + 1/2*x1*
x2^7*x3 - 518798828125/76169967501312*x1*x2^7*x3^26 - 103759765625/
12694994583552*x1*x2^8*x3^24 - 20751953125/2115832430592*x1*x2^9*x3^22
- 29052734375/2821109907456*x1*x2^10*x3^20 - 830078125/78364164096*x1*
x2^11*x3^18 - 830078125/78364164096*x1*x2^12*x3^16 - 33203125/3265173504
*x1*x2^13*x3^14 - 6640625/725594112*x1*x2^14*x3^12 - 5703125/725594112*
x1*x2^15*x3^10 - 765625/120932352*x1*x2^16*x3^8 - 15625/3359232*x1*x2^17
*x3^6 - 625/209952*x1*x2^18*x3^4 - 875/559872*x1*x2^19*x3^2 - 25/46656*
x1*x2^20 - 125/72*x1^2*x3^8 + 64849853515625/2742118830047232*x1^2*x3^33
- 103759765625/2821109907456*x1^3*x3^26 - 5/432*x1^3*x2^13 + 48828125/
725594112*x1^4*x3^19 - 15625/93312*x1^5*x3^12 - 1/2*x1^5*x2^6 + 25/12*
x1^6*x3^5
")
*--#] divmod_4 :
*--#[ partitions_ :
* Test partitions function
#-
V p1,p2,p3,p4,p5,p6;
CF f1,f2,f3;
L F1 = partitions_(3,f1,2,f1,2,f1,2,p1,p2,p3,p4,p5,p6) - dd_(p1,p2,p3,p4,p5,p6);
L F2 = partitions_(0,f1,2,p1,p2,p3,p4,p5,p6) - dd_(p1,p2,p3,p4,p5,p6);
L F3 = partitions_(4,f1,2,f1,2,f2,1,f3,1,p1,p1,p1,p1,p1,p1) - 90*f1(p1,p1)^2*f2(p1)*f3(p1);
L F4 = partitions_(2,f1,2,f2,0,p1,p2,p3,p4,p5,p6) - distrib_(1,2,f1,f2,p1,p2,p3,p4,p5,p6);
id p1?.p2? = f1(p1,p2); * for dd_
P;
.end
assert succeeded?
assert result("F1") =~ expr("0")
assert result("F2") =~ expr("0")
assert result("F3") =~ expr("0")
assert result("F4") =~ expr("0")
*--#] partitions_ :
*--#[ AppendPath :
#include foo/foo1.h
* foo/bar/p1.prc
#call p1
P;
.end
#:path foo:bar
#include foo1.h
* foo/bar/p2.prc
#call p2
P;
.end
#:path foo:bar
#include foo2.h
* bar/p1.prc
#call p1
P;
.end
#prepare write "foo/foo1.h", "#prependpath bar\n"
#prepare write "foo/foo2.h", "#appendpath bar\n"
#prepare write "foo/bar/p1.prc", "#procedure p1()\nL F=1234;\n#endprocedure\n"
#prepare write "foo/bar/p2.prc", "#procedure p2()\nL G=5678;\n#endprocedure\n"
#prepare write "bar/p1.prc", "#procedure p1()\nL H=9012;\n#endprocedure\n"
assert succeeded?
assert result("F") =~ expr("1234")
assert result("G") =~ expr("5678")
assert result("H") =~ expr("9012")
*--#] AppendPath :
*--#[ dedup :
* Test deduplication
#-
Auto S n;
Auto V p;
CF f1,f2,f3,f,g;
T t1,t2,t3;
L F1 =
#do i = 1,20
+ranperm_(f,,...,)
#enddo
;
L F2 = f1(1,2,3,p,1,1,2,2,p);
L F3 = f2(1,2,3,p,1,1,2,2,p);
L F4 = f3(1,2,3,p,1,1,2,2,p);
L F5 = t1(1,2,3,p,1,1,2,2,p);
L F6 = t2(1,2,3,p,1,1,2,2,p);
L F7 = t3(1,2,3,p,1,1,2,2,p);
L F8 = f1(1,2,1,100000000,n^4,100,n^4,n^5,-10000,p1.p2,p6,p1.p2);
id f(?a) = f(?a)*g(?a);
transform f,dedup(1,last);
repeat id g(?a,p?,?b,p?,?c) = g(?a,p,?b,?c);
id f(?a)*g(?a) = 0;
* Test functions
transform f1,dedup(1,last);
transform f2,dedup(3,last);
transform f3,dedup(1,5);
* Test tensors
transform t1,dedup(1,last);
transform t2,dedup(3,last);
transform t3,dedup(1,5);
P;
.end
assert succeeded?
assert result("F1") =~ expr("0")
assert result("F2") =~ expr("f1(1,2,3,p)")
assert result("F3") =~ expr("f2(1,2,3,p,1,2)")
assert result("F4") =~ expr("f3(1,2,3,p,1,2,2,p)")
assert result("F5") =~ expr("t1(1,2,3,p)")
assert result("F6") =~ expr("t2(1,2,3,p,1,2)")
assert result("F7") =~ expr("t3(1,2,3,p,1,2,2,p)")
assert result("F8") =~ expr("f1(1,2,100000000,n^4,100,n^5,-10000,p1.p2,p6)")
*--#] dedup :
*--#[ CoToTensor :
V p1,p2,q1,q2,nosquare;
Set pp:p1,p2;
CF f;
T Q1,functions;
#$q1 = q1;
#$Q1 = Q1;
L F0 = f(q1,q2) * p1.q1 * p2.q1 * q1.q1 * q1.q2;
#do i={1,...,7,11,...,17,51,61,71,72}
L F`i' = F0;
#enddo
inexpression F1;
totensor q1,Q1;
endinexpression;
inexpression F2;
totensor nosquare,q1,Q1;
endinexpression;
inexpression F3;
totensor functions,q1,Q1;
endinexpression;
inexpression F4;
totensor nosquare,functions,q1,Q1;
endinexpression;
inexpression F5;
totensor !pp,q1,Q1;
endinexpression;
inexpression F6;
totensor !{p1},q1,Q1;
endinexpression;
inexpression F7;
totensor nosquare,functions,!pp,q1,Q1;
endinexpression;
inexpression F11;
totensor $q1,Q1;
endinexpression;
inexpression F12;
totensor q1,$Q1;
endinexpression;
inexpression F13;
totensor $q1,$Q1;
endinexpression;
inexpression F14;
totensor Q1,q1;
endinexpression;
inexpression F15;
totensor $Q1,q1;
endinexpression;
inexpression F16;
totensor Q1,$q1;
endinexpression;
inexpression F17;
totensor $Q1,$q1;
endinexpression;
inexpression F51;
totensor !{p1,p2},q1,Q1;
endinexpression;
inexpression F61;
totensor !p1,q1,Q1;
endinexpression;
inexpression F71;
multiply replace_(q1,nosquare);
totensor nosquare,functions;
endinexpression;
inexpression F72;
multiply replace_(q1,nosquare);
totensor nosquare,functions,nosquare,functions;
endinexpression;
P;
.end
assert succeeded?
assert result("F0") =~ expr("f(q1,q2)*p1.q1*p2.q1*q1.q1*q1.q2")
assert result("F1") =~ expr("f(q1,q2)*Q1(p1,p2,q2,N1_?,N1_?)")
assert result("F2") =~ expr("f(q1,q2)*Q1(p1,p2,q2)*q1.q1")
assert result("F3") =~ expr("f(N1_?,q2)*Q1(p1,p2,q2,N1_?,N2_?,N2_?)")
assert result("F4") =~ expr("f(N1_?,q2)*Q1(p1,p2,q2,N1_?)*q1.q1")
assert result("F5") =~ expr("f(q1,q2)*Q1(q2,N1_?,N1_?)*p1.q1*p2.q1")
assert result("F6") =~ expr("f(q1,q2)*Q1(p2,q2,N1_?,N1_?)*p1.q1")
assert result("F7") =~ expr("f(N1_?,q2)*Q1(q2,N1_?)*p1.q1*p2.q1*q1.q1")
assert result("F1") == result("F11")
assert result("F1") == result("F12")
assert result("F1") == result("F13")
assert result("F1") == result("F14")
assert result("F1") == result("F15")
assert result("F1") == result("F16")
assert result("F1") == result("F17")
assert result("F5") == result("F51")
assert result("F6") == result("F61")
assert result("F71") =~ expr("f(nosquare,q2)*functions(p1,p2,q2,N1_?,N1_?)")
assert result("F72") =~ expr("f(N1_?,q2)*functions(p1,p2,q2,N1_?)*nosquare.nosquare")
*--#] CoToTensor :
*--#[ Issue49 :
* Add mul_ function for polynomial multiplications
Symbols x,y,z;
#$p = (1+x+y+z)^4;
#$q = $p+1;
#$r = mul_($p,$q);
L r1 = $r;
L r2 = $p^2 + $p;
.sort
Drop;
L Zero = r1 - r2;
P;
.end
assert succeeded?
assert result("Zero") =~ expr("0")
*--#] Issue49 :
*--#[ Issue72 :
* "Setups: PATHVALUE not yet implemented"
#:incdir foo
#:path
* foo/p1.prc
#call p1()
P;
.end
#:incdir
#:path foo/bar
* foo/bar/p1.prc
#call p1()
P;
.end
#prepare write "foo/p1.prc", "#procedure p1()\nL F=12345;\n#endprocedure\n"
#prepare write "foo/bar/p1.prc", "#procedure p1()\nL G=123456;\n#endprocedure\n"
assert succeeded?
assert result("F") =~ expr("12345")
assert result("G") =~ expr("123456")
*--#] Issue72 :
*--#[ Issue84 :
* Set to match with a vector
V p,p1,...,p6;
CF f,g,h;
L F = f(p1,-p1,p2,-p2);
id,all,f(?a,-p?vector_,?b) = f(?a,p,?b)*g(p);
Print +s;
.end
assert succeeded?
assert result("F") =~ expr("
+ f(p1,p1,p2,-p2)*g(p1)
+ f(p1,-p1,p2,p2)*g(p2)
")
*--#] Issue84 :
*--#[ Issue86_1 :
* Feature request: take/drop n-th argument of list
* [with zero-dimensional tables]
CF f;
S x,n,n1,n2;
* Get [1,1]. nargs >= 1.
Table first(f?(x?,?a));
Fill first = f(x);
* Get [last,last]. nargs >= 1.
Table last(f?(?a,x?));
Fill last = f(x);
* Get [2,last]. nargs >= 1.
Table rest(f?(x?,?a));
Fill rest = f(?a);
* Get [1,last-1]. nargs >= 1.
Table most(f?(?a,x?));
Fill most = f(?a);
* Join two functions.
Table join(f?(?a),f?(?b));
Fill join = f(?a,?b);
* Rotate left by n. nargs >= 1.
Table roll(n?int_,f?(?a));
Fill roll =
+ delta_(n) * f(?a)
+ thetap_(n) * roll(n-1,join(rest(f(?a)),first(f(?a))))
+ thetap_(-n) * roll(n+1,join(last(f(?a)),most(f(?a))))
;
* Get [1,n]. 1 <= n <= nargs.
Table firstn(n?pos_,f?(?a));
Table firstnimpl(n?pos0_,f?(?a),f?(x?,?b));
Fill firstn = firstnimpl(n,f,f(?a,dum_));
Fill firstnimpl =
+ delta_(n) * f(?a)
+ thetap_(n) * firstnimpl(n-1,f(?a,x),f(?b))
;
* Get the n-th argument. 1 <= n <= nargs.
Table take(n?pos_,f?(?a));
Fill take = first(roll(n-1,f(?a)));
* Drop the n-th argument. 1 <= n <= nargs.
Table drop(n?pos_,f?(?a));
Fill drop = roll(1-n,most(roll(n,f(?a))));
* Get [n1,n2]. Negative indices count from the end. 1 <= n1 <= n2 <= nargs.
Table slice(n1?!{0,},n2?!{0,},f?(?a));
Fill slice =
+ thetap_(n1) * thetap_(n2) * firstn(n2-n1+1,roll(n1-1,f(?a)))
+ thetap_(n1) * thetap_(-n2) * slice(n1,nargs_(?a)+n2+1,f(?a))
+ thetap_(-n1) * thetap_(n2) * slice(nargs_(?a)+n1+1,n2,f(?a))
+ thetap_(-n1) * thetap_(-n2) * slice(nargs_(?a)+n1+1,nargs_(?a)+n2+1,f(?a))
;
L F0 = f(1,...,9);
L F1 = first(F0);
L F2 = last(F0);
L F3 = rest(F0);
L F4 = most(F0);
L F5 = roll(0,F0);
L F6 = roll(2,F0);
L F7 = roll(-2,F0);
L F8 = firstn(3,F0);
L F9 = take(3,F0);
L F10 = drop(3,F0);
L F11 = slice(3,3,F0);
L F12 = slice(3,6,F0);
L F13 = slice(3,-4,F0);
L F14 = slice(-7,6,F0);
L F15 = slice(-7,-4,F0);
P;
.end
assert succeeded?
assert result("F0") =~ expr("f(1,2,3,4,5,6,7,8,9)")
assert result("F1") =~ expr("f(1)")
assert result("F2") =~ expr("f(9)")
assert result("F3") =~ expr("f(2,3,4,5,6,7,8,9)")
assert result("F4") =~ expr("f(1,2,3,4,5,6,7,8)")
assert result("F5") =~ expr("f(1,2,3,4,5,6,7,8,9)")
assert result("F6") =~ expr("f(3,4,5,6,7,8,9,1,2)")
assert result("F7") =~ expr("f(8,9,1,2,3,4,5,6,7)")
assert result("F8") =~ expr("f(1,2,3)")
assert result("F9") =~ expr("f(3)")
assert result("F10") =~ expr("f(1,2,4,5,6,7,8,9)")
assert result("F11") =~ expr("f(3)")
assert result("F12") =~ expr("f(3,4,5,6)")
assert result("F13") =~ expr("f(3,4,5,6)")
assert result("F14") =~ expr("f(3,4,5,6)")
assert result("F15") =~ expr("f(3,4,5,6)")
*--#] Issue86_1 :
*--#[ Issue86_2 :
* [with the Translate statement]
CF f;
L F0 = f(1,2,3,4,5,6,7,8,9);
#do i=1,4
#do j=1,8
L F`i'`j' = F0;
#enddo
#enddo
$n1 = 3;
$n2 = 5;
$n3 = 4;
#procedure Test(F,trans)
inexpression `F'1;
transform,f,`trans'(3,5);
endinexpression;
inexpression `F'2;
transform,f,`trans'(3,$n2);
endinexpression;
inexpression `F'3;
transform,f,`trans'(3,last-4);
endinexpression;
inexpression `F'4;
transform,f,`trans'(3,last-$n3);
endinexpression;
inexpression `F'5;
transform,f,`trans'($n1,5);
endinexpression;
inexpression `F'6;
transform,f,`trans'($n1,$n2);
endinexpression;
inexpression `F'7;
transform,f,`trans'($n1,last-4);
endinexpression;
inexpression `F'8;
transform,f,`trans'($n1,last-$n3);
endinexpression;
#endprocedure
#call Test(F1,dropargs)
#call Test(F2,selectargs)
#call Test(F3,addargs)
#call Test(F4,mulargs)
P;
ModuleOption local, $n1,$n2,$n3;
.end
assert succeeded?
assert result("F0") =~ expr("f(1,2,3,4,5,6,7,8,9)")
assert result("F11") =~ expr("f(1,2,6,7,8,9)")
assert result("F21") =~ expr("f(3,4,5)")
assert result("F31") =~ expr("f(1,2,12,6,7,8,9)")
assert result("F41") =~ expr("f(1,2,60,6,7,8,9)")
assert result("F12") == result("F11")
assert result("F13") == result("F11")
assert result("F14") == result("F11")
assert result("F15") == result("F11")
assert result("F16") == result("F11")
assert result("F17") == result("F11")
assert result("F18") == result("F11")
assert result("F22") == result("F21")
assert result("F23") == result("F21")
assert result("F24") == result("F21")
assert result("F25") == result("F21")
assert result("F26") == result("F21")
assert result("F27") == result("F21")
assert result("F28") == result("F21")
assert result("F32") == result("F31")
assert result("F33") == result("F31")
assert result("F34") == result("F31")
assert result("F35") == result("F31")
assert result("F36") == result("F31")
assert result("F37") == result("F31")
assert result("F38") == result("F31")
assert result("F42") == result("F41")
assert result("F43") == result("F41")
assert result("F44") == result("F41")
assert result("F45") == result("F41")
assert result("F46") == result("F41")
assert result("F47") == result("F41")
assert result("F48") == result("F41")
*--#] Issue86_2 :
*--#[ Issue87 :
* Feature request: (anti)bracketing w.r.t. a set
s a, b, c, d;
set ab: a, b;
L test = (a + b)*(c + d);
b ab;
print +s;
.end
assert succeeded?
assert result("test") =~ expr("
+ b * (
+ d
+ c
)
+ a * (
+ d
+ c
)
")
*--#] Issue87 :
*--#[ Issue135_1 :
* "Assign instructions cannot occur inside statements" without inside statements
L F =
#do i=1,10
#$x = `i';
+ `$x'
#enddo
;
P;
.end
assert succeeded?
assert result("F") =~ expr("55")
*--#] Issue135_1 :
*--#[ Issue135_2 :
S a1,...,a10;
L F =
#do i = 1,10
#$x = `i'*a`i'
+2;
+`$x'
#enddo
;
P;
.end
assert succeeded?
assert result("F") =~ expr("
20 + 10*a10 + 9*a9 + 8*a8 + 7*a7 + 6*a6 + 5*a5 + 4*a4 + 3*a3 + 2*a2 + a1
")
*--#] Issue135_2 :
*--#[ Issue135_3 :
S a1,...,a10,x;
CF f;
CTable sparse,tab(1);
#do i=1,10
Fill tab(`i') = f(`i'*a`i') + 2;
#enddo
L F =
#do i = 1,10
#$tmp = tab(`i');
#inside $tmp
id f(x?) = x;
#endinside
+ (`$tmp')
#enddo
;
P;
.end
assert succeeded?
assert result("F") =~ expr("
20 + 10*a10 + 9*a9 + 8*a8 + 7*a7 + 6*a6 + 5*a5 + 4*a4 + 3*a3 + 2*a2 + a1
")
*--#] Issue135_3 :
*--#[ Issue137_1 :
* New command: ArgToExtraSymbol (,ToNumber)
S a,b;
CF f;
L F = f(1) + f(a) + f(b) + f(a+b);
ArgToExtraSymbol f;
P;
.end
assert succeeded?
assert result("F") =~ expr("f(Z4_) + f(Z3_) + f(Z2_) + f(Z1_)")
*--#] Issue137_1 :
*--#[ Issue137_2 :
S a,b;
CF f;
L F = f(1) + f(a) + f(b) + f(a+b);
ArgToExtraSymbol,ToNumber,f;
P;
.end
assert succeeded?
assert result("F") =~ expr("f(1) + f(2) + f(3) + f(4)")
*--#] Issue137_2 :
*--#[ Issue137_3 :
CF f;
S s;
I i;
V v;
* Fast notation.
L F = f(0) + f(1) + f(-1) + f(s) + f(i) + f(v) + f(-v) + f(f);
argtoextrasymbol;
P;
.end
assert succeeded?
assert result("F") =~ expr("
f(Z8_) + f(Z7_) + f(Z6_) + f(Z5_) + f(Z4_) + f(Z3_) + f(Z2_) + f(Z1_)
")
*--#] Issue137_3 :
*--#[ Issue137_4 :
#:threadbucketsize 10
#:processbucketsize 10
CF f;
Auto S x;
* NOTE: Large N gives another problem with ParFORM (#141).
#define N "500"
L F0 =
#do i=1,`N'
+ f(1+x`i') * f(1+x{`i'+100}) * f(1+x{`i'+200})
#enddo
;
.sort
Hide;
L F1 = F0;
.sort
* If all workers fail to share an unique mapping in a consistent way,
* the following code gives a non-zero result or a crash.
argtoextrasymbol;
.sort
argument;
frompolynomial;
endargument;
.sort
Drop;
L ZERO = F1 - F0;
P;
.end
assert succeeded?
assert result("ZERO") =~ expr("0")
*--#] Issue137_4 :
*--#[ Issue175_1 :
* Loop over currently active expressions #175
L FF = 1;
L [FF|a,b] = 1;
L [FF,[GG]] = 1;
#do e={`activeexprnames_'}
L `e' = `e' + 1;
#enddo
L N = `numactiveexprs_';
P;
.end
assert succeeded?
assert result("FF") =~ expr("2")
assert result("[FF|a,b]") =~ expr("2")
assert result("[FF,[GG]]") =~ expr("2")
assert result("N") =~ expr("3")
*--#] Issue175_1 :
*--#[ Issue175_2 :
L F1 = 1;
L F2 = 1;
L F3 = 1;
L F1 = 1; * redefine in the same module!
*.sort ;* workaround
#message `numactiveexprs_'
#message `activeexprnames_'
#do e={`activeexprnames_'}
L `e' = `e' + 1;
#enddo
P;
.end
assert succeeded?
assert result("F1") =~ expr("2")
assert result("F2") =~ expr("2")
assert result("F3") =~ expr("2")
*--#] Issue175_2 :
*--#[ Issue175_3 :
L F1 = 1;
L F2 = 1;
L F3 = 1;
.sort
L F1 = 1; * replace an existing expression!
*.sort ;* workaround
#message `numactiveexprs_'
#message `activeexprnames_'
#do e={`activeexprnames_'}
L `e' = `e' + 1;
#enddo
P;
.end
assert succeeded?
assert result("F1") =~ expr("2")
assert result("F2") =~ expr("2")
assert result("F3") =~ expr("2")
*--#] Issue175_3 :
*--#[ Issue175_4 :
CF F1,F2,F3;
L [F1(1,1,1,1)] = F1(1,1,1,1);
L [F2(-1,1,1,1)] = F2(-1,1,1,1);
.sort
* Redefine.
Local [F1(1,1,1,1)] = F1(1,1,1,1);
.sort
#message `numactiveexprs_'
#message `activeexprnames_'
#do e={`activeexprnames_'}
L `e' = `e' + 1;
#enddo
P;
.end
assert succeeded?
assert result("[F1(1,1,1,1)]") =~ expr("1 + F1(1,1,1,1)")
assert result("[F2(-1,1,1,1)]") =~ expr("1 + F2(-1,1,1,1)")
*--#] Issue175_4 :
*--#[ Issue187 :
* What is the fastest equivalent of Foreach in FORM?
* distrib_ generates combinations in lexicographical order (in the given
* arguments.)
S x1,...,x5;
CF f;
L F = f(x2,x5,x3,x1,x4);
#$counter = 0;
id f(?a$a) = 1;
term;
multiply distrib_(1,3,f,dummy_,$a);
$counter = $counter + 1;
id f(?a) = f($counter,?a);
endterm;
P +s;
ModuleOption noparallel;
.end
assert succeeded?
assert result("F") =~ expr("
+ f(1,x2,x5,x3)
+ f(2,x2,x5,x1)
+ f(3,x2,x5,x4)
+ f(4,x2,x3,x1)
+ f(5,x2,x3,x4)
+ f(6,x2,x1,x4)
+ f(7,x5,x3,x1)
+ f(8,x5,x3,x4)
+ f(9,x5,x1,x4)
+ f(10,x3,x1,x4)
")
*--#] Issue187 :
form-master/check/fixes.frm 0000664 0000000 0000000 00000130266 13565763364 0016245 0 ustar 00root root 0000000 0000000 #ifndef `TEST'
#message Use -D TEST=XXX
#terminate
#else
#include `NAME_' # `TEST'
#endif
.end
*--#[ SparseTable1 :
#ifndef `TableSize'
#define TableSize "10"
#endif
* Bugs reported 2004-04-06 by Misha Tentukov
* PrintTable and FillExpression did not work with non-sparse tables
* Fixed 2005-09-27
cf f;
s x;
ctable Tab(1:`TableSize');
ctable TabNew(1:`TableSize');
#do i=1,`TableSize',1
Fill Tab(`i')=f(`i');
.sort
#enddo
* BUG1 (not all elements are printed):
PrintTable Tab;
bracket x;
.sort
L expr1=table_(Tab,x);
print;
.sort
bracket x;
.sort
* BUG 2 ( seems only TabNew(1) is ok - further everything is broken):
Fillexpression TabNew=expr1(x);
.sort
#do i=1,`TableSize'
L e`i'=TabNew(`i');
#enddo
print;
.sort
.end
assert succeeded?
assert result("expr1") =~ expr("f(1)*x + f(2)*x^2 + f(3)*x^3 + f(4)*x^4 + f(5)*x^5 + f(6)*x^6 + f(7)*x^7 + f(8)*x^8 + f(9)*x^9 + f(10)*x^10")
assert result("e10") =~ expr("f(10)")
*--#] SparseTable1 :
*--#[ SymNonZero :
* Bug reported 2005-09-27 by Aneesh Manohar
* Symmetrize did not make expression y equal to zero
* Fixed 2005-10-09
cfunctions f,g;
symbols a,b;
local x=f(a,b)-f(b,a);
local y=f(g(a),b)-f(b,g(a));
symmetrize f;
.sort
print;
.end
assert succeeded?
assert result("x") =~ expr("0")
assert result("y") =~ expr("0")
*--#] SymNonZero :
*--#[ NegDimension :
* Parser accepted negative numbers as arguments to Dimension, Tracen, ...
* Fixed 2009-09-08
Dimension -1;
I i;
L f = d_(i,i);
print;
.end
assert compile_error?
*--#] NegDimension :
*--#[ Transform-mulargs_1 :
CF f;
Auto S x;
L F = f(,...,);
* Consume ebuf. (Assume the default setup parameters on 64-bit systems.)
#do i=1,10
id f(?a,x1?,x2?,?c) = f(?a,x1,x2,?c);
#enddo
* This extends ebuf.
transform f,mulargs(1,last);
* Crashed here.
id f(?a) = f(?a);
* Check a "hash", just in case.
multiply replace_(,...,);
id f(x?) = x;
P;
.end
# Only for 64-bit systems. Otherwise "Sorted function argument too long".
#require wordsize == 4
assert succeeded?
assert result("F") =~ expr("2187")
*--#] Transform-mulargs_1 :
*--#[ Forum3t187 :
* bug in argument environment? [function specified by a set]
CF f1,f2,f3;
Set ff1: f1;
Set ff2: f2;
Set ff3: f3;
L F = f1(1) + f2(2) + f3(3);
argument ff2;
discard;
endargument;
P;
.end
assert succeeded?
assert result("F") =~ expr("f1(1)+f2(0)+f3(3)")
*--#] Forum3t187 :
*--#[ Issue8 :
* Bug with function replacement
Symbols a, b;
Functions fun, nDUMMY1, nDUMMY2;
Local expr= fun(a)*fun(b) ;
Id nDUMMY1?(?args1) * nDUMMY2?(?args2) = 1;
.sort
Print;
.end
assert succeeded?
assert result("expr") =~ expr("1")
*--#] Issue8 :
*--#[ Issue21 :
* Occurs() with two or more terms in function arguments may get freeze
S x;
CF f;
L F = f(1+x);
if (occurs(x));
id f(?a) = 1;
endif;
P;
.end
assert succeeded?
assert result("F") =~ expr("1")
*--#] Issue21 :
*--#[ Issue25 :
* [tform] ZERO_ is always 1 when InParallel mode
L F1 = 1;
ModuleOption inparallel;
.sort
#message ZERO_F1 = `ZERO_F1'
#message ZERO_ = `ZERO_'
.end
assert succeeded?
assert stdout =~ /~~~ZERO_F1 = 0/
assert stdout =~ /~~~ZERO_ = 0/
*--#] Issue25 :
*--#[ Issue30_1 :
* Substitutions just after putinside/antiputinside may fail
S x;
CF f;
L F1 = 1+x+x^2;
L F2 =-1-x-x^2;
putinside f, x;
*argument; endargument; * <-- (1)
id f( 1) = 0;
id f(-1) = 0;
id f( x) = 0;
id f(-x) = 0;
id f( x^2) = 0;
id f(-x^2) = 0;
P;
.end
assert succeeded?
assert result("F1") =~ expr("0")
assert result("F2") =~ expr("0")
*--#] Issue30_1 :
*--#[ Issue30_2 :
S x;
CF f;
L F1 = 1+x+x^2;
L F2 =-1-x-x^2;
antiputinside f, x;
*argument; endargument; * <-- (1)
id f( 1) = 0;
id f(-1) = 0;
P;
.end
assert succeeded?
assert result("F1") =~ expr("0")
assert result("F2") =~ expr("0")
*--#] Issue30_2 :
*--#[ Issue30_3:
CF f;
S x;
L F = 1;
$a = f;
inside $a;
putinside f,x;
endinside;
*inside $a; endinside; * <-- (1) workaround
P " a=%$;", $a;
$a = f($a);
P " a=%$;", $a;
.end
assert succeeded?
assert result("a", 0) =~ expr("f*f(1)")
assert result("a", 1) =~ expr("f(f*f(1))")
*--#] Issue30_3 :
*--#[ Issue37_1 :
* Polyratfun infinite loop in Print statement
S ep;
CF rat;
PolyRatFun rat(expand,ep,6);
L F = rat(ep,ep);
Print;
.end
assert succeeded?
assert result("F") =~ expr("rat(1)")
*--#] Issue37_1 :
*--#[ Issue37_2 :
S ep;
CF rat;
PolyRatFun rat(expand,ep,6);
L F = rat(1,1)*rat(ep,ep);
Print;
.end
assert succeeded?
assert result("F") =~ expr("rat(1)")
*--#] Issue37_2 :
*--#[ Issue38 :
* Wrong normalization of PolyRatFun
CF num,rat;
PolyRatFun rat;
S n1,x,ep;
L F1 = num(n1)*num(1/2);
L F2 = num(n1)*num(-1/2);
L F3 = rat(1,1) - rat(1,1);
L F4 = rat(x,1)*rat(1+ep,1);
id num(x?) = rat(x,1);
P;
.end
assert succeeded?
assert result("F1") =~ expr("rat(n1,2)")
assert result("F2") =~ expr("rat( - n1,2)")
assert result("F3") =~ expr("0")
assert result("F4") =~ expr("rat(x*ep + x,1)")
*--#] Issue38 :
*--#[ Issue39 :
* Freeze when PolyRatFun contains dot products
V a;
CF rat;
PolyRatFun rat;
L F = rat(a.a,1);
P;
.end
# Runtime errors may freeze ParFORM.
#pend_if mpi?
assert runtime_error?
*--#] Issue39 :
*--#[ Issue41 :
* replace_ in #assign
S n;
#$x = n * replace_(n,n+1);
L F = `$x';
P;
.end
assert succeeded?
assert result("F") =~ expr("1+n")
*--#] Issue41 :
*--#[ Issue42_1 :
* Factorize/FactDollar are much slower than FactArg
CF num;
S ep,n1,...,n14;
L F =
+(n1)^5*(n2)^2*(n5)^4*(n6)^4*(n7)^7*(n8)^10*(n9)^21*(-8589934592)
*(-1+n5)^2*(1+n9)*(30-19*n9+2*n9^2-18*n8+2*n8*n9-64*n7+14*n7*n9+
16*n7*n8+12*n7^2-22*n6+4*n6*n9+4*n6*n8+8*n6*n7+4*n6^2+12*n5-2*n5*
n9+4*n5*n8-4*n5*n6-4*n5^2+20*n4-6*n4*n9+4*n4*n8-4*n4*n7-8*n4*n6+4
*n4^2-2*n3*n9+4*n3*n7+4*n3*n5-4*n3*n4-32*n2+8*n2*n9+2*n2*n8+22*n2
*n7+8*n2*n6-2*n2*n5-4*n2*n4+6*n2^2+6*n1+7*n1*n9+6*n1*n7+10*n1*n6-
6*n1*n5-12*n1*n4+4*n1*n2-2*n1^2-16*ep+8*ep*n9+8*ep*n8+32*ep*n7+8*
ep*n6-16*ep*n5-16*ep*n4+16*ep*n2)
;
.sort
* FactArg
L F1 = num(F);
factarg num;
chainout num;
.sort
* #FactDollar
* FIXME: ParFORM hangs. (#46)
#$F2 = F;
#factdollar $F2
L F2 =
num(`$F2[1]')
#do i=2,`$F2[0]'
* num(`$F2[`i']')
#enddo
;
.sort
* FactDollar
L F3 = 1;
inexpression F3;
$F3 = F;
factdollar $F3;
do $i=1,$F3[0];
multiply num($F3[$i]);
enddo;
endinexpression;
.sort
* FIXME: Factorize still have the performance issue. (#44)
#if 0
L F4 = F;
Factorize F4;
.sort
#endif
P;
.end
# ParFORM hangs for #FactDollar (#46)
#pend_if mpi?
assert succeeded?
f = expr("""
num(n1)^5*num(n2)^2*num(n5)^4*num(n6)^4*num(n7)^7*num(n8)^10*num(n9)^21*
num( - 8589934592)*num( - 1 + n5)^2*num(1 + n9)*num(30 - 19*n9 + 2*n9^2
- 18*n8 + 2*n8*n9 - 64*n7 + 14*n7*n9 + 16*n7*n8 + 12*n7^2 - 22*n6 + 4*
n6*n9 + 4*n6*n8 + 8*n6*n7 + 4*n6^2 + 12*n5 - 2*n5*n9 + 4*n5*n8 - 4*n5*n6
- 4*n5^2 + 20*n4 - 6*n4*n9 + 4*n4*n8 - 4*n4*n7 - 8*n4*n6 + 4*n4^2 - 2*
n3*n9 + 4*n3*n7 + 4*n3*n5 - 4*n3*n4 - 32*n2 + 8*n2*n9 + 2*n2*n8 + 22*n2*
n7 + 8*n2*n6 - 2*n2*n5 - 4*n2*n4 + 6*n2^2 + 6*n1 + 7*n1*n9 + 6*n1*n7 +
10*n1*n6 - 6*n1*n5 - 12*n1*n4 + 4*n1*n2 - 2*n1^2 - 16*ep + 8*ep*n9 + 8*
ep*n8 + 32*ep*n7 + 8*ep*n6 - 16*ep*n5 - 16*ep*n4 + 16*ep*n2)
""")
assert result("F1") =~ f
assert result("F2") =~ f
assert result("F3") =~ f
*--#] Issue42_1 :
*--#[ Issue42_2 :
S x;
L F = gcd_(
(1+x),
2*(1+x),
3*(1+x)
);
P;
.end
assert succeeded?
assert result("F") =~ expr("1+x")
*--#] Issue42_2 :
*--#[ Issue42_3 :
S n1,...,n4;
L F1 = (1+n1)*(1+n2)*n1*n2*n3;
L F2 = (1+n2)*n1*n2*n3*n4;
L F3 = (1+n4)*n1*n2*n3*n4^2;
L F = gcd_(F1,F2,F3);
P F;
.end
assert succeeded?
assert result("F") =~ expr("n1*n2*n3")
*--#] Issue42_3 :
*--#[ Issue42_4 :
#procedure PrintFactorizedDollar(name,dollar)
#write " `name' = (%$)%", `dollar'[1]
#do i=2,``dollar'[0]'
#write "*(%$)%", `dollar'[`i']
#enddo
#write ";"
#endprocedure
S x,y;
#$a = (1-x)*(1+y);
#$b = (1-x)*(1-y);
#factdollar $a
#factdollar $b
#call PrintFactorizedDollar(F1,$a)
#call PrintFactorizedDollar(F2,$b)
.end
assert succeeded?
assert result("F1") =~ expr("(-1)*(-1+x)*(1+y)")
assert result("F2") =~ expr("(-1+y)*(-1+x)")
*--#] Issue42_4 :
*--#[ Issue45 :
* FactDollar still broken
#procedure PrintFactorizedDollar(name,dollar)
#write " `name' = (%$)%", `dollar'[1]
#do i=2,``dollar'[0]'
#write "*(%$)%", `dollar'[`i']
#enddo
#write ";"
#endprocedure
S x,y;
#$a = 1+x-y; * <-- The bug was found for this.
#$b = 2*(1+x-y);
#$c = (1+x+y)*(1+x-y);
#factdollar $a
#factdollar $b
#factdollar $c
#call PrintFactorizedDollar(F1,$a)
#call PrintFactorizedDollar(F2,$b)
#call PrintFactorizedDollar(F3,$c)
.end
assert succeeded?
assert result("F1") =~ expr("(-1)*(-1+y-x)")
assert result("F2") =~ expr("(-1+y-x)*(-2)")
assert result("F3") =~ expr("(-1)*(-1+y-x)*(1+y+x)")
*--#] Issue45 :
*--#[ Issue48 :
* Memory error on dollar matching
CFunction TOPO,topo;
CFunction color;
Symbol M1,M2,x,cOlNA,cOlNR,ca,cf,nf,[dabc^2/n],[d4RR/n],[d4RA/n],[d4AA/n];
L Diagrams=
+topo(M1)*color(24*[d4RR/n]*cOlNA*cOlNR^-1+12*ca*[dabc^2/n]+
ca^2*cf*nf)
+topo(M2)*color(24*[d4RA/n]*cOlNA*cOlNR^-1+24*cf^4-72*ca*cf^3
+66*ca^2*cf^2-19*ca^3*cf)
;
.sort
id topo(x?$topo) = 1;
id color(x?$color) = 1;
$color = $color * topo($topo);
.sort
L Color = `$color';
P;
.end
assert succeeded?
assert result("Diagrams") =~ expr("2")
assert result("Color") =~ expr("
24*topo(M2)*cf^4 - 72*topo(M2)*ca*cf^3 + 66*topo(M2)*ca^2*cf^2 - 19*
topo(M2)*ca^3*cf + 24*topo(M2)*cOlNA*cOlNR^-1*[d4RA/n]")
*--#] Issue48 :
*--#[ Issue52 :
* CopySpectator crashes when empty
CreateSpectator TMP, "xTMP";
S x;
L F = (1+x)^2;
.sort
CopySpectator G = TMP;
P;
.end
assert succeeded?
assert result("F") =~ expr("1 + 2*x + x^2")
assert result("G") =~ expr("0")
*--#] Issue52 :
*--#[ Issue54_1 :
* Transform,replace xarg_ acts only on symbols
CF f;
S a;
L xx = f(a,1);
Transform,f,replace(1,last)=(xarg_,2*xarg_);
P;
.end
assert succeeded?
assert result("xx") =~ expr("f(2*a,2)")
*--#] Issue54_1 :
*--#[ Issue54_2 :
CF f;
S a;
L xx = f(a,a^2,1,2);
Transform,f,replace(1,last)=(xarg_,2*xarg_,1,3);
Print;
.end
assert succeeded?
assert result("xx") =~ expr("f(2*a,2*a^2,3,4)")
*--#] Issue54_2 :
*--#[ Issue55_1 :
* Pattern matching with sets, and (ex-)PolyRatFun CFunction
CFunction coeff,coeff2;
Symbol x,y,z;
Symbol ca,cf,zeta2;
Local test1 = + dum_( - 7117/81 - 64/9*zeta2)*ca^2*cf;
Local test2 = + dum_(1 + 576/7117*zeta2)*coeff(- 7117,81)*ca^2*cf;
.sort
Identify coeff(x?neg_,y?) = -coeff(-x,y);
Identify dum_(z?)*coeff(x?,y?) = dum_(z * x/y);
Print +s;
.sort
PolyRatFun coeff;
Normalize dum_;
Print +s;
.sort
PolyRatFun;
.sort
Identify coeff(x?neg_,y?) = -coeff(-x,y);
*Identify coeff(x?,y?) = coeff2(x,y);
*Identify coeff2(x?neg_,y?) = -coeff2(-x,y);
Print +s;
.end
assert succeeded?
assert result("test1") =~ expr("- (1 + 576/7117*zeta2)*coeff(7117,81)*ca^2*cf")
assert result("test2") =~ expr("- (1 + 576/7117*zeta2)*coeff(7117,81)*ca^2*cf")
*--#] Issue55_1 :
*--#[ Issue55_2 :
* Pattern matching with sets, and (ex-)PolyRatFun CFunction
CF frac;
S x,y;
L F = - 2/3*x;
P;
.sort(PolyRatFun=frac);
*.sort; * putting .sort is useless for this bug
*argument frac,1;endargument; * workaround
id frac(x?neg_,y?) = - frac(-x,y); * doesn't match
P;
.end
assert succeeded?
assert result("F") =~ expr("- frac(2,3)*x")
*--#] Issue55_2 :
*--#[ Issue56 :
* PolyRatFun(expand) does not expand substituted expressions
CF rat;
S x;
PolyRatFun rat;
L F = rat(1,1+x);
L G = rat(1-x,1);
.sort
PolyRatFun rat(expand,x,2);
Drop;
L H = F - G;
*.sort; * <-- (1)
P;
.end
assert succeeded?
assert result("H") =~ expr("rat(x^2)")
*--#] Issue56 :
*--#[ Issue59_1 :
* Crash when PolyRatFun(expand)
CF num,rat;
S x;
PolyRatFun rat(expand,x,2);
L F = *...*
* *...*
;
id num(x?) = rat(x,1);
.sort
P +s;
.end
assert succeeded?
assert result("F") =~ expr('
+ rat( - 3319889381431113865517677688157339126513795072000000000000 -
103946485016901161789833595241629175725192946647040000000000*x -
1536456092437457859275118833518144965878613654110208000000000*x^2)
')
*--#] Issue59_1 :
*--#[ Issue59_2 :
CF rat;
S x;
PolyRatFun rat(expand,x,2);
L F1 = rat(1+x,1)^270;
L F2 = rat(10+10*x,1)^47;
P;
.end
assert succeeded?
assert result("F1") =~ expr('
rat(1 + 270*x + 36315*x^2)
')
assert result("F2") =~ expr('
rat(100000000000000000000000000000000000000000000000 + 47000000000000000\
00000000000000000000000000000000*x + 10810000000000000000000000000000000\
0000000000000000*x^2)
')
*--#] Issue59_2 :
*--#[ Issue60 :
* No error for skipped semicolon in Save statement
Symbol x;
Global test = x;
.store
Save test.sav
.end
assert compile_error?
*--#] Issue60 :
*--#[ Issue61 :
* IntoHide + Bracket for expressions with bracket index
S x,y;
L F = 1+x;
B+ y;
.sort
IntoHide F;
B x;
.sort
L G = F[x];
P;
.end
assert succeeded?
assert result("G") =~ expr("1")
*--#] Issue61 :
*--#[ Issue69 :
* No warnings/errors for the same labels
On allwarning;
L F = 1;
goto 1;
label 1;
multiply 2;
label 1;
multiply 3;
label 1;
multiply 5;
P;
.end
assert compile_error?
*--#] Issue69 :
*--#[ Issue73 :
* "PolyRatFun cannot have zero arguments" when used in function
S ep;
CF rat,K;
PolyRatfun rat;
L F = K(rat(ep+1,1)) + K(rat(1,1));
P;
.end
assert succeeded?
assert result("F") =~ expr("K(rat(ep + 1,1))*rat(1,1) + K(rat(1,1))*rat(1,1)")
*--#] Issue73 :
*--#[ Issue74 :
* occurs() freezes with tensors #74
CF a,acc;
S x,y;
I i,j;
V p,q;
CT t;
CF f,g;
L F1 = 1;
L F2 = x;
L F3 = 1/x;
L F4 = i;
L F5 = p;
L F6 = p(i);
L F7 = p(N1_?);
L F8 = p.p;
L F9 = p.q;
L F10 = t;
L F11 = t(i);
L F12 = t(p);
L F13 = f;
L F14 = f(1);
L F15 = f(x);
L F16 = f(-x);
L F17 = f(1/x);
L F18 = f(x+y);
L F19 = f(i);
L F20 = f(-i);
L F21 = f(i+j);
L F22 = f(p);
L F23 = f(-p);
L F24 = f(p+q);
L F25 = f(p(i));
L F26 = f(p(N1_?));
L F27 = f(p.p);
L F28 = f(p.q);
L F29 = f(t);
L F30 = f(t(i));
L F31 = f(t(p));
L F32 = g(f(x));
L F33 = g(f(i));
L F34 = g(f(p));
L F35 = g(f(t));
L F36 = g(g(f));
L F37 = g_(i,p);
L F38 = g_(1,i,p);
L F39 = g(1,g(2,3-f(x))+g(t(p),t(i)));
L F40 = d_(p,i);
if (occurs(x)) multiply a(1);
if (occurs(i)) multiply a(2);
if (occurs(p)) multiply a(3);
if (occurs(t)) multiply a(4);
if (occurs(f)) multiply a(5);
chainin a;
antiputinside acc,a;
id acc(?a) = 1;
P;
.end
assert succeeded?
assert result("F1") =~ expr("1")
assert result("F2") =~ expr("a(1)")
assert result("F3") =~ expr("a(1)")
assert result("F4") =~ expr("a(2)")
assert result("F5") =~ expr("a(3)")
assert result("F6") =~ expr("a(2,3)")
assert result("F7") =~ expr("a(3)")
assert result("F8") =~ expr("a(3)")
assert result("F9") =~ expr("a(3)")
assert result("F10") =~ expr("a(4)")
assert result("F11") =~ expr("a(2,4)")
assert result("F12") =~ expr("a(3,4)")
assert result("F13") =~ expr("a(5)")
assert result("F14") =~ expr("a(5)")
assert result("F15") =~ expr("a(1,5)")
assert result("F16") =~ expr("a(1,5)")
assert result("F17") =~ expr("a(1,5)")
assert result("F18") =~ expr("a(1,5)")
assert result("F19") =~ expr("a(2,5)")
assert result("F20") =~ expr("a(2,5)")
assert result("F21") =~ expr("a(2,5)")
assert result("F22") =~ expr("a(3,5)")
assert result("F23") =~ expr("a(3,5)")
assert result("F24") =~ expr("a(3,5)")
assert result("F25") =~ expr("a(2,3,5)")
assert result("F26") =~ expr("a(3,5)")
assert result("F27") =~ expr("a(3,5)")
assert result("F28") =~ expr("a(3,5)")
assert result("F29") =~ expr("a(4,5)")
assert result("F30") =~ expr("a(2,4,5)")
assert result("F31") =~ expr("a(3,4,5)")
assert result("F32") =~ expr("a(1,5)")
assert result("F33") =~ expr("a(2,5)")
assert result("F34") =~ expr("a(3,5)")
assert result("F35") =~ expr("a(4,5)")
assert result("F36") =~ expr("a(5)")
assert result("F37") =~ expr("a(2,3)")
assert result("F38") =~ expr("a(2,3)")
assert result("F39") =~ expr("a(1,2,3,4,5)")
assert result("F40") =~ expr("a(2,3)")
*--#] Issue74 :
*--#[ Issue77_1 :
* Freeze when pattern matchings with powers of dollar variables ($x^n?)
S x,n;
L F = 1;
#$x = x;
id $x^n? = 1;
P;
.end
assert succeeded?
assert result("F") =~ expr("1")
*--#] Issue77_1 :
*--#[ Issue77_2 :
S x,y,z,n;
V p,q;
L F = x^3 * y^5 * p.q^6;
#$x = x*y*p.q;
id $x^n? = z^n;
P;
.end
assert succeeded?
assert result("F") =~ expr("p.q^3*y^2*z^3")
*--#] Issue77_2 :
*--#[ Issue78_1 :
* Minus sign is ignored in set restriction
V p,p1;
CF vx;
L F1 = vx(-p1);
L F2 = F1;
inexpression F1;
id vx(p?!{p1,-p1}) = 1;
endinexpression;
inexpression F2;
id vx(p?!{-p1,p1}) = 1;
endinexpression;
Print;
.end
assert succeeded?
assert result("F1") =~ expr("vx(-p1)")
assert result("F2") =~ expr("vx(-p1)")
*--#] Issue78_1 :
*--#[ Issue78_2 :
V Q;
CF vx;
L F1 = vx(-Q);
L F2 = F1;
inexpression F1;
id vx(Q?{Q,-Q}) = 1;
endinexpression;
inexpression F2;
id vx(Q?{-Q,Q}) = 1;
endinexpression;
Print;
.end
assert succeeded?
assert result("F1") =~ expr("1")
assert result("F2") =~ expr("1")
*--#] Issue78_2 :
*--#[ Issue82 :
* Minus sign matching bug in latest version
V p1,p2;
CF vx;
L F = vx(-p2);
id vx(p2?!{p1}) = 1;
Print;
.end
assert succeeded?
assert result("F") =~ expr("1")
*--#] Issue82 :
*--#[ Issue88 :
* Strange error in 'also once' in combination with 'replace_'
cf ABB;
i mu;
L test = 1;
once ABB(mu?) * ABB(mu?) = 1;
also once ABB(mu?, ?b, mu?) = replace_(mu, N100_?);
P;
.end
assert succeeded?
assert result("test") =~ expr("1")
*--#] Issue88 :
*--#[ Issue90_1 :
* Errors in symbol powers
CFunction SP;
Symbol nn, shat;
Vector k1,k2,k3;
Local testExpr0 = shat^(-1+nn);
Local testExpr1 = shat^(-3+nn);
Local testExpr4 = SP(k2,k3)*(shat)^(-3+nn);
Argument;
Identify nn = 2;
EndArgument;
Print +s;
.end
assert succeeded?
assert result("testExpr0") =~ expr("+shat")
assert result("testExpr1") =~ expr("+shat^-1")
assert result("testExpr4") =~ expr("+SP(k2,k3)*shat^-1")
*--#] Issue90_1 :
*--#[ Issue90_2:
Symbol i,x,y,n;
Local test1 = 5^(n) * sum_(i,1,n, x^i);
Multiply replace_(n,3);
Print +s test1;
.sort
Local test2 = 5^(-n);
Multiply replace_(n,3);
Print +s test2;
.sort
Local test3 = 5^(-n) * sum_(i,1,n, x^i);
Multiply replace_(n,3);
Print +s test3;
.end
assert succeeded?
assert result("test1") =~ expr("+ 125*x + 125*x^2 + 125*x^3")
assert result("test2") =~ expr("+ 1/125")
assert result("test3") =~ expr("+ 1/125*x + 1/125*x^2 + 1/125*x^3")
*--#] Issue90_2 :
*--#[ Issue94 :
* No check for Dirac gamma matrices without any arguments
CF f;
L F1 = 123*g5_;
L F2 = 123*g6_;
L F3 = 123*g7_;
L F4 = 123*g_;
L F5 = 123*gi_;
L F6 = f(1000*g5_);
L F7 = f(10000*g5_);
.end
# Runtime errors may freeze ParFORM.
#pend_if mpi?
assert runtime_error?
*--#] Issue94 :
*--#[ Issue97_1 :
* "Program terminating" with oldFactArg and dot products
V e1, e2, k1, k2;
S a, b;
CF dotM;
L testbad = dotM(e1.k1*e2.k1);
L testok = dotM(a*b);
.sort
On oldFactArg;
factarg dotM;
P;
.end
assert succeeded?
assert result("testbad") =~ expr("dotM(e1.k1,e2.k1,1)")
assert result("testok") =~ expr("dotM(a,b,1)")
*--#] Issue97_1 :
*--#[ Issue97_2 :
On OldFactArg;
V p1,p2,p3,p4;
S x;
CF f;
T t;
L OK1 = f(t(p1)*x);
L OK2 = f(t(p1,p2)*x);
L OK3 = f(t(p1,p2,p3)*x);
L BAD = f(t(p1,p2,p3,p4)*x);
factarg f;
P;
.end
assert succeeded?
assert result("OK1") =~ expr("f(t(p1),x,1)")
assert result("OK2") =~ expr("f(t(p1,p2),x,1)")
assert result("OK3") =~ expr("f(t(p1,p2,p3),x,1)")
assert result("BAD") =~ expr("f(t(p1,p2,p3,p4),x,1)")
*--#] Issue97_2 :
*--#[ Issue104 :
* Leading zeroes in rational numbers not handled consistently
Local test1 = 0001;
Local test2 = 00001;
Local test3 = 00010;
Local test4 = 00011;
Print +s;
.end
assert succeeded?
assert result("test1") =~ expr("+ 1")
assert result("test2") =~ expr("+ 1")
assert result("test3") =~ expr("+ 10")
assert result("test4") =~ expr("+ 11")
*--#] Issue104 :
*--#[ Issue105 :
* Crash by replace_(x,0)
S x;
V p;
CF f;
L F = f(p.p+x);
L G = f(p.p*x);
multiply replace_(x,0);
P;
.end
assert succeeded?
assert result("F") =~ expr("f(p.p)")
assert result("G") =~ expr("f(0)")
*--#] Issue105 :
*--#[ Issue106 :
* Crash with replace_ and nested functions
cfunction prop, mom;
vector q1, q2, k1, k2, p;
l test = prop(mom(-q1-q2+p));
multiply replace_(q1,k1-k2);
print+s;
.sort
multiply replace_(q2,k2);
print+s;
.end
CF f,g;
V p1,p2;
L F1 = f(f(p1-p2));
L F2 = f(f(f(p1-p2)));
L F3 = f(f(f(f(p1-p2)+g(p1-p2))+g(p1-p2)));
multiply replace_(p1,p2);
P;
.end
assert succeeded?
assert result("test") =~ expr("+ prop(mom(- k1 + p))")
assert result("F1") =~ expr("f(f(0))")
assert result("F2") =~ expr("f(f(f(0)))")
assert result("F3") =~ expr("f(f(f(f(0)+g(0))+g(0)))")
*--#] Issue106 :
*--#[ Issue111 :
* PolyRatFun(expand) doesn't expand numeric coefficients in one go
S x;
CF rat;
PolyRatFun rat(expand,x,3);
L F = rat(1+x);
.sort
multiply 2;
*.sort; * <-- workaround
P;
.sort
Drop;
L F1 = 3/5;
L F2 = 6/5;
L F3 = 2/5;
L F4 = 12345678901234567890123456789012345678901234567890;
L F5 = 2/5 * rat(1+x);
L F6 = 2/5 * rat(1,1-x);
L F7 = 2/5 * rat(1+x) * rat(1-2*x);
L F8 = 2/5 * rat(1+x) * rat(1,1-x);
L F9 = 2/5 * rat(1,1+x) * rat(1,1-2*x);
multiply 5/3;
P;
.end
assert succeeded?
assert result("F") =~ expr("rat(2 + 2*x)")
assert result("F1") =~ expr("rat(1)")
assert result("F2") =~ expr("rat(2)")
assert result("F3") =~ expr("rat(2/3)")
assert result("F4") =~ expr("rat(20576131502057613150205761315020576131502057613150)")
assert result("F5") =~ expr("rat(2/3 + 2/3*x)")
assert result("F6") =~ expr("rat(2/3 + 2/3*x + 2/3*x^2 + 2/3*x^3)")
assert result("F7") =~ expr("rat(2/3 - 2/3*x - 4/3*x^2)")
assert result("F8") =~ expr("rat(2/3 + 4/3*x + 4/3*x^2 + 4/3*x^3)")
assert result("F9") =~ expr("rat(2/3 + 2/3*x + 2*x^2 + 10/3*x^3)")
*--#] Issue111 :
*--#[ Issue113 :
* ?a crashes the program if used only on the rhs
CF f;
L F = f;
id f(?a) = f(?a);
id f = f(?a);
Print;
.end
assert compile_error?
*--#] Issue113 :
*--#[ Issue114 :
* Crash on PolyRatFun(expand) when the result is zero
CF rat;
S x;
L F = rat(x^10,1-x);
P;
.sort
PolyRatFun rat(expand,x,5);
P;
.end
assert succeeded?
assert result("F") =~ expr("rat(x^10 + x^11 + x^12 + x^13 + x^14 + x^15)")
*--#] Issue114 :
*--#[ Issue117_1 :
* Id not matching when using ?a and symmetric function
S n1,n2;
CF f,g(s);
L F = f(n1,n2)*g(n1,n2);
id f(n1?,n2?,?a)*g(n1?,n2?) = 1; * works if g not symmetric or ?a is removed
Print;
.end
assert succeeded?
assert result("F") =~ expr("1")
*--#] Issue117_1 :
*--#[ Issue117_2 :
S n1,n2;
CF f(s),g(s);
id f(n1?,n2?,?a)*g(n1?,n2?) = 1;
.end
assert compile_error?
*--#] Issue117_2 :
*--#[ Issue117_3 :
S n1,n2;
S x1,x2,x3;
CF f,g(s);
L F1 = f(x1,x2)*g(x1,x2);
L F2 = f(x2,x1)*g(x1,x2);
L F3 = f(x1,x2,x3)*g(x1,x2);
L F4 = f(x2,x1,x3)*g(x1,x2);
L F5 = f(x1,x2)*f(x2,x1,x3)*g(x2,x1)^2;
id f(n1?,n2?,?a) * g(n1?,n2?) = 1;
P;
.end
assert succeeded?
assert result("F1") =~ expr("1")
assert result("F2") =~ expr("1")
assert result("F3") =~ expr("1")
assert result("F4") =~ expr("1")
assert result("F5") =~ expr("1")
*--#] Issue117_3 :
*--#[ Issue121 :
* repeat ignored in some output terms of dd_
V p1,p2,p3,p4;
CF f;
L F = f(p1,p2,p3,p4)*f(p3,p4);
repeat id once f(?a) = dd_(?a);
P +s;
.end
assert succeeded?
assert result("F") =~ expr("
+ p1.p2*p3.p4^2
+ p1.p3*p2.p4*p3.p4
+ p1.p4*p2.p3*p3.p4
")
*--#] Issue121 :
*--#[ Issue125_1 :
* Form compiler allows lone ? on rhs
CF f;
L F = f;
id f = f(?);
.end
assert compile_error?
*--#] Issue125_1 :
*--#[ Issue125_2 :
V p;
I mu;
CF f;
L F = p(mu);
id p = f(?);
P;
.end
assert succeeded?
assert result("F") =~ expr("f(mu)")
*--#] Issue125_2 :
*--#[ Issue126 :
* Print rejects local-to be unhidden expressions
L F = 1;
.sort
Hide;
.sort
Unhide;
P F;
.end
assert succeeded?
assert result("F") =~ expr("1")
*--#] Issue126 :
*--#[ Issue128 :
* Rational arithmetic giving pi_
CF rat;
PolyRatFun rat;
S cw,sw,e;
*S MZ,sp12; * <-- This fixes the problem.
S sp12,MZ;
L F = cw * sw * e * rat(- MZ, 2 * sp12 - 1 * MZ);
L G = 2 * cw * sw * e * rat(- MZ, 4 * sp12 - 2 * MZ);
.sort
PolyRatFun rat; * <-- workaround: renormalize rat
.sort
L FF = F^2;
L GG = G^2;
P +s;
.end
assert succeeded?
assert result("F") =~ expr("
+ cw*sw*e*rat(MZ, - 2*sp12 + MZ)
")
assert result("G") =~ expr("
+ cw*sw*e*rat(MZ, - 2*sp12 + MZ)
")
assert result("FF") =~ expr("
+ cw^2*sw^2*e^2*rat(MZ^2,4*sp12^2 - 4*sp12*MZ + MZ^2)
")
assert result("GG") =~ expr("
+ cw^2*sw^2*e^2*rat(MZ^2,4*sp12^2 - 4*sp12*MZ + MZ^2)
")
*--#] Issue128 :
*--#[ Issue129_1 :
* Redefining a hidden expression #129
L F = 1;
.sort
#procedure redefine()
Hide F;
.sort
L F = F + 1;
.sort
#endprocedure
#do i=1,5
#call redefine()
#enddo
On names;
P;
.end
assert succeeded?
assert result("F") =~ expr("6")
assert stdout =~ exact_pattern(<<'EOF')
Expressions
F(local)
Expressions to be printed
F
EOF
*--#] Issue129_1 :
*--#[ Issue129_2:
L F = 1;
.sort
#procedure redefine()
Hide F;
.sort
L tmp = 1;
.sort
Drop tmp;
L F = F + 1;
.sort
#endprocedure
#do i=1,5
#call redefine()
#enddo
On names;
P;
.end
assert succeeded?
assert result("F") =~ expr("6")
assert stdout =~ exact_pattern(<<'EOF')
Expressions
F(local)
Expressions to be printed
F
EOF
*--#] Issue129_2 :
*--#[ Issue139 :
* Corrupted characters in printing f(-2147483648)
CF f;
* Check numbers near danguous ones up to 64 bits.
* 2^15 = 32768
L F15p6 = f(+32766);
L F15p7 = f(+32767);
L F15p8 = f(+32768);
L F15p9 = f(+32769);
L F15p0 = f(+32770);
L F15m6 = f(-32766);
L F15m7 = f(-32767);
L F15m8 = f(-32768);
L F15m9 = f(-32769);
L F15m0 = f(-32770);
* 2^16 = 65536
L F16p4 = f(+65534);
L F16p5 = f(+65535);
L F16p6 = f(+65536);
L F16p7 = f(+65537);
L F16p8 = f(+65538);
L F16m4 = f(-65534);
L F16m5 = f(-65535);
L F16m6 = f(-65536);
L F16m7 = f(-65537);
L F16m8 = f(-65538);
* 2^31 = 2147483648
L F31p6 = f(+2147483646);
L F31p7 = f(+2147483647);
L F31p8 = f(+2147483648);
L F31p9 = f(+2147483649);
L F31p0 = f(+2147483650);
L F31m6 = f(-2147483646);
L F31m7 = f(-2147483647);
L F31m8 = f(-2147483648);
L F31m9 = f(-2147483649);
L F31m0 = f(-2147483650);
* 2^32 = 4294967296
L F32p4 = f(+4294967294);
L F32p5 = f(+4294967295);
L F32p6 = f(+4294967296);
L F32p7 = f(+4294967297);
L F32p8 = f(+4294967298);
L F32m4 = f(-4294967294);
L F32m5 = f(-4294967295);
L F32m6 = f(-4294967296);
L F32m7 = f(-4294967297);
L F32m8 = f(-4294967298);
* 2^63 = 9223372036854775808
L F63p6 = f(+9223372036854775806);
L F63p7 = f(+9223372036854775807);
L F63p8 = f(+9223372036854775808);
L F63p9 = f(+9223372036854775809);
L F63p0 = f(+9223372036854775810);
L F63m6 = f(-9223372036854775806);
L F63m7 = f(-9223372036854775807);
L F63m8 = f(-9223372036854775808);
L F63m9 = f(-9223372036854775809);
L F63m0 = f(-9223372036854775810);
* 2^64 = 18446744073709551616
L F64p4 = f(+18446744073709551614);
L F64p5 = f(+18446744073709551615);
L F64p6 = f(+18446744073709551616);
L F64p7 = f(+18446744073709551617);
L F64p8 = f(+18446744073709551618);
L F64m4 = f(-18446744073709551614);
L F64m5 = f(-18446744073709551615);
L F64m6 = f(-18446744073709551616);
L F64m7 = f(-18446744073709551617);
L F64m8 = f(-18446744073709551618);
P;
.end
assert succeeded?
assert result("F15p6") =~ expr("f(32766)")
assert result("F15p7") =~ expr("f(32767)")
assert result("F15p8") =~ expr("f(32768)")
assert result("F15p9") =~ expr("f(32769)")
assert result("F15p0") =~ expr("f(32770)")
assert result("F15m6") =~ expr("f(-32766)")
assert result("F15m7") =~ expr("f(-32767)")
assert result("F15m8") =~ expr("f(-32768)")
assert result("F15m9") =~ expr("f(-32769)")
assert result("F15m0") =~ expr("f(-32770)")
assert result("F16p4") =~ expr("f(65534)")
assert result("F16p5") =~ expr("f(65535)")
assert result("F16p6") =~ expr("f(65536)")
assert result("F16p7") =~ expr("f(65537)")
assert result("F16p8") =~ expr("f(65538)")
assert result("F16m4") =~ expr("f(-65534)")
assert result("F16m5") =~ expr("f(-65535)")
assert result("F16m6") =~ expr("f(-65536)")
assert result("F16m7") =~ expr("f(-65537)")
assert result("F16m8") =~ expr("f(-65538)")
assert result("F31p6") =~ expr("f(2147483646)")
assert result("F31p7") =~ expr("f(2147483647)")
assert result("F31p8") =~ expr("f(2147483648)")
assert result("F31p9") =~ expr("f(2147483649)")
assert result("F31p0") =~ expr("f(2147483650)")
assert result("F31m6") =~ expr("f(-2147483646)")
assert result("F31m7") =~ expr("f(-2147483647)")
assert result("F31m8") =~ expr("f(-2147483648)")
assert result("F31m9") =~ expr("f(-2147483649)")
assert result("F31m0") =~ expr("f(-2147483650)")
assert result("F32p4") =~ expr("f(4294967294)")
assert result("F32p5") =~ expr("f(4294967295)")
assert result("F32p6") =~ expr("f(4294967296)")
assert result("F32p7") =~ expr("f(4294967297)")
assert result("F32p8") =~ expr("f(4294967298)")
assert result("F32m4") =~ expr("f(-4294967294)")
assert result("F32m5") =~ expr("f(-4294967295)")
assert result("F32m6") =~ expr("f(-4294967296)")
assert result("F32m7") =~ expr("f(-4294967297)")
assert result("F32m8") =~ expr("f(-4294967298)")
assert result("F63p6") =~ expr("f(9223372036854775806)")
assert result("F63p7") =~ expr("f(9223372036854775807)")
assert result("F63p8") =~ expr("f(9223372036854775808)")
assert result("F63p9") =~ expr("f(9223372036854775809)")
assert result("F63p0") =~ expr("f(9223372036854775810)")
assert result("F63m6") =~ expr("f(-9223372036854775806)")
assert result("F63m7") =~ expr("f(-9223372036854775807)")
assert result("F63m8") =~ expr("f(-9223372036854775808)")
assert result("F63m9") =~ expr("f(-9223372036854775809)")
assert result("F63m0") =~ expr("f(-9223372036854775810)")
assert result("F64p4") =~ expr("f(18446744073709551614)")
assert result("F64p5") =~ expr("f(18446744073709551615)")
assert result("F64p6") =~ expr("f(18446744073709551616)")
assert result("F64p7") =~ expr("f(18446744073709551617)")
assert result("F64p8") =~ expr("f(18446744073709551618)")
assert result("F64m4") =~ expr("f(-18446744073709551614)")
assert result("F64m5") =~ expr("f(-18446744073709551615)")
assert result("F64m6") =~ expr("f(-18446744073709551616)")
assert result("F64m7") =~ expr("f(-18446744073709551617)")
assert result("F64m8") =~ expr("f(-18446744073709551618)")
*--#] Issue139 :
*--#[ Issue146 :
* Memory bug via expanding the triple dot operator
Auto S x;
L F = x1+...+x123;
#$n = 1;
.sort
L G = x1+...+x1000;
#$m = F;
.end
assert succeeded?
*--#] Issue146 :
*--#[ Issue149_1 :
* Index matches to -1 but crashes in output
Index mu;
CF f;
L F1 = f(-1);
L F2 = +...+;
id f(mu?) = mu;
P;
.end
assert succeeded?
assert result("F1") =~ expr("f(-1)")
assert result("F2") =~ expr("8256 + f(-2) + f(-1) + f(129) + f(130)")
*--#] Issue149_1 :
*--#[ Issue149_2 :
Index mu;
CF f1(s),f2(a),f3(c),f4(r);
L F1 = +...+;
L F2 = +...+;
L F3 = +...+;
L F4 = +...+;
id f1?(mu?) = mu;
P;
.end
assert succeeded?
assert result("F1") =~ expr("8256 + f1(-2) + f1(-1) + f1(129) + f1(130)")
assert result("F2") =~ expr("8256 + f2(-2) + f2(-1) + f2(129) + f2(130)")
assert result("F3") =~ expr("8256 + f3(-2) + f3(-1) + f3(129) + f3(130)")
assert result("F4") =~ expr("8256 + f4(-2) + f4(-1) + f4(129) + f4(130)")
*--#] Issue149_2 :
*--#[ Issue151 :
* Compiler crashes with Print
#do i=1,200
P "123456789012345678901234567890";
P "%t";
#enddo
.end
assert succeeded?
*--#] Issue151 :
*--#[ Issue153_1 :
* Pattern with index and set restriction matches to number
I mu1,...,mu9;
CF f;
Set indices: mu1,...,mu9;
Set indices2: mu1,...,mu9, 127, 128;
L F1 = f(132);
L F2 = +...+;
id f(mu1?indices) = 1;
id f(mu1?indices2) = 0;
P;
.end
assert succeeded?
assert result("F1") =~ expr("f(132)")
assert result("F2") =~ expr("f(126) + f(129) + f(130) + f(131) + f(132)")
*--#] Issue153_1 :
*--#[ Issue153_2 :
I mu1,...,mu9;
CF f1(s),f2(a),f3(c),f4(r);
Set indices: mu1,...,mu9;
Set indices2: mu1,...,mu9, 127, 128;
L F1 = +...+;
L F2 = +...+;
L F3 = +...+;
L F4 = +...+;
id f1?(mu1?indices) = 1;
id f1?(mu1?indices2) = 0;
P;
.end
assert succeeded?
assert result("F1") =~ expr("f1(126) + f1(129) + f1(130) + f1(131) + f1(132)")
assert result("F2") =~ expr("f2(126) + f2(129) + f2(130) + f2(131) + f2(132)")
assert result("F3") =~ expr("f3(126) + f3(129) + f3(130) + f3(131) + f3(132)")
assert result("F4") =~ expr("f4(126) + f4(129) + f4(130) + f4(131) + f4(132)")
*--#] Issue153_2 :
*--#[ Issue154 :
* CompressSize insufficient while the compression is off, when Keep Brackets
Off compress;
I mu1,...,mu16;
L F = g_(1,mu1,...,mu16);
B g_;
.sort;
Keep Brackets;
tracen,1;
.sort
Drop;
L F1 = termsin_(F);
P;
.end
# Too slow on Travis CI. ParFORM didn't have this bug.
#pend_if travis? && (!linux? || valgrind? || mpi?)
assert succeeded?
assert result("F1") =~ expr("2027025")
*--#] Issue154 :
*--#[ Issue162 :
* Missing Expr[x] with B+ for functions
#define N "5"
#define M "2"
#define P "3"
S x;
CF x1,...,x`M';
S x{`M'+1},...,x`N';
* Test input.
L F = (x1+...+x`N')^`P';
.sort:input;
* Bracket for some functions.
B+ x1,...,x`M';
Print[];
.sort:bracket;
Hide;
* Check if all entries exist.
L FF = F;
B x1,...,x`M';
.sort:test input;
Keep Brackets;
#define failed "0"
$x = term_;
$y = F[$x];
$n = termsin_($y);
if ($n == 0);
P "Error: F[%$] == %$", $x, $y;
redefine failed "1";
endif;
.sort:test;
#if `failed'
#terminate
#endif
.end
assert succeeded?
*--#] Issue162 :
*--#[ Issue163 :
* Normalize statement doesn't work for "MINVECTOR"
CF f1,f2;
V p;
L F1 = f1(-p);
L F2 = f2(-p);
normalize f1;
normalize (0) f2;
P;
.end
assert succeeded?
assert result("F1") =~ expr("-f1(p)")
assert result("F2") =~ expr("f2(p)")
*--#] Issue163 :
*--#[ Issue165 :
* [tform] reading a bracket may crash with B+ when the expression doesn't fit in the scratch buffer
#:MaxTermSize 200
#:ScratchSize 12800
CF f,g;
S n;
#define N "100"
#define M "100"
L F = +...+;
multiply +...+;
B+ f;
*B- f; * <-- (1)
*ModuleOption noparallel;
.sort
id g(n?) = F[f(n)];
*ModuleOption noparallel; * <-- (2)
.sort
* Checksum
id f(n?) = n;
id g(n?) = n;
P;
.end
# Known to fail with ParFORM (#166)
#pend_if mpi?
assert succeeded?
assert result("F") =~ expr("2550250000")
*--#] Issue165 :
*--#[ Issue167 :
* Mystery of count_ in functions
S x;
CF f;
L F = 1 + x + x^2;
multiply f(count_(x,1));
P;
.sort
Drop;
L G = 1 + x + x^2;
$x = f(count_(x,1));
multiply $x;
P;
.end
assert succeeded?
assert result("F") =~ expr("f(0) + f(0)*x + f(0)*x^2")
assert result("G") =~ expr("f(0) + f(1)*x + f(2)*x^2")
*--#] Issue167 :
*--#[ Issue169 :
* Crash from multiply replace_ in large expression
S x;
CF den;
L F =
+ 16608736983689726473/192*den(2+x)
+ 18358130244940416000*den(2+x)
;
multiply replace_(x,1);
P +s;
.end
assert succeeded?
assert result("F") =~ expr("+ 3541369744012249598473/192*den(3)")
*--#] Issue169 :
*--#[ Issue178 :
* PolyRatFun performance regression
* Josh's example:
Symbol a,b,c,ep;
CFunction redprf,epprf;
Local test1 =
+ epprf(-1, - 1 + ep)*redprf(1,1)
+ epprf(-1,1 - 3*ep + 2*ep^2)*redprf(-1,1)
;
.sort
PolyRatFun redprf;
Identify redprf(a?,b?) = redprf(a*c,b*c);
Identify epprf(a?,b?) = redprf(a,b);
.sort
Print;
.end
assert succeeded?
assert result("test1") =~ expr("redprf(-2,2*ep - 1)")
*--#] Issue178 :
*--#[ Issue180 :
* Broken RAT
S ep;
CF rat,RAT;
PolyRatFun rat,RAT;
L F = 1;
P "A1:%t";
multiply RAT(1+ep,1);
P "A2:%t";
P;
.sort
P "B1:%t";
multiply RAT(1+ep,1);
P "B2:%t";
P;
.sort
P;
.end
assert succeeded?
assert result("F") =~ expr("rat(1,ep^2 + 2*ep + 1)")
*--#] Issue180 :
*--#[ Issue185 :
* Wrong result of content_
* This is OK.
S x,y;
#$p = (x/3+2/y)^2;
#$c = content_($p);
#$q = $p/$c;
L C1 = $c;
L Q1 = $q;
.sort
* This was BAD.
S x;
#$p = 1+1/x;
#$c = content_($p);
#$q = $p/$c;
L C2 = $c;
L Q2 = $q;
.sort
* Workaround.
S x,xxx;
#$p = 1+1/x;
#$tmp = $p*xxx;
#$c = content_($tmp)/xxx;
#$q = $p/$c;
L C3 = $c;
L Q3 = $q;
P;
.end
assert succeeded?
assert result("C1") =~ expr("1/9*y^-2")
assert result("Q1") =~ expr("36+12*x*y+x^2*y^2")
assert result("C2") =~ expr("x^-1")
assert result("Q2") =~ expr("1+x")
assert result("C3") =~ expr("x^-1")
assert result("Q3") =~ expr("1+x")
*--#] Issue185 :
*--#[ Issue186 :
* $args not expanded for distrib_
S x1,...,x4;
CF f;
L F = f(x1,...,x4);
id f(?a$a) = 1;
multiply distrib_(1,1,f,dummy_,$a);
P;
.end
assert succeeded?
assert result("F") =~ expr("f(x1) + f(x2) + f(x3) + f(x4)")
*--#] Issue186 :
*--#[ Issue190 :
* Polyratfun coming from function argument does not add properly
Auto S x1,x2,ep;
CF f,rat;
Polyratfun rat;
* x1 and x2 should have coefficient -1
L F =
+f((rat(1-ep,1)*x1-2*x2)*rat(1,1+ep))
+f((rat(1-ep,1)*x2-2*x1)*rat(1,1+ep))
;
id f(x1?) = x1;
Print +s;
.end
assert succeeded?
assert result("F") =~ expr("
+ x2*rat(-1,1)
+ x1*rat(-1,1)
")
*--#] Issue190 :
*--#[ Issue191 :
* gcd_ crashes for zero $-variables
S x;
* immediate values
#define a1 "10"
#define a2 "-20"
#define a3 "100000000000000000000"
#define a4 "-200000000000000000000"
#define a5 "x"
#define a6 "-x"
#define a7 "1+x"
L F0 = gcd_(0,0);
#do i=1,7
L Fa`i' = gcd_(0,`a`i'');
L Fb`i' = gcd_(`a`i'',0);
#enddo
L Fc1 = gcd_(0,1+x,0,0,0);
L Fc2 = gcd_(0,1+x,0,-x,0,0);
L Fc3 = gcd_(0,1+x,0,1-x^2,0,0);
P;
.sort
Drop;
* subexpressions
L a0 = 0;
L a00 = 0;
L a1 = 10;
L a2 = -20;
L a3 = 100000000000000000000;
L a4 = -200000000000000000000;
L a5 = x;
L a6 = -x;
L a7 = 1+x;
L G0 = gcd_(a0,a00);
#do i=1,7
L Ga`i' = gcd_(a0,a`i');
L Gb`i' = gcd_(a`i',a0);
#enddo
L Gc1 = gcd_(0,a7,0,0,a0);
L Gc2 = gcd_(0,a7,0,a3,0,a0);
L Gc3 = gcd_(0,a7,0,1-x^2,0,a0);
P;
.sort
Drop;
* $-variables
#$a0 = 0;
#$a00 = 0;
#$a1 = 10;
#$a2 = -20;
#$a3 = 100000000000000000000;
#$a4 = -200000000000000000000;
#$a5 = x;
#$a6 = -x;
#$a7 = 1+x;
L H0 = gcd_($a0,$a00);
#do i=1,7
L Ha`i' = gcd_($a0,$a`i');
L Hb`i' = gcd_($a`i',$a0);
#enddo
L Hc1 = gcd_(0,$a7,0,0,$a0);
L Hc2 = gcd_(0,$a7,0,$a3,0,$a0);
L Hc3 = gcd_(0,$a7,0,1-x^2,0,$a0);
P;
.end
assert succeeded?
assert result("F0") =~ expr("0")
assert result("Fa1") =~ expr("10")
assert result("Fa2") =~ expr("-20")
assert result("Fa3") =~ expr("100000000000000000000")
assert result("Fa4") =~ expr("-200000000000000000000")
assert result("Fa5") =~ expr("x")
assert result("Fa6") =~ expr("-x")
assert result("Fa7") =~ expr("1+x")
for i in 1..7
assert result("Fb#{i}") == result("Fa#{i}")
end
assert result("Fc1") =~ expr("1+x")
assert result("Fc2") =~ expr("1")
assert result("Fc3") =~ expr("1+x")
assert result("G0") =~ expr("0")
for i in 1..7
assert result("Ga#{i}") == result("Fa#{i}")
assert result("Gb#{i}") == result("Fa#{i}")
end
for i in 1..3
assert result("Gc#{i}") == result("Fc#{i}")
end
assert result("H0") =~ expr("0")
for i in 1..7
assert result("Ha#{i}") == result("Fa#{i}")
assert result("Hb#{i}") == result("Fa#{i}")
end
for i in 1..3
assert result("Hc#{i}") == result("Fc#{i}")
end
*--#] Issue191 :
*--#[ Issue197 :
* mul_ ignores denominator factors
#if "{2^32}" == "0"
* LONG has 4 bytes, which indicates WORD has 2 bytes.
* Avoid the "polynomials too large" error.
#define n "3"
#else
#define n "5"
#endif
S x,y,z;
L F1 = mul_(2/3,5/7);
L F2 = mul_(1/2+x/3,1/5+x/7);
P;
.sort
Drop;
L A1 = (5000000029/7+3/2*x-5/11*x/y+7/8*y*z+z-x*z)^`n';
L A2 = (3/4-1/9*x+9/5000000039*x*y+5/12*y*z+2/z*z^3)^`n';
.sort
Drop;
L G1 = A1 * A2;
L G2 = mul_(A1,A2);
.sort
Drop;
L Nterms = termsin_(G1);
L Zero = G1 - G2;
P;
.end
assert succeeded?
assert result("F1") =~ expr("10/21")
assert result("F2") =~ expr("1/10 + 29/210*x + 1/21*x^2")
if wordsize == 2
assert result("Nterms") =~ expr("333")
else
assert result("Nterms") =~ expr("1351")
end
assert result("Zero") =~ expr("0")
*--#] Issue197 :
*--#[ Issue219 :
* Corrupted characters in {-9223372036854775808}
#$n32 = -2^31;
#$n64 = -2^63;
#$n128 = -2^127;
L F32 = {`$n32'};
L F64 = {`$n64'};
L F128 = {`$n128'};
* In previous versions, "(" was returned from the preprocessor calculator
* on systems using two's complement for signed numbers, leading to an
* "Unmatched ()" error. Note that overflow/underflow doesn't give any errors in
* the preprocessor calculator (e.g., for F128), just gives a strange number
* (though in a strict sense it is an undefined behaviour and can cause a crash;
* let's hope compilers will take a little more time to become so insidious).
P;
.end
assert succeeded?
*--#] Issue219 :
*--#[ Issue211 :
* Unexpected code in ReNumber
#: TermsInSmall 128
#: LargePatches 16
#: FilePatches 4
#: SubTermsInSmall 64
#: SubLargePatches 8
#: SubFilePatches 2
CFunction f,g;
Symbol x,y;
* 128*16=2048 terms cause a sort of the large buffer to disk.
* multiples of 2048*4=8192 terms cause a stage 4 sort
#define NTERMS "40001"
#define ARGNTERMS "2001"
Local test1 = +...+;
Local test2 = g(+...+);
.sort
* Cancel all terms, but keep distance so that most terms only cancel in the final sort
Identify f(x?) = f(x) - f(`NTERMS'-x+1);
Argument g;
Identify f(x?) = f(x) - f(`ARGNTERMS'-x+1);
EndArgument;
Print;
.end
# Only for 64-bit systems. Otherwise "Output term too large".
#require wordsize == 4
# For now it fails because
# "Currently Stage 4 sorts are not allowed for function arguments or $ variables."
assert runtime_error?
# Runtime errors may freeze ParFORM.
#pend_if mpi?
#assert succeeded?
#assert result("test1") =~ expr("0")
#assert result("test2") =~ expr("g(0)")
*--#] Issue211 :
*--#[ Issue222 :
* accessing #factdollar factors causes program termination
Symbol x;
#$a = 1; * Error
*#$a = x; * Fine
#factdollar $a;
#write "Number of factors in `$a' is `$a[0]'"
#write "Factor 1 is `$a[1]'"
.end
assert succeeded?
*--#] Issue222 :
*--#[ Issue253 :
* Memory error for local $-variable in TFORM
#$x = 0;
ModuleOption local $x;
.end
assert succeeded?
*--#] Issue253 :
*--#[ Issue258 :
* gcd_ gives wrong results
S s,t,m;
L test1 = 1/5*s + 1/5*(s+t)*m;
L test2 = (s+t)*m;
L result1 = gcd_(test1*replace_(s,t,t,m,m,s),test2*replace_(s,t,t,m,m,s));
L result2 = gcd_(test1,test2);
* Previous versions gave
* result1 = 1 (correct), but had Valgrind errors
* result2 = m (wrong)
P;
.end
assert succeeded?
assert result("result1") =~ expr("1")
assert result("result2") =~ expr("1")
*--#] Issue258 :
*--#[ Issue260 :
* gcd_ doesn't give the correct result
S x1,...,x5;
#$a = 34*x2^2*x5 + x1^2*x2*x4*x5 + x1^5;
#$b = x4^5 + x3^5 + x2*x3*x5^3;
#$g = x3*x4^4 + x2^3*x4 + x1*x3;
#$p = $a * $g;
#$q = $b * $g;
L F1 = gcd_($p,$q);
.sort
#$a = 79*x2 + x2^4 + x1*x3*x4;
#$b = x4^5 + x1*x3^4 + x1^5;
#$g = x2^4*x3 + 84*x1^5;
#$p = $a * $g;
#$q = $b * $g;
L F2 = gcd_($p,$q);
P;
.end
assert succeeded?
assert result("F1") =~ expr("x3*x4^4 + x2^3*x4 + x1*x3")
assert result("F2") =~ expr("x2^4*x3 + 84*x1^5")
*--#] Issue260 :
*--#[ Issue261_1 :
* Division by zero error by mul_(1,0)
S x;
#$x = 1 + x + x^2;
#$z = 0;
L F1 = mul_(1,0);
L F2 = mul_(0,1);
L F3 = mul_(0,0);
L F4 = mul_($x,$z);
L F5 = mul_($z,$x);
L F6 = mul_($z,$z);
L F7 = mul_($x,0);
L F8 = mul_(0,$x);
L F9 = mul_(1,$z);
L F10 = mul_($z,1);
L F12 = div_(0,1);
L F15 = div_($z,$x);
L F18 = div_(0,$x);
L F20 = div_($z,1);
L F22 = rem_(0,1);
L F25 = rem_($z,$x);
L F28 = rem_(0,$x);
L F30 = rem_($z,1);
L F32 = inverse_(0,1);
L F35 = inverse_($z,$x);
L F38 = inverse_(0,$x);
L F40 = inverse_($z,1);
P;
.end
assert succeeded?
assert result("F1") =~ expr("0")
assert result("F2") =~ expr("0")
assert result("F3") =~ expr("0")
assert result("F4") =~ expr("0")
assert result("F5") =~ expr("0")
assert result("F6") =~ expr("0")
assert result("F7") =~ expr("0")
assert result("F8") =~ expr("0")
assert result("F9") =~ expr("0")
assert result("F10") =~ expr("0")
assert result("F12") =~ expr("0")
assert result("F15") =~ expr("0")
assert result("F18") =~ expr("0")
assert result("F20") =~ expr("0")
assert result("F22") =~ expr("0")
assert result("F25") =~ expr("0")
assert result("F28") =~ expr("0")
assert result("F30") =~ expr("0")
assert result("F32") =~ expr("0")
assert result("F35") =~ expr("0")
assert result("F38") =~ expr("0")
assert result("F40") =~ expr("0")
*--#] Issue261_1 :
*--#[ Issue261_2 :
L F11 = div_(1,0);
P;
.end
# Runtime errors may freeze ParFORM.
#pend_if mpi?
assert runtime_error?
*--#] Issue261_2 :
*--#[ Issue261_3 :
L F23 = rem_(0,0);
P;
.end
# Runtime errors may freeze ParFORM.
#pend_if mpi?
assert runtime_error?
*--#] Issue261_3 :
*--#[ Issue261_4 :
S x;
#$x = 1 + x + x^2;
#$z = 0;
L F34 = inverse_($x,$z);
P;
.end
# Runtime errors may freeze ParFORM.
#pend_if mpi?
assert runtime_error?
*--#] Issue261_4 :
*--#[ Issue261_5 :
#$z = 0;
L F16 = div_($z,$z);
P;
.end
# Runtime errors may freeze ParFORM.
#pend_if mpi?
assert runtime_error?
*--#] Issue261_5 :
*--#[ Issue261_6 :
S x;
#$x = 1 + x + x^2;
L F27 = rem_($x,0);
P;
.end
# Runtime errors may freeze ParFORM.
#pend_if mpi?
assert runtime_error?
*--#] Issue261_6 :
*--#[ Issue261_7 :
#$z = 0;
L F39 = inverse_(1,$z);
P;
.end
# Runtime errors may freeze ParFORM.
#pend_if mpi?
assert runtime_error?
*--#] Issue261_7 :
*--#[ Issue277 :
* A question about addargs
CFunction f,g,h;
S x,y;
Local test = f(-1,1)*g(2);
Transform f addargs(1,last);
P;
.sort
id f(?x)*g(y?) = h(y,?x);
P;
.end
assert succeeded?
assert result("test", -2) =~ expr("f(0)*g(2)")
assert result("test", -1) =~ expr("h(2,0)")
*--#] Issue277 :
form-master/check/forcer/ 0000775 0000000 0000000 00000000000 13565763364 0015671 5 ustar 00root root 0000000 0000000 form-master/check/forcer/forcer.frm 0000664 0000000 0000000 00000014760 13565763364 0017667 0 ustar 00root root 0000000 0000000 * Requires the Forcer library: https://github.com/benruijl/forcer.
#ifndef `TEST'
#message Use -D TEST=XXX
#terminate
#else
#include `NAME_' # `TEST'
#endif
.end
*--#[ Forcer_example :
#include- forcer.h
L F =
+ 1//.../*
Q.p3*Q.p4*vx(Q,p1,p5,p6)*vx(-p1,p2,p3)*vx(-p5,-p6,p4)*vx(-Q,-p2,-p3,-p4)
+ 1//.../*
vx(-Q,p2,p3)*vx(p1,-p2,p5)*vx(-p1,p4,Q)*vx(-p3,-p4,-p5)*ex(p1,p4)
;
#call Forcer(msbarexpand=4)
B ep;
P;
.end
assert succeeded?
assert result("F") =~ expr("
+ ep^-3 * ( 1/24 )
+ ep^-2 * ( 25/72 )
+ ep^-1 * ( 433/216 )
+ ep * ( 89089/1944 - 57/32*z4 - 725/72*z3 )
+ 6457/648 + 115/24*z3
")
*--#] Forcer_example :
*--#[ Forcer_1 :
* timeout = 60 seconds.
#include- forcer.h
CF f,f1,f2,f3;
V p2,p3;
S x3;
* Give 1 or -1. n1 is not used.
Table randomsign(n1?);
Fill randomsign() = random_(2)*2-3;
* Zip two functions as:
* zip(f1,f2(p1,...,pN),f3(q1,...,qN)) -> f1(p1,q1,...,pN,qN),
* for N >= 1.
Table zip(f1?(?a1),f2?(p2?,?a2),f3?(p3?,?a3));
Fill zip() =
+ thetap_(nargs_(?a2,?a3)) * zip(f1(?a1,p2,p3),f2(?a2),f3(?a3))
+ delta_(nargs_(?a2,?a3)) * f1(?a1,p2,p3)
;
* Element-wise multiplication as:
* emul(f1,f2(p1,...,pN),f3(a1,...,aN)) -> f1(p1*a1,...,pN*aN)
* for N >= 1.
Table emul(f1?(?a1),f2?(p2?,?a2),f3?(x3?,?a3));
Fill emul() =
+ thetap_(nargs_(?a2,?a3)) * emul(f1(?a1,p2*x3),f2(?a2),f3(?a3))
+ delta_(nargs_(?a2,?a3)) * f1(?a1,p2*x3)
;
L F1 =
#do i=1,3
+ Zno`i'(1,1,1,1,1,1,1,1,1,1,1,0,0,0)
#enddo
;
L F2 =
#do i=1,3
+ Zno`i'(2,1,1,1,1,1,1,1,1,1,1,0,0,0)
+ Zno`i'(1,2,1,1,1,1,1,1,1,1,1,0,0,0)
+ Zno`i'(1,1,2,1,1,1,1,1,1,1,1,0,0,0)
+ Zno`i'(1,1,1,2,1,1,1,1,1,1,1,0,0,0)
+ Zno`i'(1,1,1,1,2,1,1,1,1,1,1,0,0,0)
+ Zno`i'(1,1,1,1,1,2,1,1,1,1,1,0,0,0)
+ Zno`i'(1,1,1,1,1,1,2,1,1,1,1,0,0,0)
+ Zno`i'(1,1,1,1,1,1,1,2,1,1,1,0,0,0)
+ Zno`i'(1,1,1,1,1,1,1,1,2,1,1,0,0,0)
+ Zno`i'(1,1,1,1,1,1,1,1,1,2,1,0,0,0)
+ Zno`i'(1,1,1,1,1,1,1,1,1,1,2,0,0,0)
+ Zno`i'(1,1,1,1,1,1,1,1,1,1,1,-1,0,0)
+ Zno`i'(1,1,1,1,1,1,1,1,1,1,1,0,-1,0)
+ Zno`i'(1,1,1,1,1,1,1,1,1,1,1,0,0,-1)
#enddo
;
id Zno1(n1?,...,n14?) =
+vx(-Q,p4,p5)
*vx(p3,-p4,p10)
*vx(p2,-p3,p9)
*vx(p1,-p2,p11)
*vx(-p5,p6,-p11)
*vx(-p6,p7,-p10)
*vx(-p7,p8,-p9)
*vx(-p1,-p8,Q)
//.../
/p2.p4^n12/Q.p2^n13/Q.p3^n14
;
id Zno2(n1?,...,n14?) =
+vx(-Q,p4,p5)
*vx(p3,-p4,p11)
*vx(p6,p7,p10)
*vx(p2,-p3,-p10)
*vx(p1,-p2,p9)
*vx(-p5,-p6,-p9)
*vx(-p7,p8,-p11)
*vx(-p1,-p8,Q)
//.../
/Q.p2^n12/p1.p4^n13/Q.p3^n14
;
id Zno3(n1?,...,n14?) =
+vx(-Q,p3,p4)
*vx(p6,p8,p10)
*vx(p5,-p10,p11)
*vx(p1,-p3,-p5)
*vx(-p4,-p8,p9)
*vx(p7,-p9,-p11)
*vx(p2,-p6,-p7)
*vx(-p1,-p2,Q)
//.../
/Q.p6^n12/Q.p8^n13/p3.p6^n14
;
* Make a random permutation of the loop momenta. The result should be the same.
multiply f1(p1,...,p11);
multiply ranperm_(f2,p1,...,p11);
multiply f3(,...,);
id f2(?a)*f3(?b) = emul(f2,f2(?a),f3(?b));
id f1(?a)*f2(?b) = zip(f1,f1(?a),f2(?b));
id f1(?a) = replace_(?a);
ModuleOption noparallel;
.sort:input;
#call Forcer(msbarexpand=4)
B ep;
P;
.end
assert succeeded?
assert result("F1") =~ expr("
+ ep^-1 * ( - 35/2*z5 )
+ 21/2*z7 - 175/4*z6 + 105/4*z5 - 95/2*z3^2
")
assert result("F2") =~ expr("
+ ep^-4 * ( 15/2 )
+ ep^-3 * ( 383/12 )
+ ep^-2 * ( - 2089/18 )
+ ep^-1 * ( 1466/9 - 1245/8*z5 - 728*z3 )
- 2183/12 + 441/2*z7 - 6225/16*z6 - 57155/48*z5 - 2169/2*z4 - 17198/9*
z3 - 3705/8*z3^2
")
*--#] Forcer_1 :
*--#[ Forcer_1-expand :
* timeout = 60 seconds.
#include- forcer.h
CF f,f1,f2,f3;
V p2,p3;
S x3;
* Give 1 or -1. n1 is not used.
Table randomsign(n1?);
Fill randomsign() = random_(2)*2-3;
* Zip two functions as:
* zip(f1,f2(p1,...,pN),f3(q1,...,qN)) -> f1(p1,q1,...,pN,qN),
* for N >= 1.
Table zip(f1?(?a1),f2?(p2?,?a2),f3?(p3?,?a3));
Fill zip() =
+ thetap_(nargs_(?a2,?a3)) * zip(f1(?a1,p2,p3),f2(?a2),f3(?a3))
+ delta_(nargs_(?a2,?a3)) * f1(?a1,p2,p3)
;
* Element-wise multiplication as:
* emul(f1,f2(p1,...,pN),f3(a1,...,aN)) -> f1(p1*a1,...,pN*aN)
* for N >= 1.
Table emul(f1?(?a1),f2?(p2?,?a2),f3?(x3?,?a3));
Fill emul() =
+ thetap_(nargs_(?a2,?a3)) * emul(f1(?a1,p2*x3),f2(?a2),f3(?a3))
+ delta_(nargs_(?a2,?a3)) * f1(?a1,p2*x3)
;
L F1 =
#do i=1,3
+ Zno`i'(1,1,1,1,1,1,1,1,1,1,1,0,0,0)
#enddo
;
L F2 =
#do i=1,3
+ Zno`i'(2,1,1,1,1,1,1,1,1,1,1,0,0,0)
+ Zno`i'(1,2,1,1,1,1,1,1,1,1,1,0,0,0)
+ Zno`i'(1,1,2,1,1,1,1,1,1,1,1,0,0,0)
+ Zno`i'(1,1,1,2,1,1,1,1,1,1,1,0,0,0)
+ Zno`i'(1,1,1,1,2,1,1,1,1,1,1,0,0,0)
+ Zno`i'(1,1,1,1,1,2,1,1,1,1,1,0,0,0)
+ Zno`i'(1,1,1,1,1,1,2,1,1,1,1,0,0,0)
+ Zno`i'(1,1,1,1,1,1,1,2,1,1,1,0,0,0)
+ Zno`i'(1,1,1,1,1,1,1,1,2,1,1,0,0,0)
+ Zno`i'(1,1,1,1,1,1,1,1,1,2,1,0,0,0)
+ Zno`i'(1,1,1,1,1,1,1,1,1,1,2,0,0,0)
+ Zno`i'(1,1,1,1,1,1,1,1,1,1,1,-1,0,0)
+ Zno`i'(1,1,1,1,1,1,1,1,1,1,1,0,-1,0)
+ Zno`i'(1,1,1,1,1,1,1,1,1,1,1,0,0,-1)
#enddo
;
id Zno1(n1?,...,n14?) =
+vx(-Q,p4,p5)
*vx(p3,-p4,p10)
*vx(p2,-p3,p9)
*vx(p1,-p2,p11)
*vx(-p5,p6,-p11)
*vx(-p6,p7,-p10)
*vx(-p7,p8,-p9)
*vx(-p1,-p8,Q)
//.../
/p2.p4^n12/Q.p2^n13/Q.p3^n14
;
id Zno2(n1?,...,n14?) =
+vx(-Q,p4,p5)
*vx(p3,-p4,p11)
*vx(p6,p7,p10)
*vx(p2,-p3,-p10)
*vx(p1,-p2,p9)
*vx(-p5,-p6,-p9)
*vx(-p7,p8,-p11)
*vx(-p1,-p8,Q)
//.../
/Q.p2^n12/p1.p4^n13/Q.p3^n14
;
id Zno3(n1?,...,n14?) =
+vx(-Q,p3,p4)
*vx(p6,p8,p10)
*vx(p5,-p10,p11)
*vx(p1,-p3,-p5)
*vx(-p4,-p8,p9)
*vx(p7,-p9,-p11)
*vx(p2,-p6,-p7)
*vx(-p1,-p2,Q)
//.../
/Q.p6^n12/Q.p8^n13/p3.p6^n14
;
* Make a random permutation of the loop momenta. The result should be the same.
multiply f1(p1,...,p11);
multiply ranperm_(f2,p1,...,p11);
multiply f3(,...,);
id f2(?a)*f3(?b) = emul(f2,f2(?a),f3(?b));
id f1(?a)*f2(?b) = zip(f1,f1(?a),f2(?b));
id f1(?a) = replace_(?a);
ModuleOption noparallel;
.sort:input;
#call Forcer(msbarexpand=4,polyratfunexpand=15)
B ep;
P;
.end
assert succeeded?
assert result("F1") =~ expr("
+ ep^-1 * ( - 35/2*z5 )
+ 21/2*z7 - 175/4*z6 + 105/4*z5 - 95/2*z3^2
")
assert result("F2") =~ expr("
+ ep^-4 * ( 15/2 )
+ ep^-3 * ( 383/12 )
+ ep^-2 * ( - 2089/18 )
+ ep^-1 * ( 1466/9 - 1245/8*z5 - 728*z3 )
- 2183/12 + 441/2*z7 - 6225/16*z6 - 57155/48*z5 - 2169/2*z4 - 17198/9*
z3 - 3705/8*z3^2
")
*--#] Forcer_1-expand :
form-master/check/formunit/ 0000775 0000000 0000000 00000000000 13565763364 0016254 5 ustar 00root root 0000000 0000000 form-master/check/formunit/fu.frm 0000664 0000000 0000000 00000003451 13565763364 0017377 0 ustar 00root root 0000000 0000000 #-
* Print an estimate for form units per hour on the current machine.
* The definition of 1 form unit is:
*
* 1fu = performing a trace with 14 Dirac's gamma matrices 3600 times.
*
* This program is based on aap1000.frm.
*
* Caveat: the number obtained by this program may (strongly) depend on the
* number of CPUs, buffer sizes as well as other environmental conditions.
* Parallel versions can have relatively large overheads for such simple tasks.
* MPI implementations tend to use busy-wait on blocking operations, which
* suppresses the number. Moreover, here we use the CPU time instead of the real
* time.
*--#[ Fuph :
#ifndef `N'
#define N "192"
#endif
#define NUM "14"
#ifdef `QUIET'
Off stats;
#endif
#procedure FormatFloat(x,str,n)
#$x = `x';
#do i=0,`n'
#$i = integer_($x);
#$x = ($x - $i) * 10;
#if `i' == 0
#redefine `str' "`$i'."
#else
#redefine `str' "``str''`$i'"
#endif
#enddo
#endprocedure
I m1,...,m`NUM';
S x,j;
CF f;
#$t0 = `timer_';
L FF = sum_(j,1,`N',f(j));
.sort
id f(x?) = 1;
multiply g_(1,m1,...,m`NUM');
trace4,1;
.sort
Drop;
#$t = (`timer_' - `$t0') / 1000;
#$fu = `N' / 3600;
#$fuph = $fu / $t * 3600;
#define t
#call FormatFloat($t,t,3)
#define fu
#call FormatFloat($fu,fu,6)
#define fuph
#call FormatFloat($fuph,fuph,2)
Format 120;
#if `NTHREADS_' >= 2
#write " `fu' form units in `t' seconds (total cpu time) with {`NTHREADS_'-1} workers"
#write " corresponding to `fuph' form units per hour per core"
#elseif `NPARALLELTASKS_' >= 2
#write " `fu' form units in `t' seconds (total cpu time) with `NPARALLELTASKS_' processes"
#write " corresponding to `fuph' form units per hour per core"
#else
#write " `fu' form units in `t' seconds (cpu time)"
#write " corresponding to `fuph' form units per hour"
#endif
*--#] Fuph :
.end
form-master/configure.ac 0000664 0000000 0000000 00000110230 13565763364 0015617 0 ustar 00root root 0000000 0000000 # m4_esyscmd_s implementation for autoconf < 2.64.
# (Taken from m4sugar.m4 in autoconf 2.69.)
m4_ifndef([m4_esyscmd_s], [m4_define([m4_esyscmd_s],
[m4_chomp_all(m4_esyscmd([$1]))])])
m4_ifndef([m4_chomp_all], [m4_define([m4_chomp_all],
[m4_format([[%.*s]], m4_bregexp(m4_translit([[$1]], [
/], [/ ]), [/*$]), [$1])])])
# Get the version from
# (1) .version file available in a tarball, or
# (2) the latest tag in the repository.
m4_define([FORM_VERSION], m4_esyscmd_s([
if test -f .version; then
cat .version
else
scripts/git-version-gen.sh -C . -v || {
# As a fallback, try for form3.h.
major_version=`grep MAJORVERSION sources/form3.h | sed -e 's/ *#define *MAJORVERSION *//'`
minor_version=`grep MINORVERSION sources/form3.h | sed -e 's/ *#define *MINORVERSION *//'`
if test "x$major_version" != x && test "x$minor_version" != x; then
# Make the version files.
echo "$major_version.$minor_version" >.version
echo "#define REPO_MAJOR_VERSION $major_version" >sources/version.h.in
echo "#define REPO_MINOR_VERSION $minor_version" >>sources/version.h.in
echo '\\def\\repomajorversion'"{$major_version}" >doc/manual/version.tex.in
echo '\\def\\repominorversion'"{$minor_version}" >>doc/manual/version.tex.in
cp doc/manual/version.tex.in doc/devref/version.tex.in
fi
cat <&2
========================================================================
Failed to determine the revision of the source code.
The reason may be
- this is neither a source distribution (containing the configure
script) nor a cloned Git repository,
- this is a shallow clone and no version tags are reachable,
- some required utilities (e.g., git) are missing.
Source distributions and some binaries can be found in:
http://www.nikhef.nl/~form/maindir/binaries/binaries.html
https://github.com/vermaseren/form/releases
The latest source code can be cloned by:
git clone https://github.com/vermaseren/form.git
END
test -f .version && cat <&2
You can continue the build, but binaries will not contain the revision
information.
END
cat <&2
========================================================================
END
test -f .version && cat .version
}
fi
]))
# Use the serial-tests option of AM_INIT_AUTOMAKE if automake >= 1.13.
# Assume the automake command is ${AUTOMAKE:-automake} as autoreconf does.
# It may not work if "make" re-runs a different version of automake.
m4_define([serial_tests], [m4_esyscmd_s([
${AUTOMAKE:-automake} --version | head -1 |
awk '{split ($NF,a,"."); if (a[1] >= 2 || (a[1] == 1 && a[2] >= 13)) { print "serial-tests" }}'
])])
AC_PREREQ(2.59)
AC_INIT([FORM], FORM_VERSION, [https://github.com/vermaseren/form/issues])
AC_CONFIG_SRCDIR([sources/form3.h])
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_AUX_DIR([build-aux])
AM_INIT_AUTOMAKE([1.7 foreign -Wall dist-bzip2] serial_tests)
# Check for .version file
AM_CONDITIONAL([FIXED_VERSION], [test -f $srcdir/.version])
# Check for automake >= 1.10
flag=false
case $am__api_version in
1.6|1.7|1.8|1.9)
;;
*)
flag=:
;;
esac
AM_CONDITIONAL([AUTOMAKE_GE_110], [$flag])
# Check for programs
: ${CFLAGS=''} # avoid autoconf's default CFLAGS/CXXFLAGS
: ${CXXFLAGS=''}
AC_PROG_CC([gcc cc icc])
AM_PROG_CC_C_O
AC_PROG_CXX([g++ c++ icpc])
AC_PROG_LN_S
# Checks for header files
AC_HEADER_STDC
AC_HEADER_TIME
AC_CHECK_HEADERS([fcntl.h limits.h sys/file.h])
AC_LANG_PUSH([C++])
AC_CHECK_HEADERS([unordered_map tr1/unordered_map boost/unordered_map.hpp])
AC_CHECK_HEADERS([unordered_set tr1/unordered_set boost/unordered_set.hpp])
AC_LANG_POP([C++])
# Checks for builtin functions
ok=no
AS_IF([test $ok != yes],
[AC_MSG_CHECKING([__builtin_popcount])
AC_LINK_IFELSE(
[AC_LANG_PROGRAM([], [
int x = __builtin_popcount((unsigned int)(-1));
])],
[ok=yes;
AC_DEFINE([HAVE_BUILTIN_POPCOUNT], [1], [Define to 1 if you have __builtin_popcount function.])])
AC_MSG_RESULT($ok)])
AS_IF([test $ok != yes],
[AC_MSG_CHECKING([__popcnt])
AC_LINK_IFELSE(
[AC_LANG_PROGRAM([#include ], [
unsigned int x = __popcnt((unsigned int)(-1));
])],
[ok=yes;
AC_DEFINE([HAVE_POPCNT], [1], [Define to 1 if you have __popcnt function.])])
AC_MSG_RESULT($ok)])
# Check for inline
AC_C_INLINE
# Sets _FILE_OFFSET_BITS if possible
AC_SYS_LARGEFILE
# Check for architecture and OS
AC_CANONICAL_HOST
case $host_os in
darwin* )
print_os="OSX"
;;
linux* )
print_os="Linux"
# "LINUX" is still used in mallocprotect.h. (TU 16 Oct 2011)
AC_DEFINE(LINUX, , [Compiling for a Linux system.])
;;
cygwin* )
print_os="Cygwin"
;;
freebsd* )
print_os="FreeBSD"
;;
netbsd* )
print_os="NetBSD"
;;
openbsd* )
print_os="OpenBSD"
;;
* )
print_os="UNKNOWN OS"
;;
esac
case $host_cpu in
i?86 )
print_cpu="Pentium"
;;
x86_64 )
print_cpu="Opteron"
;;
alpha* )
print_cpu="Alpha"
;;
* )
print_cpu="UNKNOWN CPU"
;;
esac
# Check for C compiler vendor. We assume that all compilers (CC, CXX, MPICC and
# MPICXX) have the same vendor and the same version. Clang most likely works
# as GCC.
vendors="
intel: __ICC,__ECC,__INTEL_COMPILER
gnu: __GNUC__
microsoft: _MSC_VER
unknown: UNKNOWN
"
for ventest in $vendors; do
case $ventest in
*:)
vendor=$ventest
continue
;;
*)
vencpp="defined("`echo $ventest | sed 's/,/) || defined(/g'`")"
;;
esac
AC_COMPILE_IFELSE([AC_LANG_PROGRAM(,[
#if !($vencpp)
choke me
#endif
])], [break])
done
vendor=`echo $vendor | cut -d: -f1`
# POSIX or Windows API
AC_ARG_WITH([api],
[AS_HELP_STRING([--with-api=API],
[use POSIX (posix) or Windows (windows) API @<:@default=posix@:>@])],
[AS_IF([test "x$withval" != xposix && test "x$withval" != xwindows],
[AC_MSG_FAILURE([Invalid argument for API. Use --with-api=posix or --with-api=windows])])],
[with_api=posix])
AS_IF([test "x$with_api" = xposix],
[print_api=POSIX
AC_CHECK_HEADERS([unistd.h], [], [AC_MSG_FAILURE([unistd.h is not found])])
AC_DEFINE(UNIX, , [Compiling for UNIX system])])
AS_IF([test "x$with_api" = xwindows],
[print_api=Windows
AC_CHECK_HEADERS([windows.h], [],[AC_MSG_FAILURE([windows.h is not found])] )
AC_DEFINE(WINDOWS, , [Compiling for WINDOWS system])])
AM_CONDITIONAL([ONUNIX], [test "x$with_api" = xposix])
AM_CONDITIONAL([ONWINDOWS], [test "x$with_api" = xwindows])
# Check for data model
AC_CHECK_SIZEOF([char])
AC_CHECK_SIZEOF([short])
AC_CHECK_SIZEOF([int])
AC_CHECK_SIZEOF([long])
AC_CHECK_SIZEOF([long long])
AC_CHECK_SIZEOF([void *])
AC_CHECK_SIZEOF([off_t])
case $ac_cv_sizeof_char-$ac_cv_sizeof_short-$ac_cv_sizeof_int-$ac_cv_sizeof_long-$ac_cv_sizeof_long_long-$ac_cv_sizeof_void_p-$ac_cv_sizeof_off_t in
1-2-4-4-*-4-*)
# Most of today's 32 bit systems.
print_data_model="ILP32"
ac_cv_sizeof_WORD=$ac_cv_sizeof_short
ac_cv_sizeof_LONG=$ac_cv_sizeof_long
AC_DEFINE(ILP32, , [Compiling for ILP32 data model])
# We need INT64.
AS_IF([test $ac_cv_sizeof_long_long -ne 8],
[AC_MSG_FAILURE([64-bit integers are not available])])
;;
1-2-4-4-8-8-*)
# Microsoft Windows (X64/IA-64).
print_data_model="LLP64"
ac_cv_sizeof_WORD=$ac_cv_sizeof_int
ac_cv_sizeof_LONG=$ac_cv_sizeof_long_long
AC_DEFINE(LLP64, , [Compiling for LLP64 data model])
;;
1-2-4-8-*-8-*)
# Most Unix and Unix-like systems, e.g., Solaris, Linux and Mac OS X.
print_data_model="LP64"
ac_cv_sizeof_WORD=$ac_cv_sizeof_int
ac_cv_sizeof_LONG=$ac_cv_sizeof_long
AC_DEFINE(LP64, , [Compiling for LP64 data model])
;;
*)
AC_MSG_FAILURE([Cannot recognize the data model used in the compiler])
;;
esac
# Our basic assumption:
# sizeof(off_t) >= sizeof(LONG) >= sizeof(void *) >= sizeof(int)
# >= sizeof(WORD) >= sizeof(char) == 1.
flag=:
$flag && test $ac_cv_sizeof_off_t -lt $ac_cv_sizeof_LONG && flag=false
$flag && test $ac_cv_sizeof_LONG -lt $ac_cv_sizeof_void_p && flag=false
$flag && test $ac_cv_sizeof_void_p -lt $ac_cv_sizeof_int && flag=false
$flag && test $ac_cv_sizeof_int -lt $ac_cv_sizeof_WORD && flag=false
$flag && test $ac_cv_sizeof_WORD -lt $ac_cv_sizeof_char && flag=false
$flag && test $ac_cv_sizeof_char -ne 1 && flag=false
AS_IF([$flag], [], [AC_MSG_FAILURE([Basic assumption sizeof(off_t) >= sizeof(LONG) >= sizeof(void *) >= sizeof(int) >= sizeof(WORD) >= sizeof(char) == 1 does not hold.])])
# sizeof(off_t) <= 4 means files must <= 2 GB.
AS_IF([test $ac_cv_sizeof_off_t -le 4], [AC_MSG_WARN([Large files more than 2 GB are not supported])])
AC_MSG_NOTICE([The data model is $print_data_model])
# Check for gmp
AC_ARG_WITH([gmp],
[AS_HELP_STRING([--with-gmp@<:@=DIR@:>@],
[use GMP for long integer arithmetic (installed in prefix DIR) @<:@default=check@:>@])],
[AS_IF([test "x$withval" != xyes && test "x$withval" != xno && test "x$withval" != xcheck],
[with_gmp=yes
CPPFLAGS="$CPPFLAGS -I$withval/include"
LDFLAGS="$LDFLAGS -L$withval/lib"])],
[with_gmp=check])
AS_IF([test "x$with_gmp" != xno],
[flag=:
AS_IF([$flag], [AC_CHECK_HEADER([gmp.h], [], [flag=false])])
AS_IF([$flag], [AC_CHECK_LIB([gmp], [__gmpz_init], [LIBS="-lgmp $LIBS"], [flag=false])])
AS_IF([$flag],
[AC_DEFINE(WITHGMP, [], [Define to use GMP for long integer arithmetic.])
with_gmp=yes],
[AS_IF([test "x$with_gmp" = xyes],
[AC_MSG_FAILURE([test for GMP failed. Give --without-gmp if you want to compile without GMP])])
AC_MSG_NOTICE([GMP is not available])
with_gmp=no])])
# Check for zlib
AC_ARG_WITH([zlib],
[AS_HELP_STRING([--with-zlib@<:@=DIR@:>@],
[use zlib for compression (installed in prefix DIR) @<:@default=check@:>@])],
[AS_IF([test "x$withval" != xyes && test "x$withval" != xno && test "x$withval" != xcheck],
[with_zlib=yes
CPPFLAGS="$CPPFLAGS -I$withval/include"
LDFLAGS="$LDFLAGS -L$withval/lib"])],
[with_zlib=check])
AS_IF([test "x$with_zlib" != xno],
[flag=:
AS_IF([$flag], [AC_CHECK_HEADER([zlib.h], [], [flag=false])])
AS_IF([$flag], [AC_CHECK_LIB([z], [get_crc_table], [LIBS="-lz $LIBS"], [flag=false])])
AS_IF([$flag],
[AC_DEFINE(WITHZLIB, [], [Define to use zlib for compression.])
with_zlib=yes],
[AS_IF([test "x$with_zlib" = xyes],
[AC_MSG_FAILURE([test for zlib failed. Give --without-zlib if you want to compile without zlib])])
AC_MSG_NOTICE([zlib is not available])
with_zlib=no])])
# enable-scalar/threaded/parform/debug
AC_ARG_ENABLE([scalar],
[AS_HELP_STRING([--enable-scalar],
[build scalar version (form) @<:@default=yes@:>@])],
[AS_IF([test "x$enableval" != xno], [enable_scalar=yes])],
[enable_scalar=yes])
AC_ARG_ENABLE([threaded],
[AS_HELP_STRING([--enable-threaded],
[build multi-threaded version (tform) @<:@default=check@:>@])],
[AS_IF([test "x$enableval" != xno && test "x$enableval" != xcheck], [enable_threaded=yes])],
[enable_threaded=check])
AC_ARG_ENABLE([parform],
[AS_HELP_STRING([--enable-parform],
[build parallel version using MPI (parform) @<:@default=no@:>@])],
[AS_IF([test "x$enableval" != xno && test "x$enableval" != xcheck], [enable_parform=yes])],
[enable_parform=no])
AC_ARG_ENABLE([debug],
[AS_HELP_STRING([--enable-debug],
[build debugging versions (vorm/tvorm/parvorm) @<:@default=no@:>@])],
[AS_IF([test "x$enableval" != xno], [enable_debug=yes])],
[enable_debug=no])
# Check for scalar version
build_form=$enable_scalar
AS_IF([test "x$enable_scalar" = xyes && test "x$enable_debug" = xyes], [build_vorm=yes], [build_vorm=no])
AM_CONDITIONAL([BUILD_FORM], [test "x$build_form" = xyes])
AM_CONDITIONAL([BUILD_VORM], [test "x$build_vorm" = xyes])
# Check for threaded version
PTHREAD_CFLAGS=
PTHREAD_CPPFLAGS=
PTHREAD_LIBS=
AH_VERBATIM([WITHPOSIXCLOCK],
[/* Define to use POSIX thread clock. */
#ifdef WITHPTHREADS
#undef WITHPOSIXCLOCK
#endif])
thread_clock_ok=no
AS_IF([test "x$enable_threaded" != xno],
[flag=:
# Check the flag/library for pthreads
AS_IF([$flag],
[ok=no
# none : Cygwin
# -pthread : Linux/gcc (kernel threads), BSD/gcc (userland threads)
# pthread : Linux, OSX
for a in none -pthread pthread; do
case $a in
none)
AC_MSG_CHECKING([whether pthreads works without any flags])
;;
-*)
AC_MSG_CHECKING([whether pthreads works with $a])
PTHREAD_CFLAGS="$a"
;;
*)
AC_MSG_CHECKING([for the pthreads library -l$a])
PTHREAD_LIBS="-l$a"
;;
esac
save_CFLAGS=$CFLAGS
save_LIBS=$LIBS
CFLAGS="$PTHREAD_CFLAGS $CFLAGS"
LIBS="$PTHREAD_LIBS $LIBS"
AC_LINK_IFELSE([AC_LANG_PROGRAM([
#include
static void *start_routine(void *a) { return a; }
], [
pthread_t th;
pthread_condattr_t attr;
pthread_create(&th, 0, start_routine, 0);
pthread_condattr_setpshared(&attr, PTHREAD_PROCESS_PRIVATE);
])],
[ok=yes],
[])
CFLAGS=$save_CFLAGS
LIBS=$save_LIBS
AC_MSG_RESULT($ok)
test "x$ok" = xyes && break
PTHREAD_CFLAGS=
PTHREAD_LIBS=
done
test "x$ok" = xno && flag=false])
# Check pthread_rwlock_t
AS_IF([$flag],
[ok=no
# -D_XOPEN_SOURCE=500: Scientific Linux 4.8
for a in none -D_XOPEN_SOURCE=500; do
case $a in
none)
AC_MSG_CHECKING([for pthread_rwlock_t])
;;
-D*)
AC_MSG_CHECKING([for pthread_rwlock_t with $a])
PTHREAD_CPPFLAGS="$a"
;;
esac
save_CPPFLAGS=$CPPFLAGS
CPPFLAGS="$PTHREAD_CPPFLAGS $CPPFLAGS"
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([
#include
#include
pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
], [
while (pthread_rwlock_tryrdlock(&rwlock) == EBUSY) {}
pthread_rwlock_unlock(&rwlock);
])],
[ok=yes],
[])
CPPFLAGS=$save_CPPFLAGS
AC_MSG_RESULT($ok)
test "x$ok" = xyes && break
PTHREAD_CPPFLAGS=
done
test "x$ok" = xno && flag=false])
# Check clock_gettime with CLOCK_THREAD_CPUTIME_ID
AS_IF([$flag && test "x$with_api" = xposix],
[ok=yes
AS_IF([test "x$ok" = xyes],
[AC_MSG_CHECKING([for the POSIX thread clock])
save_CPPFLAGS=$CPPFLAGS
CPPFLAGS="$PTHREAD_CPPFLAGS $CPPFLAGS"
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([
#include
], [
struct timespec t;
clock_gettime(CLOCK_THREAD_CPUTIME_ID, &t);
])],
[],
[ok=no])
CPPFLAGS=$save_CPPFLAGS
AC_MSG_RESULT($ok)])
AS_IF([test "x$ok" = xyes],
[save_LIBS=$LIBS
AC_SEARCH_LIBS([clock_gettime], [rt], [], [ok=no])
LIBS=$save_LIBS
if test "x$ac_cv_search_clock_gettime" != "xnone required" && test "x$ac_cv_search_clock_gettime" != "xno"; then
PTHREAD_LIBS="$ac_cv_search_clock_gettime $PTHREAD_LIBS"
fi])
AS_IF([test "x$ok" = xyes],
[cat >>confdefs.h </dev/null`
AS_IF([test $? -eq 0], [
AC_MSG_RESULT([yes])
ax_ok=:
break
], [
AC_MSG_RESULT([no])
])
done
AS_IF([$ax_ok], [], [AC_MSG_WARN([Cannot extract compiler and linker flags from $$1])])
# Extract the compile and link flags.
ax_mpi_cflags=
ax_mpi_cppflags=
ax_mpi_ldflags=
ax_mpi_libs=
ax_first=:
for ax_opt in $ax_mpi_cmdline; do
case $ax_opt in
-I*|-D*)
ax_mpi_cppflags="$ax_mpi_cppflags $ax_opt"
;;
-L*|-Wl,*)
ax_mpi_ldflags="$ax_mpi_ldflags $ax_opt"
;;
-l*)
ax_mpi_libs="$ax_mpi_libs $ax_opt"
;;
*)
$ax_first || ax_mpi_cflags="$ax_mpi_cflags $ax_opt"
;;
esac
ax_first=false
done
MPI_$2FLAGS=` echo "$ax_mpi_cflags" | sed 's/^ *//;s/ *$//;s/ */ /g'`
MPI_$2PPFLAGS=`echo "$ax_mpi_cppflags" | sed 's/^ *//;s/ *$//;s/ */ /g'`
MPI_$2LDFLAGS=`echo "$ax_mpi_ldflags" | sed 's/^ *//;s/ *$//;s/ */ /g'`
MPI_$2LIBS=` echo "$ax_mpi_libs" | sed 's/^ *//;s/ *$//;s/ */ /g'`
], [
AC_MSG_RESULT([no])
$1=
MPI_$2FLAGS=
MPI_$2LDFLAGS=
MPI_$2LIBS=
])
])
AC_DEFUN([_AX_CHECK_MPI_SOURCE], [_AC_LANG_DISPATCH([$0], _AC_LANG, $@)])
m4_define([_AX_CHECK_MPI_SOURCE(C)],
[#include
int main(int argc, char **argv) {
int rank, size;
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Finalize();
return 0;
}])
m4_copy([_AX_CHECK_MPI_SOURCE(C)], [_AX_CHECK_MPI_SOURCE(C++)])
# Check for MPI version
AS_IF([test "x$enable_parform" != xno],
[flag=:
AS_IF([$flag], [AX_PROG_MPICC
AS_IF([test "x$MPICC" = x], [flag=false])])
AS_IF([$flag], [AX_PROG_MPICXX
AS_IF([test "x$MPICXX" = x], [flag=false])])
AS_IF([$flag],
[enable_parform=yes
AC_SUBST([MPI_CFLAGS])
AC_SUBST([MPI_CXXFLAGS])
AC_SUBST([MPI_CPPFLAGS])],
[AS_IF([test "x$enable_parform" = xyes],
[AC_MSG_FAILURE([test for parform failed. Give --disable-parform if you do not need to build parform])])
AC_MSG_NOTICE([building parform has been disabled])
AS_IF([test "x$enable_debug" = xyes],
[AC_MSG_NOTICE([building parvorm has been disabled])])
enable_parform=no])])
build_parform=$enable_parform
AS_IF([test "x$enable_parform" = xyes && test "x$enable_debug" = xyes], [build_parvorm=yes], [build_parvorm=no])
AM_CONDITIONAL([BUILD_PARFORM], [test "x$build_parform" = xyes])
AM_CONDITIONAL([BUILD_PARVORM], [test "x$build_parvorm" = xyes])
# Check for wall-clock time
ok=no
AS_IF([test $ok != yes],
[AC_SEARCH_LIBS([clock_gettime], [rt],
[AC_DEFINE([HAVE_CLOCK_GETTIME], [1], [Define to 1 if you have clock_gettime.])
ok=yes])])
AS_IF([test $ok != yes],
# TODO: gettimeofday is also deprecated.
[AC_SEARCH_LIBS([gettimeofday], [],
[AC_DEFINE([HAVE_GETTIMEOFDAY], [1], [Define to 1 if you have gettimeofday.])
ok=yes])])
AS_IF([test $ok != yes],
# Fallback: ftime. Available also on Windows. Some BSDs require -lcompat.
[AC_SEARCH_LIBS([ftime], [compat],
[AC_DEFINE([HAVE_FTIME], [1], [Define to 1 if you have ftime.])
ok=yes])])
AS_IF([test $ok != yes],
[AC_MSG_FAILURE([Wall-clock time not available])])
# Check for static linking
STATIC_LDFLAGS=
MPI_STATIC_LDFLAGS=
AC_ARG_ENABLE([static-link],
[AS_HELP_STRING([--enable-static-link],
[link with static libraries (release versions) @<:@default=no@:>@])],
[AS_IF([test "x$enableval" != xno && test "x$enableval" != xcheck], [enable_static_link=yes])],
[enable_static_link=no])
AS_IF([test "x$enable_static_link" != xno],
[flag=:
if test "x$vendor" = xgnu; then
static_list='-static -static-libgcc,-static-libstdc++ -static-libgcc'
elif test "x$vendor" = xintel; then
static_list='-static -static-libgcc,-static-intel -static-intel -static-libgcc'
else
static_list='-static -static-libgcc'
fi
for a in $static_list; do
a=`echo $a | sed 's/,/ /g'`
AC_MSG_CHECKING([for static linking with $CXX $a])
AC_LANG_PUSH([C++])
save_CFLAGS=$CFLAGS
save_LDFLAGS=$LDFLAGS
save_LIBS=$LIBS
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
LDFLAGS="$LDFLAGS $a"
LIBS="$PTHREAD_LIBS $LIBS"
AC_LINK_IFELSE([AC_LANG_PROGRAM(,)],
[AC_MSG_RESULT([yes]); STATIC_LDFLAGS=$a],
[AC_MSG_RESULT([no]); flag=false])
CFLAGS=$save_CFLAGS
LDFLAGS=$save_LDFLAGS
LIBS=$save_LIBS
AC_LANG_POP([C++])
test "x$STATIC_LDFLAGS" != x && break
done
AS_IF([test "x$enable_parform" = xyes],
[for a in $static_list; do
a=`echo $a | sed 's/,/ /g'`
AC_MSG_CHECKING([for static linking with $MPICXX $a])
AC_LANG_PUSH([C++])
save_CXX=$CXX
save_LDFLAGS=$LDFLAGS
CXX=$MPICXX
LDFLAGS="$LDFLAGS $a"
AC_LINK_IFELSE([AC_LANG_PROGRAM(,)],
[AC_MSG_RESULT([yes]); MPI_STATIC_LDFLAGS=$a],
[AC_MSG_RESULT([no]); flag=false])
CXX=$save_CXX
LDFLAGS=$save_LDFLAGS
AC_LANG_POP([C++])
test "x$MPI_STATIC_LDFLAGS" != x && break
done])
AS_IF([$flag],
[enable_static_link=yes],
[AS_IF([test "x$enable_static_link" = xyes],
[AC_MSG_FAILURE([test for static linking failed. Give --disable-static-link if you want to build without static libraries.])])
AS_IF([test "x$STATIC_LDFLAGS" = x && test "x$MPI_STATIC_LDFLAGS" = x],
[AC_MSG_NOTICE([static linking has been disabled])],
[AC_MSG_NOTICE([static linking has been partially disabled])])
enable_static_link=no])])
AC_SUBST([STATIC_LDFLAGS])
AC_SUBST([MPI_STATIC_LDFLAGS])
# Check for native/universal build
AC_ARG_ENABLE([native],
[AS_HELP_STRING([--enable-native],
[tune for the compiling machine (release versions) @<:@default=yes@:>@])],
[AS_IF([test "x$enableval" = xno || test "x$cross_compiling" = xyes],
[enable_native=no], [enable_native=yes])],
[enable_native=yes])
# Check for profiling option
AC_ARG_ENABLE([profile],
[AS_HELP_STRING([--enable-profile@<:@=PROFILER@:>@],
[build with profiling (release versions) @<:@default=no@:>@ PROFILER: gprof (default) or gperftools])],
[AS_IF([test "x$enableval" = xyes],
[enable_profile=gprof],
[AS_IF([test "x$enableval" = xgprof || test "x$enableval" = xgperftools],
[enable_profile=$enableval],
[enable_profile=no])])],
[enable_profile=no])
# Check for coverage option
AC_ARG_ENABLE([coverage],
[AS_HELP_STRING([--enable-coverage],
[generate coverage files (debugging versions) @<:@default=no@:>@])],
[AS_IF([test "x$enableval" = xyes && test "x$enable_debug" = xyes],
[enable_coverage=yes], [enable_coverage=no])],
[enable_coverage=no])
# Check for sanitizers
AC_ARG_ENABLE([sanitize],
[AS_HELP_STRING([--enable-sanitize@<:@=CHECKS@:>@],
[enable sanitizers (debugging versions) @<:@default=no@:>@ Optionally CHECKS will be passed via the compiler sanitizer option])],
[AS_IF([test "x$enable_debug" != xyes],
[enable_sanitize=no])],
[enable_sanitize=no])
# Optimization/debugging flags
AC_ARG_VAR([COMPILEFLAGS], [Compiler flags for release versions])
AC_ARG_VAR([LINKFLAGS], [Linker flags for release versions])
AC_ARG_VAR([DEBUGCOMPILEFLAGS], [Compiler flags for debugging versions])
AC_ARG_VAR([DEBUGLINKFLAGS], [Linker flags for debugging versions])
TOOL_LIBS=
DEBUGTOOL_LIBS=
my_test_COMPILEFLAGS=${COMPILEFLAGS+set}
if test "$my_test_COMPILEFLAGS" != set; then
if test "x$vendor" = xgnu; then
# We don't use -pedantic option because of horrible warnings.
COMPILEFLAGS="-Wall -Wextra -Wpadded -O3"
if test "x$enable_profile" != xgprof; then
# -pg conflicts with -fomit-frame-pointer.
COMPILEFLAGS="$COMPILEFLAGS -fomit-frame-pointer"
fi
if test "x$enable_native" = xyes; then
# Check for -march=native.
AC_MSG_CHECKING([whether compiler accepts -march=native])
ok=no
save_CFLAGS=$CFLAGS
CFLAGS="$CFLAGS -march=native"
AC_COMPILE_IFELSE([AC_LANG_PROGRAM()], [ok=yes])
CFLAGS=$save_CFLAGS
AC_MSG_RESULT($ok)
if test "x$ok" = xyes; then
COMPILEFLAGS="$COMPILEFLAGS -march=native"
else
if test "x$print_data_model" = xILP32; then
if test "x$print_cpu" = xPentium; then
# NOTE: In a strict sense, i686 must be used for Pentium Pro or later.
COMPILEFLAGS="$COMPILEFLAGS -march=i686"
elif test "x$print_cpu" = xOpteron; then
COMPILEFLAGS="$COMPILEFLAGS -march=opteron"
fi
fi
fi
fi
# Profiling option.
if test "x$enable_profile" = xgprof; then
COMPILEFLAGS="$COMPILEFLAGS -g -pg"
elif test "x$enable_profile" = xgperftools; then
COMPILEFLAGS="$COMPILEFLAGS -g"
TOOL_LIBS="$TOOL_LIBS -lprofiler"
fi
elif test "x$vendor" = xintel; then
# NOTE: -fast option includes -static and may cause an error in linking.
COMPILEFLAGS="-Wall -ipo -O3 -no-prec-div"
if test "x$enable_native" = xyes; then
COMPILEFLAGS="$COMPILEFLAGS -xHost"
fi
if test "x$enable_profile" != xno; then
enable_profile=unavailable
fi
else
COMPILEFLAGS=-O2
if test "x$enable_profile" != xno; then
enable_profile=unavailable
fi
fi
fi
my_test_LINKFLAGS=${LINKFLAGS+set}
if test "$my_test_LINKFLAGS" != set; then
if test "x$vendor" = xgnu && test "x$print_os" = xOSX; then
# On OS X Mavericks, -s option has a funny effect: though the linker
# warns the option is obsolete and being ignored, it causes an internal
# error "atom not found in symbolIndex...".
LINKFLAGS=
elif test "x$enable_profile" != xno && test "x$enable_profile" != xunavailable; then
# Profilers needs symbol tables.
LINKFLAGS=
else
LINKFLAGS=-s
fi
fi
my_test_DEBUGCOMPILEFLAGS=${DEBUGCOMPILEFLAGS+set}
if test "$my_test_DEBUGCOMPILEFLAGS" != set && test "x$enable_debug" = xyes; then
if test "x$vendor" = xgnu; then
DEBUGCOMPILEFLAGS='-g3 -Wall -Wextra'
if test "x$enable_sanitize" = xno; then
# UBSan puts many paddings (at least in gcc 5.3).
DEBUGCOMPILEFLAGS="$DEBUGCOMPILEFLAGS -Wpadded"
fi
# Check for -Og.
AC_MSG_CHECKING([whether compiler accepts -Og])
ok=no
save_CFLAGS=$CFLAGS
CFLAGS="$CFLAGS -Og"
AC_COMPILE_IFELSE([AC_LANG_PROGRAM()], [ok=yes])
CFLAGS=$save_CFLAGS
AC_MSG_RESULT($ok)
if test "x$ok" = xyes; then
DEBUGCOMPILEFLAGS="$DEBUGCOMPILEFLAGS -Og"
else
DEBUGCOMPILEFLAGS="$DEBUGCOMPILEFLAGS -O0"
fi
# Coverage option.
if test "x$enable_coverage" = xyes; then
DEBUGCOMPILEFLAGS="$DEBUGCOMPILEFLAGS -coverage"
fi
# Sanitizer option.
if test "x$enable_sanitize" = xyes; then
enable_sanitize=
for san in address undefined; do
if test "x$enable_sanitize" = x; then
tmp_sanitize=$san
else
tmp_sanitize=$enable_sanitize,$san
fi
AC_MSG_CHECKING([whether compiler accepts -fsanitize=$tmp_sanitize])
ok=no
save_CFLAGS=$CFLAGS
CFLAGS="$CFLAGS -fsanitize=$tmp_sanitize"
AC_COMPILE_IFELSE([AC_LANG_PROGRAM()], [ok=yes])
CFLAGS=$save_CFLAGS
AC_MSG_RESULT($ok)
if test "x$ok" = xyes; then
enable_sanitize=$tmp_sanitize
fi
done
if test "x$enable_sanitize" = x; then
enable_sanitize=failed
fi
elif test "x$enable_sanitize" != xno; then
AC_MSG_CHECKING([whether compiler accepts -fsanitize=$enable_sanitize])
ok=no
save_CFLAGS=$CFLAGS
CFLAGS="$CFLAGS -fsanitize=$enable_sanitize"
AC_COMPILE_IFELSE([AC_LANG_PROGRAM()], [ok=yes])
CFLAGS=$save_CFLAGS
AC_MSG_RESULT($ok)
if test "x$ok" != xyes; then
enable_sanitize=failed
fi
fi
if test "x$enable_sanitize" = xfailed; then
AC_MSG_FAILURE([test for sanitizer failed. Give --disable-sanitize if you want to compile without sanitizer])
fi
if test "x$enable_sanitize" != xno; then
DEBUGCOMPILEFLAGS="$DEBUGCOMPILEFLAGS -fsanitize=$enable_sanitize"
fi
elif test "x$vendor" = xintel; then
DEBUGCOMPILEFLAGS='-g3 -Wall -O0'
if test "x$enable_coverage" != xno; then
enable_coverage=unavailable
fi
if test "x$enable_sanitize" != xno; then
enable_sanitize=unavailable
fi
else
DEBUGCOMPILEFLAGS=-g
if test "x$enable_coverage" != xno; then
enable_coverage=unavailable
fi
if test "x$enable_sanitize" != xno; then
enable_sanitize=unavailable
fi
fi
fi
my_test_DEBUGLINKFLAGS=${DEBUGLINKFLAGS+set}
if test "$my_test_DEBUGLINKFLAGS" != set && test "x$enable_debug" = xyes; then
DEBUGLINKFLAGS=
if test "x$vendor" = xgnu; then
# Coverage option.
if test "x$enable_coverage" = xyes; then
DEBUGLINKFLAGS="$DEBUGLINKFLAGS -coverage"
fi
# Sanitizer option.
if test "x$enable_sanitize" != xno; then
# Workaround for https://bugs.launchpad.net/ubuntu/+source/gcc-defaults/+bug/1650186
ok=no
save_CFLAGS=$CFLAGS
save_LDFLAGS=$LDFLAGS
CFLAGS="$CFLAGS -fsanitize=$enable_sanitize"
AC_LINK_IFELSE([AC_LANG_PROGRAM()], [ok=yes])
if test "x$ok" != xyes; then
LDFLAGS="$LDFLAGS -fuse-ld=gold"
AC_LINK_IFELSE([AC_LANG_PROGRAM()], [ok=yes])
if test "x$ok" = xyes; then
DEBUGLINKFLAGS="$DEBUGLINKFLAGS -fuse-ld=gold"
fi
fi
CFLAGS=$save_CFLAGS
LDFLAGS=$save_LDFLAGS
fi
fi
fi
AC_SUBST([TOOL_LIBS])
AC_SUBST([DEBUGTOOL_LIBS])
# Check for doxygen
AC_PATH_PROG(DOXYGEN, doxygen, "")
AM_CONDITIONAL(CONFIG_DOXYGEN, [test "x$DOXYGEN" != x])
# Check for LaTeX programs
AC_PATH_PROG(LATEX, latex, "")
AC_PATH_PROG(PDFLATEX, pdflatex, "")
AC_PATH_PROG(DVIPS, dvips, "")
AC_PATH_PROG(MAKEINDEX, makeindex, "")
AC_PATH_PROG(HTLATEX, htlatex, "")
AC_PATH_PROG(LATEX2HTML, latex2html, "")
AM_CONDITIONAL(CONFIG_TEX, [test "x$LATEX" != x])
AM_CONDITIONAL(CONFIG_PS, [test "x$LATEX" != x && test "x$DVIPS" != x])
AM_CONDITIONAL(CONFIG_PDF, [test "x$PDFLATEX" != x])
AM_CONDITIONAL(CONFIG_MAKEINDEX, [test "x$MAKEINDEX" != x])
AM_CONDITIONAL(CONFIG_HTLATEX, [test "x$HTLATEX" != x])
AM_CONDITIONAL(CONFIG_LATEX2HTML, [test "x$LATEX2HTML" != x])
# Check for Ruby >= 1.8 and test/unit.
AC_PATH_PROG(RUBY, ruby, "")
ok=yes
test "x$RUBY" = x && ok=no
if test "x$ok" = xyes; then
AC_MSG_CHECKING([whether ruby >= 1.8])
$RUBY -e 'exit(1) if RUBY_VERSION < "1.8.0"' >/dev/null 2>&1 || ok=no
AC_MSG_RESULT([$ok])
fi
if test "x$ok" = xyes; then
AC_MSG_CHECKING([for ruby test/unit])
{ cat >conftest.rb </dev/null 2>&1 || ok=no
require 'test/unit'
EOF
AC_MSG_RESULT([$ok])
fi
with_ruby_test=$ok
AM_CONDITIONAL(CONFIG_RUBY, [test "x$with_ruby_test" = xyes])
AC_CONFIG_FILES([
Makefile
sources/Makefile
doc/Makefile
doc/manual/Makefile
doc/manual/manual.tex
doc/devref/Makefile
doc/devref/devref.tex
doc/doxygen/Makefile
doc/doxygen/DoxyfileHTML
doc/doxygen/DoxyfileLATEX
doc/doxygen/DoxyfilePDFLATEX
check/Makefile
])
AC_OUTPUT
# Print configuration
echo
echo "##################### CONFIGURATION #####################"
echo
outputdir=$(eval "echo $bindir")
outputdir=$(eval "echo $outputdir")
manoutputdir=$(eval "echo $mandir")
manoutputdir=$(eval "echo $manoutputdir")
echo "FORM $VERSION"
echo
echo "Compiling for: $print_cpu $print_os ($print_data_model $print_api)"
echo
echo "Optionally linked libraries:"
atleastone=no
if test "x$with_gmp" = xyes; then
echo " gmp"
atleastone=yes
fi
if test "x$with_zlib" = xyes; then
echo " zlib"
atleastone=yes
fi
if test $atleastone = no; then
echo " "
fi
echo
echo "The following executables can be compiled:"
atleastone=no
if test "x$build_form" = xyes; then
opts=
if test "x$enable_native" = xyes; then
if test "x$opts" != x; then
opts="${opts}, "
fi
opts="${opts}native"
fi
if test "x$STATIC_LDFLAGS" != x; then
if test "x$opts" != x; then
opts="${opts}, "
fi
opts="${opts}statically linked"
fi
if test "x$enable_profile" = xgprof; then
if test "x$opts" != x; then
opts="${opts}, "
fi
opts="${opts}gprof"
fi
if test "x$enable_profile" = xgperftools; then
if test "x$opts" != x; then
opts="${opts}, "
fi
opts="${opts}gperftools"
fi
if test "x$opts" != x; then
opts=" (${opts})"
fi
echo " form scalar version$opts"
atleastone=yes
fi
if test "x$build_vorm" = xyes; then
opts=
if test "x$enable_coverage" = xyes; then
if test "x$opts" != x; then
opts="${opts}, "
fi
opts="${opts}gcov"
fi
if test "x$enable_sanitize" != xno; then
if test "x$opts" != x; then
opts="${opts}, "
fi
opts="${opts}sanitize=$enable_sanitize"
fi
if test "x$opts" != x; then
opts=" (${opts})"
fi
echo " vorm debugging version$opts"
atleastone=yes
fi
if test "x$build_tform" = xyes; then
opts=
if test "x$enable_native" = xyes; then
if test "x$opts" != x; then
opts="${opts}, "
fi
opts="${opts}native"
fi
if test "x$STATIC_LDFLAGS" != x; then
if test "x$opts" != x; then
opts="${opts}, "
fi
opts="${opts}statically linked"
fi
if test "x$enable_profile" = xgprof; then
if test "x$opts" != x; then
opts="${opts}, "
fi
opts="${opts}gprof"
fi
if test "x$enable_profile" = xgperftools; then
if test "x$opts" != x; then
opts="${opts}, "
fi
opts="${opts}gperftools"
fi
if test "x$opts" != x; then
opts=" (${opts})"
fi
echo " tform multi-threaded version$opts"
atleastone=yes
fi
if test "x$build_tvorm" = xyes; then
opts=
if test "x$enable_coverage" = xyes; then
if test "x$opts" != x; then
opts="${opts}, "
fi
opts="${opts}gcov"
fi
if test "x$enable_sanitize" != xno; then
if test "x$opts" != x; then
opts="${opts}, "
fi
opts="${opts}sanitize=$enable_sanitize"
fi
if test "x$opts" != x; then
opts=" (${opts})"
fi
echo " tvorm multi-threaded debugging version$opts"
atleastone=yes
fi
if test "x$build_parform" = xyes; then
opts=
if test "x$enable_native" = xyes; then
if test "x$opts" != x; then
opts="${opts}, "
fi
opts="${opts}native"
fi
if test "x$MPI_STATIC_LDFLAGS" != x; then
if test "x$opts" != x; then
opts="${opts}, "
fi
opts="${opts}statically linked"
fi
if test "x$enable_profile" = xgprof; then
if test "x$opts" != x; then
opts="${opts}, "
fi
opts="${opts}gprof"
fi
if test "x$enable_profile" = xgperftools; then
if test "x$opts" != x; then
opts="${opts}, "
fi
opts="${opts}gperftools"
fi
if test "x$opts" != x; then
opts=" (${opts})"
fi
echo " parform parallel version using MPI$opts"
atleastone=yes
fi
if test "x$build_parvorm" = xyes; then
opts=
if test "x$enable_coverage" = xyes; then
if test "x$opts" != x; then
opts="${opts}, "
fi
opts="${opts}gcov"
fi
if test "x$enable_sanitize" != xno; then
if test "x$opts" != x; then
opts="${opts}, "
fi
opts="${opts}sanitize=$enable_sanitize"
fi
if test "x$opts" != x; then
opts=" (${opts})"
fi
echo " parvorm parallel debugging version using MPI$opts"
atleastone=yes
fi
if test $atleastone = no; then
echo " "
fi
if test $ac_cv_sizeof_off_t -le 4; then
echo
echo "***CAUTION*** Large files more than 2 GB will be"
echo "not supported."
fi
if test "x$thread_clock_ok" = xno; then
s="none"
if test "x$build_tform" = xyes && test "x$build_tvorm" = xyes; then
s="tform and tvorm"
elif test "x$build_tform" = xyes; then
s="tform"
elif test "x$build_tvorm" = xyes; then
s="tvorm"
fi
if test "x$s" != xnone; then
echo
echo "***CAUTION*** $s may have clock"
echo "problems which make that each worker registers"
echo "the complete time used by all workers and the master."
fi
fi
echo
echo "Type 'make ' in the source directory to"
echo "build a specific version. Type 'make' to build all."
echo
echo "Type 'make install' to install the executables in"
echo " $outputdir"
echo "and the man page in"
echo " $manoutputdir"
echo
if test "x$with_ruby_test" = xyes; then
echo "Type 'make check' to run automatic tests."
else
echo "Automatic tests are not available."
fi
echo
echo "Available documentation:"
atleastone=no
if test "x$DOXYGEN" != x; then
atleastone=yes
str=' doxygen ( html '
if test "x$MAKEINDEX" != x; then
if test "x$LATEX" != x; then
str=$str'dvi '
if test "x$DVIPS" != x; then
str=$str'ps '
fi
fi
if test "x$PDFLATEX" != x; then
str=$str'pdf '
fi
fi
str=$str')'
echo "$str"
fi
if test "x$LATEX" != x || test "x$PDFLATEX" != x; then
atleastone=yes
str=' manual ( '
if test "x$HTLATEX" != x; then
str=$str'html '
fi
if test "x$LATEX" != x; then
str=$str'dvi '
if test "x$DVIPS" != x; then
str=$str'ps '
fi
fi
if test "x$PDFLATEX" != x; then
str=$str'pdf '
fi
str=$str')'
echo "$str"
fi
if test $atleastone = no; then
echo " "
else
echo
echo "Type 'make ' in the directories doc/manual or"
echo "doc/doxygen to generate the respective documentation with"
echo "the specified format."
fi
echo
echo "#########################################################"
echo
form-master/doc/ 0000775 0000000 0000000 00000000000 13565763364 0014101 5 ustar 00root root 0000000 0000000 form-master/doc/Makefile.am 0000664 0000000 0000000 00000000070 13565763364 0016132 0 ustar 00root root 0000000 0000000 SUBDIRS = doxygen manual devref
dist_man1_MANS = form.1
form-master/doc/devref/ 0000775 0000000 0000000 00000000000 13565763364 0015354 5 ustar 00root root 0000000 0000000 form-master/doc/devref/.latex2html-init 0000664 0000000 0000000 00000000466 13565763364 0020410 0 ustar 00root root 0000000 0000000 $DVIPSOPT = ' -E';
$TITLE = `grep '\\title{' devref.tex`;
$TITLE =~ s/^\s*\\title\s*{//;
$TITLE =~ s/}\s*$//;
$TITLE =~ s/\\\s*(Huge|huge|Large|large|\\)//g;
$TITLE =~ s/^\s+//;
$TITLE =~ s/\s+$//;
$TITLE =~ s/\s+/ /g;
$MAX_SPLIT_DEPTH = 0;
$NO_NAVIGATION = 1;
$NO_FOOTNODE = 1;
$ADDRESS = '';
$INFO = '';
1;
form-master/doc/devref/Makefile.am 0000664 0000000 0000000 00000007474 13565763364 0017424 0 ustar 00root root 0000000 0000000 TEXSRC = \
cvs.tex \
devref.tex \
formrun.tex \
indepth.tex \
source.tex \
testsuite.tex
MAIN = devref
TEXFILES = $(TEXSRC) $(MAIN).tex version.tex
EXTRA_DIST = $(TEXSRC) .latex2html-init
.PHONY: dvi latex2html html ps pdf clean-local update_version_tex
# NOTE: htlatex invalidate .aux, .idx, .dvi files.
HTMLCLEANFILES = idxmake.dvi idxmake.log $(MAIN).4ct $(MAIN).4dx $(MAIN).4ix \
$(MAIN).4tc $(MAIN).aux $(MAIN).css $(MAIN).dvi $(MAIN).html $(MAIN)2.html \
$(MAIN).idv $(MAIN).idx $(MAIN).ilg $(MAIN).ind $(MAIN).lg $(MAIN).log \
$(MAIN).tmp $(MAIN).xref
CLEANFILES = $(MAIN).pdf $(MAIN).ps $(MAIN).toc $(DATEFILE) texput.log \
version.tex $(HTMLCLEANFILES)
clean-local:
rm -rf html $(MAIN)
# Automatic versioning.
version.tex: update_version_tex
$(UPDATE_VERSION_TEX)
dist-hook:
$(DISTHOOK_VERSION_TEX)
if FIXED_VERSION
UPDATE_VERSION_TEX = \
[ -f version.tex ] || $(LN_S) "$(srcdir)/version.tex.in" version.tex
DISTHOOK_VERSION_TEX = \
cp "$(srcdir)/version.tex.in" "$(distdir)/version.tex.in"
else
UPDATE_VERSION_TEX = \
$(SHELL) "$(top_srcdir)/scripts/git-version-gen.sh" -C "$(srcdir)" -t -o version.tex --date-format '%e %B %Y'
DISTHOOK_VERSION_TEX = \
$(SHELL) "$(top_srcdir)/scripts/git-version-gen.sh" -C "$(srcdir)" -t -o "$(distdir)/version.tex.in" --date-format '%e %B %Y'
endif
#################### CONFIG_TEX
if CONFIG_TEX
dvi: $(MAIN).dvi
if CONFIG_MAKEINDEX
$(MAIN).dvi: $(TEXFILES)
$(LATEX) $(MAIN).tex; while [ `grep -v 'Package: rerunfilecheck' $(MAIN).log | grep -c Rerun` -gt 0 ]; do $(LATEX) $(MAIN).tex; done
$(MAKEINDEX) $(MAIN)
$(LATEX) $(MAIN).tex; while [ `grep -v 'Package: rerunfilecheck' $(MAIN).log | grep -c Rerun` -gt 0 ]; do $(LATEX) $(MAIN).tex; done
else
$(MAIN).dvi: $(TEXFILES)
$(LATEX) $(MAIN).tex; while [ `grep -v 'Package: rerunfilecheck' $(MAIN).log | grep -c Rerun` -gt 0 ]; do $(LATEX) $(MAIN).tex; done
endif
########## CONFIG_LATEX2HTML
if CONFIG_LATEX2HTML
latex2html: $(MAIN)/$(MAIN).html
$(MAIN)/$(MAIN).html: $(MAIN).dvi
$(LATEX2HTML) -init_file $(srcdir)/.latex2html-init $(MAIN).tex
cat $(MAIN)/index.html | sed 's/$(MAIN).html#/#/g' >$(MAIN)/index.html.tmp
mv $(MAIN)/index.html.tmp $(MAIN)/index.html
cat $(MAIN)/$(MAIN).html | sed 's/$(MAIN).html#/#/g' >$(MAIN)/$(MAIN).html.tmp
mv $(MAIN)/$(MAIN).html.tmp $(MAIN)/$(MAIN).html
endif
########## CONFIG_LATEX2HTML
########## CONFIG_HTLATEX
if CONFIG_HTLATEX
html: html/$(MAIN).html
if CONFIG_MAKEINDEX
html/$(MAIN).html: $(TEXFILES)
mkdir -p html
$(HTLATEX) $(MAIN) "html,mathml-" "" "-dhtml/"
$(TEX) '\def\filename{{$(MAIN)}{idx}{4dx}{ind}} \input idxmake.4ht'
$(MAKEINDEX) -o $(MAIN).ind $(MAIN).4dx
$(HTLATEX) $(MAIN) "html,mathml-" "" "-dhtml/"
sed 's/table.tabular {margin-left: auto; margin-right: auto;}/table.tabular {margin-left: inherit;}/' html/$(MAIN).css >html/$(MAIN).css.tmp
mv html/$(MAIN).css.tmp html/$(MAIN).css
rm -f $(HTMLCLEANFILES)
else
html/$(MAIN).html: $(DATEFILE)
mkdir -p html
$(HTLATEX) $(MAIN) "html,mathml-" "" "-dhtml/"
rm -f $(HTMLCLEANFILES)
endif
endif
########## CONFIG_HTLATEX
########## CONFIG_PS
if CONFIG_PS
ps: $(DATEFILE) $(MAIN).ps
$(MAIN).ps: $(DATEFILE) $(MAIN).dvi
$(DVIPS) -o $(MAIN).ps $(MAIN).dvi
endif
########## CONFIG_PS
########## CONFIG_PDF
if CONFIG_PDF
pdf: $(MAIN).pdf
if CONFIG_MAKEINDEX
$(MAIN).pdf: $(TEXFILES)
$(PDFLATEX) $(MAIN).tex; while [ `grep -v 'Package: rerunfilecheck' $(MAIN).log | grep -c Rerun` -gt 0 ]; do $(PDFLATEX) $(MAIN).tex; done
$(MAKEINDEX) $(MAIN)
$(PDFLATEX) $(MAIN).tex; while [ `grep -v 'Package: rerunfilecheck' $(MAIN).log | grep -c Rerun` -gt 0 ]; do $(PDFLATEX) $(MAIN).tex; done
else
$(MAIN).pdf: $(TEXFILES)
$(PDFLATEX) $(MAIN).tex; while [ `grep -v 'Package: rerunfilecheck' $(MAIN).log | grep -c Rerun` -gt 0 ]; do $(PDFLATEX) $(MAIN).tex; done
endif
endif
########## CONFIG_PDF
endif
#################### CONFIG_TEX
form-master/doc/devref/cvs.tex 0000664 0000000 0000000 00000004531 13565763364 0016674 0 ustar 00root root 0000000 0000000 \section{CVS}
The CVS repository resides in \C{/user/form/cvs\_repository}. It is advisable to
set the enviroment variable \C{CVSROOT} accordingly, like (using bash shell syntax)
{\scriptsize
\begin{verbatim}
export CVSROOT=:ext:myusername@mytrustedmachine.nikhef.nl:/user/form/cvs_repository
\end{verbatim}}
A mailing list exists for CVS commits. The administration interface for this mailing list can be
found under the web address
\LINK{https://mailman.nikhef.nl/cgi-bin/admin/form-cvs}
A password is required.
Click {\it Membership Management} and then {\it Mass Subscription} to add
new people. The personal details of the subscribers like the email address or
the name can be changed under {\it Membership Management} as well.
The triggering of the CVS commits mails is done in the following way. In the
file \C{loginfo} in the directory \C{CVSROOT} (inside the repository) the
default action for logging is set such that the script \C{/user/form/cvs-log.sh}
will be called with the committer's user name and the CVS mailing list user
name. The shell script does some simple message transformation and then uses the
command \C{mail} to send the commit mail to the mailing list.
\subsection{Some useful CVS idioms}
To just show what would be updated/changed without actually
modifiying anything, use
\begin{verbatim}
cvs -n update
\end{verbatim}
If \C{cvs -n update} has shown you that something new in the repository will be
merged into your directory and you want to know in advance what the details are,
you can do for each of the files involved a
\begin{verbatim}
cvs status
\end{verbatim}
and note the version number of your local file, and then do a
\begin{verbatim}
cvs diff -r
\end{verbatim}
to see the differences.
In case you want to compile an older version of \FORM\ (maybe to find out
whether a certain bug is already present or not), do
\begin{verbatim}
cvs update -D ""
\end{verbatim}
to checkout the sources as they were on a certain date, e.g. \\
\C{cvs update -D "2006-05-12"}. The files will get the so-called sticky flag,
which do prevent simple \C{cvs update} commands in the future to update to the
latest version from the repository. To remove the sticky flag on a file use
\begin{verbatim}
cvs update -A
\end{verbatim}
Without the filename all files will have the sticky flag removed.
form-master/doc/devref/devref.tex.in 0000664 0000000 0000000 00000004454 13565763364 0017765 0 ustar 00root root 0000000 0000000 \input{version.tex}
\def\formdate{\repodate}
%begin{latexonly} % To avoid latex2html/latex2html#37
\providecommand{\repodate}{\today}
%end{latexonly}
\documentclass[11pt,titlepage]{article}
\usepackage{makeidx}
%begin{latexonly}
\makeatletter
\renewcommand*\l@section{\@dottedtocline{1}{1.5em}{3.0em}}
\renewcommand*\l@subsection{\@dottedtocline{2}{1.5em}{3.0em}}
\makeatother
% Use hyperref package (hyperlinks) with correct option for pdflatex/latex:
\usepackage{ifpdf}
\ifpdf
\RequirePackage[pdftex]{hyperref}
\else
\RequirePackage[hypertex]{hyperref}
\fi
% and link indices back to text:
\hypersetup{hyperindex,pagebackref,pdfpagemode={None},draft=false}
%end{latexonly}
\providecommand{\texorpdfstring}[2]{#1}% htmlonly
\newcommand{\emptypage}{\newpage \thispagestyle{empty} \tiny{.} \normalsize}
\newcommand{\clearemptydoublepage}{\newpage{\pagestyle{empty}\cleardoublepage}}
\newcommand{\C}[1]{{\tt #1}}
\newcommand{\LINK}[1]{{\tt #1}}
\newcommand{\FORM}{{\sc FORM}}
\newcommand{\TFORM}{{\sc TFORM}}
\newcommand{\PARFORM}{{\sc ParFORM}}
\makeindex
\begin{document}
\thispagestyle{empty}
\title{\Huge FORM \\ \Large version @VERSION@ \\ \huge Developer's reference manual}
\date{\formdate}
\author{J.A.M.Vermaseren et al.}
\maketitle
\clearemptydoublepage
\emptypage
\clearemptydoublepage
\pagenumbering{roman}
\setcounter{page}{1}
\clearemptydoublepage
\tableofcontents
\clearemptydoublepage
\emptypage
\clearemptydoublepage
\pagenumbering{arabic}
\setcounter{page}{1}
\section{Initial remarks}
This document is intended for people who are interested in understanding how \FORM\ works internally,
how to find and correct bugs in the source code, and how to extend \FORM\ by implementing new
features.
It is assumed, that the source code is available, either as a package or directly via CVS access to
the \FORM\ repository. The \FORM\ package contains many files and several subdirectories. The actual
sources of \FORM, \TFORM, and \PARFORM\ are all in the directory \C{sources} (see section
\ref{sec:source} for an overview). Documentation can be found in the directory \C{doc}. The testing
suite is contained in the directory \C{check}.
\input{@srcdir@/source}
\input{@srcdir@/formrun}
\section{Specific topics}
\input{@srcdir@/indepth}
\input{@srcdir@/testsuite}
\input{@srcdir@/cvs}
\printindex
\end{document}
form-master/doc/devref/formrun.tex 0000664 0000000 0000000 00000075415 13565763364 0017602 0 ustar 00root root 0000000 0000000 \section{Discussion of a typical \FORM\ run}
We discuss in the following what is happening inside \FORM\ when it executes a
given program. The discussion focuses more on the interplay between the various
parts of \FORM\ and on key concepts of the internal data representation than on
in-depth details of the code. For the latter, the reader is referred to section
\ref{sec:indepth}. This section should for better comprehension be read with the
referenced \FORM\ source files opened aside.
We consider the following exemplary \FORM\ program \C{test.frm} (which we run
with the command "\C{form test}"):
\begin{verbatim}
1 #define N "3"
2
3 Symbol x, y, z;
4
5 L f = (x+y)^2 - (x+z)^`N';
6 L g = f - x;
7
8 Brackets x;
9 Print;
10 .sort
11
12 #do i=2,3
13 Id x?^`i' = x;
14 #enddo
15
16 Print +s;
17 .end
\end{verbatim}
The entry function \C{main()} is in \C{startup.c}. It does various
initializations before it calls the preprocessor \C{PreProcessor()}, which
actually deals with the \FORM\ program. The code shows some typical features:
Preprocessor macros are frequently used to select code specific to certain
configurations. The two most common macros can be seen here: \C{WITHPTHREADS}
for a \TFORM\ executable and \C{PARALLEL} for a \PARFORM\ executable. Macros are
used to access the global data contained in the variable \C{A}, like
\C{AX.timeout} for example. The code uses (usually) own functions instead of
standard functions provided by the C library for common tasks. Examples in
\C{main()} are \C{strDup1} or \C{MesPrint} (replacing \C{printf()}). Another
very often used function is \C{Malloc1()} replacing \C{malloc()}. The reasons
are better portability and the inclusion of special features. \C{Malloc1()} for
example makes a custom memory debugger available while \C{MesPrint()} knows
among other things how to print encoded expressions from the internal buffers.
% needs to be rewritten ->
The initializations in \C{main()} are done in several steps. Some like the
initialization of \C{A} with zeros is done directly, most others are done by
calls to dedicated functions. The initializations are split up according to the
type of objects involved and the available information at this point. The
command line parameters passed to \FORM\ (none in our example run) are treated
in the function \C{DoTail()}. After that, files are opened and also parsed for
addtional settings. Then, as all settings are known, the large part of the
internal data is allocated and initialized. Finally, recovery settings are
checked, threads are started if necessary, timers are started, and variable
initializations that might need to be repeated later (e.g. clear modules) are
done in \C{IniVars()}.
The call to \C{OpenInput()} reads the actual \FORM\ program into memory. The
input is handled in an abstract fashion as character streams. The stream implementation
(\C{tools.c}) offers several functions to open, close, and read from a stream.
Streams can be of different types including files, in-memory data like parts of
other streams or dollar variables, as well as external channels. The access to
the characters in all streams though is nicely uniform. In
\C{OpenInput()} a stream is representing our input file. Most of the logic
there deals with the jump to the requested module (skipping clear instructions).
It uses the function \C{GetInput()} to get the next character in the stream.
Which stream it reads from is determined by the variable \C{AC.CurrentStream}.
This global variable in the sub-struct \C{C\_const} of the \C{ALLGLOBALS}
variable \C{A} is an example of how the different parts of \FORM\ typically
communicate with each other by means of global variables.
Next is the preprocessor. The preprocessor is implemented in the function
\C{PreProcessor()} in \C{pre.c}. This function consists basically of two nested
for-loops without conditions (\C{for (;;) \{ \ldots \}}). The outer loop deals
with one \FORM\ module for each iteration, the inner loop deals with one input
line. We have certain initializations done before in our example the code runs
into the inner loop, where \C{GetInput()} reads our input file. The variables
are all set such that the reading starts from the beginning of out input file.
The input in variable \C{c} is tested for special cases. Whitespaces are
skipped. Comments starting with a star \C{*} (unless \C{AP.ComChar} is set to a
different character) are also skipped including whole folds. The crucial check
on \C{c} is the if-clause that checks it for being a preprocessor command (\C{\#}),
a module statement (\C{.}), or something else which is usually an ordinary
statement.
\begin{verbatim}
1 #define N "3"
\end{verbatim}
In our case, we have a preprocessor command in the input. The function
\C{PreProInstruction()} is called to read and interpret the rest of the line.
The first part deals with the loading of the command in a dedicated buffer. For
the moment, we ignore the details for the special treatment of cases when we are
already inside a if or switch clause in a \FORM\ program. In our run, the
function \C{LoadInstruction(0)} is simply called.
\C{LoadInstruction()} copies input into the preprocessor instruction buffer.
Three variables govern this buffer: \C{AP.preStart} points to the start of the
buffer, \C{AP.preFill} to the point where new input can be copied to, and
\C{AP.preStop} to (roughly) the end of the buffer. This setup is quite typical
for buffers in \FORM. The memory is allocated at the start of \FORM. Later, like
at the end of \C{LoadInstruction()}, if the buffer gets to small, it can be
replaced by a larger memory patch with the help of utility functions like
\C{DoubleLList()}. The contents is copied from the old to the new buffer. Since
this dynamical resizing of buffers needs to be done with most buffers
occationally, most buffers in \FORM\ store data such that it easily allows for
copying, i.e. usually C pointers are avoided and instead numbers representing
offsets are used. Since the preprocessor instruction buffer just contains
characters there is no problem here.
In \C{LoadInstruction()} with our input and the mode set to 5 the input is just
copied directly without any special actions taking except for a zero that is
added at the end of the data. \C{PreProInstruction()} examines the data in the
preprocessor instruction buffer for special cases, and then does a look-up in
the \C{precommands} variable. This is a vector of type \C{KEYWORD} which enables
the translation of a string (the command) to a function pointer (the C function
that performs the operations requested by preprocessor command).
\C{FindKeyword()} does these translations and the found function pointer is then
dereferenced with the rest of the input in the instruction buffer as an argument.
The function pointer will point to \C{DoDefine()} in our case. \C{DoDefine()}
just calls \C{TheDefine()} that does the work. The if-clauses for \\
\C{AP.PreSwitchModes} and \C{AP.PreIfStack} are present in most of the
functions dealing with preprocessor commands. They check whether we are in a
preprocessor if or switch block that is not to be considered, because the
condition didn't hold. Then, the standard action is to just exit the current
function leaving it with no effect. Since there are preprocessor commands like
\C{\#else} or \C{\#endif} this decision can only be taken at this level of the
execution and requires the repeated use of this idiom.
The function scans through possible arguments and the value. In the value, special
characters are interpreted. Ultimately, the preprocessor variable is created and
assigned in the called function \C{PutPreVar()}. The variable \C{chartype}
deserves an explanation. One will find it used very often in the C code that
does input parsing. \C{chartype} is actually a macro standing in for
\C{FG.cTable}. This global, statically initialized (in \C{inivar.h}) vector
contains a value of every possible ASCII character describing its parsing type.
The parsing type groups different ASCII characters such that the syntax checking
is facilitated, see \C{inivar.h} for details.
In \C{PutPreVar()} we get into the details of the name administration. We will
just comment on some of the more general features. \C{NumPre} and \C{PreVar} are
macros to access elements in \C{AP.PreVarList}. The type of \C{AP.PreVarList} is
\C{LIST}. This is a generic type for all kinds of lists and it is used for many
other variables in \FORM. A \C{LIST} stores list entries in a piece of
dynamically allocated memory that has no defined type (\C{void *}). The utility
functions for managing \C{LIST}s like \C{FromList()} are ignorant about the
actual contents and perform list-specific operations like adding, removing or
resizing a list. An actual entry can be accessed by some pointer arithmetic and
type casting. The \C{PreVar} macro contains such a cast to the type \C{PREVAR}
which represents a preprocessor variable.
\C{PutPreVar()} creates a new list entry for us and basically copies the
contents of the parameter \C{value} to the memory allocated to \C{PREVAR}'s
\C{name}. So, by writing \C{PreVar[0]->name} or \C{PreVar[0]->value} we could
access the strings \C{N} or \C{3}.
In \C{TheDefine()} the function \C{Terminate()} is used several times. This
function ultimately exits the program, but first tries to clean up things and
print information about the problems causing this program termination.
\begin{verbatim}
2
3 Symbol x, y, z;
\end{verbatim}
In our run, we return to the function \C{PreProcessor()} and start a new inner
loop iteration that reads a new line. After skipping the empty line we end up
in the else-branch of the big if-clause testing \C{c} this time. Here the major
steps are: we check again whether we are in a preprocessor if or switch, call
\C{LoadStatement()} to read and prepare the input, and call
\C{CompileStatement()} to perform the actions requested by the statement. Th
programs enters the compiler stage.
We also see a call to \C{UngetChar()}, which puts back the character that has
been read into the input stream. This is necessary, because \\
\C{LoadStatement()} and \C{CompileStatement()} need the complete line for
parsing. The variable \C{AP.PreContinuation} is used several times. This variable
deals with statements that span several input lines. \C{LoadStatement()} can
recognize unfinished statements and sets this variable accordingly.
\C{LoadStatement()} basically copies the input to the compiler's input buffer at
\C{AC.iBuffer} (which has \C{AC.iPointer} and \C{AC.iStop} associated to it). It
modifies the copy if necessary. The modification are to replace spaces by commas
or insert commas at teh right spots to separate tokens. The interpretation steps
that are following rely on these synactic conventions.
The call to \C{CompileStatement()} is done only if no errors occured and all
lines of a statement have been gathered into the compiler's input buffer.
\C{CompileStatement()} is called with the address of this input buffer and tries
to identify the statement. Like in the preprocessor, the input string is search
in a vector of \C{KEYWORD}s (in \C{compiler.c} and if found, a function pointer
is dereferenced to the function that actually deals with the command and its
options and arguments. Here, we have actually two vectors of \C{KEYWORD}s,
because some statements might be stated in abbreviated form. The function
\C{findcommand()} deals with the search. \C{CompileStatement()} does some small
extra work, like for example checking the correct order of statements. In our
case, it calls the function \C{CoSymbol()}. This functions is in file
\C{name.c}, because as a declaration it basically adds something to the name
administration. Functions for other statements can be found in \C{compcomm.c}
and \C{compexpr.c}.
\C{CoSymbol()} loops over the arguments and adds proper variable names together
with their options to the symbols list \C{AC.Symbols} and the name
administration (in the call to \C{AddSymbol()}. In our case, we have \C{x},
\C{y}, and \C{z} added. We have already encountered the basic mechanism of how a
specific struct is added to a \C{LIST}. The name administration was not
explained before, though.
Symbols can appear in expressions that need to be encoded. The coding for
symbols can simply be its entry index in the list \C{AC.Symbols}, but symbols
also need to be recognized when an expression is parsed. Therefore a efficient
look-up mechnism is required. This is achieved by a second data structure that
holds the name strings in a tree for fast searching. The data in the symbol list
does not contain the name string itself, but contains a referece (a index) into
this name string tree. The tree is managed by generalized functions and types
that are also used for other, similiar objects like vectors, indices, etc. The
functions for name trees are located in the first part of the file \C{name.c}.
The types \C{NAMENODE} and \C{NAMETREE} are defined in \C{structs.h}.
\C{NAMENODE}s are the node of a balanced binary tree. It does not hold the
name string just an index into \C{NAMETREE}. The actual data is contained in
\C{NAMETREE} that constitute one tree. This type has buffers for the nodes and
for the name strings. This has the benefit of avoiding small malloc calls for
individual nodes. Also, since all referencing is done via offsets into these
buffers, a relocation or serialization of such a tree is very easy. In the
struct \C{C\_const} (aka the global \C{AC}) several name trees are defined, for
dollar variables, expressions, etc. The symbols added in our example program go
into the nametree referenced by \C{AC.activenames}, which is at this point equal
to \C{AC.varnames}.
Our program returns to the \C{PreProcessor()} and starts parsing the next lines:
\begin{verbatim}
5 L f = (x+y)^2 - (x+z)^`N';
6 L g = f - x;
\end{verbatim}
This time the function \C{DoExpr()} will get called (via \C{CoLocal()}) for each
line to do the parsing. The function \C{DoExpr()} first tries to figure out
what type of \C{Local} statement we have. In our cases we have an actual
assignment. With the call to \C{GetVar()} we check whether a variable of the same
name already exists. The search is done in the nametrees \C{AC.varnames} and
\C{AC.exprnames}. Since our names are new we don't find a previous variable and
simply call \C{EntVar()}. \C{EntVar()} creates an entry in \C{AC.ExpressionList}
and puts the name into the \C{AC.exprnames} nametree. The entry in
\C{AC.ExpressionList} is of type \C{struct ExPrEsSiOn}. There are more struct
elements than in the case of symbols, but the principle is the same. Up to now,
the right-hand-side (RHS) has not been looked at and therefore no information
about it is saved in the expression's entry yet. The connection between the
expression's entry in the \C{AC.ExpressionList} and the data containing the RHS
will be made via the elements \C{prototype} and \C{onfile} as we will describe
soon. The access to elements in \C{AC.ExpressionList} is facilitated by the
macro \C{Expressions}. The following code in \C{DoExpr()} builds up a so-called
prototype and puts the RHS in encoded form into the buffer system via the call
to \C{CompileAlgebra()}.
\FORM\ uses the allocated memory in \C{AT.WorkSpace} for operations like the
generation of terms. This memory stores \C{WORD}s and is used in a stack-like
fashion with the help of the pointer \C{AT.WorkPointer}. A function can write to
this memory and set \C{AT.WorkPointer} beyond the written data to insure that
other functions that are called and might use the workspace as well do not
overwrite this data. It is the responsibility of the function to reset
\C{AT.WorkPointer} to its original value again (see variable \C{OldWork} in our
case). Every thread in \TFORM\ will have its own private work space.
\FORM\ now uses \C{AT.WorkSpace} to build up a data structure that contains
everything that needs to be known at a later stage about the expression that is
parsed. The creation and the layout of the data is quite typical. First comes a
header that signifies what is coming. Here, it is \C{TYPEEXPRESSION}. Then comes
the length of the whole data, i.e. the total number of occupied \C{WORD}s. The
actual contents is following, which is a so-called subexpression that we will
discuss soon. The contents is followed by a coefficient and a zero, which
signifies the end of the data.
{\bf Coefficients} are coded in \FORM\ always in the following manner: Since
coefficients can in general be fractional numbers, we encode an integer
numerator and an integer denominator. The integers can have arbitrary length
(limited only by the buffer sizes, see the setup variables \C{MaxNumberSize} and
\C{MaxTermSize}) and are encoded in \C{WORD}-pieces in little-endian convention.
The number of allocated \C{WORD}s is always the same for the numerator and the
denominator. The last word of the coefficient contains the size of the whole
coefficient in words. The formal structure of a coefficients is therefore like
this:
\begin{center}
{\it NUMERATOR WORDS, DENOMINATOR WORDS, LENGTH}.
\end{center}
The integers are always
unsigned, i.e. positive. Negative fractions are encoded by a negative length.
Examples (with 16bit words): $2^{16}+2 = 65538$ gives words 2,1,1,0,5 and $-5/2$
gives $5,2,-3$.
The data structure in \C{AT.WorkSpace} is basically an instruction for the
generator, a central function that does the main work during the execution of
the \FORM\ program, to generate an expression. The content of the expression is
a subexpression. This is a pointer to the real content of the expression and
will be substituted later after the execution. The main reason for this delayed
expression insertion is that it can often save a lot of intermediate operations
and data space and thereby speed up \FORM. A case where such a thing can happen
is, when an expression is used at different places and the different parts are
brought together by some operations. Then, cancellations may occur or terms can
be factored out and when the expressions finally is inserted the workload is
less.
In our example run, the data that will later instruct the generator to
create an expression looks in total like this:
\begin{center}
{\it TYPEEXPRESSION, SUBEXPSIZE+3, 9, SUBEXPRESSION, \\
SUBEXPSIZE, 0, 1, AC.cbufnum, 1, 1, 3, 0}
\end{center}
We used the macro names as in the actual code. \C{AC.cbufnum} is a variable that
is the index of the compile buffer used for this parsed statement.
At the end of the data preparation phase the pointer \C{AT.WorkPointer} is set
beyond the data on the trailing zero, the pointer \C{AT.ProtoType}, which is
used soon in following functions is set to the word \C{SUBEXPRESSION}.
The expression will be put into the scratch buffer system. This system comprises
the small and large buffers and the scratch files. Where new data to the scratch
buffers will be stored is of no concern to a function like \C{DoExpr()}, it
simply uses several utility functions for that purpose. Still, we need to
initialize the variable \C{pos} here that will indicate the position of the
data, i.e. the expression, in the scratch file.
Next, the function \C{CompileAlgebra()} is called to parse the right hand side
and put the codified expression into the \FORM\ buffers. It basically calls two
functions: \C{tokenize} and \C{CompileSubExpressions}. \C{tokenize} is the
tokenizer that translates the input character string in a sanitized and partly
interpreted string of codes. It will look up the variables named in the input
string and put the index they have in the name administration into the tokenized
output. Our input string is transformed into the code string like this
\begin{verbatim}
( -13 LPARENTHESIS
x -1 TSYMBOL
5
+ -26 TPLUS
y -1 TSYMBOL
6
) -14 RPARENTHESIS
^ -25 TPOWER
2 -8 TNUMBER
2
- -27 TMINUS
( -13 LPARENTHESIS
x -1 TSYMBOL
5
+ -26 TPLUS
z -1 TSYMBOL
7
) -14 RPARENTHESIS
^ -25 TPOWER
`N' -8 TNUMBER
3
-29 TENDOFIT
\end{verbatim}
This code string then lies in the \C{AC.tokens} buffer where it is used by
subsequent functions.
The function \C{CompileSubExpression()} finds terms in an expression that might
be reused at another place and extracts them. As one can see in the code, the
function looks for terms in parentheses and works recursively. The end of such a
term is each time marked with \C{TENDOFIT}. Then, the function
\C{CodeGenerator()} called at the end of \C{CompileSubExpression()} does the
real work.
In our example \C{CodeGenerator()} first gets the data
\begin{center}
{\it LPARENTHESIS, TSYMBOL, 5, TPLUS, TSYMBOL, 6, TENDOFIT}
\end{center}
as a parameter, which is the term $x+y$. It builds up the actual term encoding
in the workspace and first reserves for that enough space there. One can see the
pointer arithmetic using constants like \C{AM.MaxTal}, which is the maximum
number of words a number can occupy. It reserves space for the coefficient, an
integer, and the actual term. Once a token is recognized, the equivalent term
data is written to the workspace and the function \C{CompleteTerm} is called.
This function completes the data to
\begin{center}
{\it 8, 1, 4, 5, 1, 1, 1, 3, 0}.
\end{center}
The first word is the total length, i.e. 8 words. This is the length of the
whole expression. The second word is the type of the term, which is a symbol. It
is the value \C{SYMBOL} as defined in \C{ftypes.h}. This macro definition
\C{SYMBOL} has the value 1 (in the \FORM\ version at this time this reference is
written). Following the type signifying word is the length of the term, which is
4. Several such terms could follow each other, but we only have one term at the
moment. Finally, we have the trailing words for the coefficient being 1 and a
terminating zero. The meaning and interpretation of the words in the data of a
single term after the type word and the length word are dependent on the type.
For symbols, we have pairs of word, where the first word is the index of the
symbol in the name administration and the second word is the exponent. Here we
have symbol 5 ($ = x$) with an exponent 1. After \C{CompleteTerm()} has
constructed the whole expression it copies the data to the compile buffers with
the help of the function \C{AddNtoC()}.
The compile buffers contain the instruction for the execution engine, the
\C{Processor()}, that will start when the \C{.sort} command is parsed. Our terms
are put into the right-hand-side buffers in the compile buffer. When the
\C{Processor()} will read these buffers one after the other, it will take the
terms and put them into the scratch buffer system. Then, they become the
expressions upon which further statements do act. The compile buffers are stored
in the list \C{AC.cbufList} and we get access to the elements via the cast
\C{((CBUF *)(AC.cbufList.lijst))}. This cast is defined as a preprocessor macro
called \C{cbuf}. The element \C{cbuf[0]->numrhs} (0 is the current compile
buffer we are using) gives the number of entries in \C{cbuf[0]->rhs}, which is
an array of pointer into \C{cbuf[0]->Buffer}. We have 3 elements:
\begin{verbatim}
cbuf[0]->rhs[1] -->
8, 1, 4, 5, 1, 1, 1, 3, 8, 1, 4, 6, 1, 1, 1, 3, 0
cbuf[0]->rhs[2] -->
8, 1, 4, 5, 1, 1, 1, 3, 8, 1, 4, 7, 1, 1, 1, 3, 0
cbuf[0]->rhs[3] -->
9, 6, 5, 1, 2, 0, 1, 1, 3, 9, 6, 5, 2, 3, 0, 1, 1, -3, 0
\end{verbatim}
\C{cbuf[0]->rhs[0]} is not used and the data lies consecutively in \\
\C{cbuf[0]->Buffer}. The meaning of the first two entries has already been
explained. These are expressions containing $x+y$ and $x+z$, respectively.
The last expression uses subexpressions that have the type \C{SUBEXPRESSION}
$ = 6$. The length of a subexpression is 5 and the contents $1,2,0$ means
that expression 1 needs to be inserted with an exponent of 2. The zero is a
dirty flag that signals to the processor the state of the subexpression. Here in
the compile buffers it is simply cleared to zero. The contents $2,3,0$ of the
second subexpression should be obvious. Finally, we have an negative
coefficient for the second subexpression which accounts for the minus sign
between the parentheses in our original expression.
We return to the function \C{DoExpr()} where the prototype of the expression is
put into the scratch system via the call \C{PutOut()} and we are finished with
this line in the input file. The next line defining a second local expression
works the same.
We come to the parsing of the following statements:
\begin{verbatim}
7
8 Brackets x;
9 Print;
\end{verbatim}
The bracket statement is dealt with in function \C{DoBrackets()}. It sets the
flag \C{AR.BracketOn} to 1 and constructs the term that will stand outside the
bracket. This term is copied into the \C{AT.BrackBuf} buffer, where it can be
used by the execution engine when it needs to insert this heading term into an
expression.
The print statement is parsed in function \C{DoPrint()}. Since we don't have any
arguments to \C{Print} all active expressions shall be printed.
\C{DoPrint()} just loops through the \C{Expressions} list and sets the
\C{printflag} to 1 for each expression.
With the next statement in our input file
\begin{verbatim}
10 .sort
\end{verbatim}
we will get to know the other central parts of \FORM: the processor and the
sorting routines. The code in the \C{PreProcessor()} will call
\C{ExecModule()}
which calls \C{DoExecute()}. We can ignore a lot of code there that is only for
parallelized versions of \FORM. There are three important functions calls
happening. First, \C{RevertScratch()} is called. \FORM\ uses three scratch
buffers: input buffer, output buffer, and the hide buffer. The usual mode of
operation is to apply statements on expressions in the input buffer, sort and
normalize the result, and write it into the output buffer. This repeats for
every executing module and therefore an important optimization is made: the
input buffer and the output buffer simply change their roles.
\C{RevertScratch()} does this job. The second and third important calls are to
\C{Processor()} and \C{WriteAll()}.
\C{Processor()} is, as the name suggests, the main processor that executes
statements and deals with the results. A lot of initialization work is done
before we go into the large loop over the expressions that spans almost the
whole function. Our expressions have as regular expressions from the scratch
buffers the \C{inmem} flag set to zero, so we go into the else branch of the
checking if-clause. There we go to the case of a \C{LOCALEXPRESSION}. The main
logic here is to do a single call to \C{GetTerm()} to get the first term from
the input file and copy that to the output with the call to \C{PutOut()}. This
first term, which is a subexpression, serves as a header for the expression. It
follows a (while-)loop that calls \C{GetTerm()}, and if there are still terms,
the loop executes its body and calls \C{Generator()}. After this loop, some
clean-up and a final \C{EndSort()} is done, before the outer loop over the
expressions repeats. \C{Generator()} is the function where the read input, which
is {\it 9, 6, 5, 3, 1, 0, 1, 1, 3}, will be substituted and expanded.
\C{Generator()} gets the term in the workspace and first tries to do all
substitutions (\C{SUBEXPRESSION}), then applies the statements in the compile
buffers to the normalized terms, substitutes again if necessary, do brackets,
and finally sorts the result.
The call to \C{TestSub()} does the search for subexpressions. \C{TestSub()} will
find a subexpression in our case and return the number (3) of this subexpression
and set other global variables ready for the following steps. In \C{Generator()}
we enter therefore the if-clause checking \C{replac}$> 0$. Depending on the
power of the subexpression different operations are taken. We have our
subexpression to the power one only, which is an easy case. The actual
substitution is performed by the function \C{InsertTerm()}. Since the new term
might again contain subexpressions we do a recursive call to \C{Generator()}.
Our expression contains several layers of subexpressions which are all dealt
with as described above. Only the powers of the other subexpressions are
different from one, so we get slightly more work to be done which involves the
expansion of the terms using binomials.
Finally, the call to \C{TestSub()} at the beginning of \C{Generator()} will
return zero. The function \C{Normalize()} is called, which puts the terms in a
canonical form, i.e. terms are ordered and collected with the correct
coefficient. In our example, as the first fully subsituted term we have
{\it 12, 1, 4, 6, 1, 1, 4, 6, 1, 1, 1, 3} before the call to
\C{Normalize()}, which means we have a term $x*x$. \C{Normalize()}
makes this into {\it 8, 1, 4, 6, 2, 1, 1, 3}, which is $x^2$.
Then, we loop over the statements in the compile buffer. \C{level} is the
instruction counter. We have a long switch-clause that interprets the statement
type identifiers like \C{TYPECOUNT}. Statements with \C{TYPEEXPRESSION} are not
treated here. So we loop over all the compile buffer statements here and only
call \C{TestMatch()} at the loop's end. This function has no effect in our
example, because we have no pattern matching going on.
Then, the function \C{PutBracket()} is called to deal with brackets. Brackets
are implemented by putting the special code \C{HAAKJE} inside the expression.
The terms before the \C{HAAKJE} are outside the bracket, everything following it
will be inside the bracket.
At the end of the loop over the terms in the expressions, the function
\C{StoreTerm()} is called. This function puts the result of the processing in
the output scratch buffers. Finally, we return to \C{Processor()}. There the
final sorting is started. Also, the printing of the expressions is done here.
The parsing in \C{PreProcessor()} continues with
\begin{verbatim}
11
12 #do i=2,3
13 Id x?^`i' = x;
14 #enddo
\end{verbatim}
Here we have a somewhat more complicated example of preprocessor instructions.
The do-loop is treated in \C{DoDo()} which sets up data structures (\C{DOLOOP})
to guide the preprocessor when it is parsing the loop body. The statement line
will then be presented to the compiler two times and with the correct values of
the preprocessor variable \C{i}. The compiler deals with this statement in
\C{CoId()} which is just calling \C{CoIdExpression()}. \C{CoIdExpression()}
puts a \C{TYPEIDNEW} code into the lhs compile buffer. This tells the processor
later how to do the pattern matching. The rhs is the term \C{x} that will be
inserted.
The parsing continues and ends with
\begin{verbatim}
15
16 Print +s;
17 .end
\end{verbatim}
The way these statements are treated and how the program is executed has already
been described. The pattern matching is something that has not occurred before,
though. We will not describe it here, since there is a dedicated section in this
manual for that. After the final sorting, \FORM\ will clean up tempory files and
other resources that are not automatically freed by the operating system before
\FORM\ ends itself.
form-master/doc/devref/indepth.tex 0000664 0000000 0000000 00000017144 13565763364 0017540 0 ustar 00root root 0000000 0000000 \label{sec:indepth}
\subsection{Pattern matching}
to be written
\subsection{The problem of dummy indices}
\FORM\ has a indices that can be automatically renumbered. With this we mean
that when we have an expression like
\begin{verbatim}
f(i)*g(i)*h(j)*k(j)-f(j)*g(j)*h(i)*k(i)
\end{verbatim}
we can say
\begin{verbatim}
Sum i,j;
\end{verbatim}
and \FORM\ will change the expression into
\begin{verbatim}
f(N1_?)*g(N1_?)*h(N2_?)*k(N2_?)-f(N2_?)*g(N2_?)*h(N1_?)*k(N1_?)
\end{verbatim}
in which \C{Ni\_?} are internal indices.
These internal indices follow a number of rules:
\begin{enumerate}
\item
their numbers (\C{AC.CurDum}) start at \C{AM.IndDum}, which again starts at
\C{AM.DumInd+WILDOFFSET} and \C{AM.DumInd} starts at \C{AM.OffsetIndex + 2*WILDOFFSET}.
Hence \C{AC.CurDum} starts at \C{AM.OffsetIndex +\\ 3*WILDOFFSET}.
Because we need this extra space \C{WILDOFFSET} cannot be too large and this
limits the number of indices that is allowed.
\item The dimension of the dummy indices is equal to the default dimension.
\item The internal (dummy) indices can be renamed at any time in order to
create uniquely minimal terms. In the above expression that would mean
that the second term would be 'rearranged' into
\begin{verbatim}
f(N2_?)*g(N2_?)*h(N1_?)*k(N1_?) -->
f(N1_?)*g(N1_?)*h(N2_?)*k(N2_?)
\end{verbatim}
and the expression becomes zero.
\end{enumerate}
There are problems with this concept.
\begin{enumerate}
\item Multiplying expressions with dummy indices could give a repetition of
the same indices as in \C{(f(N1\_?)*g(N1\_?))\^{}3}. This has been solved
partially as can be seen with the following program:
\begin{verbatim}
CF f,g;
L F = (f(N1_?)*g(N1_?))^3;
L G = f(N1_?)*g(N1_?);
.sort
L G3 = G^3;
Print;
.end
\end{verbatim}
The routine that takes care of the proper shifts in dummy numbers is
\C{MoveDummies()}. As one can see from the example, the \C{SUBEXPRESSION} to a
power isn't treated this way. It would have a serious impact on the
speed. With the \C{G\^{}3} it is different because that is slower to begin
with.
\item Keep Brackets is extremely dangerous. The problem here is
\begin{verbatim}
f(N1_?)*(g(N1_?)*h(N2_?)*k(N2_?)+g(N2_?)*h(N1_?)*k(N2_?))
\end{verbatim}
What is inside the brackets is invisible during the module. Hence a
renumbering that involves \C{f(N1\_?)} only can change \C{N1\_?} into \C{N2?\_}
(\FORM\ doesn't know there is already a \C{N2\_?}) and anyway, the
corresponding \C{N1\_?} remains as it is.
It means that there are complicatetions with \C{Sum}, \C{Trace4} and things like
\C{id p = f(?);} which can generate dummy indices.
\end{enumerate}
The second problem requires some action.
\begin{enumerate}
\item[A] When Keep Brackets is active, renumbering should not be allowed, until
the contents are multiplied with the outside of the brackets.
\item[B] The multiplying with the contents of the bracket should follow the same
procedure as the multiplication with a complete expression \\
(\C{MoveDummies()}).
\item[C] Introduction of new dummy indices should be above \C{AM.IndDum + WILDOFFSET/2}.
These should vanish when the term is renumbered after multiplying the
outside of the bracket with the inside.
\end{enumerate}
\C{Trace4} involves the creation of dummy indices, but these vanish again
without renumbering. Hence they don't cause problems.
In order to implement \C{A-C} we have to have a good look at all routines that
use \C{AR.CurDum} and call \C{ReNumber()} or \C{DetCurDum()}.
\subsection{Values of indices (and vectors)}
The indices and vectors share common use. That means that vectors can occur
in the places that are reserved for indices. In addition we have various
types of indices. Hence it is important to know what range of values in an
index location refers to what.
\begin{enumerate}
\item Special values:
\begin{tabular}{p{6em}rp{20em}}
\C{GAMMA1} & 0 & Dirac unit matrix \\
\C{GAMMA5} & -1 & Dirac gamma 5 (only defined in 4 dimensions) \\
\C{GAMMA6} & -2 & Dirac (1+gamma5) (only defined in 4 dimensions) \\
\C{GAMMA7} & -3 & Dirac (1-gamma5) (only defined in 4 dimensions)
\end{tabular}
The above 4 indices are to be used only inside the function \C{g\_}.
\begin{tabular}{p{6em}rp{20em}}
\C{FUNNYVEC} & -4 & Used in \C{replace\_} to indicate a vector with an
unspecified index. Hence \C{VECTOR,4,numvec,FUNNYVEC}
instead of \C{INDEX,3,numvec}. \\
\C{FUNNYWILD} & -5 & Used to indicate an argument field wildcard like
\C{?a} inside a tensor. \\
\C{SUMMEDIND} & -6 & Used in \C{DELTA} to indicate \C{d\_(mu,mu)-4} as generated
in traces. \\
\C{NOINDEX} & -7 & Used by \C{ExecArg()} in splitting a multi-delta or
multi-index. Taking out one to make a new argument
we leave the old one with two or one empty spots. \\
\C{FUNNYDOLLAR} & -8 & Used to indicate a dollar variable inside a tensor. \\
\C{EMPTYINDEX} & -9 & Used in the bracket statement to indicate a \C{d\_}.
Because \C{d\_} isn't a regular function we cannot use
the function notation and it needs two arguments. \\
\C{MINSPEC} & -10 &
\end{tabular}
\C{MINSPEC} must be smaller than all the other special values.
\item Fixed indices. They are in the range of 1 to \C{AM.OffsetIndex-1}.
\item Vectors are in the range from \\
\C{AM.OffsetVector = -2*WILDOFFSET+MINSPEC;} \\
to \\
\C{AM.OffsetVector + WILDOFFSET}
\item Wildcard vectors are in the range \\
\C{AM.OffsetVector + WILDOFFSET} \\
to \\
\C{AM.OffsetVector + 2*WILDOFFSET}
\item Regular indices are in the range from \\
\C{AM.OffsetIndex} to \C{AM.OffsetIndex + WILDOFFSET}
\item Wildcard indices are in the range \\
\C{AM.OffsetIndex + WILDOFFSET (=AM.WilInd)} \\
to \\
\C{AM.OffsetIndex + 2*WILDOFFSET (=AM.DumInd)}
\item Unused in the range of \\
\C{AM.OffsetIndex + 2*WILDOFFSET (=AM.DumInd)} \\
to \\
\C{AM.OffsetIndex + 3*WILDOFFSET (=AM.IndDum)}
\item Summed indices (\C{Ni\_?}) are in the range of \\
\C{AM.OffsetIndex + 3*WILDOFFSET (=AM.IndDum)}
to \\
\C{AM.OffsetIndex + 4*WILDOFFSET}
\item Unused in the range of \\
\C{AM.OffsetIndex + 4*WILDOFFSET} \\
to \\
\C{AM.OffsetIndex + 5*WILDOFFSET (=AM.mTraceDum)}
\item Summed indices as generated by the trace routines are above \\
\C{AM.OffsetIndex + 5*WILDOFFSET (=AM.mTraceDum)}
\end{enumerate}
{\it Note (JV)}: I am not sure why there are unused regions. I must have had a
reason for them, but I have forgotten about it (it was more than 20 years
ago). And then, maybe it is used somewhere in a totally untransparent way.
{\it Note 2 (JV)}: It was good to make this list. It turned out that in several
places the code that checks for wildcard indices was only limited from
below, not from above. It would of course be very rare to run into trouble
with this, but it is better to have the code formally correct. One never
knows. This was particularly the case in \C{FindRest()} (in \C{findpat.c}). There may
be more. It is best to repair this, whenever encountered.
From the above it should be clear that on a 32-bits computer \\
\C{5*WILDOFFSET+AM.OffsetIndex+nTraceDummies < 2\^{}{15}} \\
in which \C{nTraceDummies} is the number of dummies that can be introduced when
taking a 4-dimensional trace.
If we assume that we will not take traces of more than 200 gamma matrices
(each with a different index, because otherwise there are contractions)
\C{nTraceDummies} will be at most 100. \C{AM.OffsetIndex} is by default 128.
The value that we selected for \C{WILDOFFSET} is 6100 which allows a maximum
value of 2167 for \C{AM.OffsetIndex}.
form-master/doc/devref/source.tex 0000664 0000000 0000000 00000032536 13565763364 0017407 0 ustar 00root root 0000000 0000000 \section{Overview of the source code}
\label{sec:source}
Here we will discuss general aspects of the source code, i.e. the files contained in the directory
\C{sources}.
\FORM\ is written in ANSI C. The code is split up in header files \C{*.h} and source files
\C{*.c}. Files usually don't come in pairs of a header file with the declarations and a source file
with the definitions, but instead most declarations are collected in a few headers. The declaration
of function headers is done in \C{declare.h} for example. The most prominent exceptions are
\C{parallel.h} and \C{minos.h}.
Each file usually contains many hundred lines of code. To make the files more accessible, the code
is structure by so--called folds. If you use the editor STedi, the code will be visualized
correctly. If you use a vi--compatible editor, it is advisable to activate folds and set the
foldmarkers to \C{set foldmarker=\#[,\#]}
% Folds in Emacs anybody??
\subsection{The header files}
% INDENTATION HACK to be improved!
$\quad\;\:$\begin{tabular}{p{0.2\textwidth}p{0.65\textwidth}}
\C{declare.h} & Contains the declarations of all publicly relevant functions as
well as of commonly used macros like \C{NCOPY} or \C{LOCK}. \\
\C{form3.h} & Global settings and macro definitions like word size or version
number. It includes several different system
header files depending on the computer's architecture.\\
\C{fsizes.h} & Defines macros that determine the size and layout of \FORM's internal data like the
sizes of the work buffers etc. \\
\end{tabular}
\begin{tabular}{p{0.2\textwidth}p{0.65\textwidth}}
\C{ftypes.h} & Contains preprocessor definitions of the codes used in the internal representation of
parsed input and expressions. \\
\C{fwin.h} & Special settings for the Windows operating system. \\
\C{inivar.h} & Contains the initialization of various global data like the
\FORM\
function names or the character table for parsing. It also defines the global
struct \C{A}, and for \TFORM\ the struct pointer \C{AB}. \\
\end{tabular}
\begin{tabular}{p{0.2\textwidth}p{0.65\textwidth}}
\C{minos.h} & Dedicated header to the minos.c source file. \\
\C{parallel.h} & Dedicated header to the parallel.c source file. \\
\C{portsignals.h} & Preprocessor definition of the OS signals \FORM\ can deal with. \\
\end{tabular}
\begin{tabular}{p{0.2\textwidth}p{0.65\textwidth}}
\C{structs.h} & Defines the structs that contain almost all of
\FORM's internal data. \\
\C{unix.h} & Special definitions for Unix--like operating systems. \\
\C{variable.h} & Some convinience preprocessor definitions to ease the access to
global variables, like \C{cbuf} or \C{AC}. \\
\end{tabular}
\subsection{The source files}
% INDENTATION HACK to be improved!
$\quad\;\:$\begin{tabular}{p{0.2\textwidth}p{0.65\textwidth}}
\C{argument.c} & Code for the \C{argument} and \C{term}
\FORM\ statements. \\
\C{bugtool.c} & Low-level debugging code. \\
\C{checkpoint.c} & Code to test for checkpoint conditions, to create
snapshots, and to recover from snapshot data. \\
\end{tabular}
\begin{tabular}{p{0.2\textwidth}p{0.65\textwidth}}
\C{comexpr.c} & Functions the compiler calls to translate a statement that
involves an algebraic expression, e.g. \C{Local} or \C{Id}. \\
\C{compcomm.c} & Functions the compiler calls to translate a statement that
neither involves an algebraic expression nor is a variable declaration. \\
\C{compiler.c} & Main compiler code. \\
\end{tabular}
\begin{tabular}{p{0.2\textwidth}p{0.65\textwidth}}
\C{compress.c} & Code for GZIP (de-)compression in sort files. \\
\C{comtool.c} & Utility functions for the compiler, like \C{AddRHS}. \\
\C{dollar.c} & Code dealing with dollar variables. \\
\end{tabular}
\begin{tabular}{p{0.2\textwidth}p{0.65\textwidth}}
\C{execute.c} & Code for the execution phase of a module. Also, code dealing
with brackets in \FORM\ expressions. \\
\C{extcmd.c} & External command code. \\
\C{factor.c} & Simple factorizing code for dollar variables and expressions. \\
\end{tabular}
\begin{tabular}{p{0.2\textwidth}p{0.65\textwidth}}
\C{findpat.c} & Pattern matching for symbols and dot products. \\
\C{function.c} & Pattern matching for functions. \\
\C{if.c} & Code for the \C{if} statement. \\
\end{tabular}
\begin{tabular}{p{0.2\textwidth}p{0.65\textwidth}}
\C{index.c} & Code for bracket indexing. \\
\C{lus.c} & Code to find loops in index contractions. \\
\C{message.c} & Text output functions, like \C{MesPrint} or \C{PrintTerm}. \\
\end{tabular}
\begin{tabular}{p{0.2\textwidth}p{0.65\textwidth}}
\C{minos.c} & The minos database. \\
\C{module.c} & Code for module execution and the \C{moduleoption}, \C{exec} and
\C{pipe} statements. \\
\C{mpi2.c} & MPI2 code for \PARFORM. \\
\end{tabular}
\begin{tabular}{p{0.2\textwidth}p{0.65\textwidth}}
\C{mpi.c} & MPI1 code for \PARFORM. \\
\C{names.c} & Name administration code to deal with the declaration of
\FORM\ variables. \\
\C{normal.c} & Code to normalize terms, i.e. bring them to standard form. \\
\end{tabular}
\begin{tabular}{p{0.2\textwidth}p{0.65\textwidth}}
\C{opera.c} & Code for doing traces, contractions, and tensor conversions. \\
\C{optim.c} & Code to optimize FORTRAN or C output. \\
\C{parallel.c} & \PARFORM\ (MPI-independant code). \\
\end{tabular}
\begin{tabular}{p{0.2\textwidth}p{0.65\textwidth}}
\C{pattern.c} & General pattern matching and substitution. \\
\C{poly.c} & Code for polynomial arithmetic (experimental). \\
\C{polynito.c} & Code for polynomial arithmetic and manipulation. \\
\end{tabular}
\begin{tabular}{p{0.2\textwidth}p{0.65\textwidth}}
\C{pre.c} & The preprocessor. \\
\C{proces.c} & The central processor. \\
\C{ratio.c} & Partial fractioning and summing functions. \\
\end{tabular}
\begin{tabular}{p{0.2\textwidth}p{0.65\textwidth}}
\C{reken.c} & Code for numerics. \\
\C{reshuf.c} & Utility functions for the renumbering of dummy indices, and for
statements like \C{shuffle}, \C{stuffle}, \C{multiply}. \\
\C{sch.c} & Code for the textual output of terms and expressions. \\
\end{tabular}
\begin{tabular}{p{0.2\textwidth}p{0.65\textwidth}}
\C{setfile.c} & Code to deal with setup parameters and setup files. \\
\C{smart.c} & Code doing optimized pattern matching. \\
\C{sort.c} & Code for the sorting of expressions. \\
\end{tabular}
\begin{tabular}{p{0.2\textwidth}p{0.65\textwidth}}
\C{startup.c} & Start of program (\C{main()}). Code for the startup and shutdown
phase of \FORM. \\
\C{store.c} & Code to read from disk or write to disk terms and expressions.
Also, store file and save file management. \\
\C{symmetr.c} & Pattern matching for functions with symmetric properties. \\
\end{tabular}
\begin{tabular}{p{0.2\textwidth}p{0.65\textwidth}}
\C{tables.c} & Code for the tablebases. \\
\C{threads.c} & \TFORM. Almost all of the \TFORM\ specific code. \\
\C{token.c} & The tokenizer. \\
\end{tabular}
\begin{tabular}{p{0.2\textwidth}p{0.65\textwidth}}
\C{tools.c} & Utility functions to deal with streams, files, strings, memory
management, and timers. \\
\C{unixfile.c} & Wrapper functions for UNIX file I/O functions. \\
\C{wildcard.c} & Code for wildcards.
\end{tabular}
\subsection{The global structs}
\FORM\ keeps its data organized in several global structs. These structs are defined in
\C{structs.h} (in the fold \C{A}) and come by the names \C{M\_const}, \C{P\_const}, \ldots. The
various global variables are grouped in these structs according to their r\^ole in the
program. The fold commentaries give details on this. \C{M\_const} is for global settings at startup
and \C{.clear}, for example.
The various structs are collected in the struct \C{AllGlobals}. In the case of sequential \FORM,
this struct is made into the type \C{ALLGLOBALS}, and in \C{inivar.h}, the global variable \C{A} is
defined having this type. This global variable \C{A} holds all the data defined in the various
structs. In \C{variable.h} several macros are defined to simplify (and more importantly unify) the
access to the struct elements. For example, one can access the variable \C{S0} in \C{T\_const} as
\C{AT.S0}.
With the multi-threaded version \TFORM\ things are a little bit more complicated, because some data
needs to be replicated and made private for each thread. This kind of data is situated in the
structs \C{N\_const}, \C{R\_const}, and \C{T\_const}. For \TFORM, these structs are collected in the
struct \C{AllPrivates} (which makes up the type \C{ALLPRIVATES}), all other structs go into the
\C{AllGlobals} struct. The global variable \C{A} now contains only the non-thread specific data. For
each thread a \C{AllPrivates} struct is dynamically allocated and the global pointer variable (in
\C{inivar.h}) \C{AB} holds their references. \C{AB} is an array of pointers where the index
corresponds to the thread number. The macros defined in \C{variable.h} to access the global struct
data are made such that they transparently work with the \C{AB} array. The user doesn't need to care
about these details and can still write as in the previous example \C{AT.S0}. This keeps the code
of sequential \FORM\ and multi-threaded \TFORM\ uniform.
The only small price one has to pay to make this uniform access by macros possible is to make sure
every function in \FORM\ knows in which thread it is executed. The \C{AN}, \C{AR}, and \C{AT} macros
use a variable \C{B}, which is set to the correct entry in \C{AB} by one of two ways. First, a
function can use the macro \C{GETIDENTITY} (defined in \C{declare.h}). In \TFORM\, it calls
\C{WhoAmI()} to get the thread number, declares the pointer \C{B}, and sets \C{B} to point to the
correct entry in \C{AB}. In sequential \FORM\ this macro is empty. The second way is to get the
variable \C{B} as a parameter from the caller. For this method the macros \C{PHEAD}, \C{PHEAD0},
\C{BHEAD}, and \C{BHEAD0} exist (defined in \C{ftypes.h}), which can be used in the parameter list of
the function declarations. The variants with a zero differ only by not including a trailing comma,
which is not allowed if no other parameters are following in the declaration. Usually, \C{PHEAD} is
used in the declaration (it includes type information), while \C{BHEAD} appears in the calling of
functions. Which way to set \C{B} is chosen, depends on the use of the function. The \C{PHEAD} method
is faster than \C{GETIDENTITY} and should be preferred in functions that are called very often. On
the other hand, \C{GETIDENTITY} is more general as it does not rely on every caller to supply \C{B}.
The elements of the structs are of various types. Some types are just simple macros mapping directly
to built-in types (see \C{form3.h}) like \C{WORD}, others are names for structs that are defined
(mostly) in \C{structs.h}. Often, variables of the same type are grouped together to help the
compiler with alignment. Also, a lot of structs use macros like \C{PADLONG} (\C{unix.h} or
\C{fwin.h}) to pad a struct such that its size is a multiple of a built-in type size. This again
is to help with the data alignment.
Most struct elements have comments that explain their use. These commentaries often include
the information where this element was once located in the old version 2 of \FORM\ (it is the pair
of parentheses with or without a capital letter inside). Pointers come in two flavors: Some
pointers reference a dynamically allocated piece of memory, basically owning this memory. Others
just reference another variable or point into allocated memory. The first kind is usually marked
with \C{[D]} for easy identification. These pointers often need to be treated particularly, e.g. during the
snapshot creation, when recovering, or when shutting down.
During start up (\C{main()}), all the memory of these global structs, i.e. their element variables, is
initialized to zero.
\subsection{Configuration}
The source code evaluates several preprocessor definitions that can be defined by the user.
According to these definitions the executable can be configured in different ways. As a default, the
sequential version of \FORM\ is generated. But if, for example, the preprocessor variable
\C{WITHPTHREADS} is defined, the multi-threaded version \TFORM\ will be compiled. These preprocessor
variables can be set when calling the compiler, like
\C{gcc -c -DWITHPTHREADS -o pre.o pre.c}
The most commonly considered preprocessor variables are: \\ \C{WITHPTHREADS}, \C{PARALLEL},
\C{WITHZLIB}, \C{WITHGMP}, \C{WITHSORTBOTS}, \C{LINUX}, \\ \C{OPTERON}, \C{DEBUGGING}. The first two
change the flavor of the executable: \TFORM\ or \PARFORM. The next two configure whether \FORM\ uses
the zlib library for compression during sorts or the GMP library for arbitrary precision arithmetics.
The next decides whether \FORM\ uses dedicated sorting threads in \TFORM. \C{LINUX}
specifies that the executable is to be compiled for a Linux or UNIX compliant operating system. An
alternative here would be to set the variable \C{ALPHA} or \C{MYWIN64} instead, but these builds are
less common. \C{OPTERON} has to be set if one compiles a 64bit executable. \C{DEBUGGING} enables
some features for a non-release debugging version of the executable (commonly named \C{vorm} or
\C{tvorm}).
When using the autoconf setup, the settings concerning the operating system, architecture (32/64bit), and
flavor of the executable are automatically done right. Additional settings like \C{WITHZLIB} can be
changed by manually editing the file \C{config.h}, which is included in \C{form3.h}.
Version numbers and production date can also be set, but then one either needs to edit the
appropriate lines in \C{form3.h} when in a manual compiling setup, or by editing \C{configure.ac} in
an autoconf setup.
form-master/doc/devref/testsuite.tex 0000664 0000000 0000000 00000035746 13565763364 0020146 0 ustar 00root root 0000000 0000000 \section{The test suite}
The subdirectory \C{check} contains a test suite for \FORM. Using the autoconf
facilities the checks can be started with the command \C{make check}.
Otherwise, one can issue the command \C{ruby form.rb} in the \C{check}
directory.
The test suite is written in the language
Ruby\footnote{\LINK{http://www.ruby-lang.org}}. Ruby itself already offers a
unit testing framework and this is used with as minimal as possible extensions
to make the creation of test cases for \FORM\ programs easy. All the extensions
to the built-in Ruby testing framework (\C{Test::Unit}) are contained in the
file \C{form.rb}. This file also contains code to load test cases from other
\C{*.rb} files in the \C{check} directory. Therefore all test cases are
contained in appropriately named \C{*.rb} files. The makefile's purpose is to
integrate the call \C{ruby form.rb} into the autoconf system.
{\it Side note:}
The choice to use Ruby and its built-in test framework was taken for several
reasons: It makes sense to use or adapt already existing testing frameworks in
order to keep the extra cost of maintenance as low as possible for the \FORM\
programmers. There are numerous systems available on the market, some are part
of a language runtime environment (libraries), and some are dedicated programs
with a custom configuration language. Since the tests for \FORM\ programs center
mainly about text processing, i.e. comparing the textual \FORM\ output to a
correct answer, we need powerful text processing facilities like pattern
matching. But we also need file operations and information from the operating
system to check the run of a \FORM\ program, eventually. All this is readily
available in the testing frameworks of scripting languages, like Ruby, Python,
or Tcl. Ruby was ultimately chosen, because the mixing of \FORM\ code with the
steering scripting language code looked nicest, and the small amount of extra
(Ruby) syntax necessary makes it convenient to add new test cases.
A new test case can be implemented in the following way. First of all, we need a
\FORM\ program that is to be run. It might be a program that exhibits an actual
bug in (a previous version of) \FORM\ or that contains generic code that should
be guaranteed to work, also in coming releases of \FORM. It might also be code
that deliberately crashes \FORM\ or causes other errors, like syntax errors, if
this behavior of \FORM\ is to be assumed. Usually, the \FORM\ program is rather
short or can be made such. In this case, we are going to mix the Ruby and the
\FORM\ code in one file. Alternatively, the \FORM\ program can also be kept in a
separate file. This option will be discussed later.
Now, either one choses an existing \C{*.rb} file (not \C{form.rb}) or starts a
new one. The name of the file should fit the test case scenario. In this file we
need to define a Ruby class that will contain our \FORM\ code as well as the
checks (assertions) we want to impose on the run.
The generic frame of this test case definition looks like this:
\begin{verbatim}
class [Test name] < FormTest
def setup
[Setup code, usually this includes the FORM program code]
end
def test1
[Execution code, and the assertion and testing code]
end
end
\end{verbatim}
The text in the brackets [ ] needs to be filled with our specific code. The
details of the Ruby code itself will be explained later. For a start, it is
usually advisable just to copy an existing test case and modify it.
Every class defined in this way will be used for the testing. First, Ruby will
run the code in the class method \C{setup}, and then it runs \C{test1}.
A complete test might look like this:
\begin{verbatim}
class SymbolIdTest < FormTest
def setup
input <<-EOF
S x, y;
L f = (x+y)^100;
id x = y;
print;
.end
EOF
end
def test1
execute FORM
assert no_problem
assert result("f") =~
pattern("1267650600228229401496703205376*y^100;")
end
end
\end{verbatim}
We have chose the name \C{SymbolIdTest} for our class. We defined the \FORM\
program in-line with a so called here document (\C{<<-EOF ... EOF}). We do run
the \FORM\ executable. Alternatives would be \TFORM, for example. The assertions
we have are that no problem occurred, i.e. no syntax error, no runtime error, or
similar things. We also check the output of our \FORM\ program. We compare via
pattern matching the result of the expression \C{f} with the correct answer. The
function \C{result()} extracts the appropriate line from the output, \C{=\~{}} is
the pattern matching operator in Ruby, and the function \C{pattern()} prepares
special characters like the caret (\^{}) for the pattern matcher.
Next time we run the test suite, our test will be run as well. If no assertions
are violated, we will only see the number of successful tests and assertions
increased in the summary output.
Even though the extra Ruby syntax is kept to a minimum and is rather
straightforward, some remarks about the Ruby language are useful here. Classes
are defined by the keyword \C{class}, and methods (or functions) are declared
with the keyword \C{def}. These definitions are always ended with the keyword
\C{end}. \C{FormTest} is a class defined in \C{form.rb} that contains all the
special code for \FORM\ test and that is derived from the built-in Ruby test
case class \C{TestCase}. For every test case we derive again from this class
(\C{class B < A} says that \C{B} is derived from \C{A}). We don't need
semicolons to end a line and indentation is arbitrary. Class names should be
capitalized. In Ruby, parentheses around the arguments of functions can often be
omitted. We use this possibility when we call the functions \C{input},
\C{execute}, and \C{assert}. We could have written \C{execute(FORM)} as well,
for example. The here document (\C{<<-EOF ... EOF}) can also use other markers
instead of \C{EOF}, of course. The minus sign before \C{EOF} allows the end
marker to be indented. Comments are started with a \#.
One class can actually contain more than one test. The testing framework will
call the method \C{setup} and then a method whose name starts with \C{test}
(Note: in newer versions of Ruby the name could be just \C{test}, but older
versions ($\ge$1.8.x) require at least one following extra character). If there are
more methods starting with \C{test}, each will be called and for each \C{setup}
will be called first.
In \C{setup} we need to prepare everything for the execution of \FORM. We can
either use \C{input} to in-line the source directly, or we can use
\C{input\_file} with a string as an argument to reference an external file,
e.g.
\begin{verbatim}
input_file "parsebug.frm"
\end{verbatim}
The function \C{input} will create a temporary
\FORM\ file for the contents. The name of the file is defined in \C{form.rb}.
The executable will later be run with the given name or the name of the
temporary file as an argument. If additional arguments need to be given to the
executable, the function \C{extra\_parameter} can be used, like e.g.
\begin{verbatim}
extra_parameter "-w4 -l"
\end{verbatim}
Sometimes one might need to prepare more things for a \FORM\ run, like setting
up certain files or starting an external program. This needs to be done
by ordinary Ruby code. For this, some more of the Ruby language needs to be
known by the user.
In the class methods with a name starting with \C{test} we put the code to run
the \FORM\ executable and to test the outcome. Usually, the first line will be
the call to the executable itself, either
\begin{verbatim}
execute FORM
\end{verbatim}
or
\begin{verbatim}
execute TFORM
\end{verbatim}
(\PARFORM\ is not supported yet). The function \C{execute} will run the
executable with the necessary or requested arguments, but it will run it under
the supervision of the \C{strace} system utility. Therefore \C{strace} needs to
be present on the system (options to enable or disable the use of \C{strace}
will probably be added in the future). \C{strace} is used to get detailed
information about the return value or possible failure states of the executable.
The output of \C{strace} will be saved in a temporary file and made available to
the test case programmer in a Ruby variable. The regular output and the error
channel output will be available in Ruby variables as well.
The Ruby variables containing the output are \C{\@@strace\_out}, \C{\@@stdout},
and \C{\@@stderr} (the leading \@@-sign is Ruby syntax for specifying instance
variables, i.e. variables belonging to a certain object). These variables are the
primary source for doing tests. In principle, these variables can be
investigated directly, for example via pattern matching like
\begin{verbatim}
if @strace_out =~ /Segmentation fault/
...
end
\end{verbatim}
which checks whether a segmentation fault has occurred (the slashes in Ruby
define a pattern). But for the most common cases some test functions
exist that encapsulate necessary pattern matching details. These functions
return true or false values which can be used as arguments to the \C{assert}
function. The \C{assert} function raises an error if the argument is false.
Available tests functions are:
\begin{tabular}{lp{20em}}
\C{crash} & true if a crash (segmentation fault) occurred \\
\C{warning} & true if \FORM\ has issued a warning \\
\C{compile\_error} & true if \FORM\ has found a syntax error \\
\C{runtime\_error} & true if \FORM\ has terminated prematurely \\
\C{error} & true if \C{compile\_error} or \C{runtime\_error} is true or
the standard error channel contains data \\
\C{problem} & true if \C{warning} or \C{error} or \C{crash} is true
\end{tabular}
Additionally, the logical opposite of each function exists with a name starting
with \C{no\_}, like \C{no\_problem} or \C{no\_crash}.
There is also the function \C{return\_value} which gives the return value of the
\FORM\ program as an integer, so one could do a check like
\begin{verbatim}
assert return_value == 66
\end{verbatim}
If pattern matching is coded directly, like in our example, some details have to
be considered. The operator \C{=\~{}} will try to match a string with a pattern.
The variables like \C{\@@stdout} are actually strings (they do contain the
carriage return and/or line feed for multi-line output). Patterns in Ruby are
written between slashes and various characters are interpreted in a special way
(following the widely used regex-syntax).
There are four functions to facilitate things: \C{result()}, \C{pattern()},
\C{exact\_result()}, and \C{exact\_pattern()}. \C{result()} takes a string being
the name of an expression and returns a string that only contains the lines
belonging to the last output of this expression. If it is not the last output of
an expression that is wished for, a second numeric parameter can be given that
specifies the index of the output (counting starts at 0). While \C{result()}
removes all line breaks and whitespaces, \C{exact\_result()} leaves them in
place. \C{pattern()} transforms special characters in the given string, removes
whitespaces and line breaks, and returns the string as a pattern. Since \FORM\
expressions usually contain a lot of special characters like +, *, ., etc. they
cannot not be simply used in a pattern. \C{pattern()} transforms these
characters automatically into the correct regex equivalent, e.g. + becomes
\textbackslash +. With it, a \FORM\ expression can be directly given as an
argument and used in a pattern matching (see example). \C{exact\_pattern()} does
not treat whitespaces and line breaks in a special way as \C{pattern()} does and
can therefore be used when a exact comparison is required (if for example a bug
in the output functions of \FORM\ had caused some whitespace or line breaks to
be missing and a test case were required to check for this behavior).
If one doesn't want or cannot use the \C{assert} function, one can signal a test
failure to the testing framework by raising an \C{AssertionFailedError}
directly, like for example
\begin{verbatim}
if return_value != 2
raise AssertionFailedError.new("return value is wrong!")
end
\end{verbatim}
Suppose a \FORM\ program should have deleted some file (\C{\#remove}), one could
implement the following test
\begin{verbatim}
if File.exist?("thenameofthefile")
raise AssertionFailedError.new("File still exists!")
end
\end{verbatim}
The testing framework actually not only calls \C{setup} and each \C{test} method
but also a method called \C{teardown}. This method is responsible for cleaning
up things at the end of each test run. The class \C{FormTest} provides such a
\C{teardown} method that will be inherited by the users test case class unless
it is overwritten. It calls the method \C{remove\_files} to delete all temporary
files that have been created so far. \C{remove\_files} can be called by the user
directly. If \C{teardown} is to be replaced by a specific implementation, it is
advisable to still call \C{FormTest}'s \C{teardown} (using Ruby's command
\C{super}), like for example
\begin{verbatim}
...
def teardown
super
File.delete("extra.log")
end
...
\end{verbatim}
At last, a complete example as it is actually contained in the repository.
{\scriptsize
\begin{verbatim}
#[ SparseTable1 :
=begin
Bugs reported 2004-04-06 by Misha Tentukov
PrintTable and FillExpression did not work with non-sparse tables
Fixed 2005-09-27
=end
class SparseTable1 < FormTest
def setup
input <<-EOF
cf f;
s x;
ctable Tab(1:`TableSize');
ctable TabNew(1:`TableSize');
#do i=1,`TableSize',1
Fill Tab(`i')=f(`i');
.sort
#enddo
* BUG1 (not all elements are printed):
PrintTable Tab;
bracket x;
.sort
L expr1=table_(Tab,x);
print;
.sort
bracket x;
.sort
* BUG 2 ( seems only TabNew(1) is ok - further everything is broken):
Fillexpression TabNew=expr1(x);
.sort
#do i=1,`TableSize'
L e`i'=TabNew(`i');
#enddo
print;
.sort
.end
EOF
extra_parameter "-D TableSize=10"
end
def test1
execute FORM
assert no_problem
assert result("expr1") =~ pattern(<<-EOF
f(1)*x + f(2)*x^2 + f(3)*x^3 + f(4)*x^4 + f(5)*x^5 + f(6)*x^6 + f(7)*x^7
+ f(8)*x^8 + f(9)*x^9 + f(10)*x^10;
EOF
)
assert result("e10") =~ /\s+f\(10\);/
end
end
#] SparseTable1 :
\end{verbatim}}
Some remarks. Folds are used (to structure a long file). \C{=begin} and \C{=end}
define a commentary block. Here useful information are given about the bug that
triggered the test case. The input is not modified compared to the original
\FORM\ program, it is just directly pasted into this Ruby file. We use
\C{extra\_parameter} to define a preprocessor variable for the run. We check
\C{expr1} to a multi-line reference. Since we use \C{pattern()} (instead of
\C{exact\_pattern()}), we can be sloppy about the indentation and the whitespaces.
The expression \C{e10} is matched to a pattern done "by hand" instead (just to
show the principle). For such a test case, where we are mostly interested about
the correctness of the calculation, the first assertion (\C{assert no\_problem})
is a standard.
form-master/doc/doxygen/ 0000775 0000000 0000000 00000000000 13565763364 0015556 5 ustar 00root root 0000000 0000000 form-master/doc/doxygen/DoxyfileHTML.in 0000664 0000000 0000000 00000127504 13565763364 0020367 0 ustar 00root root 0000000 0000000 # Doxyfile 1.3.5
# This file describes the settings to be used by the documentation system
# doxygen (www.doxygen.org) for a project
#
# All text after a hash (#) is considered a comment and will be ignored
# The format is:
# TAG = value [value, ...]
# For lists items can also be appended using:
# TAG += value [value, ...]
# Values that contain spaces should be placed between quotes (" ")
#---------------------------------------------------------------------------
# Project related configuration options
#---------------------------------------------------------------------------
# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
# by quotes) that should identify the project.
PROJECT_NAME = FORM
# The PROJECT_NUMBER tag can be used to enter a project or revision number.
# This could be handy for archiving the generated documentation or
# if some version control system is used.
PROJECT_NUMBER = @VERSION@
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
# base path where the generated documentation will be put.
# If a relative path is entered, it will be relative to the location
# where doxygen was started. If left blank the current directory will be used.
OUTPUT_DIRECTORY =
# The OUTPUT_LANGUAGE tag is used to specify the language in which all
# documentation generated by doxygen is written. Doxygen will use this
# information to generate all constant output in the proper language.
# The default language is English, other supported languages are:
# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch,
# Finnish, French, German, Greek, Hungarian, Italian, Japanese, Japanese-en
# (Japanese with English messages), Korean, Norwegian, Polish, Portuguese,
# Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish, and Ukrainian.
OUTPUT_LANGUAGE = English
# This tag can be used to specify the encoding used in the generated output.
# The encoding is not always determined by the language that is chosen,
# but also whether or not the output is meant for Windows or non-Windows users.
# In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES
# forces the Windows encoding (this is the default for the Windows binary),
# whereas setting the tag to NO uses a Unix-style encoding (the default for
# all platforms other than Windows).
USE_WINDOWS_ENCODING = NO
# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
# include brief member descriptions after the members that are listed in
# the file and class documentation (similar to JavaDoc).
# Set to NO to disable this.
BRIEF_MEMBER_DESC = YES
# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
# the brief description of a member or function before the detailed description.
# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
# brief descriptions will be completely suppressed.
REPEAT_BRIEF = YES
# This tag implements a quasi-intelligent brief description abbreviator
# that is used to form the text in various listings. Each string
# in this list, if found as the leading text of the brief description, will be
# stripped from the text and the result after processing the whole list, is used
# as the annotated text. Otherwise, the brief description is used as-is. If left
# blank, the following values are used ("$name" is automatically replaced with the
# name of the entity): "The $name class" "The $name widget" "The $name file"
# "is" "provides" "specifies" "contains" "represents" "a" "an" "the"
ABBREVIATE_BRIEF =
# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
# Doxygen will generate a detailed section even if there is only a brief
# description.
ALWAYS_DETAILED_SEC = NO
# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all inherited
# members of a class in the documentation of that class as if those members were
# ordinary class members. Constructors, destructors and assignment operators of
# the base classes will not be shown.
INLINE_INHERITED_MEMB = NO
# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
# path before files name in the file list and in the header files. If set
# to NO the shortest path that makes the file name unique will be used.
FULL_PATH_NAMES = NO
# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
# can be used to strip a user-defined part of the path. Stripping is
# only done if one of the specified strings matches the left-hand part of
# the path. It is allowed to use relative paths in the argument list.
STRIP_FROM_PATH =
# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
# (but less readable) file names. This can be useful is your file systems
# doesn't support long names like on DOS, Mac, or CD-ROM.
SHORT_NAMES = NO
# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
# will interpret the first line (until the first dot) of a JavaDoc-style
# comment as the brief description. If set to NO, the JavaDoc
# comments will behave just like the Qt-style comments (thus requiring an
# explicit @brief command for a brief description.
JAVADOC_AUTOBRIEF = NO
# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
# treat a multi-line C++ special comment block (i.e. a block of //! or ///
# comments) as a brief description. This used to be the default behaviour.
# The new default is to treat a multi-line C++ comment block as a detailed
# description. Set this tag to YES if you prefer the old behaviour instead.
MULTILINE_CPP_IS_BRIEF = NO
# If the DETAILS_AT_TOP tag is set to YES then Doxygen
# will output the detailed description near the top, like JavaDoc.
# If set to NO, the detailed description appears after the member
# documentation.
DETAILS_AT_TOP = NO
# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
# member inherits the documentation from any documented member that it
# re-implements.
INHERIT_DOCS = YES
# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
# tag is set to YES, then doxygen will reuse the documentation of the first
# member in the group (if any) for the other members of the group. By default
# all members of a group must be documented explicitly.
DISTRIBUTE_GROUP_DOC = YES
# The TAB_SIZE tag can be used to set the number of spaces in a tab.
# Doxygen uses this value to replace tabs by spaces in code fragments.
TAB_SIZE = 4
# This tag can be used to specify a number of aliases that acts
# as commands in the documentation. An alias has the form "name=value".
# For example adding "sideeffect=\par Side Effects:\n" will allow you to
# put the command \sideeffect (or @sideeffect) in the documentation, which
# will result in a user-defined paragraph with heading "Side Effects:".
# You can put \n's in the value part of an alias to insert newlines.
ALIASES =
# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
# only. Doxygen will then generate output that is more tailored for C.
# For instance, some of the names that are used will be different. The list
# of all members will be omitted, etc.
OPTIMIZE_OUTPUT_FOR_C = YES
# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java sources
# only. Doxygen will then generate output that is more tailored for Java.
# For instance, namespaces will be presented as packages, qualified scopes
# will look different, etc.
OPTIMIZE_OUTPUT_JAVA = NO
# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
# the same type (for instance a group of public functions) to be put as a
# subgroup of that type (e.g. under the Public Functions section). Set it to
# NO to prevent subgrouping. Alternatively, this can be done per class using
# the \nosubgrouping command.
SUBGROUPING = YES
#---------------------------------------------------------------------------
# Build related configuration options
#---------------------------------------------------------------------------
# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
# documentation are documented, even if no documentation was available.
# Private class members and static file members will be hidden unless
# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
EXTRACT_ALL = NO
# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
# will be included in the documentation.
EXTRACT_PRIVATE = NO
# If the EXTRACT_STATIC tag is set to YES all static members of a file
# will be included in the documentation.
EXTRACT_STATIC = NO
# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
# defined locally in source files will be included in the documentation.
# If set to NO only classes defined in header files are included.
EXTRACT_LOCAL_CLASSES = YES
# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
# undocumented members of documented classes, files or namespaces.
# If set to NO (the default) these members will be included in the
# various overviews, but no documentation section is generated.
# This option has no effect if EXTRACT_ALL is enabled.
HIDE_UNDOC_MEMBERS = NO
# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
# undocumented classes that are normally visible in the class hierarchy.
# If set to NO (the default) these classes will be included in the various
# overviews. This option has no effect if EXTRACT_ALL is enabled.
HIDE_UNDOC_CLASSES = NO
# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
# friend (class|struct|union) declarations.
# If set to NO (the default) these declarations will be included in the
# documentation.
HIDE_FRIEND_COMPOUNDS = NO
# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
# documentation blocks found inside the body of a function.
# If set to NO (the default) these blocks will be appended to the
# function's detailed documentation block.
HIDE_IN_BODY_DOCS = NO
# The INTERNAL_DOCS tag determines if documentation
# that is typed after a \internal command is included. If the tag is set
# to NO (the default) then the documentation will be excluded.
# Set it to YES to include the internal documentation.
INTERNAL_DOCS = NO
# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
# file names in lower-case letters. If set to YES upper-case letters are also
# allowed. This is useful if you have classes or files whose names only differ
# in case and if your file system supports case sensitive file names. Windows
# users are advised to set this option to NO.
CASE_SENSE_NAMES = YES
# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
# will show members with their full class and namespace scopes in the
# documentation. If set to YES the scope will be hidden.
HIDE_SCOPE_NAMES = YES
# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
# will put a list of the files that are included by a file in the documentation
# of that file.
SHOW_INCLUDE_FILES = YES
# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
# is inserted in the documentation for inline members.
INLINE_INFO = YES
# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
# will sort the (detailed) documentation of file and class members
# alphabetically by member name. If set to NO the members will appear in
# declaration order.
SORT_MEMBER_DOCS = NO
# The GENERATE_TODOLIST tag can be used to enable (YES) or
# disable (NO) the todo list. This list is created by putting \todo
# commands in the documentation.
GENERATE_TODOLIST = YES
# The GENERATE_TESTLIST tag can be used to enable (YES) or
# disable (NO) the test list. This list is created by putting \test
# commands in the documentation.
GENERATE_TESTLIST = YES
# The GENERATE_BUGLIST tag can be used to enable (YES) or
# disable (NO) the bug list. This list is created by putting \bug
# commands in the documentation.
GENERATE_BUGLIST = YES
# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
# disable (NO) the deprecated list. This list is created by putting
# \deprecated commands in the documentation.
GENERATE_DEPRECATEDLIST= YES
# The ENABLED_SECTIONS tag can be used to enable conditional
# documentation sections, marked by \if sectionname ... \endif.
ENABLED_SECTIONS =
# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
# the initial value of a variable or define consists of for it to appear in
# the documentation. If the initializer consists of more lines than specified
# here it will be hidden. Use a value of 0 to hide initializers completely.
# The appearance of the initializer of individual variables and defines in the
# documentation can be controlled using \showinitializer or \hideinitializer
# command in the documentation regardless of this setting.
MAX_INITIALIZER_LINES = 30
# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
# at the bottom of the documentation of classes and structs. If set to YES the
# list will mention the files that were used to generate the documentation.
SHOW_USED_FILES = YES
#---------------------------------------------------------------------------
# configuration options related to warning and progress messages
#---------------------------------------------------------------------------
# The QUIET tag can be used to turn on/off the messages that are generated
# by doxygen. Possible values are YES and NO. If left blank NO is used.
QUIET = NO
# The WARNINGS tag can be used to turn on/off the warning messages that are
# generated by doxygen. Possible values are YES and NO. If left blank
# NO is used.
WARNINGS = YES
# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
# automatically be disabled.
WARN_IF_UNDOCUMENTED = YES
# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
# potential errors in the documentation, such as not documenting some
# parameters in a documented function, or documenting parameters that
# don't exist or using markup commands wrongly.
WARN_IF_DOC_ERROR = YES
# The WARN_FORMAT tag determines the format of the warning messages that
# doxygen can produce. The string should contain the $file, $line, and $text
# tags, which will be replaced by the file and line number from which the
# warning originated and the warning text.
WARN_FORMAT = "$file:$line: $text"
# The WARN_LOGFILE tag can be used to specify a file to which warning
# and error messages should be written. If left blank the output is written
# to stderr.
WARN_LOGFILE =
#---------------------------------------------------------------------------
# configuration options related to the input files
#---------------------------------------------------------------------------
# The INPUT tag can be used to specify the files and/or directories that contain
# documented source files. You may enter file names like "myfile.cpp" or
# directories like "/usr/src/myproject". Separate the files or directories
# with spaces.
INPUT = @top_srcdir@/sources
# If the value of the INPUT tag contains directories, you can use the
# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
# and *.h) to filter out the source-files in the directories. If left
# blank the following patterns are tested:
# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp
# *.h++ *.idl *.odl *.cs *.php *.php3 *.inc
FILE_PATTERNS = *.c *.cc *.h
# The RECURSIVE tag can be used to turn specify whether or not subdirectories
# should be searched for input files as well. Possible values are YES and NO.
# If left blank NO is used.
RECURSIVE = NO
# The EXCLUDE tag can be used to specify files and/or directories that should
# excluded from the INPUT source files. This way you can easily exclude a
# subdirectory from a directory tree whose root is specified with the INPUT tag.
EXCLUDE =
# The EXCLUDE_SYMLINKS tag can be used select whether or not files or directories
# that are symbolic links (a Unix filesystem feature) are excluded from the input.
EXCLUDE_SYMLINKS = NO
# If the value of the INPUT tag contains directories, you can use the
# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
# certain files from those directories.
EXCLUDE_PATTERNS =
# The EXAMPLE_PATH tag can be used to specify one or more files or
# directories that contain example code fragments that are included (see
# the \include command).
EXAMPLE_PATH =
# If the value of the EXAMPLE_PATH tag contains directories, you can use the
# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
# and *.h) to filter out the source-files in the directories. If left
# blank all files are included.
EXAMPLE_PATTERNS =
# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
# searched for input files to be used with the \include or \dontinclude
# commands irrespective of the value of the RECURSIVE tag.
# Possible values are YES and NO. If left blank NO is used.
EXAMPLE_RECURSIVE = NO
# The IMAGE_PATH tag can be used to specify one or more files or
# directories that contain image that are included in the documentation (see
# the \image command).
IMAGE_PATH =
# The INPUT_FILTER tag can be used to specify a program that doxygen should
# invoke to filter for each input file. Doxygen will invoke the filter program
# by executing (via popen()) the command , where
# is the value of the INPUT_FILTER tag, and is the name of an
# input file. Doxygen will then use the output that the filter program writes
# to standard output.
INPUT_FILTER =
# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
# INPUT_FILTER) will be used to filter the input files when producing source
# files to browse (i.e. when SOURCE_BROWSER is set to YES).
FILTER_SOURCE_FILES = NO
#---------------------------------------------------------------------------
# configuration options related to source browsing
#---------------------------------------------------------------------------
# If the SOURCE_BROWSER tag is set to YES then a list of source files will
# be generated. Documented entities will be cross-referenced with these sources.
# Note: To get rid of all source code in the generated output, make sure also
# VERBATIM_HEADERS is set to NO.
SOURCE_BROWSER = YES
# Setting the INLINE_SOURCES tag to YES will include the body
# of functions and classes directly in the documentation.
INLINE_SOURCES = NO
# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
# doxygen to hide any special comment blocks from generated source code
# fragments. Normal C and C++ comments will always remain visible.
STRIP_CODE_COMMENTS = YES
# If the REFERENCED_BY_RELATION tag is set to YES (the default)
# then for each documented function all documented
# functions referencing it will be listed.
REFERENCED_BY_RELATION = YES
# If the REFERENCES_RELATION tag is set to YES (the default)
# then for each documented function all documented entities
# called/used by that function will be listed.
REFERENCES_RELATION = YES
# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
# will generate a verbatim copy of the header file for each class for
# which an include is specified. Set to NO to disable this.
VERBATIM_HEADERS = YES
#---------------------------------------------------------------------------
# configuration options related to the alphabetical class index
#---------------------------------------------------------------------------
# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
# of all compounds will be generated. Enable this if the project
# contains a lot of classes, structs, unions or interfaces.
ALPHABETICAL_INDEX = YES
# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
# in which this list will be split (can be a number in the range [1..20])
COLS_IN_ALPHA_INDEX = 5
# In case all classes in a project start with a common prefix, all
# classes will be put under the same header in the alphabetical index.
# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
# should be ignored while generating the index headers.
IGNORE_PREFIX =
#---------------------------------------------------------------------------
# configuration options related to the HTML output
#---------------------------------------------------------------------------
# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
# generate HTML output.
GENERATE_HTML = YES
# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
# If a relative path is entered the value of OUTPUT_DIRECTORY will be
# put in front of it. If left blank `html' will be used as the default path.
HTML_OUTPUT = html
# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
# doxygen will generate files with .html extension.
HTML_FILE_EXTENSION = .html
# The HTML_HEADER tag can be used to specify a personal HTML header for
# each generated HTML page. If it is left blank doxygen will generate a
# standard header.
HTML_HEADER =
# The HTML_FOOTER tag can be used to specify a personal HTML footer for
# each generated HTML page. If it is left blank doxygen will generate a
# standard footer.
HTML_FOOTER =
# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
# style sheet that is used by each HTML page. It can be used to
# fine-tune the look of the HTML output. If the tag is left blank doxygen
# will generate a default style sheet. Note that doxygen will try to copy
# the style sheet file to the HTML output directory, so don't put your own
# stylesheet in the HTML output directory as well, or it will be erased!
HTML_STYLESHEET =
# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
# files or namespaces will be aligned in HTML using tables. If set to
# NO a bullet list will be used.
HTML_ALIGN_MEMBERS = YES
# If the GENERATE_HTMLHELP tag is set to YES, additional index files
# will be generated that can be used as input for tools like the
# Microsoft HTML help workshop to generate a compressed HTML help file (.chm)
# of the generated HTML documentation.
GENERATE_HTMLHELP = NO
# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
# be used to specify the file name of the resulting .chm file. You
# can add a path in front of the file if the result should not be
# written to the html output directory.
CHM_FILE =
# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
# be used to specify the location (absolute path including file name) of
# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
# the HTML help compiler on the generated index.hhp.
HHC_LOCATION =
# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
# controls if a separate .chi index file is generated (YES) or that
# it should be included in the master .chm file (NO).
GENERATE_CHI = NO
# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
# controls whether a binary table of contents is generated (YES) or a
# normal table of contents (NO) in the .chm file.
BINARY_TOC = NO
# The TOC_EXPAND flag can be set to YES to add extra items for group members
# to the contents of the HTML help documentation and to the tree view.
TOC_EXPAND = NO
# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
# top of each HTML page. The value NO (the default) enables the index and
# the value YES disables it.
DISABLE_INDEX = NO
# This tag can be used to set the number of enum values (range [1..20])
# that doxygen will group on one line in the generated HTML documentation.
ENUM_VALUES_PER_LINE = 4
# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be
# generated containing a tree-like index structure (just like the one that
# is generated for HTML Help). For this to work a browser that supports
# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+,
# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are
# probably better off using the HTML help feature.
GENERATE_TREEVIEW = NO
# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
# used to set the initial width (in pixels) of the frame in which the tree
# is shown.
TREEVIEW_WIDTH = 250
#---------------------------------------------------------------------------
# configuration options related to the LaTeX output
#---------------------------------------------------------------------------
# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
# generate Latex output.
GENERATE_LATEX = NO
# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
# If a relative path is entered the value of OUTPUT_DIRECTORY will be
# put in front of it. If left blank `latex' will be used as the default path.
LATEX_OUTPUT = latex
# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
# invoked. If left blank `latex' will be used as the default command name.
LATEX_CMD_NAME = latex
# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
# generate index for LaTeX. If left blank `makeindex' will be used as the
# default command name.
MAKEINDEX_CMD_NAME = makeindex
# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
# LaTeX documents. This may be useful for small projects and may help to
# save some trees in general.
COMPACT_LATEX = NO
# The PAPER_TYPE tag can be used to set the paper type that is used
# by the printer. Possible values are: a4, a4wide, letter, legal and
# executive. If left blank a4wide will be used.
PAPER_TYPE = a4wide
# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
# packages that should be included in the LaTeX output.
EXTRA_PACKAGES =
# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
# the generated latex document. The header should contain everything until
# the first chapter. If it is left blank doxygen will generate a
# standard header. Notice: only use this tag if you know what you are doing!
LATEX_HEADER =
# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
# is prepared for conversion to pdf (using ps2pdf). The pdf file will
# contain links (just like the HTML output) instead of page references
# This makes the output suitable for online browsing using a pdf viewer.
PDF_HYPERLINKS = NO
# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
# plain latex in the generated Makefile. Set this option to YES to get a
# higher quality PDF documentation.
USE_PDFLATEX = NO
# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
# command to the generated LaTeX files. This will instruct LaTeX to keep
# running if errors occur, instead of asking the user for help.
# This option is also used when generating formulas in HTML.
LATEX_BATCHMODE = NO
# If LATEX_HIDE_INDICES is set to YES then doxygen will not
# include the index chapters (such as File Index, Compound Index, etc.)
# in the output.
LATEX_HIDE_INDICES = NO
#---------------------------------------------------------------------------
# configuration options related to the RTF output
#---------------------------------------------------------------------------
# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
# The RTF output is optimized for Word 97 and may not look very pretty with
# other RTF readers or editors.
GENERATE_RTF = NO
# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
# If a relative path is entered the value of OUTPUT_DIRECTORY will be
# put in front of it. If left blank `rtf' will be used as the default path.
RTF_OUTPUT = rtf
# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
# RTF documents. This may be useful for small projects and may help to
# save some trees in general.
COMPACT_RTF = NO
# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
# will contain hyperlink fields. The RTF file will
# contain links (just like the HTML output) instead of page references.
# This makes the output suitable for online browsing using WORD or other
# programs which support those fields.
# Note: wordpad (write) and others do not support links.
RTF_HYPERLINKS = NO
# Load stylesheet definitions from file. Syntax is similar to doxygen's
# config file, i.e. a series of assignments. You only have to provide
# replacements, missing definitions are set to their default value.
RTF_STYLESHEET_FILE =
# Set optional variables used in the generation of an rtf document.
# Syntax is similar to doxygen's config file.
RTF_EXTENSIONS_FILE =
#---------------------------------------------------------------------------
# configuration options related to the man page output
#---------------------------------------------------------------------------
# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
# generate man pages
GENERATE_MAN = NO
# The MAN_OUTPUT tag is used to specify where the man pages will be put.
# If a relative path is entered the value of OUTPUT_DIRECTORY will be
# put in front of it. If left blank `man' will be used as the default path.
MAN_OUTPUT = man
# The MAN_EXTENSION tag determines the extension that is added to
# the generated man pages (default is the subroutine's section .3)
MAN_EXTENSION = .3
# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
# then it will generate one additional man file for each entity
# documented in the real man page(s). These additional files
# only source the real man page, but without them the man command
# would be unable to find the correct page. The default is NO.
MAN_LINKS = NO
#---------------------------------------------------------------------------
# configuration options related to the XML output
#---------------------------------------------------------------------------
# If the GENERATE_XML tag is set to YES Doxygen will
# generate an XML file that captures the structure of
# the code including all documentation.
GENERATE_XML = NO
# The XML_OUTPUT tag is used to specify where the XML pages will be put.
# If a relative path is entered the value of OUTPUT_DIRECTORY will be
# put in front of it. If left blank `xml' will be used as the default path.
XML_OUTPUT = xml
# The XML_SCHEMA tag can be used to specify an XML schema,
# which can be used by a validating XML parser to check the
# syntax of the XML files.
XML_SCHEMA =
# The XML_DTD tag can be used to specify an XML DTD,
# which can be used by a validating XML parser to check the
# syntax of the XML files.
XML_DTD =
# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
# dump the program listings (including syntax highlighting
# and cross-referencing information) to the XML output. Note that
# enabling this will significantly increase the size of the XML output.
XML_PROGRAMLISTING = YES
#---------------------------------------------------------------------------
# configuration options for the AutoGen Definitions output
#---------------------------------------------------------------------------
# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
# generate an AutoGen Definitions (see autogen.sf.net) file
# that captures the structure of the code including all
# documentation. Note that this feature is still experimental
# and incomplete at the moment.
GENERATE_AUTOGEN_DEF = NO
#---------------------------------------------------------------------------
# configuration options related to the Perl module output
#---------------------------------------------------------------------------
# If the GENERATE_PERLMOD tag is set to YES Doxygen will
# generate a Perl module file that captures the structure of
# the code including all documentation. Note that this
# feature is still experimental and incomplete at the
# moment.
GENERATE_PERLMOD = NO
# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
# the necessary Makefile rules, Perl scripts and LaTeX code to be able
# to generate PDF and DVI output from the Perl module output.
PERLMOD_LATEX = NO
# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
# nicely formatted so it can be parsed by a human reader. This is useful
# if you want to understand what is going on. On the other hand, if this
# tag is set to NO the size of the Perl module output will be much smaller
# and Perl will parse it just the same.
PERLMOD_PRETTY = YES
# The names of the make variables in the generated doxyrules.make file
# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
# This is useful so different doxyrules.make files included by the same
# Makefile don't overwrite each other's variables.
PERLMOD_MAKEVAR_PREFIX =
#---------------------------------------------------------------------------
# Configuration options related to the preprocessor
#---------------------------------------------------------------------------
# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
# evaluate all C-preprocessor directives found in the sources and include
# files.
ENABLE_PREPROCESSING = YES
# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
# names in the source code. If set to NO (the default) only conditional
# compilation will be performed. Macro expansion can be done in a controlled
# way by setting EXPAND_ONLY_PREDEF to YES.
MACRO_EXPANSION = YES
# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
# then the macro expansion is limited to the macros specified with the
# PREDEFINED and EXPAND_AS_PREDEFINED tags.
EXPAND_ONLY_PREDEF = YES
# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
# in the INCLUDE_PATH (see below) will be search if a #include is found.
SEARCH_INCLUDES = YES
# The INCLUDE_PATH tag can be used to specify one or more directories that
# contain include files that are not input files but should be processed by
# the preprocessor.
INCLUDE_PATH =
# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
# patterns (like *.h and *.hpp) to filter out the header-files in the
# directories. If left blank, the patterns specified with FILE_PATTERNS will
# be used.
INCLUDE_FILE_PATTERNS = *.h
# The PREDEFINED tag can be used to specify one or more macro names that
# are defined before the preprocessor is started (similar to the -D option of
# gcc). The argument of the tag is a list of macros of the form: name
# or name=definition (no spaces). If the definition and the = are
# omitted =1 is assumed.
PREDEFINED = \
"PADPOINTER(a1,a2,a3,a4)=" \
"PADLONG(a1,a2,a3)=" \
"PADINT(a1,a2)=" \
"PADWORD(a1)="
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
# this tag can be used to specify a list of macro names that should be expanded.
# The macro definition that is found in the sources will be used.
# Use the PREDEFINED tag if you want to use a different macro definition.
EXPAND_AS_DEFINED =
# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
# doxygen's preprocessor will remove all function-like macros that are alone
# on a line, have an all uppercase name, and do not end with a semicolon. Such
# function macros are typically used for boiler-plate code, and will confuse the
# parser if not removed.
SKIP_FUNCTION_MACROS = YES
#---------------------------------------------------------------------------
# Configuration::addtions related to external references
#---------------------------------------------------------------------------
# The TAGFILES option can be used to specify one or more tagfiles.
# Optionally an initial location of the external documentation
# can be added for each tagfile. The format of a tag file without
# this location is as follows:
# TAGFILES = file1 file2 ...
# Adding location for the tag files is done as follows:
# TAGFILES = file1=loc1 "file2 = loc2" ...
# where "loc1" and "loc2" can be relative or absolute paths or
# URLs. If a location is present for each tag, the installdox tool
# does not have to be run to correct the links.
# Note that each tag file must have a unique name
# (where the name does NOT include the path)
# If a tag file is not located in the directory in which doxygen
# is run, you must also specify the path to the tagfile here.
TAGFILES =
# When a file name is specified after GENERATE_TAGFILE, doxygen will create
# a tag file that is based on the input files it reads.
GENERATE_TAGFILE =
# If the ALLEXTERNALS tag is set to YES all external classes will be listed
# in the class index. If set to NO only the inherited external classes
# will be listed.
ALLEXTERNALS = NO
# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
# in the modules index. If set to NO, only the current project's groups will
# be listed.
EXTERNAL_GROUPS = YES
# The PERL_PATH should be the absolute path and name of the perl script
# interpreter (i.e. the result of `which perl').
PERL_PATH = /usr/bin/perl
#---------------------------------------------------------------------------
# Configuration options related to the dot tool
#---------------------------------------------------------------------------
# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base or
# super classes. Setting the tag to NO turns the diagrams off. Note that this
# option is superseded by the HAVE_DOT option below. This is only a fallback. It is
# recommended to install and use dot, since it yields more powerful graphs.
CLASS_DIAGRAMS = YES
# If set to YES, the inheritance and collaboration graphs will hide
# inheritance and usage relations if the target is undocumented
# or is not a class.
HIDE_UNDOC_RELATIONS = YES
# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
# available from the path. This tool is part of Graphviz, a graph visualization
# toolkit from AT&T and Lucent Bell Labs. The other options in this section
# have no effect if this option is set to NO (the default)
HAVE_DOT = NO
# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
# will generate a graph for each documented class showing the direct and
# indirect inheritance relations. Setting this tag to YES will force the
# the CLASS_DIAGRAMS tag to NO.
CLASS_GRAPH = YES
# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
# will generate a graph for each documented class showing the direct and
# indirect implementation dependencies (inheritance, containment, and
# class references variables) of the class with other documented classes.
COLLABORATION_GRAPH = YES
# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
# collaboration diagrams in a style similar to the OMG's Unified Modeling
# Language.
UML_LOOK = NO
# If set to YES, the inheritance and collaboration graphs will show the
# relations between templates and their instances.
TEMPLATE_RELATIONS = NO
# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
# tags are set to YES then doxygen will generate a graph for each documented
# file showing the direct and indirect include dependencies of the file with
# other documented files.
INCLUDE_GRAPH = YES
# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
# documented header file showing the documented files that directly or
# indirectly include this file.
INCLUDED_BY_GRAPH = YES
# If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will
# generate a call dependency graph for every global function or class method.
# Note that enabling this option will significantly increase the time of a run.
# So in most cases it will be better to enable call graphs for selected
# functions only using the \callgraph command.
CALL_GRAPH = NO
# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
# will graphical hierarchy of all classes instead of a textual one.
GRAPHICAL_HIERARCHY = YES
# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
# generated by dot. Possible values are png, jpg, or gif
# If left blank png will be used.
DOT_IMAGE_FORMAT = png
# The tag DOT_PATH can be used to specify the path where the dot tool can be
# found. If left blank, it is assumed the dot tool can be found on the path.
DOT_PATH =
# The DOTFILE_DIRS tag can be used to specify one or more directories that
# contain dot files that are included in the documentation (see the
# \dotfile command).
DOTFILE_DIRS =
# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width
# (in pixels) of the graphs generated by dot. If a graph becomes larger than
# this value, doxygen will try to truncate the graph, so that it fits within
# the specified constraint. Beware that most browsers cannot cope with very
# large images.
MAX_DOT_GRAPH_WIDTH = 1024
# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height
# (in pixels) of the graphs generated by dot. If a graph becomes larger than
# this value, doxygen will try to truncate the graph, so that it fits within
# the specified constraint. Beware that most browsers cannot cope with very
# large images.
MAX_DOT_GRAPH_HEIGHT = 1024
# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
# graphs generated by dot. A depth value of 3 means that only nodes reachable
# from the root by following a path via at most 3 edges will be shown. Nodes that
# lay further from the root node will be omitted. Note that setting this option to
# 1 or 2 may greatly reduce the computation time needed for large code bases. Also
# note that a graph may be further truncated if the graph's image dimensions are
# not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH and MAX_DOT_GRAPH_HEIGHT).
# If 0 is used for the depth value (the default), the graph is not depth-constrained.
MAX_DOT_GRAPH_DEPTH = 0
# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
# generate a legend page explaining the meaning of the various boxes and
# arrows in the dot generated graphs.
GENERATE_LEGEND = YES
# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
# remove the intermediate dot files that are used to generate
# the various graphs.
DOT_CLEANUP = YES
#---------------------------------------------------------------------------
# Configuration::addtions related to the search engine
#---------------------------------------------------------------------------
# The SEARCHENGINE tag specifies whether or not a search engine should be
# used. If set to NO the values of all tags below this one will be ignored.
SEARCHENGINE = NO
form-master/doc/doxygen/DoxyfileLATEX.in 0000664 0000000 0000000 00000127503 13565763364 0020477 0 ustar 00root root 0000000 0000000 # Doxyfile 1.3.5
# This file describes the settings to be used by the documentation system
# doxygen (www.doxygen.org) for a project
#
# All text after a hash (#) is considered a comment and will be ignored
# The format is:
# TAG = value [value, ...]
# For lists items can also be appended using:
# TAG += value [value, ...]
# Values that contain spaces should be placed between quotes (" ")
#---------------------------------------------------------------------------
# Project related configuration options
#---------------------------------------------------------------------------
# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
# by quotes) that should identify the project.
PROJECT_NAME = FORM
# The PROJECT_NUMBER tag can be used to enter a project or revision number.
# This could be handy for archiving the generated documentation or
# if some version control system is used.
PROJECT_NUMBER = @VERSION@
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
# base path where the generated documentation will be put.
# If a relative path is entered, it will be relative to the location
# where doxygen was started. If left blank the current directory will be used.
OUTPUT_DIRECTORY =
# The OUTPUT_LANGUAGE tag is used to specify the language in which all
# documentation generated by doxygen is written. Doxygen will use this
# information to generate all constant output in the proper language.
# The default language is English, other supported languages are:
# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch,
# Finnish, French, German, Greek, Hungarian, Italian, Japanese, Japanese-en
# (Japanese with English messages), Korean, Norwegian, Polish, Portuguese,
# Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish, and Ukrainian.
OUTPUT_LANGUAGE = English
# This tag can be used to specify the encoding used in the generated output.
# The encoding is not always determined by the language that is chosen,
# but also whether or not the output is meant for Windows or non-Windows users.
# In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES
# forces the Windows encoding (this is the default for the Windows binary),
# whereas setting the tag to NO uses a Unix-style encoding (the default for
# all platforms other than Windows).
USE_WINDOWS_ENCODING = NO
# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
# include brief member descriptions after the members that are listed in
# the file and class documentation (similar to JavaDoc).
# Set to NO to disable this.
BRIEF_MEMBER_DESC = YES
# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
# the brief description of a member or function before the detailed description.
# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
# brief descriptions will be completely suppressed.
REPEAT_BRIEF = YES
# This tag implements a quasi-intelligent brief description abbreviator
# that is used to form the text in various listings. Each string
# in this list, if found as the leading text of the brief description, will be
# stripped from the text and the result after processing the whole list, is used
# as the annotated text. Otherwise, the brief description is used as-is. If left
# blank, the following values are used ("$name" is automatically replaced with the
# name of the entity): "The $name class" "The $name widget" "The $name file"
# "is" "provides" "specifies" "contains" "represents" "a" "an" "the"
ABBREVIATE_BRIEF =
# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
# Doxygen will generate a detailed section even if there is only a brief
# description.
ALWAYS_DETAILED_SEC = NO
# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all inherited
# members of a class in the documentation of that class as if those members were
# ordinary class members. Constructors, destructors and assignment operators of
# the base classes will not be shown.
INLINE_INHERITED_MEMB = NO
# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
# path before files name in the file list and in the header files. If set
# to NO the shortest path that makes the file name unique will be used.
FULL_PATH_NAMES = NO
# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
# can be used to strip a user-defined part of the path. Stripping is
# only done if one of the specified strings matches the left-hand part of
# the path. It is allowed to use relative paths in the argument list.
STRIP_FROM_PATH =
# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
# (but less readable) file names. This can be useful is your file systems
# doesn't support long names like on DOS, Mac, or CD-ROM.
SHORT_NAMES = NO
# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
# will interpret the first line (until the first dot) of a JavaDoc-style
# comment as the brief description. If set to NO, the JavaDoc
# comments will behave just like the Qt-style comments (thus requiring an
# explicit @brief command for a brief description.
JAVADOC_AUTOBRIEF = NO
# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
# treat a multi-line C++ special comment block (i.e. a block of //! or ///
# comments) as a brief description. This used to be the default behaviour.
# The new default is to treat a multi-line C++ comment block as a detailed
# description. Set this tag to YES if you prefer the old behaviour instead.
MULTILINE_CPP_IS_BRIEF = NO
# If the DETAILS_AT_TOP tag is set to YES then Doxygen
# will output the detailed description near the top, like JavaDoc.
# If set to NO, the detailed description appears after the member
# documentation.
DETAILS_AT_TOP = NO
# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
# member inherits the documentation from any documented member that it
# re-implements.
INHERIT_DOCS = YES
# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
# tag is set to YES, then doxygen will reuse the documentation of the first
# member in the group (if any) for the other members of the group. By default
# all members of a group must be documented explicitly.
DISTRIBUTE_GROUP_DOC = YES
# The TAB_SIZE tag can be used to set the number of spaces in a tab.
# Doxygen uses this value to replace tabs by spaces in code fragments.
TAB_SIZE = 4
# This tag can be used to specify a number of aliases that acts
# as commands in the documentation. An alias has the form "name=value".
# For example adding "sideeffect=\par Side Effects:\n" will allow you to
# put the command \sideeffect (or @sideeffect) in the documentation, which
# will result in a user-defined paragraph with heading "Side Effects:".
# You can put \n's in the value part of an alias to insert newlines.
ALIASES =
# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
# only. Doxygen will then generate output that is more tailored for C.
# For instance, some of the names that are used will be different. The list
# of all members will be omitted, etc.
OPTIMIZE_OUTPUT_FOR_C = YES
# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java sources
# only. Doxygen will then generate output that is more tailored for Java.
# For instance, namespaces will be presented as packages, qualified scopes
# will look different, etc.
OPTIMIZE_OUTPUT_JAVA = NO
# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
# the same type (for instance a group of public functions) to be put as a
# subgroup of that type (e.g. under the Public Functions section). Set it to
# NO to prevent subgrouping. Alternatively, this can be done per class using
# the \nosubgrouping command.
SUBGROUPING = YES
#---------------------------------------------------------------------------
# Build related configuration options
#---------------------------------------------------------------------------
# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
# documentation are documented, even if no documentation was available.
# Private class members and static file members will be hidden unless
# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
EXTRACT_ALL = NO
# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
# will be included in the documentation.
EXTRACT_PRIVATE = NO
# If the EXTRACT_STATIC tag is set to YES all static members of a file
# will be included in the documentation.
EXTRACT_STATIC = NO
# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
# defined locally in source files will be included in the documentation.
# If set to NO only classes defined in header files are included.
EXTRACT_LOCAL_CLASSES = YES
# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
# undocumented members of documented classes, files or namespaces.
# If set to NO (the default) these members will be included in the
# various overviews, but no documentation section is generated.
# This option has no effect if EXTRACT_ALL is enabled.
HIDE_UNDOC_MEMBERS = NO
# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
# undocumented classes that are normally visible in the class hierarchy.
# If set to NO (the default) these classes will be included in the various
# overviews. This option has no effect if EXTRACT_ALL is enabled.
HIDE_UNDOC_CLASSES = NO
# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
# friend (class|struct|union) declarations.
# If set to NO (the default) these declarations will be included in the
# documentation.
HIDE_FRIEND_COMPOUNDS = NO
# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
# documentation blocks found inside the body of a function.
# If set to NO (the default) these blocks will be appended to the
# function's detailed documentation block.
HIDE_IN_BODY_DOCS = NO
# The INTERNAL_DOCS tag determines if documentation
# that is typed after a \internal command is included. If the tag is set
# to NO (the default) then the documentation will be excluded.
# Set it to YES to include the internal documentation.
INTERNAL_DOCS = NO
# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
# file names in lower-case letters. If set to YES upper-case letters are also
# allowed. This is useful if you have classes or files whose names only differ
# in case and if your file system supports case sensitive file names. Windows
# users are advised to set this option to NO.
CASE_SENSE_NAMES = YES
# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
# will show members with their full class and namespace scopes in the
# documentation. If set to YES the scope will be hidden.
HIDE_SCOPE_NAMES = NO
# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
# will put a list of the files that are included by a file in the documentation
# of that file.
SHOW_INCLUDE_FILES = YES
# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
# is inserted in the documentation for inline members.
INLINE_INFO = YES
# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
# will sort the (detailed) documentation of file and class members
# alphabetically by member name. If set to NO the members will appear in
# declaration order.
SORT_MEMBER_DOCS = NO
# The GENERATE_TODOLIST tag can be used to enable (YES) or
# disable (NO) the todo list. This list is created by putting \todo
# commands in the documentation.
GENERATE_TODOLIST = YES
# The GENERATE_TESTLIST tag can be used to enable (YES) or
# disable (NO) the test list. This list is created by putting \test
# commands in the documentation.
GENERATE_TESTLIST = YES
# The GENERATE_BUGLIST tag can be used to enable (YES) or
# disable (NO) the bug list. This list is created by putting \bug
# commands in the documentation.
GENERATE_BUGLIST = YES
# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
# disable (NO) the deprecated list. This list is created by putting
# \deprecated commands in the documentation.
GENERATE_DEPRECATEDLIST= YES
# The ENABLED_SECTIONS tag can be used to enable conditional
# documentation sections, marked by \if sectionname ... \endif.
ENABLED_SECTIONS =
# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
# the initial value of a variable or define consists of for it to appear in
# the documentation. If the initializer consists of more lines than specified
# here it will be hidden. Use a value of 0 to hide initializers completely.
# The appearance of the initializer of individual variables and defines in the
# documentation can be controlled using \showinitializer or \hideinitializer
# command in the documentation regardless of this setting.
MAX_INITIALIZER_LINES = 30
# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
# at the bottom of the documentation of classes and structs. If set to YES the
# list will mention the files that were used to generate the documentation.
SHOW_USED_FILES = YES
#---------------------------------------------------------------------------
# configuration options related to warning and progress messages
#---------------------------------------------------------------------------
# The QUIET tag can be used to turn on/off the messages that are generated
# by doxygen. Possible values are YES and NO. If left blank NO is used.
QUIET = NO
# The WARNINGS tag can be used to turn on/off the warning messages that are
# generated by doxygen. Possible values are YES and NO. If left blank
# NO is used.
WARNINGS = YES
# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
# automatically be disabled.
WARN_IF_UNDOCUMENTED = YES
# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
# potential errors in the documentation, such as not documenting some
# parameters in a documented function, or documenting parameters that
# don't exist or using markup commands wrongly.
WARN_IF_DOC_ERROR = YES
# The WARN_FORMAT tag determines the format of the warning messages that
# doxygen can produce. The string should contain the $file, $line, and $text
# tags, which will be replaced by the file and line number from which the
# warning originated and the warning text.
WARN_FORMAT = "$file:$line: $text"
# The WARN_LOGFILE tag can be used to specify a file to which warning
# and error messages should be written. If left blank the output is written
# to stderr.
WARN_LOGFILE =
#---------------------------------------------------------------------------
# configuration options related to the input files
#---------------------------------------------------------------------------
# The INPUT tag can be used to specify the files and/or directories that contain
# documented source files. You may enter file names like "myfile.cpp" or
# directories like "/usr/src/myproject". Separate the files or directories
# with spaces.
INPUT = @top_srcdir@/sources
# If the value of the INPUT tag contains directories, you can use the
# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
# and *.h) to filter out the source-files in the directories. If left
# blank the following patterns are tested:
# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp
# *.h++ *.idl *.odl *.cs *.php *.php3 *.inc
FILE_PATTERNS = *.c *.cc *.h
# The RECURSIVE tag can be used to turn specify whether or not subdirectories
# should be searched for input files as well. Possible values are YES and NO.
# If left blank NO is used.
RECURSIVE = NO
# The EXCLUDE tag can be used to specify files and/or directories that should
# excluded from the INPUT source files. This way you can easily exclude a
# subdirectory from a directory tree whose root is specified with the INPUT tag.
EXCLUDE =
# The EXCLUDE_SYMLINKS tag can be used select whether or not files or directories
# that are symbolic links (a Unix filesystem feature) are excluded from the input.
EXCLUDE_SYMLINKS = NO
# If the value of the INPUT tag contains directories, you can use the
# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
# certain files from those directories.
EXCLUDE_PATTERNS =
# The EXAMPLE_PATH tag can be used to specify one or more files or
# directories that contain example code fragments that are included (see
# the \include command).
EXAMPLE_PATH =
# If the value of the EXAMPLE_PATH tag contains directories, you can use the
# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
# and *.h) to filter out the source-files in the directories. If left
# blank all files are included.
EXAMPLE_PATTERNS =
# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
# searched for input files to be used with the \include or \dontinclude
# commands irrespective of the value of the RECURSIVE tag.
# Possible values are YES and NO. If left blank NO is used.
EXAMPLE_RECURSIVE = NO
# The IMAGE_PATH tag can be used to specify one or more files or
# directories that contain image that are included in the documentation (see
# the \image command).
IMAGE_PATH =
# The INPUT_FILTER tag can be used to specify a program that doxygen should
# invoke to filter for each input file. Doxygen will invoke the filter program
# by executing (via popen()) the command , where
# is the value of the INPUT_FILTER tag, and is the name of an
# input file. Doxygen will then use the output that the filter program writes
# to standard output.
INPUT_FILTER =
# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
# INPUT_FILTER) will be used to filter the input files when producing source
# files to browse (i.e. when SOURCE_BROWSER is set to YES).
FILTER_SOURCE_FILES = NO
#---------------------------------------------------------------------------
# configuration options related to source browsing
#---------------------------------------------------------------------------
# If the SOURCE_BROWSER tag is set to YES then a list of source files will
# be generated. Documented entities will be cross-referenced with these sources.
# Note: To get rid of all source code in the generated output, make sure also
# VERBATIM_HEADERS is set to NO.
SOURCE_BROWSER = YES
# Setting the INLINE_SOURCES tag to YES will include the body
# of functions and classes directly in the documentation.
INLINE_SOURCES = NO
# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
# doxygen to hide any special comment blocks from generated source code
# fragments. Normal C and C++ comments will always remain visible.
STRIP_CODE_COMMENTS = YES
# If the REFERENCED_BY_RELATION tag is set to YES (the default)
# then for each documented function all documented
# functions referencing it will be listed.
REFERENCED_BY_RELATION = YES
# If the REFERENCES_RELATION tag is set to YES (the default)
# then for each documented function all documented entities
# called/used by that function will be listed.
REFERENCES_RELATION = YES
# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
# will generate a verbatim copy of the header file for each class for
# which an include is specified. Set to NO to disable this.
VERBATIM_HEADERS = YES
#---------------------------------------------------------------------------
# configuration options related to the alphabetical class index
#---------------------------------------------------------------------------
# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
# of all compounds will be generated. Enable this if the project
# contains a lot of classes, structs, unions or interfaces.
ALPHABETICAL_INDEX = YES
# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
# in which this list will be split (can be a number in the range [1..20])
COLS_IN_ALPHA_INDEX = 5
# In case all classes in a project start with a common prefix, all
# classes will be put under the same header in the alphabetical index.
# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
# should be ignored while generating the index headers.
IGNORE_PREFIX =
#---------------------------------------------------------------------------
# configuration options related to the HTML output
#---------------------------------------------------------------------------
# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
# generate HTML output.
GENERATE_HTML = NO
# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
# If a relative path is entered the value of OUTPUT_DIRECTORY will be
# put in front of it. If left blank `html' will be used as the default path.
HTML_OUTPUT = html
# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
# doxygen will generate files with .html extension.
HTML_FILE_EXTENSION = .html
# The HTML_HEADER tag can be used to specify a personal HTML header for
# each generated HTML page. If it is left blank doxygen will generate a
# standard header.
HTML_HEADER =
# The HTML_FOOTER tag can be used to specify a personal HTML footer for
# each generated HTML page. If it is left blank doxygen will generate a
# standard footer.
HTML_FOOTER =
# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
# style sheet that is used by each HTML page. It can be used to
# fine-tune the look of the HTML output. If the tag is left blank doxygen
# will generate a default style sheet. Note that doxygen will try to copy
# the style sheet file to the HTML output directory, so don't put your own
# stylesheet in the HTML output directory as well, or it will be erased!
HTML_STYLESHEET =
# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
# files or namespaces will be aligned in HTML using tables. If set to
# NO a bullet list will be used.
HTML_ALIGN_MEMBERS = YES
# If the GENERATE_HTMLHELP tag is set to YES, additional index files
# will be generated that can be used as input for tools like the
# Microsoft HTML help workshop to generate a compressed HTML help file (.chm)
# of the generated HTML documentation.
GENERATE_HTMLHELP = NO
# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
# be used to specify the file name of the resulting .chm file. You
# can add a path in front of the file if the result should not be
# written to the html output directory.
CHM_FILE =
# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
# be used to specify the location (absolute path including file name) of
# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
# the HTML help compiler on the generated index.hhp.
HHC_LOCATION =
# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
# controls if a separate .chi index file is generated (YES) or that
# it should be included in the master .chm file (NO).
GENERATE_CHI = NO
# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
# controls whether a binary table of contents is generated (YES) or a
# normal table of contents (NO) in the .chm file.
BINARY_TOC = NO
# The TOC_EXPAND flag can be set to YES to add extra items for group members
# to the contents of the HTML help documentation and to the tree view.
TOC_EXPAND = NO
# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
# top of each HTML page. The value NO (the default) enables the index and
# the value YES disables it.
DISABLE_INDEX = NO
# This tag can be used to set the number of enum values (range [1..20])
# that doxygen will group on one line in the generated HTML documentation.
ENUM_VALUES_PER_LINE = 4
# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be
# generated containing a tree-like index structure (just like the one that
# is generated for HTML Help). For this to work a browser that supports
# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+,
# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are
# probably better off using the HTML help feature.
GENERATE_TREEVIEW = NO
# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
# used to set the initial width (in pixels) of the frame in which the tree
# is shown.
TREEVIEW_WIDTH = 250
#---------------------------------------------------------------------------
# configuration options related to the LaTeX output
#---------------------------------------------------------------------------
# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
# generate Latex output.
GENERATE_LATEX = YES
# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
# If a relative path is entered the value of OUTPUT_DIRECTORY will be
# put in front of it. If left blank `latex' will be used as the default path.
LATEX_OUTPUT = latex
# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
# invoked. If left blank `latex' will be used as the default command name.
LATEX_CMD_NAME = latex
# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
# generate index for LaTeX. If left blank `makeindex' will be used as the
# default command name.
MAKEINDEX_CMD_NAME = makeindex
# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
# LaTeX documents. This may be useful for small projects and may help to
# save some trees in general.
COMPACT_LATEX = NO
# The PAPER_TYPE tag can be used to set the paper type that is used
# by the printer. Possible values are: a4, a4wide, letter, legal and
# executive. If left blank a4wide will be used.
PAPER_TYPE = a4wide
# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
# packages that should be included in the LaTeX output.
EXTRA_PACKAGES =
# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
# the generated latex document. The header should contain everything until
# the first chapter. If it is left blank doxygen will generate a
# standard header. Notice: only use this tag if you know what you are doing!
LATEX_HEADER =
# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
# is prepared for conversion to pdf (using ps2pdf). The pdf file will
# contain links (just like the HTML output) instead of page references
# This makes the output suitable for online browsing using a pdf viewer.
PDF_HYPERLINKS = NO
# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
# plain latex in the generated Makefile. Set this option to YES to get a
# higher quality PDF documentation.
USE_PDFLATEX = NO
# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
# command to the generated LaTeX files. This will instruct LaTeX to keep
# running if errors occur, instead of asking the user for help.
# This option is also used when generating formulas in HTML.
LATEX_BATCHMODE = NO
# If LATEX_HIDE_INDICES is set to YES then doxygen will not
# include the index chapters (such as File Index, Compound Index, etc.)
# in the output.
LATEX_HIDE_INDICES = NO
#---------------------------------------------------------------------------
# configuration options related to the RTF output
#---------------------------------------------------------------------------
# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
# The RTF output is optimized for Word 97 and may not look very pretty with
# other RTF readers or editors.
GENERATE_RTF = NO
# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
# If a relative path is entered the value of OUTPUT_DIRECTORY will be
# put in front of it. If left blank `rtf' will be used as the default path.
RTF_OUTPUT = rtf
# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
# RTF documents. This may be useful for small projects and may help to
# save some trees in general.
COMPACT_RTF = NO
# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
# will contain hyperlink fields. The RTF file will
# contain links (just like the HTML output) instead of page references.
# This makes the output suitable for online browsing using WORD or other
# programs which support those fields.
# Note: wordpad (write) and others do not support links.
RTF_HYPERLINKS = NO
# Load stylesheet definitions from file. Syntax is similar to doxygen's
# config file, i.e. a series of assignments. You only have to provide
# replacements, missing definitions are set to their default value.
RTF_STYLESHEET_FILE =
# Set optional variables used in the generation of an rtf document.
# Syntax is similar to doxygen's config file.
RTF_EXTENSIONS_FILE =
#---------------------------------------------------------------------------
# configuration options related to the man page output
#---------------------------------------------------------------------------
# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
# generate man pages
GENERATE_MAN = NO
# The MAN_OUTPUT tag is used to specify where the man pages will be put.
# If a relative path is entered the value of OUTPUT_DIRECTORY will be
# put in front of it. If left blank `man' will be used as the default path.
MAN_OUTPUT = man
# The MAN_EXTENSION tag determines the extension that is added to
# the generated man pages (default is the subroutine's section .3)
MAN_EXTENSION = .3
# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
# then it will generate one additional man file for each entity
# documented in the real man page(s). These additional files
# only source the real man page, but without them the man command
# would be unable to find the correct page. The default is NO.
MAN_LINKS = NO
#---------------------------------------------------------------------------
# configuration options related to the XML output
#---------------------------------------------------------------------------
# If the GENERATE_XML tag is set to YES Doxygen will
# generate an XML file that captures the structure of
# the code including all documentation.
GENERATE_XML = NO
# The XML_OUTPUT tag is used to specify where the XML pages will be put.
# If a relative path is entered the value of OUTPUT_DIRECTORY will be
# put in front of it. If left blank `xml' will be used as the default path.
XML_OUTPUT = xml
# The XML_SCHEMA tag can be used to specify an XML schema,
# which can be used by a validating XML parser to check the
# syntax of the XML files.
XML_SCHEMA =
# The XML_DTD tag can be used to specify an XML DTD,
# which can be used by a validating XML parser to check the
# syntax of the XML files.
XML_DTD =
# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
# dump the program listings (including syntax highlighting
# and cross-referencing information) to the XML output. Note that
# enabling this will significantly increase the size of the XML output.
XML_PROGRAMLISTING = YES
#---------------------------------------------------------------------------
# configuration options for the AutoGen Definitions output
#---------------------------------------------------------------------------
# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
# generate an AutoGen Definitions (see autogen.sf.net) file
# that captures the structure of the code including all
# documentation. Note that this feature is still experimental
# and incomplete at the moment.
GENERATE_AUTOGEN_DEF = NO
#---------------------------------------------------------------------------
# configuration options related to the Perl module output
#---------------------------------------------------------------------------
# If the GENERATE_PERLMOD tag is set to YES Doxygen will
# generate a Perl module file that captures the structure of
# the code including all documentation. Note that this
# feature is still experimental and incomplete at the
# moment.
GENERATE_PERLMOD = NO
# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
# the necessary Makefile rules, Perl scripts and LaTeX code to be able
# to generate PDF and DVI output from the Perl module output.
PERLMOD_LATEX = NO
# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
# nicely formatted so it can be parsed by a human reader. This is useful
# if you want to understand what is going on. On the other hand, if this
# tag is set to NO the size of the Perl module output will be much smaller
# and Perl will parse it just the same.
PERLMOD_PRETTY = YES
# The names of the make variables in the generated doxyrules.make file
# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
# This is useful so different doxyrules.make files included by the same
# Makefile don't overwrite each other's variables.
PERLMOD_MAKEVAR_PREFIX =
#---------------------------------------------------------------------------
# Configuration options related to the preprocessor
#---------------------------------------------------------------------------
# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
# evaluate all C-preprocessor directives found in the sources and include
# files.
ENABLE_PREPROCESSING = YES
# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
# names in the source code. If set to NO (the default) only conditional
# compilation will be performed. Macro expansion can be done in a controlled
# way by setting EXPAND_ONLY_PREDEF to YES.
MACRO_EXPANSION = YES
# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
# then the macro expansion is limited to the macros specified with the
# PREDEFINED and EXPAND_AS_PREDEFINED tags.
EXPAND_ONLY_PREDEF = YES
# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
# in the INCLUDE_PATH (see below) will be search if a #include is found.
SEARCH_INCLUDES = YES
# The INCLUDE_PATH tag can be used to specify one or more directories that
# contain include files that are not input files but should be processed by
# the preprocessor.
INCLUDE_PATH =
# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
# patterns (like *.h and *.hpp) to filter out the header-files in the
# directories. If left blank, the patterns specified with FILE_PATTERNS will
# be used.
INCLUDE_FILE_PATTERNS = *.h
# The PREDEFINED tag can be used to specify one or more macro names that
# are defined before the preprocessor is started (similar to the -D option of
# gcc). The argument of the tag is a list of macros of the form: name
# or name=definition (no spaces). If the definition and the = are
# omitted =1 is assumed.
PREDEFINED = \
"PADPOINTER(a1,a2,a3,a4)=" \
"PADLONG(a1,a2,a3)=" \
"PADINT(a1,a2)=" \
"PADWORD(a1)="
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
# this tag can be used to specify a list of macro names that should be expanded.
# The macro definition that is found in the sources will be used.
# Use the PREDEFINED tag if you want to use a different macro definition.
EXPAND_AS_DEFINED =
# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
# doxygen's preprocessor will remove all function-like macros that are alone
# on a line, have an all uppercase name, and do not end with a semicolon. Such
# function macros are typically used for boiler-plate code, and will confuse the
# parser if not removed.
SKIP_FUNCTION_MACROS = YES
#---------------------------------------------------------------------------
# Configuration::addtions related to external references
#---------------------------------------------------------------------------
# The TAGFILES option can be used to specify one or more tagfiles.
# Optionally an initial location of the external documentation
# can be added for each tagfile. The format of a tag file without
# this location is as follows:
# TAGFILES = file1 file2 ...
# Adding location for the tag files is done as follows:
# TAGFILES = file1=loc1 "file2 = loc2" ...
# where "loc1" and "loc2" can be relative or absolute paths or
# URLs. If a location is present for each tag, the installdox tool
# does not have to be run to correct the links.
# Note that each tag file must have a unique name
# (where the name does NOT include the path)
# If a tag file is not located in the directory in which doxygen
# is run, you must also specify the path to the tagfile here.
TAGFILES =
# When a file name is specified after GENERATE_TAGFILE, doxygen will create
# a tag file that is based on the input files it reads.
GENERATE_TAGFILE =
# If the ALLEXTERNALS tag is set to YES all external classes will be listed
# in the class index. If set to NO only the inherited external classes
# will be listed.
ALLEXTERNALS = NO
# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
# in the modules index. If set to NO, only the current project's groups will
# be listed.
EXTERNAL_GROUPS = YES
# The PERL_PATH should be the absolute path and name of the perl script
# interpreter (i.e. the result of `which perl').
PERL_PATH = /usr/bin/perl
#---------------------------------------------------------------------------
# Configuration options related to the dot tool
#---------------------------------------------------------------------------
# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base or
# super classes. Setting the tag to NO turns the diagrams off. Note that this
# option is superseded by the HAVE_DOT option below. This is only a fallback. It is
# recommended to install and use dot, since it yields more powerful graphs.
CLASS_DIAGRAMS = YES
# If set to YES, the inheritance and collaboration graphs will hide
# inheritance and usage relations if the target is undocumented
# or is not a class.
HIDE_UNDOC_RELATIONS = YES
# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
# available from the path. This tool is part of Graphviz, a graph visualization
# toolkit from AT&T and Lucent Bell Labs. The other options in this section
# have no effect if this option is set to NO (the default)
HAVE_DOT = NO
# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
# will generate a graph for each documented class showing the direct and
# indirect inheritance relations. Setting this tag to YES will force the
# the CLASS_DIAGRAMS tag to NO.
CLASS_GRAPH = YES
# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
# will generate a graph for each documented class showing the direct and
# indirect implementation dependencies (inheritance, containment, and
# class references variables) of the class with other documented classes.
COLLABORATION_GRAPH = YES
# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
# collaboration diagrams in a style similar to the OMG's Unified Modeling
# Language.
UML_LOOK = NO
# If set to YES, the inheritance and collaboration graphs will show the
# relations between templates and their instances.
TEMPLATE_RELATIONS = NO
# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
# tags are set to YES then doxygen will generate a graph for each documented
# file showing the direct and indirect include dependencies of the file with
# other documented files.
INCLUDE_GRAPH = YES
# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
# documented header file showing the documented files that directly or
# indirectly include this file.
INCLUDED_BY_GRAPH = YES
# If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will
# generate a call dependency graph for every global function or class method.
# Note that enabling this option will significantly increase the time of a run.
# So in most cases it will be better to enable call graphs for selected
# functions only using the \callgraph command.
CALL_GRAPH = NO
# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
# will graphical hierarchy of all classes instead of a textual one.
GRAPHICAL_HIERARCHY = YES
# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
# generated by dot. Possible values are png, jpg, or gif
# If left blank png will be used.
DOT_IMAGE_FORMAT = png
# The tag DOT_PATH can be used to specify the path where the dot tool can be
# found. If left blank, it is assumed the dot tool can be found on the path.
DOT_PATH =
# The DOTFILE_DIRS tag can be used to specify one or more directories that
# contain dot files that are included in the documentation (see the
# \dotfile command).
DOTFILE_DIRS =
# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width
# (in pixels) of the graphs generated by dot. If a graph becomes larger than
# this value, doxygen will try to truncate the graph, so that it fits within
# the specified constraint. Beware that most browsers cannot cope with very
# large images.
MAX_DOT_GRAPH_WIDTH = 1024
# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height
# (in pixels) of the graphs generated by dot. If a graph becomes larger than
# this value, doxygen will try to truncate the graph, so that it fits within
# the specified constraint. Beware that most browsers cannot cope with very
# large images.
MAX_DOT_GRAPH_HEIGHT = 1024
# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
# graphs generated by dot. A depth value of 3 means that only nodes reachable
# from the root by following a path via at most 3 edges will be shown. Nodes that
# lay further from the root node will be omitted. Note that setting this option to
# 1 or 2 may greatly reduce the computation time needed for large code bases. Also
# note that a graph may be further truncated if the graph's image dimensions are
# not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH and MAX_DOT_GRAPH_HEIGHT).
# If 0 is used for the depth value (the default), the graph is not depth-constrained.
MAX_DOT_GRAPH_DEPTH = 0
# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
# generate a legend page explaining the meaning of the various boxes and
# arrows in the dot generated graphs.
GENERATE_LEGEND = YES
# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
# remove the intermediate dot files that are used to generate
# the various graphs.
DOT_CLEANUP = YES
#---------------------------------------------------------------------------
# Configuration::addtions related to the search engine
#---------------------------------------------------------------------------
# The SEARCHENGINE tag specifies whether or not a search engine should be
# used. If set to NO the values of all tags below this one will be ignored.
SEARCHENGINE = NO
form-master/doc/doxygen/DoxyfilePDFLATEX.in 0000664 0000000 0000000 00000127513 13565763364 0021032 0 ustar 00root root 0000000 0000000 # Doxyfile 1.3.5
# This file describes the settings to be used by the documentation system
# doxygen (www.doxygen.org) for a project
#
# All text after a hash (#) is considered a comment and will be ignored
# The format is:
# TAG = value [value, ...]
# For lists items can also be appended using:
# TAG += value [value, ...]
# Values that contain spaces should be placed between quotes (" ")
#---------------------------------------------------------------------------
# Project related configuration options
#---------------------------------------------------------------------------
# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
# by quotes) that should identify the project.
PROJECT_NAME = FORM
# The PROJECT_NUMBER tag can be used to enter a project or revision number.
# This could be handy for archiving the generated documentation or
# if some version control system is used.
PROJECT_NUMBER = @VERSION@
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
# base path where the generated documentation will be put.
# If a relative path is entered, it will be relative to the location
# where doxygen was started. If left blank the current directory will be used.
OUTPUT_DIRECTORY =
# The OUTPUT_LANGUAGE tag is used to specify the language in which all
# documentation generated by doxygen is written. Doxygen will use this
# information to generate all constant output in the proper language.
# The default language is English, other supported languages are:
# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch,
# Finnish, French, German, Greek, Hungarian, Italian, Japanese, Japanese-en
# (Japanese with English messages), Korean, Norwegian, Polish, Portuguese,
# Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish, and Ukrainian.
OUTPUT_LANGUAGE = English
# This tag can be used to specify the encoding used in the generated output.
# The encoding is not always determined by the language that is chosen,
# but also whether or not the output is meant for Windows or non-Windows users.
# In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES
# forces the Windows encoding (this is the default for the Windows binary),
# whereas setting the tag to NO uses a Unix-style encoding (the default for
# all platforms other than Windows).
USE_WINDOWS_ENCODING = NO
# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
# include brief member descriptions after the members that are listed in
# the file and class documentation (similar to JavaDoc).
# Set to NO to disable this.
BRIEF_MEMBER_DESC = YES
# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
# the brief description of a member or function before the detailed description.
# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
# brief descriptions will be completely suppressed.
REPEAT_BRIEF = YES
# This tag implements a quasi-intelligent brief description abbreviator
# that is used to form the text in various listings. Each string
# in this list, if found as the leading text of the brief description, will be
# stripped from the text and the result after processing the whole list, is used
# as the annotated text. Otherwise, the brief description is used as-is. If left
# blank, the following values are used ("$name" is automatically replaced with the
# name of the entity): "The $name class" "The $name widget" "The $name file"
# "is" "provides" "specifies" "contains" "represents" "a" "an" "the"
ABBREVIATE_BRIEF =
# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
# Doxygen will generate a detailed section even if there is only a brief
# description.
ALWAYS_DETAILED_SEC = NO
# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all inherited
# members of a class in the documentation of that class as if those members were
# ordinary class members. Constructors, destructors and assignment operators of
# the base classes will not be shown.
INLINE_INHERITED_MEMB = NO
# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
# path before files name in the file list and in the header files. If set
# to NO the shortest path that makes the file name unique will be used.
FULL_PATH_NAMES = NO
# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
# can be used to strip a user-defined part of the path. Stripping is
# only done if one of the specified strings matches the left-hand part of
# the path. It is allowed to use relative paths in the argument list.
STRIP_FROM_PATH =
# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
# (but less readable) file names. This can be useful is your file systems
# doesn't support long names like on DOS, Mac, or CD-ROM.
SHORT_NAMES = NO
# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
# will interpret the first line (until the first dot) of a JavaDoc-style
# comment as the brief description. If set to NO, the JavaDoc
# comments will behave just like the Qt-style comments (thus requiring an
# explicit @brief command for a brief description.
JAVADOC_AUTOBRIEF = NO
# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
# treat a multi-line C++ special comment block (i.e. a block of //! or ///
# comments) as a brief description. This used to be the default behaviour.
# The new default is to treat a multi-line C++ comment block as a detailed
# description. Set this tag to YES if you prefer the old behaviour instead.
MULTILINE_CPP_IS_BRIEF = NO
# If the DETAILS_AT_TOP tag is set to YES then Doxygen
# will output the detailed description near the top, like JavaDoc.
# If set to NO, the detailed description appears after the member
# documentation.
DETAILS_AT_TOP = NO
# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
# member inherits the documentation from any documented member that it
# re-implements.
INHERIT_DOCS = YES
# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
# tag is set to YES, then doxygen will reuse the documentation of the first
# member in the group (if any) for the other members of the group. By default
# all members of a group must be documented explicitly.
DISTRIBUTE_GROUP_DOC = YES
# The TAB_SIZE tag can be used to set the number of spaces in a tab.
# Doxygen uses this value to replace tabs by spaces in code fragments.
TAB_SIZE = 4
# This tag can be used to specify a number of aliases that acts
# as commands in the documentation. An alias has the form "name=value".
# For example adding "sideeffect=\par Side Effects:\n" will allow you to
# put the command \sideeffect (or @sideeffect) in the documentation, which
# will result in a user-defined paragraph with heading "Side Effects:".
# You can put \n's in the value part of an alias to insert newlines.
ALIASES =
# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
# only. Doxygen will then generate output that is more tailored for C.
# For instance, some of the names that are used will be different. The list
# of all members will be omitted, etc.
OPTIMIZE_OUTPUT_FOR_C = YES
# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java sources
# only. Doxygen will then generate output that is more tailored for Java.
# For instance, namespaces will be presented as packages, qualified scopes
# will look different, etc.
OPTIMIZE_OUTPUT_JAVA = NO
# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
# the same type (for instance a group of public functions) to be put as a
# subgroup of that type (e.g. under the Public Functions section). Set it to
# NO to prevent subgrouping. Alternatively, this can be done per class using
# the \nosubgrouping command.
SUBGROUPING = YES
#---------------------------------------------------------------------------
# Build related configuration options
#---------------------------------------------------------------------------
# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
# documentation are documented, even if no documentation was available.
# Private class members and static file members will be hidden unless
# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
EXTRACT_ALL = NO
# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
# will be included in the documentation.
EXTRACT_PRIVATE = NO
# If the EXTRACT_STATIC tag is set to YES all static members of a file
# will be included in the documentation.
EXTRACT_STATIC = NO
# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
# defined locally in source files will be included in the documentation.
# If set to NO only classes defined in header files are included.
EXTRACT_LOCAL_CLASSES = YES
# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
# undocumented members of documented classes, files or namespaces.
# If set to NO (the default) these members will be included in the
# various overviews, but no documentation section is generated.
# This option has no effect if EXTRACT_ALL is enabled.
HIDE_UNDOC_MEMBERS = NO
# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
# undocumented classes that are normally visible in the class hierarchy.
# If set to NO (the default) these classes will be included in the various
# overviews. This option has no effect if EXTRACT_ALL is enabled.
HIDE_UNDOC_CLASSES = NO
# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
# friend (class|struct|union) declarations.
# If set to NO (the default) these declarations will be included in the
# documentation.
HIDE_FRIEND_COMPOUNDS = NO
# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
# documentation blocks found inside the body of a function.
# If set to NO (the default) these blocks will be appended to the
# function's detailed documentation block.
HIDE_IN_BODY_DOCS = NO
# The INTERNAL_DOCS tag determines if documentation
# that is typed after a \internal command is included. If the tag is set
# to NO (the default) then the documentation will be excluded.
# Set it to YES to include the internal documentation.
INTERNAL_DOCS = NO
# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
# file names in lower-case letters. If set to YES upper-case letters are also
# allowed. This is useful if you have classes or files whose names only differ
# in case and if your file system supports case sensitive file names. Windows
# users are advised to set this option to NO.
CASE_SENSE_NAMES = YES
# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
# will show members with their full class and namespace scopes in the
# documentation. If set to YES the scope will be hidden.
HIDE_SCOPE_NAMES = NO
# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
# will put a list of the files that are included by a file in the documentation
# of that file.
SHOW_INCLUDE_FILES = YES
# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
# is inserted in the documentation for inline members.
INLINE_INFO = YES
# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
# will sort the (detailed) documentation of file and class members
# alphabetically by member name. If set to NO the members will appear in
# declaration order.
SORT_MEMBER_DOCS = NO
# The GENERATE_TODOLIST tag can be used to enable (YES) or
# disable (NO) the todo list. This list is created by putting \todo
# commands in the documentation.
GENERATE_TODOLIST = YES
# The GENERATE_TESTLIST tag can be used to enable (YES) or
# disable (NO) the test list. This list is created by putting \test
# commands in the documentation.
GENERATE_TESTLIST = YES
# The GENERATE_BUGLIST tag can be used to enable (YES) or
# disable (NO) the bug list. This list is created by putting \bug
# commands in the documentation.
GENERATE_BUGLIST = YES
# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
# disable (NO) the deprecated list. This list is created by putting
# \deprecated commands in the documentation.
GENERATE_DEPRECATEDLIST= YES
# The ENABLED_SECTIONS tag can be used to enable conditional
# documentation sections, marked by \if sectionname ... \endif.
ENABLED_SECTIONS =
# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
# the initial value of a variable or define consists of for it to appear in
# the documentation. If the initializer consists of more lines than specified
# here it will be hidden. Use a value of 0 to hide initializers completely.
# The appearance of the initializer of individual variables and defines in the
# documentation can be controlled using \showinitializer or \hideinitializer
# command in the documentation regardless of this setting.
MAX_INITIALIZER_LINES = 30
# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
# at the bottom of the documentation of classes and structs. If set to YES the
# list will mention the files that were used to generate the documentation.
SHOW_USED_FILES = YES
#---------------------------------------------------------------------------
# configuration options related to warning and progress messages
#---------------------------------------------------------------------------
# The QUIET tag can be used to turn on/off the messages that are generated
# by doxygen. Possible values are YES and NO. If left blank NO is used.
QUIET = NO
# The WARNINGS tag can be used to turn on/off the warning messages that are
# generated by doxygen. Possible values are YES and NO. If left blank
# NO is used.
WARNINGS = YES
# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
# automatically be disabled.
WARN_IF_UNDOCUMENTED = YES
# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
# potential errors in the documentation, such as not documenting some
# parameters in a documented function, or documenting parameters that
# don't exist or using markup commands wrongly.
WARN_IF_DOC_ERROR = YES
# The WARN_FORMAT tag determines the format of the warning messages that
# doxygen can produce. The string should contain the $file, $line, and $text
# tags, which will be replaced by the file and line number from which the
# warning originated and the warning text.
WARN_FORMAT = "$file:$line: $text"
# The WARN_LOGFILE tag can be used to specify a file to which warning
# and error messages should be written. If left blank the output is written
# to stderr.
WARN_LOGFILE =
#---------------------------------------------------------------------------
# configuration options related to the input files
#---------------------------------------------------------------------------
# The INPUT tag can be used to specify the files and/or directories that contain
# documented source files. You may enter file names like "myfile.cpp" or
# directories like "/usr/src/myproject". Separate the files or directories
# with spaces.
INPUT = @top_srcdir@/sources
# If the value of the INPUT tag contains directories, you can use the
# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
# and *.h) to filter out the source-files in the directories. If left
# blank the following patterns are tested:
# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp
# *.h++ *.idl *.odl *.cs *.php *.php3 *.inc
FILE_PATTERNS = *.c *.cc *.h
# The RECURSIVE tag can be used to turn specify whether or not subdirectories
# should be searched for input files as well. Possible values are YES and NO.
# If left blank NO is used.
RECURSIVE = NO
# The EXCLUDE tag can be used to specify files and/or directories that should
# excluded from the INPUT source files. This way you can easily exclude a
# subdirectory from a directory tree whose root is specified with the INPUT tag.
EXCLUDE =
# The EXCLUDE_SYMLINKS tag can be used select whether or not files or directories
# that are symbolic links (a Unix filesystem feature) are excluded from the input.
EXCLUDE_SYMLINKS = NO
# If the value of the INPUT tag contains directories, you can use the
# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
# certain files from those directories.
EXCLUDE_PATTERNS =
# The EXAMPLE_PATH tag can be used to specify one or more files or
# directories that contain example code fragments that are included (see
# the \include command).
EXAMPLE_PATH =
# If the value of the EXAMPLE_PATH tag contains directories, you can use the
# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
# and *.h) to filter out the source-files in the directories. If left
# blank all files are included.
EXAMPLE_PATTERNS =
# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
# searched for input files to be used with the \include or \dontinclude
# commands irrespective of the value of the RECURSIVE tag.
# Possible values are YES and NO. If left blank NO is used.
EXAMPLE_RECURSIVE = NO
# The IMAGE_PATH tag can be used to specify one or more files or
# directories that contain image that are included in the documentation (see
# the \image command).
IMAGE_PATH =
# The INPUT_FILTER tag can be used to specify a program that doxygen should
# invoke to filter for each input file. Doxygen will invoke the filter program
# by executing (via popen()) the command , where
# is the value of the INPUT_FILTER tag, and is the name of an
# input file. Doxygen will then use the output that the filter program writes
# to standard output.
INPUT_FILTER =
# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
# INPUT_FILTER) will be used to filter the input files when producing source
# files to browse (i.e. when SOURCE_BROWSER is set to YES).
FILTER_SOURCE_FILES = NO
#---------------------------------------------------------------------------
# configuration options related to source browsing
#---------------------------------------------------------------------------
# If the SOURCE_BROWSER tag is set to YES then a list of source files will
# be generated. Documented entities will be cross-referenced with these sources.
# Note: To get rid of all source code in the generated output, make sure also
# VERBATIM_HEADERS is set to NO.
SOURCE_BROWSER = YES
# Setting the INLINE_SOURCES tag to YES will include the body
# of functions and classes directly in the documentation.
INLINE_SOURCES = NO
# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
# doxygen to hide any special comment blocks from generated source code
# fragments. Normal C and C++ comments will always remain visible.
STRIP_CODE_COMMENTS = YES
# If the REFERENCED_BY_RELATION tag is set to YES (the default)
# then for each documented function all documented
# functions referencing it will be listed.
REFERENCED_BY_RELATION = YES
# If the REFERENCES_RELATION tag is set to YES (the default)
# then for each documented function all documented entities
# called/used by that function will be listed.
REFERENCES_RELATION = YES
# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
# will generate a verbatim copy of the header file for each class for
# which an include is specified. Set to NO to disable this.
VERBATIM_HEADERS = YES
#---------------------------------------------------------------------------
# configuration options related to the alphabetical class index
#---------------------------------------------------------------------------
# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
# of all compounds will be generated. Enable this if the project
# contains a lot of classes, structs, unions or interfaces.
ALPHABETICAL_INDEX = YES
# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
# in which this list will be split (can be a number in the range [1..20])
COLS_IN_ALPHA_INDEX = 5
# In case all classes in a project start with a common prefix, all
# classes will be put under the same header in the alphabetical index.
# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
# should be ignored while generating the index headers.
IGNORE_PREFIX =
#---------------------------------------------------------------------------
# configuration options related to the HTML output
#---------------------------------------------------------------------------
# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
# generate HTML output.
GENERATE_HTML = NO
# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
# If a relative path is entered the value of OUTPUT_DIRECTORY will be
# put in front of it. If left blank `html' will be used as the default path.
HTML_OUTPUT = html
# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
# doxygen will generate files with .html extension.
HTML_FILE_EXTENSION = .html
# The HTML_HEADER tag can be used to specify a personal HTML header for
# each generated HTML page. If it is left blank doxygen will generate a
# standard header.
HTML_HEADER =
# The HTML_FOOTER tag can be used to specify a personal HTML footer for
# each generated HTML page. If it is left blank doxygen will generate a
# standard footer.
HTML_FOOTER =
# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
# style sheet that is used by each HTML page. It can be used to
# fine-tune the look of the HTML output. If the tag is left blank doxygen
# will generate a default style sheet. Note that doxygen will try to copy
# the style sheet file to the HTML output directory, so don't put your own
# stylesheet in the HTML output directory as well, or it will be erased!
HTML_STYLESHEET =
# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
# files or namespaces will be aligned in HTML using tables. If set to
# NO a bullet list will be used.
HTML_ALIGN_MEMBERS = YES
# If the GENERATE_HTMLHELP tag is set to YES, additional index files
# will be generated that can be used as input for tools like the
# Microsoft HTML help workshop to generate a compressed HTML help file (.chm)
# of the generated HTML documentation.
GENERATE_HTMLHELP = NO
# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
# be used to specify the file name of the resulting .chm file. You
# can add a path in front of the file if the result should not be
# written to the html output directory.
CHM_FILE =
# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
# be used to specify the location (absolute path including file name) of
# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
# the HTML help compiler on the generated index.hhp.
HHC_LOCATION =
# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
# controls if a separate .chi index file is generated (YES) or that
# it should be included in the master .chm file (NO).
GENERATE_CHI = NO
# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
# controls whether a binary table of contents is generated (YES) or a
# normal table of contents (NO) in the .chm file.
BINARY_TOC = NO
# The TOC_EXPAND flag can be set to YES to add extra items for group members
# to the contents of the HTML help documentation and to the tree view.
TOC_EXPAND = NO
# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
# top of each HTML page. The value NO (the default) enables the index and
# the value YES disables it.
DISABLE_INDEX = NO
# This tag can be used to set the number of enum values (range [1..20])
# that doxygen will group on one line in the generated HTML documentation.
ENUM_VALUES_PER_LINE = 4
# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be
# generated containing a tree-like index structure (just like the one that
# is generated for HTML Help). For this to work a browser that supports
# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+,
# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are
# probably better off using the HTML help feature.
GENERATE_TREEVIEW = NO
# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
# used to set the initial width (in pixels) of the frame in which the tree
# is shown.
TREEVIEW_WIDTH = 250
#---------------------------------------------------------------------------
# configuration options related to the LaTeX output
#---------------------------------------------------------------------------
# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
# generate Latex output.
GENERATE_LATEX = YES
# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
# If a relative path is entered the value of OUTPUT_DIRECTORY will be
# put in front of it. If left blank `latex' will be used as the default path.
LATEX_OUTPUT = pdflatex
# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
# invoked. If left blank `latex' will be used as the default command name.
LATEX_CMD_NAME = pdflatex
# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
# generate index for LaTeX. If left blank `makeindex' will be used as the
# default command name.
MAKEINDEX_CMD_NAME = makeindex
# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
# LaTeX documents. This may be useful for small projects and may help to
# save some trees in general.
COMPACT_LATEX = NO
# The PAPER_TYPE tag can be used to set the paper type that is used
# by the printer. Possible values are: a4, a4wide, letter, legal and
# executive. If left blank a4wide will be used.
PAPER_TYPE = a4wide
# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
# packages that should be included in the LaTeX output.
EXTRA_PACKAGES =
# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
# the generated latex document. The header should contain everything until
# the first chapter. If it is left blank doxygen will generate a
# standard header. Notice: only use this tag if you know what you are doing!
LATEX_HEADER =
# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
# is prepared for conversion to pdf (using ps2pdf). The pdf file will
# contain links (just like the HTML output) instead of page references
# This makes the output suitable for online browsing using a pdf viewer.
PDF_HYPERLINKS = YES
# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
# plain latex in the generated Makefile. Set this option to YES to get a
# higher quality PDF documentation.
USE_PDFLATEX = YES
# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
# command to the generated LaTeX files. This will instruct LaTeX to keep
# running if errors occur, instead of asking the user for help.
# This option is also used when generating formulas in HTML.
LATEX_BATCHMODE = NO
# If LATEX_HIDE_INDICES is set to YES then doxygen will not
# include the index chapters (such as File Index, Compound Index, etc.)
# in the output.
LATEX_HIDE_INDICES = NO
#---------------------------------------------------------------------------
# configuration options related to the RTF output
#---------------------------------------------------------------------------
# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
# The RTF output is optimized for Word 97 and may not look very pretty with
# other RTF readers or editors.
GENERATE_RTF = NO
# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
# If a relative path is entered the value of OUTPUT_DIRECTORY will be
# put in front of it. If left blank `rtf' will be used as the default path.
RTF_OUTPUT = rtf
# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
# RTF documents. This may be useful for small projects and may help to
# save some trees in general.
COMPACT_RTF = NO
# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
# will contain hyperlink fields. The RTF file will
# contain links (just like the HTML output) instead of page references.
# This makes the output suitable for online browsing using WORD or other
# programs which support those fields.
# Note: wordpad (write) and others do not support links.
RTF_HYPERLINKS = NO
# Load stylesheet definitions from file. Syntax is similar to doxygen's
# config file, i.e. a series of assignments. You only have to provide
# replacements, missing definitions are set to their default value.
RTF_STYLESHEET_FILE =
# Set optional variables used in the generation of an rtf document.
# Syntax is similar to doxygen's config file.
RTF_EXTENSIONS_FILE =
#---------------------------------------------------------------------------
# configuration options related to the man page output
#---------------------------------------------------------------------------
# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
# generate man pages
GENERATE_MAN = NO
# The MAN_OUTPUT tag is used to specify where the man pages will be put.
# If a relative path is entered the value of OUTPUT_DIRECTORY will be
# put in front of it. If left blank `man' will be used as the default path.
MAN_OUTPUT = man
# The MAN_EXTENSION tag determines the extension that is added to
# the generated man pages (default is the subroutine's section .3)
MAN_EXTENSION = .3
# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
# then it will generate one additional man file for each entity
# documented in the real man page(s). These additional files
# only source the real man page, but without them the man command
# would be unable to find the correct page. The default is NO.
MAN_LINKS = NO
#---------------------------------------------------------------------------
# configuration options related to the XML output
#---------------------------------------------------------------------------
# If the GENERATE_XML tag is set to YES Doxygen will
# generate an XML file that captures the structure of
# the code including all documentation.
GENERATE_XML = NO
# The XML_OUTPUT tag is used to specify where the XML pages will be put.
# If a relative path is entered the value of OUTPUT_DIRECTORY will be
# put in front of it. If left blank `xml' will be used as the default path.
XML_OUTPUT = xml
# The XML_SCHEMA tag can be used to specify an XML schema,
# which can be used by a validating XML parser to check the
# syntax of the XML files.
XML_SCHEMA =
# The XML_DTD tag can be used to specify an XML DTD,
# which can be used by a validating XML parser to check the
# syntax of the XML files.
XML_DTD =
# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
# dump the program listings (including syntax highlighting
# and cross-referencing information) to the XML output. Note that
# enabling this will significantly increase the size of the XML output.
XML_PROGRAMLISTING = YES
#---------------------------------------------------------------------------
# configuration options for the AutoGen Definitions output
#---------------------------------------------------------------------------
# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
# generate an AutoGen Definitions (see autogen.sf.net) file
# that captures the structure of the code including all
# documentation. Note that this feature is still experimental
# and incomplete at the moment.
GENERATE_AUTOGEN_DEF = NO
#---------------------------------------------------------------------------
# configuration options related to the Perl module output
#---------------------------------------------------------------------------
# If the GENERATE_PERLMOD tag is set to YES Doxygen will
# generate a Perl module file that captures the structure of
# the code including all documentation. Note that this
# feature is still experimental and incomplete at the
# moment.
GENERATE_PERLMOD = NO
# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
# the necessary Makefile rules, Perl scripts and LaTeX code to be able
# to generate PDF and DVI output from the Perl module output.
PERLMOD_LATEX = NO
# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
# nicely formatted so it can be parsed by a human reader. This is useful
# if you want to understand what is going on. On the other hand, if this
# tag is set to NO the size of the Perl module output will be much smaller
# and Perl will parse it just the same.
PERLMOD_PRETTY = YES
# The names of the make variables in the generated doxyrules.make file
# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
# This is useful so different doxyrules.make files included by the same
# Makefile don't overwrite each other's variables.
PERLMOD_MAKEVAR_PREFIX =
#---------------------------------------------------------------------------
# Configuration options related to the preprocessor
#---------------------------------------------------------------------------
# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
# evaluate all C-preprocessor directives found in the sources and include
# files.
ENABLE_PREPROCESSING = YES
# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
# names in the source code. If set to NO (the default) only conditional
# compilation will be performed. Macro expansion can be done in a controlled
# way by setting EXPAND_ONLY_PREDEF to YES.
MACRO_EXPANSION = YES
# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
# then the macro expansion is limited to the macros specified with the
# PREDEFINED and EXPAND_AS_PREDEFINED tags.
EXPAND_ONLY_PREDEF = YES
# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
# in the INCLUDE_PATH (see below) will be search if a #include is found.
SEARCH_INCLUDES = YES
# The INCLUDE_PATH tag can be used to specify one or more directories that
# contain include files that are not input files but should be processed by
# the preprocessor.
INCLUDE_PATH =
# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
# patterns (like *.h and *.hpp) to filter out the header-files in the
# directories. If left blank, the patterns specified with FILE_PATTERNS will
# be used.
INCLUDE_FILE_PATTERNS = *.h
# The PREDEFINED tag can be used to specify one or more macro names that
# are defined before the preprocessor is started (similar to the -D option of
# gcc). The argument of the tag is a list of macros of the form: name
# or name=definition (no spaces). If the definition and the = are
# omitted =1 is assumed.
PREDEFINED = \
"PADPOINTER(a1,a2,a3,a4)=" \
"PADLONG(a1,a2,a3)=" \
"PADINT(a1,a2)=" \
"PADWORD(a1)="
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
# this tag can be used to specify a list of macro names that should be expanded.
# The macro definition that is found in the sources will be used.
# Use the PREDEFINED tag if you want to use a different macro definition.
EXPAND_AS_DEFINED =
# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
# doxygen's preprocessor will remove all function-like macros that are alone
# on a line, have an all uppercase name, and do not end with a semicolon. Such
# function macros are typically used for boiler-plate code, and will confuse the
# parser if not removed.
SKIP_FUNCTION_MACROS = YES
#---------------------------------------------------------------------------
# Configuration::addtions related to external references
#---------------------------------------------------------------------------
# The TAGFILES option can be used to specify one or more tagfiles.
# Optionally an initial location of the external documentation
# can be added for each tagfile. The format of a tag file without
# this location is as follows:
# TAGFILES = file1 file2 ...
# Adding location for the tag files is done as follows:
# TAGFILES = file1=loc1 "file2 = loc2" ...
# where "loc1" and "loc2" can be relative or absolute paths or
# URLs. If a location is present for each tag, the installdox tool
# does not have to be run to correct the links.
# Note that each tag file must have a unique name
# (where the name does NOT include the path)
# If a tag file is not located in the directory in which doxygen
# is run, you must also specify the path to the tagfile here.
TAGFILES =
# When a file name is specified after GENERATE_TAGFILE, doxygen will create
# a tag file that is based on the input files it reads.
GENERATE_TAGFILE =
# If the ALLEXTERNALS tag is set to YES all external classes will be listed
# in the class index. If set to NO only the inherited external classes
# will be listed.
ALLEXTERNALS = NO
# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
# in the modules index. If set to NO, only the current project's groups will
# be listed.
EXTERNAL_GROUPS = YES
# The PERL_PATH should be the absolute path and name of the perl script
# interpreter (i.e. the result of `which perl').
PERL_PATH = /usr/bin/perl
#---------------------------------------------------------------------------
# Configuration options related to the dot tool
#---------------------------------------------------------------------------
# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base or
# super classes. Setting the tag to NO turns the diagrams off. Note that this
# option is superseded by the HAVE_DOT option below. This is only a fallback. It is
# recommended to install and use dot, since it yields more powerful graphs.
CLASS_DIAGRAMS = YES
# If set to YES, the inheritance and collaboration graphs will hide
# inheritance and usage relations if the target is undocumented
# or is not a class.
HIDE_UNDOC_RELATIONS = YES
# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
# available from the path. This tool is part of Graphviz, a graph visualization
# toolkit from AT&T and Lucent Bell Labs. The other options in this section
# have no effect if this option is set to NO (the default)
HAVE_DOT = NO
# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
# will generate a graph for each documented class showing the direct and
# indirect inheritance relations. Setting this tag to YES will force the
# the CLASS_DIAGRAMS tag to NO.
CLASS_GRAPH = YES
# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
# will generate a graph for each documented class showing the direct and
# indirect implementation dependencies (inheritance, containment, and
# class references variables) of the class with other documented classes.
COLLABORATION_GRAPH = YES
# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
# collaboration diagrams in a style similar to the OMG's Unified Modeling
# Language.
UML_LOOK = NO
# If set to YES, the inheritance and collaboration graphs will show the
# relations between templates and their instances.
TEMPLATE_RELATIONS = NO
# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
# tags are set to YES then doxygen will generate a graph for each documented
# file showing the direct and indirect include dependencies of the file with
# other documented files.
INCLUDE_GRAPH = YES
# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
# documented header file showing the documented files that directly or
# indirectly include this file.
INCLUDED_BY_GRAPH = YES
# If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will
# generate a call dependency graph for every global function or class method.
# Note that enabling this option will significantly increase the time of a run.
# So in most cases it will be better to enable call graphs for selected
# functions only using the \callgraph command.
CALL_GRAPH = NO
# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
# will graphical hierarchy of all classes instead of a textual one.
GRAPHICAL_HIERARCHY = YES
# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
# generated by dot. Possible values are png, jpg, or gif
# If left blank png will be used.
DOT_IMAGE_FORMAT = png
# The tag DOT_PATH can be used to specify the path where the dot tool can be
# found. If left blank, it is assumed the dot tool can be found on the path.
DOT_PATH =
# The DOTFILE_DIRS tag can be used to specify one or more directories that
# contain dot files that are included in the documentation (see the
# \dotfile command).
DOTFILE_DIRS =
# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width
# (in pixels) of the graphs generated by dot. If a graph becomes larger than
# this value, doxygen will try to truncate the graph, so that it fits within
# the specified constraint. Beware that most browsers cannot cope with very
# large images.
MAX_DOT_GRAPH_WIDTH = 1024
# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height
# (in pixels) of the graphs generated by dot. If a graph becomes larger than
# this value, doxygen will try to truncate the graph, so that it fits within
# the specified constraint. Beware that most browsers cannot cope with very
# large images.
MAX_DOT_GRAPH_HEIGHT = 1024
# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
# graphs generated by dot. A depth value of 3 means that only nodes reachable
# from the root by following a path via at most 3 edges will be shown. Nodes that
# lay further from the root node will be omitted. Note that setting this option to
# 1 or 2 may greatly reduce the computation time needed for large code bases. Also
# note that a graph may be further truncated if the graph's image dimensions are
# not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH and MAX_DOT_GRAPH_HEIGHT).
# If 0 is used for the depth value (the default), the graph is not depth-constrained.
MAX_DOT_GRAPH_DEPTH = 0
# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
# generate a legend page explaining the meaning of the various boxes and
# arrows in the dot generated graphs.
GENERATE_LEGEND = YES
# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
# remove the intermediate dot files that are used to generate
# the various graphs.
DOT_CLEANUP = YES
#---------------------------------------------------------------------------
# Configuration::addtions related to the search engine
#---------------------------------------------------------------------------
# The SEARCHENGINE tag specifies whether or not a search engine should be
# used. If set to NO the values of all tags below this one will be ignored.
SEARCHENGINE = NO
form-master/doc/doxygen/Makefile.am 0000664 0000000 0000000 00000003034 13565763364 0017612 0 ustar 00root root 0000000 0000000 ######################################## CONFIG_DOXYGEN
if CONFIG_DOXYGEN
html: html/index.html
html/index.html:
@echo "Running ${DOXYGEN} DoxyfileHTML ..."; \
${DOXYGEN} DoxyfileHTML
#################### CONFIG_TEX
if CONFIG_TEX
if CONFIG_MAKEINDEX
dvi: doxygen.dvi
doxygen.dvi: latex/doxygen.dvi
cp latex/doxygen.dvi doxygen.dvi
latex/doxygen.dvi: latex/doxygen.tex
@set -e ;\
cd latex; \
${LATEX} doxygen.tex; \
${MAKEINDEX} doxygen.idx; \
${LATEX} doxygen.tex
latex/doxygen.tex:
@echo "Running ${DOXYGEN} DoxyfileLATEX ..."; \
${DOXYGEN} DoxyfileLATEX; \
mv latex/refman.tex latex/doxygen.tex
########## CONFIG_PS
if CONFIG_PS
ps: doxygen.ps
doxygen.ps: latex/doxygen.ps
cp latex/doxygen.ps doxygen.ps
latex/doxygen.ps: latex/doxygen.dvi
@echo "Running ${DVIPS} -o doxygen.ps doxygen.dvi ..."; \
cd latex; \
${DVIPS} -o doxygen.ps doxygen.dvi
endif
########## CONFIG_PS
########## CONFIG_PDF
if CONFIG_PDF
pdf: doxygen.pdf
doxygen.pdf: pdflatex/doxygen.pdf
cp pdflatex/doxygen.pdf doxygen.pdf
pdflatex/doxygen.pdf: pdflatex/doxygen.tex
@set -e ; \
cd pdflatex; \
${PDFLATEX} doxygen.tex; \
${MAKEINDEX} doxygen.idx; \
${PDFLATEX} doxygen.tex
pdflatex/doxygen.tex:
@echo "Running ${DOXYGEN} DoxyfilePDFLATEX ..."; \
${DOXYGEN} DoxyfilePDFLATEX; \
mv pdflatex/refman.tex pdflatex/doxygen.tex
endif
########## CONFIG_PDF
endif
endif
#################### CONFIG_TEX
endif
######################################## CONFIG_DOXYGEN
CLEANFILES = doxygen.dvi doxygen.ps doxygen.pdf
clean-local:
rm -rf latex pdflatex html
form-master/doc/form.1 0000664 0000000 0000000 00000007520 13565763364 0015132 0 ustar 00root root 0000000 0000000 .TH FORM 1 "2018-10-04"
.SH NAME
FORM \- Symbolic manipulation system
.SH SYNOPSIS
.B form
.RB [
.IR options
]
.IR inputfile
.SH DESCRIPTION
.PP
FORM is a symbolic manipulation system. The \fBform\fR command reads a text file
(which should have a name that ends with the extension \fB.frm\fR) containing
definitions of mathematical expressions as well as statements that tell it how
to manipulate these expressions. It is widely used in the theoretical particle
physics community, but it is not restricted to applications in this specific
field.
.PP
\fBtform\fR is the threaded version using POSIX Threads.
.PP
\fBparform\fR is the multiprocessing version using MPI.
.SH OPTIONS
.TP
.BR "-c"
Error checking only. Notice that this will not work properly if there are conditionals in the
preprocessor phase that depend on results obtained at earlier stages of the program.
.TP
.BR "-d, -D"
Next argument/option is the name of a preprocessor variable that will be defined before the
run starts. A specific value can be assigned with the syntax
\fB-d\fR\ \fIVARIABLENAME\fR=\fIVALUE\fR.
The
default value is 1.
.TP
.BR "-f"
Output goes only to log file.
.TP
.BR "-F"
Output only to log file. Further like \fB-L\fR or \fB-ll\fR.
.TP
.BR "-h"
Wait for some key to be touched before finishing the run. Basically only for some old window
based systems.
.TP
.BR "-I"
Next argument/option is the path of a directory for include, procedure and subroutine files.
.TP
.BR "-l"
Make a regular log file.
.TP
.BR "-ll, -L"
Make a log file without intermediate statistics.
.TP
.BR "-M"
Put the PID (process identifier) in the name of the temporary files. This makes for longer
names, but gives a better guarantee of uniqueness. If a file with the created name exists
already it will be overwritten. This option is for when several instances of FORM are started
at nearly the same time as can happen from minos or make (with the make -j option).
.TP
.BR "-p"
Next argument/option is the path of a directory for input, include, procedure and subroutine
files.
.TP
.BR "-pipe"
Indicates that FORM is started up as the receiving end of a pipe. Action will be taken to
set up the proper communication channels.
.TP
.BR "-q, -si"
Quiet option. Only output expressions are printed.
.TP
.BR "-R"
Recover from a crash.
.TP
.BR "-s"
Next argument/option is the path of a directory for a setup file.
.TP
.BR "-S"
Next argument/option is the name of a setup file.
.TP
.BR "-t"
Next argument/option is the path of a directory for temporary files.
.TP
.BR "-ts"
Next argument/option is the path of a directory for temporary sort files.
.TP
.BR "-T"
Puts FORM in a mode in which the maximum totalsize is measured and printed at the end of
the program.
.TP
.BR "-v"
Only the version will be printed. The program terminates immediately after it.
.TP
.BR "-w"
This should be followed immediately by a number without any space. The number
indicates the number of worker threads for \fBtform\fR. All other versions of
FORM ignore this parameter.
.TP
.BR "-W"
Turn on the wall-clock time mode in the statistics.
.TP
.BR "-z"
The number following is a timelimit for the program in second.
.TP
.BR "-Z"
Removes the \fB.str\fR file on crash, whatever its contents. Under
ordinary circumstances at a crash a \fB.str\fR file will not be removed if
it has a nonzero content.
.TP
.BR "-y"
Run only the preprocessor and dump its output.
.SH ENVIRONMENT
.TP
\fBFORMPATH\fR
The directory in which FORM will look for procedures and header files, assuming it cannot
find them in the current directory.
.TP
\fBFORMTMP\fR
The directory in which FORM will make its temporary files.
.TP
\fBFORMTMPSORT\fR
The directory in which FORM will make its temporary sort files.
.TP
\fBFORMSETUP\fR
The full path and name of a setup file.
.SH SEE ALSO
.TP
\fBhttps://www.nikhef.nl/~form/\fR
The FORM home site.
.TP
\fBhttps://github.com/vermaseren/form/\fR
The repository on GitHub.
form-master/doc/manual/ 0000775 0000000 0000000 00000000000 13565763364 0015356 5 ustar 00root root 0000000 0000000 form-master/doc/manual/.latex2html-init 0000664 0000000 0000000 00000000466 13565763364 0020412 0 ustar 00root root 0000000 0000000 $DVIPSOPT = ' -E';
$TITLE = `grep '\\title{' manual.tex`;
$TITLE =~ s/^\s*\\title\s*{//;
$TITLE =~ s/}\s*$//;
$TITLE =~ s/\\\s*(Huge|huge|Large|large|\\)//g;
$TITLE =~ s/^\s+//;
$TITLE =~ s/\s+$//;
$TITLE =~ s/\s+/ /g;
$MAX_SPLIT_DEPTH = 0;
$NO_NAVIGATION = 1;
$NO_FOOTNODE = 1;
$ADDRESS = '';
$INFO = '';
1;
form-master/doc/manual/Makefile.am 0000664 0000000 0000000 00000010065 13565763364 0017414 0 ustar 00root root 0000000 0000000 TEXSRC = \
bracket.tex \
calculus.tex \
diagrams.tex \
dict.tex \
dollar.tex \
external.tex \
functions.tex \
gamma.tex \
metric.tex \
module.tex \
optim.tex \
parallel.tex \
pattern.tex \
polynomials.tex \
prepro.tex \
setup.tex \
sorting.tex \
spectators.tex \
startup.tex \
statements.tex \
tablebas.tex \
variable.tex
MAIN = manual
TEXFILES = $(TEXSRC) $(MAIN).tex version.tex
EXTRA_DIST = $(TEXSRC) .latex2html-init
.PHONY: dvi latex2html html ps pdf clean-local update_version_tex
# NOTE: htlatex invalidate .aux, .idx, .dvi files.
HTMLCLEANFILES = idxmake.dvi idxmake.log $(MAIN).4ct $(MAIN).4dx $(MAIN).4ix \
$(MAIN).4tc $(MAIN).aux $(MAIN).css $(MAIN).dvi $(MAIN).html $(MAIN)2.html \
$(MAIN).idv $(MAIN).idx $(MAIN).ilg $(MAIN).ind $(MAIN).lg $(MAIN).log \
$(MAIN).tmp $(MAIN).xref
CLEANFILES = $(MAIN).pdf $(MAIN).ps $(MAIN).toc $(DATEFILE) texput.log \
version.tex $(HTMLCLEANFILES)
clean-local:
rm -rf html $(MAIN)
# Automatic versioning.
version.tex: update_version_tex
$(UPDATE_VERSION_TEX)
dist-hook:
$(DISTHOOK_VERSION_TEX)
if FIXED_VERSION
UPDATE_VERSION_TEX = \
[ -f version.tex ] || $(LN_S) "$(srcdir)/version.tex.in" version.tex
DISTHOOK_VERSION_TEX = \
cp "$(srcdir)/version.tex.in" "$(distdir)/version.tex.in"
else
UPDATE_VERSION_TEX = \
$(SHELL) "$(top_srcdir)/scripts/git-version-gen.sh" -C "$(srcdir)" -t -o version.tex --date-format '%e %B %Y'
DISTHOOK_VERSION_TEX = \
$(SHELL) "$(top_srcdir)/scripts/git-version-gen.sh" -C "$(srcdir)" -t -o "$(distdir)/version.tex.in" --date-format '%e %B %Y'
endif
#################### CONFIG_TEX
if CONFIG_TEX
dvi: $(MAIN).dvi
if CONFIG_MAKEINDEX
$(MAIN).dvi: $(TEXFILES)
$(LATEX) $(MAIN).tex; while [ `grep -v 'Package: rerunfilecheck' $(MAIN).log | grep -c Rerun` -gt 0 ]; do $(LATEX) $(MAIN).tex; done
$(MAKEINDEX) $(MAIN)
$(LATEX) $(MAIN).tex; while [ `grep -v 'Package: rerunfilecheck' $(MAIN).log | grep -c Rerun` -gt 0 ]; do $(LATEX) $(MAIN).tex; done
else
$(MAIN).dvi: $(TEXFILES)
$(LATEX) $(MAIN).tex; while [ `grep -v 'Package: rerunfilecheck' $(MAIN).log | grep -c Rerun` -gt 0 ]; do $(LATEX) $(MAIN).tex; done
endif
########## CONFIG_LATEX2HTML
if CONFIG_LATEX2HTML
latex2html: $(MAIN)/$(MAIN).html
$(MAIN)/$(MAIN).html: $(MAIN).dvi
$(LATEX2HTML) -init_file $(srcdir)/.latex2html-init $(MAIN).tex
cat $(MAIN)/index.html | sed 's/$(MAIN).html#/#/g' >$(MAIN)/index.html.tmp
mv $(MAIN)/index.html.tmp $(MAIN)/index.html
cat $(MAIN)/$(MAIN).html | sed 's/$(MAIN).html#/#/g' >$(MAIN)/$(MAIN).html.tmp
mv $(MAIN)/$(MAIN).html.tmp $(MAIN)/$(MAIN).html
endif
########## CONFIG_LATEX2HTML
########## CONFIG_HTLATEX
if CONFIG_HTLATEX
html: html/$(MAIN).html
if CONFIG_MAKEINDEX
html/$(MAIN).html: $(TEXFILES)
mkdir -p html
$(HTLATEX) $(MAIN) "html,mathml-" "" "-dhtml/"
$(TEX) '\def\filename{{$(MAIN)}{idx}{4dx}{ind}} \input idxmake.4ht'
$(MAKEINDEX) -o $(MAIN).ind $(MAIN).4dx
$(HTLATEX) $(MAIN) "html,mathml-" "" "-dhtml/"
sed 's/table.tabular {margin-left: auto; margin-right: auto;}/table.tabular {margin-left: inherit;}/' html/$(MAIN).css >html/$(MAIN).css.tmp
mv html/$(MAIN).css.tmp html/$(MAIN).css
rm -f $(HTMLCLEANFILES)
else
html/$(MAIN).html: $(DATEFILE)
mkdir -p html
$(HTLATEX) $(MAIN) "html,mathml-" "" "-dhtml/"
rm -f $(HTMLCLEANFILES)
endif
endif
########## CONFIG_HTLATEX
########## CONFIG_PS
if CONFIG_PS
ps: $(DATEFILE) $(MAIN).ps
$(MAIN).ps: $(DATEFILE) $(MAIN).dvi
$(DVIPS) -o $(MAIN).ps $(MAIN).dvi
endif
########## CONFIG_PS
########## CONFIG_PDF
if CONFIG_PDF
pdf: $(MAIN).pdf
if CONFIG_MAKEINDEX
$(MAIN).pdf: $(TEXFILES)
$(PDFLATEX) $(MAIN).tex; while [ `grep -v 'Package: rerunfilecheck' $(MAIN).log | grep -c Rerun` -gt 0 ]; do $(PDFLATEX) $(MAIN).tex; done
$(MAKEINDEX) $(MAIN)
$(PDFLATEX) $(MAIN).tex; while [ `grep -v 'Package: rerunfilecheck' $(MAIN).log | grep -c Rerun` -gt 0 ]; do $(PDFLATEX) $(MAIN).tex; done
else
$(MAIN).pdf: $(TEXFILES)
$(PDFLATEX) $(MAIN).tex; while [ `grep -v 'Package: rerunfilecheck' $(MAIN).log | grep -c Rerun` -gt 0 ]; do $(PDFLATEX) $(MAIN).tex; done
endif
endif
########## CONFIG_PDF
endif
#################### CONFIG_TEX
form-master/doc/manual/bracket.tex 0000664 0000000 0000000 00000025354 13565763364 0017524 0 ustar 00root root 0000000 0000000 \chapter{Brackets}
\label{brackets}
At times one would like to order the output in a specific way. In an
expression which is for instance a polynomial in terms of the symbol $x$,
one might want to make this behaviour in terms of $x$ more apparent by
printing the output in such a way, that all powers of $x$ are outside
parentheses\index{parentheses}, and the whole rest is inside parentheses.
This is done with the bracket\index{bracket} statement:
\begin{verbatim}
Bracket x;
\end{verbatim}
or in short notation
\begin{verbatim}
B x;
\end{verbatim}
One can specify more than one object in the bracket statement, but only a
single bracket statement (the last one) is considered. Bracket statements
belong to the module in which they occur. Hence they are forgotten after
the next end-of-module.
If a vector is mentioned in a bracket statement,
all occurrences of this vector as a loose vector, a vector with any index,
inside a dotproduct, or inside a tensor are taken outside brackets. If the
vector occurs inside a non-commuting tensor, all other non commuting
objects that are to the left of this tensor will also be taken outside the
parentheses.
When a function or tensor is mentioned in a bracket statement, it is not
allowed to have any arguments in the bracket statement. All occurrences of
this function will be pulled outside brackets. If the function is
non-commuting, all other functions and/or tensors that are non-commuting
and are to the left of the specific function(s) or tensor(s) will also be
outside parentheses.
The opposite of the bracket statement is the antibracket\index{antibracket}
statement:
\begin{verbatim}
AntiBracket x;
\end{verbatim}
or
\begin{verbatim}
ABracket x;
\end{verbatim}
or
\begin{verbatim}
AB x;
\end{verbatim}
This statement causes also brackets in the output, but now everything is
put outside brackets, except for powers of x and coefficients. This way one
can make the $x$-dependence apparent differently.
Because the bracket statement causes a different ordering of the terms when
storing the expression, one can use this ordering in the next module. There
are various ways to do this.
One can use the contents of a given bracket in a r.h.s. expression as in
% THIS EXAMPLE IS PART OF THE TESTSUITE. CHANGES HERE SHOULD BE APPLIED THERE AS
% WELL!
\begin{verbatim}
Symbols a,b,c,x;
L F = a*x^2+b*x+c;
B x;
.sort
L Discriminant = F[x]^2-4*F[x^2]*F[1];
Print;
.end
\end{verbatim}
The outside of the bracket is placed between braces\index{braces} after the
name of the expression. The bracket that has nothing outside is referred to
with the number 1. If a bracket is empty, its contents will be represented
by the value zero.
The regular algorithm by which \FORM{} finds brackets in an expression, is
to start from the beginning and inspect each term until it finds the
appropriate bracket. This is fully in the spirit of the sequential
treatment of expressions in \FORM{}. This can however be rather
slow\index{slow} in big
expressions that reside on a disk. Hence there is the bracket\index{bracket
index}
index\index{index!bracket}
feature. It is invoked by putting a $+$-sign after the bracket (or B)
statement as in
\begin{verbatim}
Bracket+ x;
\end{verbatim}
or
\begin{verbatim}
B+ x;
\end{verbatim}
This option causes \FORM{} to build a tree of (disk) positions for the
different brackets, with the condition that the whole storage of this tree
of brackets does not exceed a given maximum space, named
`bracketindexsize'\index{bracketindexsize}
(see chapter~\ref{setup} on the setup parameters).
If the index would need more space \FORM{} will start
skipping brackets in the index. This means that it will have to look for
the bracket in a sequential fashion, but starting from the position
indicated by the previous bracket in the index. This will still be very
fast, provided the index is not very small.
When the bracket index option is used, \FORM\ will not compress the
expressions that use such an index with the zlib compression, even if the
user asked for this in an earlier statement. The use of the index indicates
that the brackets are going to be used intensively, and hence the
continuous decompression that would result would destroy most of the profit
that comes from the index. If the brackets are only for cosmetics in the
output, it is better not to use the index option. It does use resources to
construct the index\index{index tree} tree\index{tree!index}. Also when
brackets are only used sequentially as in the features discussed below, the
presence of the index is not beneficial. It should only be applied when
contents of brackets are used in the above way (like with the
discriminant).
There are several statements that make use of the bracket ordering:
\begin{itemize}
\item Keep\index{keep brackets} Brackets;
This statement takes from the input one term at a time as usual, but
then it takes the part outside the brackets, executes the statements of the
module only on that part of the term, and then, when all statements of the
module have had their effect, the resulting term(s) is/are multiplied by
the full content of the bracket. The next term taken from the input will be
the first term of the next bracket. This way one can hide part of the terms
for the pattern matcher. Also one can avoid that the same matching will
occur many times, as in an expression of the type
\begin{verbatim}
+ f(y)*(x+x^2+x^3+x^4+1)
\end{verbatim}
If we would want to make a replacement of the type
\begin{verbatim}
Keep Brackets;
id f?{f1,f2,f3}(u?) = f(u+1)/u;
\end{verbatim}
the pattern matching and the substitution would have to be done only once,
rather than 5 times, as would be the case if the Keep bracket statement
would not be used.
\item Collect\index{collect} FunctionName;
The contents of the various brackets will be placed inside a function
with the given name. Hence
\begin{verbatim}
+ f(y)*(x+x^2+x^3+x^4+1)
+ f(y^2)*(x+2*x^2+3*x^3+4*x^4+1)
\end{verbatim}
with
\begin{verbatim}
Collect h;
\end{verbatim}
would result in:
\begin{verbatim}
+ f(y)*h(x+x^2+x^3+x^4+1)
+ f(y^2)*h(x+2*x^2+3*x^3+4*x^4+1)
\end{verbatim}
This can be very useful to locate $x$-dependence even further, because
bracketing the new expression in terms of $h$ could make very clear
whether a given polynomial in $x$ would factor the whole expression, or
which factors are occurring. To bring \verb:h(x+1): and \verb:h(2*x+2): to
multiples of the same objects one should consult the pages on the
normalize\index{normalize}
(\ref{substanormalize})
and makeinteger\index{makeinteger} (\ref{substamakeinteger}) statements.
The Collect statement, together with the PolyFun\index{polyfun} statement,
can also be very useful, if the variable $x$ (or other variables) is
temporarily not playing much of a role in the pattern matching. It can make
the program much faster.
For more information on the collect statement one should consult
section~\ref{substacollect}.
\end{itemize}
\noindent
Restrictions: The bracket index can only be used with active expressions.
Hence the access of specific brackets in stored expressions will always be
of the slow variety. To make it faster, one can copy the expression into a
local expression with indexed brackets, use it, and drop the expression
when it is not needed any longer.
The brackets can also be used to save space on the disk in problems in
which the expressions become rather large. Let us assume the following
simple problem:
\begin{verbatim}
Symbols x1,...,x12;
Local F = (x1+...+x12)^10;
.sort
id x1 = x4+x7;
.end
\end{verbatim}
If the program is run like this the expression F contains 352716 terms
after the sort and after the id the sorting in the .end results in a final
stage sort\index{sort!final stage} of which the statistics are:
\begin{verbatim}
Time = 46.87 sec
F Terms active = 504240
Bytes used = 13462248
Time = 52.09 sec Generated terms = 646646
F Terms in output = 184756
Bytes used = 4883306
\end{verbatim}
We see, that the intermediate sort file still contains more than 500000
terms and more than 13 Mbytes, while the final result contains less than 5
Mbytes. Why is this? When the terms in \FORM\ are sorted first come the
powers of \verb:x1:, because this is the variable that was declared first.
Hence the terms that do not have powers of \verb:x1: come much later in the
input and will not be compared with the terms generated by the substitution
of for instance a single power of \verb:x1: until very late in the sorting.
What can we do about this? We can try to group the terms in the first sort
such that after the substitution like terms will be `very close' to each
other and hence will add quickly. This is done in the program
\begin{verbatim}
Symbols x1,...,x12;
Local F = (x1+...+x12)^10;
AntiBracket x1,x4,x7;
.sort
id x1 = x4+x7;
.end
\end{verbatim}
Now all powers of the mentioned variables will be inside the brackets and
all other variables will be outside. Because the terms inside the brackets
are all following each other in the input of the second module, terms that
will add will be generated closely together.
The result is visible in the final statistics:
\begin{verbatim}
Time = 47.23 sec
F Terms active = 184761
Bytes used = 4928008
Time = 48.40 sec Generated terms = 646646
F Terms in output = 184756
Bytes used = 4883306
\end{verbatim}
Now the final step of the sorting has already almost the proper number of
terms. The difference is due to brackets that are half in one `patch' on
the disk and half in the next `patch' (for the meaning of the patches, one
should read the part about sorting\index{sorting} in chapter~\ref{setup} on
the setup file. It should be rather clear now that this saves disk space
and the corresponding amount of time. These early cancellations can also be
seen in the first statistics message of the second module. In the first
case it is
\begin{verbatim}
Time = 19.76 sec Generated terms = 10431
F 5216 Terms left = 8065
Bytes used = 239406
\end{verbatim}
and in the second case it is
\begin{verbatim}
Time = 22.82 sec Generated terms = 10124
F 5835 Terms left = 3186
Bytes used = 96678
\end{verbatim}
This also causes a more efficient use of the large buffer and again a
better use of the disk. There have been cases in which this `trick' was
essential to keep the sort file inside the available disk space.
form-master/doc/manual/calculus.tex 0000664 0000000 0000000 00000010161 13565763364 0017712 0 ustar 00root root 0000000 0000000
The routines inside FORM that deal with the coefficients are all written in
C (and hence not in assembler language!). This enhances the portability
greatly. It are these routines that determine the word size in FORM. The
requirement that the multiplication of two words must be a rather natural
operation makes that on a 32-bits architecture the word size becomes 16
bits and on a 64-bits architecture it becomes 32 bits. In some 32-bits
processors one could use a 32 bits multiplication and recover the full 64
bits result by looking at two registers. Similarly divisions can be done
that way. But this requires assembly language programming, because in C the
only way one can do this is by first casting one of the numbers to (long
long int) and then the compiler usually creates several multiplications all
except one being superfluous. The fact that the use of low level GMP
routines can give a slightly faster code is entirely due to the fact that
indeed they work with these longer words and use assembly level routines.
Originally the low level calculus routines (addition, multiplication,
division and the calculation of GCD's) were fully optimized for relatively
short numbers. The idea being that for most calculations that is what
occurs most of the time. Over the years computers became bigger and people
were taking expansions further and further and hence the speed of these
routines became a noticeable factor. This was mostly the GCD routine. In
the past the GCD algorithm had been studied and compares had been made
between the Euclidean and the binary algorithms. The performance of the
binary algorithm depends rather crucially on how one can shift through an
array of integers and in the C language this isn't very efficient. Hence
this algorithm was abandoned in the late 80's. Of course, due to the fact
that it uses only shifts and subtractions asymptotically it is faster than
the Euclidean algorithm, but in the past the region in which it was more
efficient wasn't reached.
When the calculation of GCD's became a real problem a new algorithm for
longer numbers was invented which turned out to be a little bit like an
improved version of the Lehmer-Euclid algorithm. This made the behaviour
for big integers much better. At yet a later stage the GMP library was
introduced and applied for numbers that are longer than just a few words.
Here we need some conversion from FORM words to the words that the GMP
routines need. For a GCD calculation this is however a negligible factor.
The improvement in speed was far less dramatic than hoped for, even though
GMP works with longer words and has its central code in assembler language.
But faster is faster and hence FORM can use now three of the low level GMP
routines (GCD, multiplication and division) for its big numbers. The
improvement coming from the multiplication and the division routines has
thus far been only a few percent. This was for calculations with numbers
that occupy tenths of words. If FORM could use the double words from the C
code, probably the conversion to the GMP notation would more than offset
the benefit of the use of low level assembly routines.
When very long numbers will be used with thousands of words the situation
is different. In that case GMP has special algorithms that were never built
for FORM. But for the moment most FORM programs have not reached such cases
yet. Whenever this is the case for a program it is best to run this program
on a computer that provides the GMP library with its operating system. When
the GMP library isn't available on a system, FORM will use its own
routines, which, as mentioned, aren't bad, but were not ment for such
extreme cases.
The higher level routines for calculus of rational numbers can be done in
any language. The algorithms are standard and can be found in any decent
text book. It are the low level routines that determine the eventual
speed. For these objects the GMP library would probably slow FORM down to a
significant degree as on the object level they are much messed up with
memory allocation problems, and at the memory management level FORM doesn't
have any of these problems.
form-master/doc/manual/diagrams.tex 0000664 0000000 0000000 00000013527 13565763364 0017677 0 ustar 00root root 0000000 0000000
\chapter{Diagram generation}
\label{diagrams}
For an accurate calculation of particle reactions a good, fast and flexible
diagram generator is a necessity. It was noticed that the fastest available
generator is the one by Toshiaki Kaneko, which was constructed for the
Grace system. Hence he was asked whether he would be willing to provide a
version that could be implemented into Form, thereby making intermediate
files with diagrams superfluous. He gracefully accepted.
In the current version the diagram generator is not complete yet.
Effectively it only accepts one type of scalar particles, but one can have
any number of particles at the vertices. Hence it can be used as a fast
topology generator. Because all source code is present and has been
commented it will be possible to extend the user interface on demand.
The completion of the full diagram generator is planned for late-2018/
early-2019, depending on the availability of time. The source code of the
generator is in C++, which makes it rather accesible from Form and possibly
from other programs as well. For more information about that one should
contact Prof. Toshiaki Kaneko directly.
The current interface is rather simple, due to the fact that one can
generate only 'topologies'. First one needs two sets of vectors, one for
the momenta of the external particles and one for the momenta of internal
particles. The call is then with the function topologies\_ as in
\begin{verbatim}
Vectors Q1,Q2,p1,...,p8;
Set QQ:Q1,Q2;
Set PP:p1,...,p8;
#define LOOPS "2"
Local F = topologies_(`LOOPS',2,{3,},QQ,PP);
Print +f +s;
.end
F =
+ node_(0,-Q1)*node_(1,-Q2)*node_(2,Q1,-p1,-p2)*node_(3,Q2,p1,-p3)*
node_(4,p2,-p4,-p5)*node_(5,p3,p4,p5)
+ node_(0,-Q1)*node_(1,-Q2)*node_(2,Q1,-p1,-p2)*node_(3,p1,-p3,-p4)*
node_(4,p2,p3,-p5)*node_(5,Q2,p4,p5)
;
\end{verbatim}
Here the second parameter indicates the number of external legs, the third
parameter is a set that tells, in this case, that 3-point vertices are
allowed, QQ is the set of external momenta and PP is the set of internal
momenta. The function node\_ is a built-in function to indicate the
vertices.
If one would like to allow more types of vertices one may change
the third parameter:
\begin{verbatim}
Vectors Q1,Q2,p1,...,p8;
Set QQ:Q1,Q2;
Set PP:p1,...,p8;
#define LOOPS "2"
Local F = topologies_(`LOOPS',2,{3,4},QQ,PP);
Print +f +s;
.end
F =
+ node_(0,-Q1)*node_(1,-Q2)*node_(2,Q1,Q2,-p1,-p2)*node_(3,p1,p2,-p3,p3
)
+ node_(0,-Q1)*node_(1,-Q2)*node_(2,Q1,-p1,-p2)*node_(3,Q2,p1,-p3)*
node_(4,p2,p3,-p4,p4)
+ node_(0,-Q1)*node_(1,-Q2)*node_(2,Q1,-p1,-p2)*node_(3,Q2,p1,-p3)*
node_(4,p2,-p4,-p5)*node_(5,p3,p4,p5)
+ node_(0,-Q1)*node_(1,-Q2)*node_(2,Q1,-p1,-p2)*node_(3,Q2,-p3,-p4)*
node_(4,p1,p2,p3,p4)
+ node_(0,-Q1)*node_(1,-Q2)*node_(2,Q1,-p1,-p2)*node_(3,p1,-p3,-p4)*
node_(4,Q2,p2,p3,p4)
+ node_(0,-Q1)*node_(1,-Q2)*node_(2,Q1,-p1,-p2)*node_(3,p1,-p3,-p4)*
node_(4,p2,p3,-p5)*node_(5,Q2,p4,p5)
+ node_(0,-Q1)*node_(1,-Q2)*node_(2,Q1,-p1,-p2,-p3)*node_(3,Q2,p1,p2,p3
)
+ node_(0,-Q1)*node_(1,-Q2)*node_(2,-p1,-p2,-p3)*node_(3,Q2,p1,-p4)*
node_(4,Q1,p2,p3,p4)
+ node_(0,-Q1)*node_(1,-Q2)*node_(2,-p1,-p2,-p3)*node_(3,p1,p2,-p4)*
node_(4,Q1,Q2,p3,p4)
;
\end{verbatim}
and suddenly there are 9 topologies.
In the above configuration the program sees the external lines as
different. If there is however a symmetry between the two external lines of
a propagator-like diagram (as is the case with boson propagators) one can
indicate this by putting a minus sign in front of the number 2:
\begin{verbatim}
Vectors Q1,Q2,p1,...,p8;
Set QQ:Q1,Q2;
Set PP:p1,...,p8;
#define LOOPS "2"
Local F = topologies_(`LOOPS',-2,{3,4},QQ,PP);
Print +f +s;
.end
F =
+ node_(0,-Q1)*node_(1,-Q2)*node_(2,Q1,Q2,-p1,-p2)*node_(3,p1,p2,-p3,p3
)
+ node_(0,-Q1)*node_(1,-Q2)*node_(2,Q1,-p1,-p2)*node_(3,Q2,p1,-p3)*
node_(4,p2,p3,-p4,p4)
+ node_(0,-Q1)*node_(1,-Q2)*node_(2,Q1,-p1,-p2)*node_(3,Q2,p1,-p3)*
node_(4,p2,-p4,-p5)*node_(5,p3,p4,p5)
+ node_(0,-Q1)*node_(1,-Q2)*node_(2,Q1,-p1,-p2)*node_(3,Q2,-p3,-p4)*
node_(4,p1,p2,p3,p4)
+ node_(0,-Q1)*node_(1,-Q2)*node_(2,Q1,-p1,-p2)*node_(3,Q2,-p3,-p4)*
node_(4,p1,p3,-p5)*node_(5,p2,p4,p5)
+ node_(0,-Q1)*node_(1,-Q2)*node_(2,Q1,-p1,-p2)*node_(3,p1,-p3,-p4)*
node_(4,Q2,p2,p3,p4)
+ node_(0,-Q1)*node_(1,-Q2)*node_(2,Q1,-p1,-p2,-p3)*node_(3,Q2,p1,p2,p3
)
+ node_(0,-Q1)*node_(1,-Q2)*node_(2,-p1,-p2,-p3)*node_(3,p1,p2,-p4)*
node_(4,Q1,Q2,p3,p4)
;
\end{verbatim}
Now the program assumes this symmetry and one notices only 8 topologies
remaining.
In the case of the topologies\_ function there are no combinatorics
factors. This will of course be different with the future function
diagrams\_.
That this function is very fast can be seen when one generates all
topologies of a 6-loop propagator with only 3-point vertices:
\begin{verbatim}
Vectors Q1,Q2,p1,...,p17;
Set QQ:Q1,Q2;
Set PP:p1,...,p17;
#define LOOPS "6"
Local F = topologies_(`LOOPS',-2,{3,},QQ,PP);
.end
Time = 1.75 sec Generated terms = 2793
F Terms in output = 2793
Bytes used = 347156
\end{verbatim}
or without the symmetry
\begin{verbatim}
Vectors Q1,Q2,p1,...,p17;
Set QQ:Q1,Q2;
Set PP:p1,...,p17;
#define LOOPS "6"
Local F = topologies_(`LOOPS',2,{3,},QQ,PP);
.end
Time = 0.63 sec Generated terms = 4999
F Terms in output = 4999
Bytes used = 616180
\end{verbatim}
This is very much faster than the program that is used most widely.
To be continued.....
form-master/doc/manual/dict.tex 0000664 0000000 0000000 00000036077 13565763364 0017040 0 ustar 00root root 0000000 0000000
\chapter{Dictionaries}
\label{dictionaries}
At times one would like to manipulate the output to facilitate further
processing. A standard example is that the output formula should be
included in a \LaTeX{} file. Also the use of terms in the output as
patterns with wildcards in the LHS of an id-statements needs textual
translation. Another example is the representation of
fractions in a numerical program that works with floating point numbers.
Complete solutions for such problems are not included in \FORM{}, but with
the partial solution of `dictionaries'\index{dictionaries} one can do quite
a lot already.
In \FORM{} a dictionary is a collection of `words'\index{word} together with
their translation\index{translation}. The word can be a number, a variable,
a function with its arguments or a special output token like a
multiplication sign or a power indicator. The translation can be any
string. Generic patterns have not been implemented. That would be more like
grammar and involves special complications. As shown later, currently there
is one exception to this rule.
A dictionary is defined with the preprocessor\index{\#opendictionary}
instruction
\begin{verbatim}
#opendictionary name
\end{verbatim}
in which `name' is the name of the dictionary. There can be more
dictionaries, provided they have different names. It is allowed to open
already existing dictionaries. Only one dictionary can be open at a given
time. Dictionaries are closed with the instruction\index{\#closedictionary}
\begin{verbatim}
#closedictionary
\end{verbatim}
and because there can be only one open dictionary, it is clear which
dictionary should be closed.
A dictionary is opened to add words to it. This is done with the \#add
instruction\index{\#add} as in
\begin{verbatim}
#add x1: "x_1"
#add *: "\ "
#add mu: "\mu"
\end{verbatim}
which would tell the system that when the dictionary is in use, the
variable \verb:x1: should be printed as the string \verb:x_1: and a
multiplication sign should become a backslash character followed by a
blank space. The (index) mu would be printed as the string \verb:\mu:.
A dictionary can be used\index{\#usedictionary} with the
\begin{verbatim}
#usedictionary name <(options)>
\end{verbatim}
instruction. At the moment a dictionary is being used there cannot be any
open dictionaries. Hence we can stop using a dictionary with the
\begin{verbatim}
#closedictionary
\end{verbatim}
instruction\index{\#closedictionary} without running into inconsistencies.
The options control partial use of a dictionary, as for instance only for
individual variables, or only for numbers. They can also control whether
translations should be made inside function arguments or inside dollar
variables (when used as preprocessor variables).
What words are allowed?
\begin{description}
\item[variable] This can be the name of a symbol, a vector, an index or a
function (this includes commuting functions, non-commuting functions,
tensors and tables).
\item[number] This must be a positive integer number.
\item[fraction] This must be a positive rational number.
\item[special character] Currently this can be the multiplication sign
(\verb:*:), or the power sign (\verb:^: or \verb:**:).
\item[a range] Indicated between parentheses, this is a range\index{range}
of extra symbols. There can be more than one range.
\item[a function with arguments] This would be a complete function subterm.
\end{description}
The options in the \#usedictionary should be enclosed between parentheses
and separated by comma's. They can be:
\begin{description}
\item[allnumbers] All numbers will be looked up in the dictionary.
\item[integersonly] Only integer numbers will be looked up.
\item[nonumbers] Numbers will not be looked up.
\item[numbersonly] Only numbers will be looked up.
\item[novariables] Loose variables will not be looked up.
\item[variablesonly] Only loose variables will be looked up.
\item[nospecials] Specials (multiplication signs and power signs) will
not be looked up.
\item[specialsonly] Only specials (multiplication signs and power signs) will
be looked up.
\item[nofunwithargs] Functions with arguments will not be looked up.
\item[funwithargsonly] Only functions with arguments will be looked up.
\item[warnings] Warnings\index{warnings} concern the look up of
numbers. If a fortran or C format is being used and the dictionary cannot
be used in such a way that floating point notation and/or decimal points
can be avoided, a warning will be given.
\item[nowarnings] No floating point warnings are given.
\item[infunctions] Substitutions are also made inside function arguments.
\item[notinfunctions] No substitutions are made inside function arguments.
\item[\$] Substitutions are made also when dollar variables are expanded.
The default is that this is not done.
\end{description}
The defaults are that all potential objects are looked up (also inside
function arguments) and no warnings are given.
The use is best illustrated with a few examples.
\begin{verbatim}
Symbols x1,y2,z3,N;
Indices mu,nu,ro,si;
Tensor tens;
CFunction S,R,f;
ExtraSymbols array w;
#OpenDictionary test
#add x1: "x_1"
#add y2: "y^{(2)}"
#add z3: "{\cal Z}"
#add *: " "
#add S(R(1),N): "S_1(N)"
#add S(R(2),N): "S_2(N)"
#add S(R(1,1),N): "S_{1,1}(N)"
#add f: "\ln"
#add mu: "\mu"
#add nu: "\nu"
#add ro: "\rho"
#add si: "\sigma"
#add tens: "T"
#CloseDictionary
Local F = x1*y2*z3
+ S(R(1),N) + S(R(1,1),N) + S(R(2),N)
+ tens(mu,nu,ro,si) + f(x1+1);
#usedictionary test
Print +s;
.end
\end{verbatim}
This program gives for its output
\begin{verbatim}
F =
+ x_1 y^2 {\cal Z}
+ T(\mu,\nu,\rho,\sigma)
+ S_1(N)
+ S_{1,1}(N)
+ S_2(N)
+ \ln(1 + x_1)
;
\end{verbatim}
Of course, there is nothing here that could not have been done with a good
text editor, but having this inside the \FORM{} program makes that if there
are changes in the \FORM{} program, it will be less work to implement them in
the eventual \LaTeX{} files.
Things become different when numerical\index{numerical output} output is
involved. Take for instance the fraction $1/3$ inside a
FORTRAN\index{fortran} program.
Using the option
\begin{verbatim}
Format Fortran;
\end{verbatim}
one would obtain
\begin{verbatim}
1./3.
\end{verbatim}
and with\index{doublefortran}
\begin{verbatim}
Format DoubleFortran;
\end{verbatim}
one would obtain
\begin{verbatim}
1.D0/3.D0
\end{verbatim}
while using\index{quadfortran}
\begin{verbatim}
Format QuadFortran;
\end{verbatim}
one would obtain
\begin{verbatim}
1.Q0/3.Q0
\end{verbatim}
which means that one might have three varieties of the same program,
depending on the precision in which one would like run it. It would be far
better to have a single version and only determine in the make file what
the precision should be. The FORTRAN code for such a program could look
like
\begin{verbatim}
REAL one,three,third
PARAMETER (one=1,three=3,third=one/three)
\end{verbatim}
after which one should either use the name 'third' or a construction like
'one/three'. Let us take a simple program like
\begin{verbatim}
Symbol x,n;
Format DoubleFortran;
Local F = (1+x)^7/7;
id x^n? = x*x^n/(n+1);
Print;
.end
F =
& 1.D0/7.D0*x + 1.D0/2.D0*x**2 + x**3 + 5.D0/4.D0*x**4 + x**5 + 1.D
& 0/2.D0*x**6 + 1.D0/7.D0*x**7 + 1.D0/56.D0*x**8
\end{verbatim}
If we define a dictionary we can make this into
\begin{verbatim}
Symbol x,n;
Format DoubleFortran;
#OpenDictionary numbers
#add 2: "TWO"
#add 5: "FIVE"
#add 7: "SEVEN"
#CloseDictionary
Local F = (1+x)^7/7;
id x^n? = x*x^n/(n+1);
#UseDictionary numbers
Print;
.end
F =
& 1/SEVEN*x + 1/TWO*x**2 + x**3 + FIVE/4*x**4 + x**5 + 1/TWO*x**6
& + 1/SEVEN*x**7 + 1.D0/56.D0*x**8
\end{verbatim}
one can see that some of the numbers have been replaced by text strings. In
particular these are the numbers 2, 5 and 7. The output is now presented in
such a way that the compiler can do the rest, provided we do this with all
numbers that occur, and we feed the proper information to the compiler.
One can also replace complete fractions as in
\begin{verbatim}
Symbol x,n;
Format DoubleFortran;
#OpenDictionary numbers
#add 2: "TWO"
#add 5: "FIVE"
#add 7: "SEVEN"
#add 1/2: "HALF"
#CloseDictionary
Local F = (1+x)^7/7;
id x^n? = x*x^n/(n+1);
#UseDictionary numbers
Print;
.end
F =
& 1/SEVEN*x + HALF*x**2 + x**3 + FIVE/4*x**4 + x**5 + HALF*x**6 +
& 1/SEVEN*x**7 + 1.D0/56.D0*x**8
\end{verbatim}
because the fractions take precedence.
The next question is how one makes sure to have all numbers that need
replacement? For that one can use the warnings option:
\begin{verbatim}
Symbol x,n;
Format DoubleFortran;
#OpenDictionary numbers
#add 2: "TWO"
#add 5: "FIVE"
#add 7: "SEVEN"
#add 1/2: "HALF"
#CloseDictionary
Local F = (1+x)^7/7;
id x^n? = x*x^n/(n+1);
#UseDictionary numbers (warnings)
Print;
.end
Time = 0.00 sec Generated terms = 8
F Terms in output = 8
Bytes used = 204
F =
& 1/SEVEN*x + HALF*x**2 + x**3 + FIVE/4*x**4 + x**5 + HALF*x**6 +
>>>>>>>>Could not translate coefficient with dictionary numbers<<<<<<<<<
<<<
& 1/SEVEN*x**7 + 1.D0/56.D0*x**8
\end{verbatim}
In this case the line after the warning contains a fraction that was not
substituted. This allows one to add either $56$ or $1/56$ to the
dictionary. This gives the program
\begin{verbatim}
Symbol x,n;
Format DoubleFortran;
#OpenDictionary numbers
#add 2: "cd2"
#add 5: "cd5"
#add 7: "cd7"
#add 56: "cd56"
#add 1/2: "c1d2"
#add 5/4: "c5d4"
#CloseDictionary
Local F = (1+x)^7/7;
id x^n? = x*x^n/(n+1);
#UseDictionary numbers (warnings)
Print;
.end
F =
& 1/cd7*x + c1d2*x**2 + x**3 + c5d4*x**4 + x**5 + c1d2*x**6 + 1/
& cd7*x**7 + 1/cd56*x**8
\end{verbatim}
Here we have selected a different notation that allows extension easily.
A good way to do this now is to put the dictionary in a file numbers.hh and
the corresponding FORTRAN definitions in a file numbers.h and then include
these files in the proper places. The numbers.hh file would be
\begin{verbatim}
#OpenDictionary numbers
#add 2: "cd2"
#add 5: "cd5"
#add 7: "cd7"
#add 56: "cd56"
#add 1/2: "c1d2"
#add 5/4: "c5d4"
#CloseDictionary
\end{verbatim}
and the numbers.h file would be
\begin{verbatim}
REAL cd2,cd5,cd7,cd56,c1d2,c5d4
PARAMETER (cd2=2,cd5=5,cd7=7,cd56=56,c1d2=1/cd2,c5d4=cd5/4)
\end{verbatim}
and when the dictionary file is updated one may update the FORTRAN file
simultaneously.
Setting the precision of the declaration REAL\index{real} can be done by
compiler options. These may depend on the compiler. One should consult the
manpages.
Printing the extra symbols\index{extra symbols} (\ref{substaextrasymbols})
may be a bit trickier. A range\index{range} is indicated with
a pair of parentheses enclosing one or two (positive) numbers. If there are
two numbers, they should be separated by a comma. There can be more than
one range. In the substitution one can use the wildcards \verb:%#: and
\verb:%@: to indicate the number of the extra symbol. The first
wildcard indicates the number of the symbol and the second starts it
counting with 1 from the beginning of the range.
\begin{verbatim}
Symbol x;
CFunction f;
#OpenDictionary ranges
#add (1,2): "w(%#)"
#add (3): "ww(%#)"
#add (4,6): "www(%@)"
#CloseDictionary
Local F = +...+;
ToPolynomial;
Print;
.sort
F =
x*Z1_ + x^2*Z2_ + x^3*Z3_ + x^4*Z4_ + x^5*Z5_ + x^6*Z6_;
#UseDictionary ranges
Print;
.end
F =
x*w(1) + x^2*w(2) + x^3*ww(3) + x^4*www(1) + x^5*www(2) + x^6*www(3);
\end{verbatim}
The use of the dictionaries in dollar variables can best be shown with an
example that has much in common with graph theory. Assume we have an
expression that contains all topologies we are interested in, with a
notation for the momenta. The function vx represents a vertex and we use it
as a symmetric function. Here we show two topologies from massless two-loop
propagators:
\begin{verbatim}
+vx(p0,p1,-p4)*vx(-p1,p2,p5)*vx(q0,-p2,-p3)*vx(p4,p3,-p5)*topo(1)
+vx(p0,p1,p2)*vx(-p1,p3,p4)*vx(q0,-p2,-p3,-p4)*topo(2)
\end{verbatim}
where the q0 momentum is taken to be -p0. The problem is what happens when
in a diagram of topology one, one of the lines is removed. If for instance
the p1 line is removed, we will end up with the second topology, but the
question is: how should we relabel the momenta to obtain the notation of
topology 2. Taking out p1 gives us:
\begin{verbatim}
+vx(p0,-p4,p2,p5)*vx(q0,-p2,-p3)*vx(p4,p3,-p5)*topo(1)
\end{verbatim}
and to see what renaming we need is usually a major source of errors.
We can do this automatically if we can substitute the second topology into
the remainder of the first using proper wildcards and storing the matches
in dollar variables. This can be done with a dictionary:
\begin{verbatim}
#OpenDictionary match
#add p0: "p0?{p0,q0}$p0"
#add q0: "q0?{p0,q0}$q0"
#do i = 1,5
#add p`i': "p`i'?$p`i'"
#enddo
#CloseDictionary
\end{verbatim}
We put the various candidate topologies that could match, one by one, into
the variable \$child as in (after using brackets on the expression with the
topologies):
\begin{verbatim}
#$child = Topologies[topo(2)];
\end{verbatim}
but generating an id-statement from it would be very laborious without the
dictionaries:
\begin{verbatim}
id `$Orig' = 1;
\end{verbatim}
would result in:
\begin{verbatim}
id vx(-p2,-p3,q0)*vx(-p4,p0,p2,p5)*vx(-p5,p3,p4) = 1;
\end{verbatim}
but with the dictionary activated as in
\begin{verbatim}
#inside $child
#UseDictionary match($)
id `$Orig' = 1;
#CloseDictionary
#endinside
\end{verbatim}
the generated code is
\begin{verbatim}
id vx(-p2?$p2,-p3?$p3,q0?{p0,q0}$q0)*vx(-p4?$p4,p0?{p0,q0}$p0,
p2?$p2,p5?$p5)*vx(-p5?$p5,p3?$p3,p4?$p4) = 1;
\end{verbatim}
and from the dollar variables we can generate a statement with the the
renumbering
\begin{verbatim}
id topo(1) = topo(2)*replace_(p0,-p0,p1,q1,p2,-p2,p3,-p1,p4,p3,p5,-p4);
\end{verbatim}
We used $p_1\rightarrow q_1$ as initialization before the pattern matching
and $p_0 = q_0$ we can replace by $p_0 = p_0$. The $q_1$ should be replaced
by means of momentum conservation, but that goes beyond the scope of this
example.
It should be clear from the above that the dictionaries are the beginning
of a new development. One should expect more capabilities in the future and
suggestions are highly appreciated, provided they lead to something that
can be implemented in a reasonable amount of time. Hence, for instance,
there will not be a complete \LaTeX{} output format that can take line length
into account.
form-master/doc/manual/dollar.tex 0000664 0000000 0000000 00000034115 13565763364 0017361 0 ustar 00root root 0000000 0000000
\chapter{The dollar variables}
\label{dollars}
In the older versions of \FORM\ there were two types of variables: the
preprocessor variables\index{variables!preprocessor} and the algebraic
variables\index{variables!algebraic}. The preprocessor
variables are string variables that are used by the edit features of the
preprocessor to prepare the input for the compiler part of \FORM. The
algebraic objects are the expressions and the various algebraic variables
like the symbols, functions, vectors etc. There existed however very few
possibilities to communicate from the algebraic level to the decision
taking at the preprocessor level. This has changed dramatically with
version 3 and the introduction of the dollar
variables\index{variables!dollar}.
Dollar variables are basically (little) expressions that can be used to
store various types of information. They can be used both as preprocessor
objects as well as algebraic objects. They can also be defined and given
contents both by the preprocessor and during execution on a term by term
basis. Dollar variables are kept in memory. Hence it is important not to
make them too big, because in that case performance might suffer.
What is a legal name for a dollar variable?
Dollar variables have a name that consists of a dollar sign (\verb:$:)
followed by an alphabetic character and then potentially more alphanumeric
characters. Hence \verb:$a: and \verb:$var: and \verb:$r4t78y0: are legal
names and \verb:$1a: is not a legal name. The variables do not have to be
declared. However \FORM\ will complain if a dollar variable is being used,
before it has encountered a statement or an instruction in which the
variable has been given a value. Hence giving a variable a value counts at
the same time as a declaration.
What can be stored in a dollar variable?
\begin{itemize}
\item Algebraic expressions as in \verb:$var = (a+b)^2;:
\item Individual objects like indices, numbers, symbols.
\item Zero.
\item Parts of a term.
\item Argument\index{argument field} fields that consist of zero, one or
more arguments.
\end{itemize}
Actually, the parts of a term are treated as a complete term and hence as a
special case of an algebraic expression. Internally they are stored
slightly differently for speed, but at the user level this should not be
noticeable. Actually, with the exception of the argument fields, \FORM\ can
convert one type into the other and will try so, depending on the use that
is made of the specific dollar variable. In the case that a variable is
used in a way that should not be possible (like the content of a variable
is a symbol, but it is used in a position where an index is expected) there
will be a runtime\index{runtime error} error\index{error!runtime}.
How is a variable used?
\begin{itemize}
\item As a preprocessor variable\index{variable!preprocessor}. This is done
by putting the variable between a pair of `' as in \verb:`$var':. In this
case the regular print routines of \FORM\ make a textual
representation\index{representation!textual} of
the variable as it exists at the moment that the preprocessor encounters
this object, and this string is then substituted by the preprocessor as if
it were the contents of a preprocessor variable.
\item Like an expression during execution time. This would be the case in
the statement
\begin{verbatim}
id x = y + $var;
\end{verbatim}
in which \verb:$var: is substituted
in a way that is similar to the substitution of a local expression \verb:F:
in the statement
\begin{verbatim}
id x = y + F;
\end{verbatim}
except for that the dollar variable is always stored in the CPU memory.
\item As an algebraic object during execution time. This could be the case
with any value of the variable that is not an expression. An example would
be
\begin{verbatim}
id f(?a) = f(?a,$var);
\end{verbatim}
in which the dollar variable contains an
argument field.
\item As an algebraic object in a delayed substitution of a pattern or a
special statement. This may need some clarification. If we have the
statement
\begin{verbatim}
id f($var) = anything;
\end{verbatim}
the compiler does not substitute
the current value of \verb:$var:. The reason is that \verb:$var: could have
a different value for each term that runs into this statement, while the
compiler compiles the statement only once. Hence \FORM\ will substitute the
value of \verb:$var: only at the moment that it will attempt the pattern
matching. This is called delayed\index{delayed substitution}
substitution\index{substitution!delayed}. If one likes the compiler to
substitute a value, one can basically let the preprocessor take care of
this by typing
\begin{verbatim}
id f(`$var') = anything;
\end{verbatim}
A similar delayed
substitution takes place in statements of the type \verb:Trace,$var;:.
\end{itemize}
How does one give a value to a dollar variable?
\begin{itemize}
\item In the preprocessor. This is done with an instruction of the type
\verb:#$var = 0;:. This is an instruction that can run over more than one
line. The r.h.s. can be any algebraic expression. Specifically it can
contain dollar variables or local/global expressions. Such
expressions are worked out during the preprocessing. Hence this variable
acquires a value immediately.
\item During execution when control reaches a statement of the form
\verb:$var = expression;:. Again the r.h.s. can contain any normal
algebraic expression including dollar variables and local/global
expressions. The r.h.s. will be evaluated and the value will be assigned to
\verb:$var:. In the case that \verb:$var: had already a value, the old
value will be deleted and the new value will be `installed'.
\item During execution when the dollar variable is assigned the value of a
wildcard as in
\begin{verbatim}
id f(x?$var) = whatever;
\end{verbatim}
If the function \verb:f:
occurs more than once in a term, \verb:$var: will have the value of the
last match. In the case that the value of the first match is needed one can
use the option `once' in the id-statement as in
\begin{verbatim}
id,once,f(x?$var) = whatever;
\end{verbatim}
In general one can paste the dollar variable to the end of any
wildcard description. Hence one can use \verb:id f(x?{1,2,3}$var) = ...;:
and
\begin{verbatim}
id f(x?set[n?$var1]$var2) = ...;
\end{verbatim}
\end{itemize}
Note the difference between \verb:#$a = 0;: and \verb:$a = 0;:. One CANNOT
make a wildcard\index{wildcard} construction for dollar variables
themselves as in \verb:id f($var?) = ...;:
Dollar variables CANNOT have arguments as in \verb:$var(2): or something
equivalent. There is however a solution at the preprocessor level for this
by defining individual variables \verb:$var1: to \verb:$varn: and then
using \verb:$var`i': or \verb:`$var`i'': for some preprocessor variable
\verb:i:. The exception is the indication of factors when a dollar variable
has been factorized (see the \#factdollar instruction~\ref{prefactdollar}
and the factdollar statement~\ref{substafactdollar}). This is explained
later in this chapter and in the chapter about
polynomials~\ref{polynomials}.
Printing dollar\index{dollar!printing} variables:
\begin{itemize}
\item In the preprocessor one can use the \verb:#write: instruction
(see \ref{prewrite}).
\item During execution one can use the Print statement (see
\ref{substaprint}).
\end{itemize}
In both cases one should use the format\index{format} string. The syntax is
described in the chapters on these statements. The format descriptor of a
dollar variable is \verb:%$: and this looks after the format string for the
next dollar variable. Of course one can also use the dollar variable as a
preprocessor variable when printing/writing in the preprocessor.
Examples.
Counting terms:
\begin{verbatim}
S a,b;
Off statistics;
L F = (a+b)^6;
#$a = 0;
$a = $a+1;
Print " >> After %t we have %$ term(s)",$a;
#write " ># $a = `$a'"
># $a = 0
.sort
>> After + a^6 we have 1 term(s)
>> After + 6*a^5*b we have 2 term(s)
>> After + 15*a^4*b^2 we have 3 term(s)
>> After + 20*a^3*b^3 we have 4 term(s)
>> After + 15*a^2*b^4 we have 5 term(s)
>> After + 6*a*b^5 we have 6 term(s)
>> After + b^6 we have 7 term(s)
#write " ># $a = `$a'"
># $a = 7
.end
\end{verbatim}
\noindent Maximum power of x in an expression:
% THIS EXAMPLE IS PART OF THE TESTSUITE. CHANGES HERE SHOULD BE APPLIED THERE AS
% WELL!
\begin{verbatim}
S x,a,b;
Off statistics;
L F = (a+b)^4+a*(a+x)^3;
.sort
#$a = 0;
if ( count(x,1) > $a ) $a = count_(x,1);
Print " >> After %t the maximum power of x is %$",$a;
#write " ># $a = `$a'"
># $a = 0
.sort
>> After + 3*x*a^3 the maximum power of x is 1
>> After + 3*x^2*a^2 the maximum power of x is 2
>> After + x^3*a the maximum power of x is 3
>> After + 4*a*b^3 the maximum power of x is 3
>> After + 6*a^2*b^2 the maximum power of x is 3
>> After + 4*a^3*b the maximum power of x is 3
>> After + 2*a^4 the maximum power of x is 3
>> After + b^4 the maximum power of x is 3
#write " ># $a = `$a'"
># $a = 3
.end
\end{verbatim}
Starting with version 4, \FORM\ has the capability to factorize polynomials
(see the chapter on polynomials~\ref{polynomials}). One type of objects
that can be factorized is the dollar variables. The immediate question here
is how to access the factors. As we mentioned before in this chapter,
normally there is no direct way to use arguments for dollar variables. For
the factors however we have a way of indexing the dollar variables as in
\verb:$var[1]:,...,\verb:$var[n]: when there are n factors. The number of
factors can be obtained as \verb:$var[0]:. In the index field can only be
(nonnegative integer) numbers, dollar variables or factors of dollar
variables that evaluate into (nonnegative integer) numbers.
\begin{verbatim}
Symbol x,y;
CFunction f1,f2;
Local F = f1(x^2+2*x*y+y^2)+f1(x^4-y^4);
id f1(x?$x) = f2(x);
FactDollar,$x;
Do $i = 1,$x[0];
Print "In %t factor %$ is %$",$i,$x[$i];
Enddo;
.end
In + f2(y^2 + 2*x*y + x^2) factor 1 is y + x
In + f2(y^2 + 2*x*y + x^2) factor 2 is y + x
In + f2( - y^4 + x^4) factor 1 is - 1
In + f2( - y^4 + x^4) factor 2 is y - x
In + f2( - y^4 + x^4) factor 3 is y + x
In + f2( - y^4 + x^4) factor 4 is y^2 + x^2
\end{verbatim}
One thing to note is that the use of
\begin{verbatim}
f(<$x[1]>,...,<$x[$x[0]]>)
\end{verbatim}
is illegal. \verb:$x[0]: will be inserted during execution time, while the
expansion of the triple dot operator is done by the preprocessor. Hence we
should use \verb:`$x[0]': but then \verb:$x: must be known and factorized
already at compile time.
\section{Dollar variables in a parallel environment}
\label{pardollars}
When \FORM\ is used for parallel\index{parallel processing} processing,
either by means of \ParFORM\index{ParFORM} or by means of
\TFORM\index{TFORM}, there can be a problem with the dollar variables as in
principle there is a central administration and dollar variables that are
defined during running will in general have the last assigned value. In a
parallel environment this can be nondeterministic\index{nondeterministic}.
Look for instance at the following example:
\begin{verbatim}
S x,a,b;
CF f;
L F = f(a+b) + f(a+2*b);
.sort
id f(x?$x) = f(x);
Multiply,$x;
Print;
.end
\end{verbatim}
Usually this program will give the 'correct' answer, but in principle one
thread could define \verb:$x: and then the next thread could overwrite this
value before the first thread has used it. This is serious. Hence \FORM\
will veto\index{veto} the use of multiple threads/processors for modules in
which dollar variables obtain values during the execution of the program,
unless the user can give \FORM\ more information about the use of the
dollar variables. In the above case the value of \verb:$x: will be local to
each term and hence to each thread\index{thread}. The value in previous
terms is unimportant. We can tell this to \FORM\ with a variety of the
moduleoption\index{moduleoption} statement (see \ref{substamoduleoption}).
This would be:
\begin{verbatim}
S x,a,b;
CF f;
L F = f(a+b) + f(a+2*b);
.sort
id f(x?$x) = f(x);
Multiply,$x;
Print;
ModuleOption,local,$x;
.end
\end{verbatim}
In this case \FORM\ makes at the start of the execution of the module a
copy of whatever value \verb:$x: has at that moment for each
thread/processor (in this case no value yet and hence it gets set to zero)
and then each thread/processor uses its own copy during execution. After
the module has been completed the local copies are removed and the original
global value is accessible again. This way execution will be safe in a
parallel environment.
There are more cases that \FORM\ can handle in a parallel environment.
These are also options in the moduleoption statement:
\begin{verbatim}
ModuleOption,maximum,$a;
ModuleOption,minimum,$b;
ModuleOption,sum,$c;
\end{verbatim}
Here we say that \verb:$a: is accumulating a maximum numerical value,
\verb:$b: collects a minimum numerical value and \verb:$c: is a numerical
sum. In all three cases there is a central administration and the use of
the variables has to be blocked for other threads/processors during the
updating of the values. Sometimes that can be efficient, but in other
programs that may actually make them slower. One should experiment. A
sample program is given below:
% THIS EXAMPLE IS PART OF THE TESTSUITE. CHANGES HERE SHOULD BE APPLIED THERE AS
% WELL!
\begin{verbatim}
S a1,...,a10;
L F = (a1+...+a10)^3;
.sort
#$c = 0;
Print +f "<%w> %t";
Multiply,(a1+...+a10);
$c = $c+1;
ModuleOption,sum,$c;
.sort
#message $c = `$c'
#$max = 0;
#$min = 10;
if ( count(a1,1) > $max ) $max = count_(a1,1);
if ( count(a4,1) < $min ) $min = count_(a4,1);
ModuleOption,maximum,$max;
ModuleOption,minimum,$min;
.sort
#message $max = `$max'
#message $min = `$min'
.end
\end{verbatim}
The print statement is showing which thread is dealing with which term.
form-master/doc/manual/external.tex 0000664 0000000 0000000 00000037557 13565763364 0017743 0 ustar 00root root 0000000 0000000 \chapter{External communication}
\label{externalcommunication}
To communicate\index{communication!external} with other programs \FORM\ is
equipped with special commands. One set of commands is rather simple in
nature: the \#pipe\index{\#pipe} (see section \ref{prepipe}) and
\#system\index{\#system} (see section \ref{presystem}) instructions allow
\FORM\ to run programs in the regular command\index{command shell}
shell\index{shell}. Sometimes however much more sophistication is needed
because these instructions have a rather large overhead and need to start
new processes each time they are executed. Hence a second more extensive
set of instructions was developed that allows the start of an external
process\index{process!external}, keep it open and maintain a two way
communication\index{communication!two way} with it. Similarly it is
possible to start \FORM\ in such a way from other programs. Many details of
the method of implementation and a number of examples are given in a
separate paper which can also found in the \FORM\ site
(http://www.nikhef.nl/$\sim$form) under publications (look for the file
extform.ps\index{extform.ps} or extform.pdf\index{extform.pdf}). Here we
will just show the essentials and the syntax.
The basic idea is to open (by means of the preprocessor) a number of
external channels\index{channel!external} (there is no reason to be
restricted to just one) by starting the corresponding program in a command
shell. This program is kept running and a number is assigned to each
channel. Next we can select a channel and communicate with it. To not run
into syntactic problems, because the external program may have different
ideas of what a formula should look like, one may have to install
filters\index{filter}. These are additional programs that should be
prepared before the \FORM\ program is started that process the communication
to convert from one notation to the other.
%--#[ #external :
\section{\#external}
\label{external}
\noindent Syntax:
\#external ["prevar"] systemcommand
\noindent See also
\noindent Starts\index{\#external} the command in the background,
connecting to its standard input and output. By default, the external
command has no controlling terminal, the standard error stream is
redirected to \verb|/dev/null| and the command is run in a subshell in a
new session and in a new process group (see the preprocessor instruction
\verb|#setexternalattr|).
The optional parameter ``prevar'' is the name of a preprocessor variable
placed between double quotes. If it is present, the ``descriptor'' (small
positive integer number) of the external command is stored into this
variable and can be used for references to this external command (if there
is more than one external command running simultaneously).
The external command that is started last becomes the ``current'' (active)
external command. All further instructions
\#fromexternal\index{\#fromexternal} and \#toexternal\index{\#toexternal}
deal with the current external command.
%--#] #external :
%--#[ #toexternal :
\section{\#toexternal}
\label{toexternalcommunication}
\noindent Syntax:
\#toexternal "formatstring" [,variables]
\noindent See also
\noindent Sends\index{\#toexternal} the output to the current
external\index{\#external} command. The semantics of the
\verb|"formatstring"| and the \verb|[,variables]| is the same as for the
\#write\index{\#write} instruction, except for the trailing end-of-line symbol. In
contrast to the \#write instruction, the \#toexternal instruction does not
append any newline\index{newline} symbol to the end of its output.
%--#] #toexternal :
%--#[ #fromexternal :
\section{\#fromexternal}
\label{fromexternalcommunication}
\noindent Syntax:
\#fromexternal[$+-$] ["[\$]varname" [maxlength]]
\noindent Appends\index{\#fromexternal} the output of the current
external\index{\#external}
command to the \FORM\ program. The semantics differ depending on the optional
arguments. After the external command sends the prompt, \FORM\ will continue
with a next line after the line containing the \#fromexternal instruction.
The prompt string is not appended. The optional + or - sign after the name
has influence on the listing of the content. The varieties are:
\#fromexternal[$+-$]
\noindent The semantics is similar to the \#include\index{\#include}
instruction but folders\index{folders} are not supported.
\#fromexternal[$+-$] "[\$]varname"
\noindent is used to read the text from the running external command into
the preprocessor variable varname, or into the dollar variable \$varname if
the name of the variable starts with the dollar sign ``\$''.
\#fromexternal[$+-$] "[\$]varname" maxlength
\noindent is used to read the text from the running external command into
the preprocessor (or dollar) variable varname. Only the first maxlength
characters are stored.
%--#] #fromexternal :
%--#[ #prompt :
\section{\#prompt}
\label{promptcommunication}
\noindent Syntax:
\#prompt [newprompt]
\noindent Sets\index{\#prompt} a new prompt for the current external
command (if present) and all further (newly started) external commands.
If newprompt is an empty string, the default prompt (an empty line) will be
used.
The prompt is a line consisting of a single prompt string. By default, this
is an empty string.
%--#] #prompt :
%--#[ #setexternal :
\section{\#setexternal}
\label{setexternalcommunication}
\noindent Syntax:
\#setexternal n
\noindent Sets the ``current'' external\index{\#setexternal} command. The
instructions \#toexternal\index{\#toexternal} and
\#fromexternal\index{\#fromexternal} deal with the current external
command. The integer number n must be the descriptor of a running external
command.
%--#] #setexternal :
%--#[ #rmexternal :
\section{\#rmexternal}
\label{rmexternalcommunication}
\noindent Syntax:
\#rmexternal [n]
\noindent Terminates an external\index{\#rmexternal} command. The integer
number n must be either the descriptor of a running external command, or 0.
If n is 0, then all external programs will be terminated.
If n is not specified, the current external command will be terminated.
The action of this instruction depends on the attributes of the external
channel (see the \#setexternalattr\index{\#setexternalattr} (section
\ref{setexternalcommunication}) instruction). By default, the instruction
closes the commands' IO channels, sends a KILL\index{KILL signal} signal to
every process in its process group and waits for the external command to be
finished.
%--#] #rmexternal :
%--#[ #setexternalattr :
\section{\#setexternalattr}
\label{setexternalattrcommunication}
\noindent Syntax:
\#setexternalattr list\_of\_attributes
\noindent sets\index{\#setexternalattr} attributes for {\em newly started}
external commands. Already running external commands are not affected. The
list of attributes is a comma separated list of pairs attribute=value,
e.g.:
\begin{verbatim}
#setexternalattr shell=noshell,kill=9,killall=false
\end{verbatim}
Possible attributes are:
\begin{description}
\item[kill\index{kill}]
Specifies which signal is to be sent to the external command either before
the termination of the \FORM\ program or by the preprocessor instruction
\#rmexternal\index{\#rmexternal}. By default this is 9
(SIGKILL\index{SIGKILL signal}). Number 0 means that no signal will be
sent.
\item[killall\index{killall}] Indicates whether the KILL\index{KILL signal}
signal will be sent to the whole group or only to the initial process.
Possible values are ``\verb|true|'' and ``\verb|false|''. By default, the
kill signal will be sent to the whole group.
\item[daemon\index{daemon}]
Indicates whether the command should be ``daemonized'', i.e.
the initial process will be passed to the init process and will belong
to the new process group in the new session.
Possible values are ``\verb|true|'' and ``\verb|false|''. By default,
``\verb|true|''.
\item[shell\index{shell}]
specifies which shell\index{shell} is used to run a command. (Starting an
external command in a subshell permits to start not only executable files
but also scripts and pipelined jobs. The disadvantage is that there is no
way to detect failure upon startup since usually the shell is started
successfully.) By default this is ``\verb|/bin/sh -c|''. If set
\verb|shell=noshell|, the command will be started by the instruction
\#external\index{\#external} directly but not in a subshell, so the command
should be a name of the executable file rather than a system command. The
instruction \#external will duplicate the actions of the shell in searching
for an executable file if the specified file name does not contain a slash
(/) character. The search path\index{path!search} is the path specified in
the environment by the PATH\index{PATH} variable. If this variable isn't
specified, the default path ``\verb|:/bin:/usr/bin|'' is used.
\item[stderr\index{stderr}]
specifies a file to redirect the standard error\index{error stream} stream
to. By default it is ``\verb|/dev/null|''. If set \verb|stderr=terminal|,
no redirection occurs.
\end{description}
Only attributes that are explicitly mentioned are changed, all others remain
unchanged. Note, changing attributes should be done with care. For example,
\begin{verbatim}
#setexternalattr daemon=false
\end{verbatim}
starts a command in the subshell within the current process group with
default attributes kill=9 and killall=true.
The instruction \#rmexternal\index{\#rmexternal} sends the
KILL\index{KILL signal} signal to the whole group, which means that also
\FORM\ itself will be killed.
%--#] #setexternalattr :
%--#[ An example :
\section{An example}
An example of the above instructions could be:
% THIS EXAMPLE IS PART OF THE TESTSUITE. CHANGES HERE SHOULD BE APPLIED THERE AS
% WELL!
\begin{verbatim}
1 symbol a,b;
2
3 #external "n1" cat -u
4
5 #external "n2" cat -u
6
7 * cat simply repeats its input. The default prompt is an
8 * empty line. So we use "\n\n" here -- one "\n" is to finish
9 * the line, and the next "\n" is the prompt:
10 #toexternal "(a+b)^2\n\n"
11
12 #setexternal `n1'
13 * For this channel the prompt will be "READY\n":
14 #toexternal "(a+b)^3\nREADY\n"
15
16 #setexternal `n2'
17 * Set the default prompt:
18 #prompt
19 Local aPLUSbTO2=
20 #fromexternal
21 ;
22
23 #setexternal `n1'
24 #prompt READY
25 Local aPLUSbTO3=
26 #fromexternal
27 ;
28
29 #rmexternal `n1'
30 #rmexternal `n2'
31
32 Print;
33 .end
\end{verbatim}
Two external channels are opened in lines 3 and 5. The UNIX\index{UNIX}
utility ``\verb|cat|'' simply repeats its input.
The option ``\verb|-u|'' is used to prevent the output buffering. The
option is ignored by the GNU\index{GNU} \verb|cat| utility but is mandatory
for non-GNU versions of \verb|cat|.
After line 5 the current external channel is `\verb|n2|'. The default
prompt is an empty line so in line 10 ``\verb|\n\n|'' is used --
one``\verb|\n|'' is to finish the line, and the next ``\verb|\n|'' is the
prompt.
Line 12 switches the current channel to `\verb|n1|'. For this channel
the prompt will be ``\verb|READY|'', see line 24, hence the expression is
finished by ``\verb|\nREADY\n|''.
Line 16 switches to the `\verb|n2|' external channel and line 18 sets
the default prompt (which is extra in this example since the default
prompt was not changed up to now).
Results (just a literal repetition of the sent expressions) are read
in lines 20 and 26.
Lines 29 and 30 close the external channels.
%--#] An example :
%--#[ Embedding :
\section{Embedding FORM in other applications}
\label{embeddingcommunication}
The external channel instructions permit \FORM\ to swallow an external
program. The same mechanism can be used in order to {\em
embed\index{embed}} \FORM\ in other applications.
There is a possibility to start \FORM\ from another program providing
one (or more) communication channels (see below). These channels will be
visible from a \FORM\ program as
``pre-opened''\index{pre-opened external channels} external channels
existing after \FORM\ starts. There is no need to open them with the
\#external\index{\#external} instruction.
In this case, the preprocessor variable ``PIPES\_''\index{PIPES\_} is
defined and is equal to the total number of the pre-opened external
channels. Pre-opened external channel descriptors are contained in the
preprocessor
variables ``PIPE1\_''\index{PIPE1\_}, ``PIPE2\_''\index{PIPE2\_}, etc.
For example, if `PIPES\_'\index{PIPES\_} is 3 then there are 3
pre-opened external channels with the descriptors `PIPE1\_',
`PIPE2\_' and `PIPE3\_' so e.g. the following instruction could
be used:
\begin{verbatim}
#setexternal `PIPE2_'
\end{verbatim}
without
\begin{verbatim}
#external "PIPE2_"
\end{verbatim}
The external channel attributes make no sense for the pre-opened channel
(see the \#setexternalattr\index{\#setexternalattr} instruction (section
\ref{setexternalattrcommunication})).
Formally, they are as follows:
\begin{verbatim}
kill=0,
killall=false,
daemon=false,
stderr=/dev/tty,
shell=noshell
\end{verbatim}
In order to activate the pre-opened external channels, the parent
application must follow some standards. Here we describe a low-level
protocol\index{protocol!lowlevel}, the corresponding
C-interface\index{C-interface} is available from the \FORM\ distribution site
under packages and then externalchannels.
Before starting \FORM, the parent application must create one or more pairs
of pipes. A pipe\index{pipe} is a pair of file descriptors, one is for
reading and another is for writing. In LINUX\index{LINUX}, see ``man 2
pipe''. The read-only descriptor of the first pipe in the pair and the
write-only descriptor of the second pipe must be passed to \FORM\ as an
argument of a command line option ``\verb|-pipe|'' in ASCII decimal format.
The argument of the option is a comma-separated list of pairs
``\verb|r#,w#|'' where ``\verb|r#|'' is a read-only descriptor and
``\verb|w#|'' is a write-only descriptor; alternatively, an environment
variable FORM\_PIPES\index{FORM\_PIPES} containing this list can be used
(the command line option overrides the environment variable). For example,
to start \FORM\ with two pre-opened external channels the parent application
has to create first four pipes. Lets us suppose the first pipe was created
with the descriptors 5 and 6, the second pipe has the descriptors 7 and 8,
the third pipe has the descriptors 9 and 10 and the fourth pipe has the
descriptors 11 and 12. The descriptors 5 and 8 will be used by \FORM\ as the
input and the output for the first pre-opened external channel while the
descriptors 9 and 12 will be used by \FORM\ as the input and the output for
the second pre-opened external channel.
Then the parent application must start \FORM\ with the following
command line option:
\begin{verbatim}
-pipe 5,8,9,12
\end{verbatim}
Upon startup, \FORM\ sends its PID\index{PID} (the Process Identifier) in
ASCIIdecimal format with an appended newline character to the descriptor 8
and then \FORM\ will wait for the answer from the descriptor 5. The answer
must be two comma-separated integers in ASCII decimal format followed by a
newline character. The first integer corresponds to the \FORM\ PID while the
second one is the parent process PID. If the answer is not obtained after
some timeout, or if it is not correct (i.e. it is not a list of two
integers or the first integer is not the \FORM\ PID) then \FORM\ fails. If
everything is correct, \FORM\ creates the pre-opened channel and puts its
descriptor in the preprocessor variable ``PIPE1\_''.
Then \FORM\ processes the second pair of arguments, ``\verb|9,12|''.
After all pairs have been processed \FORM\ creates the preprocessor variable
``PIPES\_'' and puts into this variable the total number of created
pre-opened external channels.
The order of processing the pairs of numbers in the argument is fixed
exactly as it was described above i.e. from the left to the right.
%--#] Embedding :
form-master/doc/manual/functions.tex 0000664 0000000 0000000 00000143126 13565763364 0020117 0 ustar 00root root 0000000 0000000
\chapter{Functions}
\label{functions}
%--#[ General :
\noindent Functions\index{function} are objects that can have arguments.
There exist several types of functions in \FORM. First there is the
distinction between commuting\index{commuting} and
noncommuting\index{noncommuting} functions. Commuting functions commute
with all other objects. This property is used by the normalization routines
that bring terms into standard form. Noncommuting functions do not commute
necessarily with other noncommuting functions. They do however commute with
objects that are considered to be commuting, like symbols, vectors and
commuting functions. Various instances of the same noncommuting function
but with different arguments do not commute either.
\noindent The next subdivision of the category of functions is in regular
functions\index{function!regular}, tensors\index{tensor} and
tables\index{table}. Tensors are special functions that can have only
indices or vectors for their arguments. If an argument is a vector, it is
assumed that this vector is there as the result of an index contraction.
Tables are functions with automatic substitution rules. A table must have
at least one table\index{table index} index\index{index!table}. Each time
during normalization \FORM\ will check whether an instance of a table can be
substituted. This means that undefined table elements will slow the program
down somewhat.
\noindent All the various types of functions are declared with their own
declaration statements. These are described in the chapter for the
statements (see chapter~\ref{statements}).
%--#] General :
%--#[ Wildcards :
One of the useful properties of functions is the
wildcarding\index{wildcard} of their arguments during pattern matching. The
following argument wildcards are possible:
\leftvitem{2cm}{x?}
\rightvitem{14cm}{Here x is a symbol. This symbol can match either a
symbol, any numerical argument, or a complete subexpression argument that
is not vectorlike or indexlike.}
\leftvitem{2cm}{i?}
\rightvitem{14cm}{Here i is an index. This index can match either an index,
a vector (actually the dummy\index{dummy} index\index{index!dummy} of the
vector that was contracted), or a complete subexpression that is vector like
(again actually the contracted dummy index).}
\leftvitem{2cm}{v?}
\rightvitem{14cm}{Here v is a vector. This vector can match either a vector
or a complete subexpression that is vector like.}
\leftvitem{2cm}{f?}
\rightvitem{14cm}{Here f is any functiontype. This function can match any
function. It is the responsibility of the user to avoid problems in the
right-hand side if f happens to match a tensor.}
\leftvitem{2cm}{?a}
\rightvitem{14cm}{This is an argument\index{argument field} field
wildcard\index{wildcard!argument field}. This can match a
complete set of arguments. The set can be empty. Argument field wildcards
have a name that starts with a question mark followed by a name. They do
not have to be declared as there cannot be confusion.}
%--#] Wildcards :
\noindent In addition to the above syntax \FORM\ knows a number of special
functions with well defined properties. All these functions have a name
that ends in an underscore. In addition the names of these built in objects
are case insensitive. This means for instance that the factorial function
can be referred to as \verb:fac_:, \verb:Fac_: or \verb:FAC_: or whatever
the user considers more readable. The built in functions are:
%--#[ abs_ :
\section{abs\_}\index{abs\_}\index{function!abs\_}
\label{funabs}
\noindent With one argument that is numerical it evaluates into the
absolute value of the argument.
%--#] abs_ :
%--#[ bernoulli_ :
\section{bernoulli\_}\index{bernoulli\_}\index{function!bernoulli\_}
\label{funbernoulli}
\noindent If it has one nonzero integer argument n, it evaluates into
the n-th coefficient in the power series expansion of $x/(1-e^{-x})$.
%--#] bernoulli_ :
%--#[ binom_ :
\section{binom\_}\index{binom\_}\index{function!binom\_}
\label{funbinom}
\noindent binom\_(n,i) $= n!/(i!(n-i)!)$. If the arguments are non
integer or negative, no substitution is made.
%--#] binom_ :
%--#[ conjg_ :
\section{conjg\_}\index{conjg\_}\index{function!conjg\_}
\label{funconjg}
\noindent Currently not doing anything.
%--#] conjg_ :
%--#[ content_ :
\section{content\_}\index{content\_}\index{function!content\_}
\label{funcontent}
\noindent This function expects the name of a single expression or a dollar
variable for its
argument. If it finds this the content of this expression or dollar
variable is returned. The
content is defined as a term that has
\begin{itemize}
\item for its numerator the GCD of the numerators of all terms in the
expression.
\item for its denominator the LCM of the denominators of all terms in the
expression.
\item all the common subexpressions in all terms of the expression.
\item the most negative powers of all symbols and dotproducts with negative
powers in the terms of the expression.
\end{itemize}
When there are no negative powers and no denominators in the coefficients,
this definition of the content co\"{\i}ncides with the classical definition
of the content of a polynomial over the integers. Our content has the
property that if we divide the expression by it, we are left with an
expression of which the coefficients are all integer, there are no negative
powers and the GCD of all terms combined is one.
\noindent This function has one limitation. It will not consider
noncommuting objects. Neither will it consider denominator functions.
\noindent Caveat: this function is evaluated each time it is encountered.
Therefore the best thing is to evaluate it once in the definition of a
dollar variable or an expression as in
\begin{verbatim}
#$x = content_(F);
Local G = (a+b)^10*$x;
\end{verbatim}
Here the content is computed only once. In
\begin{verbatim}
Local G = (a+b)^10*content_(F);
\end{verbatim}
11 terms are generated and the content is only worked out when the
terms are normalized. This means that it will be evaluated 11 times. If one
does not like dollar variables and still wants to evaluate the content only
once the code would be
\begin{verbatim}
Local G = ab^10*content_(F);
id ab = a+b;
\end{verbatim}
because now the term will be normalized before the substitution makes it
into eleven terms. This assumes of course that the content does not contain
the variable ab.
%--#] content_ :
%--#[ count_ :
\section{count\_}\index{count\_}\index{function!count\_}
\label{funcount}
\noindent Similar to the count object in the if statement (see
\ref{substaif}). This function expects the same arguments as the count
object and returns the corresponding count value for the current term.
%--#] count_ :
%--#[ d_ :
\section{d\_}\index{d\_}\index{function!d\_}
\label{fund}
\noindent The kronecker\index{kronecker} delta\index{delta!kronecker}.
Should have two indices for arguments. Often indicated as
$\delta^{\mu\nu}$. In automatic summation over the indices the d\_ often
vanishes again as in
\verb:d_(mu,nu)*p(mu)*q(nu): $\rightarrow$ \verb:p.q: and similar
replacements. Internally this object is treated in a rather special way.
Hence it will not match a function wildcard.
%--#] d_ :
%--#[ dd_ :
\section{dd\_}\index{dd\_}\index{function!dd\_}
\label{fundd}
\noindent This is a combinatorics\index{combinatorics} function. The tensor
dd\_ with an even number of indices is equal to the totally symmetric
tensor built up from products of kronecker delta's. Each term in this
symmetric combination is normalized to one. In principle there are
$n!/(2^{n/2}(n/2)!$ terms in this combination. The profit comes when some
or all the indices are contracted with vectors and some of these vectors
are identical. In that case \FORM\ will use combinatorics to generate only
different terms, each with the proper prefactor. This can result in great
time and space savings.
%--#] dd_ :
%--#[ delta_ :
\section{delta\_}\index{delta\_}\index{function!delta\_}
\label{fundelta}
\noindent With one numerical argument the result is one if
the argument is zero and zero otherwise. With two arguments the result is
one if the arguments are numerical and identical. If they are numerical and
they differ the result is zero. In all other cases nothing is done.
%--#] delta_ :
%--#[ deltap_ :
\section{deltap\_}\index{deltap\_}\index{function!deltap\_}
\label{fundeltap}
\noindent If one argument and it is numerical the result is zero if
the argument is zero and one otherwise. If two arguments, the result is
zero if the arguments are numerical and identical. If they are numerical and
they differ the result is one. In all other cases nothing is done.
%--#] deltap_ :
%--#[ denom_ :
\section{denom\_}\index{denom\_}\index{function!denom\_}
\label{fundenom}
\noindent Internal function to describe denominators. Has a single
argument. \verb:den(a+b): is printed as \verb:1/(a+b):.
%--#] denom_ :
%--#[ diagrams_ :
%
%\section{diagrams\_}\index{diagrams\_}\index{function!diagrams\_}
%\label{fundiagrams}
%\noindent For a description of this function, please see the section on
%diagrams~\ref{diagrams}.
%
%--#] diagrams_ :
%--#[ distrib_ :
\section{distrib\_}\index{distrib\_}\index{function!distrib\_}
\label{fundistrib}
\noindent This is a combinatorics\index{combinatorics} function. It should
have at least five arguments. If we have
\begin{verbatim}
distrib_(type,n,f1,f2,x1,...,xm)
\end{verbatim}
with type and n integers, f1 and f2 functions and then a number of
arguments there can be action if $-2 \le$ type $\le 2$. The typical action
is that the arguments \verb:x1,...,xm: will be divided over the two
functions in all possible ways. For each possibility a new term is
generated. The relative order of the arguments is kept. If type is negative
it is assumed that the collection of x-arguments is
antisymmetric\index{antisymmetric} and hence the number of permutations
needed to make the split will determine whether there will be a minus sign
on the resulting term. When type is zero all possible divisions are
generated. Hence there will be $2^m$ divisions. The second argument is then
not relevant. If type is 1 or -1 the second parameter says that the first
function should obtain n arguments. The remaining arguments go to the
second function. If type is 2 or -2 the second function should obtain n
arguments. Example:
% THIS EXAMPLE IS PART OF THE TESTSUITE. CHANGES HERE SHOULD BE APPLIED THERE AS
% WELL!
\begin{verbatim}
Symbols x1,...,x4;
CFunctions f,f1,f2;
Local F = f(x1,...,x4);
id f(?a) = distrib_(-1,2,f1,f2,?a);
Print +s;
.end
F =
+ f1(x1,x2)*f2(x3,x4)
- f1(x1,x3)*f2(x2,x4)
+ f1(x1,x4)*f2(x2,x3)
+ f1(x2,x3)*f2(x1,x4)
- f1(x2,x4)*f2(x1,x3)
+ f1(x3,x4)*f2(x1,x2)
;
\end{verbatim}
When adjacent x-arguments are identical \FORM\ uses combinatorics to avoid
generating more terms than necessary.
%--#] distrib_ :
%--#[ div_ :
\section{div\_}\index{div\_}\index{function!div\_}
\label{fundiv}
\noindent \verb:div_(x1,x2): is replaced by the quotient of the arguments.
The arguments can be any valid subexpressions, provided the whole function
fits inside a term. When an argument is only an active expression or a
\$-expression it is only expanded during the division. This way the
contents of such expressions can exceed the maximum term size. One should
however realize that in that case the operation takes place in allocated
memory. This function replaces the experimental function
polydiv\_\index{polydiv\_}\index{function!polydiv\_} that existed in
version 3.
%--#] div_ :
%--#[ dum_ :
\section{dum\_}\index{dum\_}\index{function!dum\_}
\label{fundum}
\noindent Special function for printing virtual\index{virtual bracket}
brackets\index{bracket}. \verb:dum_(a+b): is printed as \verb:(a+b):: the
name of this function is not printed!
%--#] dum_ :
%--#[ dummy_ :
\section{dummy\_}\index{dummy\_}\index{function!dummy\_}
\label{fundummy}
\noindent For internal use only.
%--#] dummy_ :
%--#[ dummyten_ :
\section{dummyten\_}\index{dummyten\_}\index{function!dummyten\_}
\label{fundummyten}
\noindent For internal use only.
%--#] dummyten_ :
%--#[ e_ :
\section{e\_}\index{e\_}\index{function!e\_}
\label{fune}
\noindent The Levi-Civita\index{Levi-Civita tensor}
tensor\index{tensor!Levi-Civita}. It is a totally
antisymmetric\index{antisymmetric} tensor with well defined contraction
rules (see \ref{substacontract}).
%--#] e_ :
%--#[ edge_ :
%
%\section{edge\_}\index{edge\_}\index{function!edge\_}
%\label{funedge}
%\noindent For a description of this function, please see the section on
%diagrams~\ref{diagrams}.
%
%--#] edge_ :
%--#[ exp_ :
\section{exp\_}\index{exp\_}\index{function!exp\_}
\label{funexp}
\noindent Internal function with two arguments. Represents
argument1 to the power argument2. Of course it is printed in the standard
power notation.
%--#] exp_ :
%--#[ exteuclidean_ :
\section{exteuclidean\_}\index{exteuclidean\_}\index{function!exteuclidean\_}
\label{funexteuclidean}
\noindent This is a number function. It expects two positive integer
arguments. It then computes the Greatest Common Divider of these arguments
with the use of the extended Euclidean algoritm. The answer will be in the
same function but now there will be four arguments as in:
\begin{verbatim}
Symbols x1,x2,x3,x4;
Local F = exteuclidean_(54,84);
Print;
.sort
F =
exteuclidean_(54,84,-3,2);
id exteuclidean_(x1?,x2?,x3?,x4?) = x1*x3+x2*x4;
Print;
.end
F =
6;
\end{verbatim}
\noindent We can see that we obtain the GCD with the relation that is
characteristic for the extended Euclidean algorithm. When the two arguments
are relative prime, one obtains the so-called modinverses of these numbers:
\begin{verbatim}
Symbols x1,x2,x3,x4,a,b;
Local F = exteuclidean_(97,101);
Print;
.sort
F =
exteuclidean_(97,101,25,-24);
id exteuclidean_(x1?,x2?,x3?,x4?) = x1*x3+x2*x4
+a*mod2_(1/97,101)+b*mod2_(1/101,97);
Print;
.end
F =
1 - 24*b + 25*a;
\end{verbatim}
\noindent Here 25 is the inverse of 97 when we calculate modulus 101 and
-24 is the inverse of 101 when we calculate modulus 97.
\noindent This function can be very handy when a calculation has been done
modulus various prime numbers and one would like to know the result modulus
the product of these numbers. This combination is done with the aid of the
Chinese remainder theorem\index{Chinese remainder theorem}:
\begin{verbatim}
#procedure ChineseRemainder(NAME,NAME1,NAME2,M1,M2,PAR)
*
* Assumes that NAME1 is an expression mod $M1
* Assumes that NAME2 is an expression mod $M2
* Creates $ch1r and $ch2r with the property that
* the expression NAME = NAME1*$ch1r+NAME2*$ch2rn
* is the corresponding equation mod $M1*$M2
*
Modulus 0; * we need to switch off previous settings.
#$ch1r = exteuclidean_($`M1',$`M2');
#inside $ch1r;
id exteuclidean_(xxx1?,xxx2?,xxx3?,xxx4?) = xxx2*xxx4;
#endinside;
#$ch2r = exteuclidean_($`M1',$`M2');
#inside $ch2r;
id exteuclidean_(xxx1?,xxx2?,xxx3?,xxx4?) = xxx1*xxx3;
#endinside;
#$MM12 = $`M1'*$`M2';
Modulus,plusmin,`$MM12';
Local `NAME' = `NAME1i'*$ch1r+`NAME2i'*$ch2r;
.sort
*
#endprocedure
\end{verbatim}
%--#] exteuclidean_ :
%--#[ extrasymbol_ :
\section{extrasymbol\_}\index{extrasymbol\_}\index{function!extrasymbol\_}
\label{funextrasymbol}
\noindent This function expects a single argument. This argument can be a
number or an extra symbol(see \ref{extrasymbols}). In either case the
function is replaced by the expression that the corresponding extra symbol
stands for.
\noindent If there are more arguments or the argument does not represent a
legal extra symbol, no substitution is made.
%--#] extrasymbol_ :
%--#[ fac_ :
\section{fac\_}\index{fac\_}\index{function!fac\_}
\label{funfac}
\noindent The factorial\index{factorial} function. If it has a single nonzero
integer argument n it is replaced by n! but if the result is bigger than
the maximum allowable number an error will result.
%--#] fac_ :
%--#[ factorin_ :
\section{factorin\_}\index{factorin\_}\index{function!factorin\_}
\label{funfactorin}
\noindent When the argument is a single \$-variable\index{\$-variable} or
an expression\index{expression} the function is replaced by the common
factor in the terms of that \verb:$:-variable or expression. This common
factor consists in the first place of all symbolic objects that occur in
all terms. In addition the numerical factor consists of the GCD\index{GCD}
of all numerators and the LCM\index{LCM} of all denominators. Hence if the
\verb:$:-variable or expression is divided by the result of factorin\_ all
coefficients become integer.
%--#] factorin_ :
%--#[ farg_ :
\section{farg\_}\index{farg\_}\index{function!farg\_}
\label{funfarg}
\noindent For internal use only.
%--#] farg_ :
%--#[ firstbracket_ :
\section{firstbracket\_}\index{firstbracket\_}\index{function!firstbracket\_}
\label{funfirstbracket}
\noindent In the case that there is a single argument and this
single argument is the name of an expression, this function is replaced by
the part that is outside brackets in the first term of the expression. If
there are no brackets the function is replaced by one.
%--#] firstbracket_ :
%--#[ firstterm_ :
\section{firstterm\_}\index{firstterm\_}\index{function!firstterm\_}
\label{funfirstterm}
\noindent This function expects the name of an expression or a dollar
variable for its (single) argument. It will return the first term in this
expression or dollar variable. When it has to obtain the first term of an
expression, FORM uses the expression in the representation in which it was
stored at the end of the previous module. If the expression did not exist
in the previous module, it will attempt to use the expression as defined
and processed in the current expression. If the expression has only been
defined in the current module and has not yet been processed (as is the
case when referring to the first term in the current expression) the answer
will be unspecified. This use is considered illegal, even though it does
not generate an error message.
%--#] firstterm_ :
%--#[ g5_ :
\section{g5\_}\index{g5\_}\index{function!g5\_}
\label{fungfive}
\noindent The $\gamma_5$ Dirac gamma matrix. We assume here that it
anticommutes with the other Dirac\index{Dirac} gamma\index{gamma matrices}
matrices. Anybody who does not like that should program private libraries
(this should not be too difficult with the cycle symmetric functions
(see~\ref{substafunctions}). There should be a single index to indicate
the spinline.
%--#] g5_ :
%--#[ g6_ :
\section{g6\_}\index{g6\_}\index{function!g6\_}
\label{fungsix}
\noindent There should be a single index to indicate the spinline.
As in Schoonschip\index{Schoonschip} we use $\gamma_6 = 1+\gamma_5$.
%--#] g6_ :
%--#[ g7_ :
\section{g7\_}\index{g7\_}\index{function!g7\_}
\label{fungseven}
\noindent There should be a single index to indicate the spinline.
As in Schoonschip\index{Schoonschip} we use $\gamma_7 = 1-\gamma_5$.
%--#] g7_ :
%--#[ g_ :
\section{g\_}\index{g\_}\index{function!g\_}
\label{fung}
\noindent The Dirac\index{Dirac} gamma\index{gamma matrices} matrix. Its
first argument should be an index (either symbolic or numeric). Then follow
zero, one or more indices to indicate a string of gamma matrices that
belong together. Gamma matrices with the same first index are considered to
belong together, but as long as the indices are symbolic no assumptions are
made about whether they go together or not. Hence no commutation or
anticommutation properties are applied for different spin lines unless the
spinline indices are both numeric.
%--#] g_ :
%--#[ gcd_ :
\section{gcd\_}\index{gcd\_}\index{function!gcd\_}
\label{fungcd}
\noindent \verb:gcd_(x1,...,xn): is replaced by the greatest common divisor
of the arguments. The arguments can be any valid subexpressions, provided
the whole function fits inside a term. When an argument is only an active
expression or a \$-expression it is only expanded during evaluation of the
GCD. This way the contents of such expressions can exceed the maximum term
size. One should however realize that in that case the operation takes
place in allocated memory.
This function replaces the experimental function
polygcd\_\index{polygcd\_}\index{function!polygcd\_} that existed in
version 3.
%--#] gcd_ :
%--#[ gi_ :
\section{gi\_}\index{gi\_}\index{function!gi\_}
\label{fungi}
\noindent The unit Dirac gamma matrix. Should have a single index
to indicate its spin line. Its is identical to a regular gamma matrix with
no Lorenz indices: \verb:gi_(n) = g_(n):
%--#] gi_ :
%--#[ id_ :
\section{id\_}\index{id\_}\index{function!id\_}
\label{funid}
\noindent This function is a crossbreed between the
replace\_\index{replace\_}~\ref{funreplace} function and the id
statement\index{substaidentify}~\ref{substaidentify}. To become active it
needs an even number of arguments. The odd numbered arguments can be
anything of the types:
\begin{description}
\item[] a single symbol, possibly to an integer power.
\item[] a single dotproducts, possibly to an integer power.
\item[] a single function, possibly with any number and type of arguments.
\end{description}
When \FORM{} encounters an id\_ function the last step of normalizing a term
is to replace the id function by a number substitutions in which the odd
arguments are replaced by the following even arguments. These are not
wildcard substitutions as in the replace\_ function, but substitutions as
in regular id statements. The matching of the odd arguments is done in a
single step as in an id-al construction~\ref{substaalso}. Hence
\begin{verbatim}
id_(x^2,y+z,y,u+v,x,z+u)
\end{verbatim}
effectively becomes
\begin{verbatim}
id x^2 = y+z;
al y = u+v;
al x = z+u;
\end{verbatim}
\FORM{} treats multiple occurrences of the id\_ function one at a time. It
takes the leftmost occurrence first, takes the patterns from the term,
expands the right hand sides, tries to normalize the resulting terms and
only then continues with the next id\_ function. For this reason the id\_
function is noncommuting.
%--#] id_ :
%--#[ integer_ :
\section{integer\_}\index{integer\_}\index{function!integer\_}
\label{funinteger}
\noindent This is a rounding\index{rounding} function. It should have
either one or two arguments. If there is a single argument and it is
numeric, it will be rounded down to become an integer. If there are two
arguments of which the first is numeric and the second is either 1, 0 or
-1, the result will be the rounded value of the first argument. If the
second argument is 1, the rounding will be down, when it is -1, the
rounding will be up and when it is zero the rounding will be towards zero.
In all other cases nothing is done.
%--#] integer_ :
%--#[ inverse_ :
\section{inverse\_}\index{inverse\_}\index{function!inverse\_}
\label{funinverse}
\noindent \verb:inverse_(x1,x2): expects two arguments which are
polynomials in the same single variable. The return expression $x_3$
has the property that $x_1 x_3$ divided by $x_2$ has remainder 1. Or in
other words: $x_3$ is the inverse of $x_1$ modulus $x_2$.
The arguments can be any valid subexpressions, provided the whole function
fits inside a term. When an argument is an active expression or a
\$-expression it is only expanded during the division. This way the
contents of such expressions can exceed the maximum term size. One should
however realize that in that case the operation takes place in allocated
memory.
%--#] inverse_ :
%--#[ invfac_ :
\section{invfac\_}\index{invfac\_}\index{function!invfac\_}
\label{funinvfac}
\noindent One divided by the factorial\index{factorial} function. If it has
a single nonzero integer argument n, it is replaced by 1/n!, but if this
results in a number bigger than the maximum allowable number an error will
result.
%--#] invfac_ :
%--#[ makerational_ :
\section{makerational\_}\index{makerational\_}\index{function!makerational\_}
\label{funmakerational}
\noindent This function takes two arguments. Both are integers. We assume
calculus modulus the second argument. The function is then replaced by a
fraction of which both elements are less than the square root of the second
argument and that, in calculus modulus this second number would give the
same result as the first number modulus the second number. Example:
\begin{verbatim}
#$m = prime_(1);
#write <> "The prime number is %$",$m
The prime number is 2147483587
L F = MakeRational_(12345678,$m);
Print;
.sort
F =
9719/38790;
Modulus `$m';
Print;
.end
F =
12345678;
\end{verbatim}
\noindent This function can be used to reconstruct fractions when calculus
has been done modulus one or more prime numbers.
%--#] makerational_ :
%--#[ match_ :
\section{match\_}\index{match\_}\index{function!match\_}
\label{funmatch}
\noindent Currently not active. Replaced automatically by 1.
%--#] match_ :
%--#[ max_ :
\section{max\_}\index{max\_}\index{function!max\_}
\label{funmax}
\noindent If all its arguments are numeric, this function returns
the maximum value of these arguments.
%--#] max_ :
%--#[ maxpowerof_ :
\section{maxpowerof\_}\index{maxpowerof\_}\index{function!maxpowerof\_}
\label{funmaxpowerof}
\noindent If this function has a single argument that is a symbol, it
returns the maximum power restriction of this symbol. If none was given it
will be the installation dependent value MAXPOWER which is 10000 on
32\index{32 bits} bit machines and 500000000 on 64\index{64 bits} bit
machines.
%--#] maxpowerof_ :
%--#[ min_ :
\section{min\_}\index{min\_}\index{function!min\_}
\label{funmin}
\noindent If all its arguments are numeric, this function returns
the minimum value of these arguments.
%--#] min_ :
%--#[ minpowerof_ :
\section{minpowerof\_}\index{minpowerof\_}\index{function!minpowerof\_}
\label{funminpowerof}
\noindent If this function has a single argument that is a symbol, it
returns the minimum power restriction of this symbol. If none was given it
will be the installation dependent value -MAXPOWER which is -10000 on 32 bit
machines.
%--#] minpowerof_ :
%--#[ mod_ :
\section{mod\_}\index{mod\_}\index{function!mod\_}
\label{funmod}
\noindent If there are two integer arguments and the second
argument is a positive short integer (less than $2^{15}$ on 32 bit
computers and less than $2^{31}$ on 64 bit computers) the return value is
the first argument modulus the second. Note that if the second argument is
not a prime number and the first argument contains a denominator, division
by zero could occur. It is up to the user to avoid such cases. See also the
mod2\_ function~\ref{funmod2} and the rem\_ function~\ref{funrem}.
The function has one peculiarity: when the second argument is one, the
function is left untouched.
%--#] mod_ :
%--#[ mod2_ :
\section{mod2\_}\index{mod2\_}\index{function!mod2\_}
\label{funmod2}
\noindent This gives basically the same action as the mod\_ function (see
\ref{funmod}), but the answer will be in the range $-[(p-1)/2]$ to
$+[(p+1)/2]$.
%--#] mod2_ :
%--#[ mul_ :
\section{mul\_}\index{mul\_}\index{function!mul\_}
\label{funmul}
\noindent \verb|mul_(x,y)| is replaced by \verb|x*y|, but internally
the multiplication is performed via polynomial routines introduced in
\FORM{} version 4. This can be faster than the normal way of multiplications
for big polynomials: e.g., \verb|mul_($x,$y)| where the \$-variables \verb|$x|
and \verb|$y| store big polynomials.
A drawback is, because the polynomial routines accept only symbols, all
non-symbolic objects in the operands are temporarily translated to (commuting)
extra symbols. This process breaks the ordering of non-commutative objects
in the result.
%--#] mul_ :
%--#[ nargs_ :
\section{nargs\_}\index{nargs\_}\index{function!nargs\_}
\label{funnargs}
\noindent Is replaced by an integer indicating the number of
arguments that the function has.
%--#] nargs_ :
%--#[ node_ :
\section{node\_}\index{topologies\_}\index{function!node\_}
\label{funtonode}
\noindent For a description of this function, please see the section on
diagrams~\ref{diagrams}.
%--#] node_ :
%--#[ nterms_ :
\section{nterms\_}\index{nterms\_}\index{function!nterms\_}
\label{funnterms}
\noindent If this function has only one argument it is replaced by
the number of terms inside this argument.
%--#] nterms_ :
%--#[ numfactors_ :
\section{numfactors\_}\index{numfactors\_}\index{function!numfactors\_}
\label{funnumfactors}
\noindent This function returns the number of factors in a factorized
expression (see the chapter on polynomials~\ref{polynomials}) or dollar
variable~\ref{dollars}. It expects a single argument which should be the
name of an expression or a dollar variable. If the expression or dollar
variable has not been factorized, the function returns zero.
%--#] numfactors_ :
%--#[ partitions_ :
\section{partitions\_}\index{partitions\_}\index{function!partitions\_}
\label{funpartitions}
\noindent This function generates all partitions of a list of arguments into
$n$ parts. Each part consists of a function name and a size.
This function exploits symmetries of the arguments to make sure that no argument
is generated twice. Instead, a combinatorial prefactor is computed.
The syntax distinguishes three cases:
\begin{verbatim}
1] partitions_(n,[function,n1,]_1,...,[function,nn,]_n,arguments)
2] partitions_(n,[function,n1,]_1,...,[function,0],arguments)
3] partitions_(0,function,n1,arguments)
\end{verbatim}
In the first case, the first entry specifies the number of partitions $n$.
It should be followed by $n$ parts, defined by a function name
and the number of arguments for that function. The final entries are the arguments
that will be distributed over the functions.
The number of arguments should be the same as the sum of
all the function argument sizes.
There are no restrictions on the type of arguments.
The second case is the same as the first, except that the last partition
has a 0 for the size. This means that any leftover arguments are collected
in this term. Thus \path{partitions_(2,f1,3,f2,0,arguments)} yields the same
as \texttt{distrib\_(1,3,f1,f2,arguments)}.
The third case, determined by a 0 for the number of partitions followed by one part, spreads
the arguments over a repeated instance of that part. Thus \path{partitions_(0,f1,2,arguments)} is similar to \texttt{dd\_(arguments)}.
In case of a deviation from the above rules, no action will be taken.
Some examples are given below:
\begin{verbatim}
partitions_(2,f1,2,f2,1,x1,x1,x3) =
+ f1(x1,x1)*f2(x3) + 2*f1(x1,x3)*f2(x1)
;
partitions_(3,f1,2,f2,1,f3,0,x1,x1,x1,x2,x2,x2) =
+ 3*f1(x1,x1)*f2(x1)*f3(x2,x2,x2)
+ 9*f1(x1,x1)*f2(x2)*f3(x1,x2,x2)
+ 18*f1(x1,x2)*f2(x1)*f3(x1,x2,x2)
+ 18*f1(x1,x2)*f2(x2)*f3(x1,x1,x2)
+ 9*f1(x2,x2)*f2(x1)*f3(x1,x1,x2)
+ 3*f1(x2,x2)*f2(x2)*f3(x1,x1,x1)
;
partitions_(0,f1,3,x1,x1,x1,x4,x5,x6) =
+ f1(x1,x1,x1)*f1(x4,x5,x6)
+ 3*f1(x1,x1,x4)*f1(x1,x5,x6)
+ 3*f1(x1,x1,x5)*f1(x1,x4,x6)
+ 3*f1(x1,x1,x6)*f1(x1,x4,x5)
;
\end{verbatim}
%--#] partitions_ :
%--#[ pattern_ :
\section{pattern\_}\index{pattern\_}\index{function!pattern\_}
\label{funpattern}
\noindent Currently not active. Replaced automatically by 1.
%--#] pattern_ :
%--#[ perm_ :
\section{perm\_}\index{perm\_}\index{function!perm\_}
\label{funperm}
\noindent Generates all permutations of the arguments, with exception
of the first argument which should be the name of a function. This function
will then have the permuted arguments as in:
\begin{verbatim}
CFunction f;
Symbols x1,...,x3;
Local F = perm_(f,x1,x2,x3);
Print +s;
.end
F =
+ f(x1,x2,x3)
+ f(x1,x3,x2)
+ f(x2,x1,x3)
+ f(x2,x3,x1)
+ f(x3,x1,x2)
+ f(x3,x2,x1)
;
\end{verbatim}
The permutations are generated with an algorithm that takes subsequent
cyclic permutations. If one puts a nonzero integer before the function
argument the output terms will be multiplied by -1 when the permutation is
odd.
When the function name is the only argument the answer will be just this
function without arguments. One could argue that technically the answer
should be zero, but this way the attention of the user may be attracted to
the occurrence which might not be the case when the term 'just vanishes'.
It is however rather simple to add a statement that makes such a function
zero.
%--#] perm_ :
%--#[ poly_ :
\section{poly\_}\index{poly\_}\index{function!poly\_}
\label{funpoly}
\noindent This was an experimental function in version 3. It was for
internal use with a whole category of other experimental functions of which
the functionality has been replaced by better working functions that are
more general. This category included the functions
polyadd\_\index{polyadd\_}\index{function!polyadd\_},
polydiv\_\index{polydiv\_}\index{function!polydiv\_},
polygcd\_\index{polygcd\_}\index{function!polygcd\_},
polyintfac\_\index{polyintfac\_}\index{function!polyintfac\_},
polymul\_\index{polymul\_}\index{function!polymul\_},
polynorm\_\index{polynorm\_}\index{function!polynorm\_},
polyrem\_\index{polyrem\_}\index{function!polyrem\_} and
polysub\_\index{polysub\_}\index{function!polysub\_}.
See also the chapter on polynomials~\ref{polynomials} and the functions
gcd\_~\ref{fungcd}, div\_~\ref{fundiv} and rem\_~\ref{funrem}.
%--#] poly_ :
%--#[ prime_ :
\section{prime\_}\index{prime\_}\index{function!prime\_}
\label{funprime}
\noindent For a number of internal operations FORM needs prime numbers that
are neither very large nor very small. Hence it generates, when needed
prime numbers that still fit inside a single FORM word, but are maximal
within that limitation. Hence for a 64-bits computer in which the largest
positive `small' integer in FORM is $2^{31}-1$, it works its way down from
there. Once it has determined that a number is prime it stores it in a
list. The function prime\_ gives access to this list. The single argument
n (n a positive integer) makes that \verb:prime_(n): will be replaced by
the n-th member of the list. There is a limitation to the size of the list
which is implementation dependent. The number will anyway never be smaller
than the maximum power that is allowed for symbols. Example:
\begin{verbatim}
Symbols x1,x2,x3,x4;
ON highfirst;
Local F = x1*prime_(1)+x2*prime_(2)
+x3*prime_(3)+x4*prime_(4);
Print;
.end
F =
2147483587*x1 + 2147483579*x2 + 2147483563*x3 + 2147483549*x4;
\end{verbatim}
This function is useful when calculations generate very large intermediate
coefficients, but in the end the answer is relatively simple again. In that
case one can do the calculation modulus one or more prime numbers. If more
prime numbers are used the Chinese remainder theorem\index{Chinese
remainder theorem}. can be used (see the exteuclidean\_
function~\ref{funexteuclidean} to combine the results and the
makerational\_ function~\ref{funmakerational} can be used if fractions have
to be reconstructed. An example of this kind of use is given in the simple
Groebner basis procedure that is in the packages library in the FORM site.
%--#] prime_ :
%--#[ putfirst_ :
\section{putfirst\_}\index{putfirst\_}\index{function!putfirst\_}
\label{funputfirst}
\noindent
This function allows one to select a given argument by its number. The
syntax is:
\begin{verbatim}
putfirst_(functionname,numberofargument,arguments.....);
\end{verbatim}
It will select the indicated argument in the argument field indicated by
arguments and output this as the first argument in the indicated function.
This argument will then be followed by the remaining arguments.
Example:
\begin{verbatim}
S a,a1,...,a10;
CF f,g;
L F = g(a,a1,...,a10);
id g(?a) = putfirst_(f,4,?a);
Print;
.end
F =
f(a3,a,a1,a2,a4,a5,a6,a7,a8,a9,a10);
\end{verbatim}
%--#] putfirst_ :
%--#[ random_ :
\section{random\_}\index{random\_}\index{function!random\_}
\label{funrandom}
\noindent A random number generator. When the function has a single
positive integer argument, the function will return a pseudo random number
in the range of one to that number inclusive. Hence one can imitate a die
roll with the call random\_(6). The program uses a random number generator
as described in vol 2 of the "Art of computer programming, vol2" by D.
Knuth with the parameters set at 89,38 to give as long a cycle as possible.
For very large numbers the program pastes several random numbers together.
The generator can be initialized with the preprocessor
\#setrandom~\ref{presetrandom}\index{\#setrandom} instruction. When running
with TFORM or ParFORM each worker runs an independent generator with its
own seed. The seeds of the workers are derived from the seed of the master
and the number of the worker in a non-trivial way. It should be noted
however that with workers it may be impossible to reproduce previous runs
as it is non-deterministic which term ends up in which worker.
%--#] random_ :
%--#[ ranperm_ :
\section{ranperm\_}\index{ranperm\_}\index{function!ranperm\_}
\label{funranperm}
\noindent Generates a random permutation of the arguments, with exception
of the first argument which should be the name of a function. This function
will then have the permuted arguments as in:
\begin{verbatim}
CFunction f;
Symbols x1,...,x5;
Local F = ranperm_(f,1,2,3,4,5,6)
+ranperm_(f,x1,x2,x3+x1,x4,x5);
Print +s;
.end
F =
+ f(x5,x1,x3 + x1,x4,x2)
+ f(3,1,6,2,4,5)
;
\end{verbatim}
The permutation is generated with the same random number generator that is
used by the function
random\_~\ref{funrandom}\index{random}\index{function!random\_} and hence
is susceptible to the same initialization procedure that can be executed
with the \#setrandom~\ref{presetrandom}\index{setrandom} instruction.
%--#] ranperm_ :
%--#[ rem_ :
\section{rem\_}\index{rem\_}\index{function!rem\_}
\label{funrem}
\noindent \verb:rem_(x1,x2): is replaced by the remainder of the division
of $x_1$ by $x_2$. The arguments can be any valid subexpressions, provided
the whole function fits inside a term. When an argument is only an active
expression or a \$-expression it is only expanded during the division. This
way the contents of such expressions can exceed the maximum term size. One
should however realize that in that case the operation takes place in
allocated memory.
This function replaces the experimental function
polyrem\_\index{polyrem\_}\index{function!polyrem\_} that existed in
version 3.
%--#] rem_ :
%--#[ replace_ :
\section{replace\_}\index{replace\_}\index{function!replace\_}
\label{funreplace}
\noindent This function defines a rather general purpose
replacement\index{replacement} mechanism. It should have pairs of
arguments. Each pair consists of a single symbol, index, vector or
function, followed by what this object should be replaced by in the entire
term. Functions can only be replaced by functions, indices only by indices.
A vector can be replaced by a single vector or by a vector like expression.
A symbol can be replaced by a single symbol, a numerical expression or a
complete subexpression that is not index like or vector like. This
mechanism is sometimes needed to make replacements in ways that are very
hard with the id\index{id} statements because those do not make
replacements automatically inside function arguments (see
\ref{substaidnew}). It also allows to exchange two variables as the
replacements are executed simultaneously by the wildcard substitution
mechanism.
\begin{verbatim}
Multiply replace_(x,y,y,x);
\end{verbatim}
will exchange x and y. Because there is no definite order in which multiple
replace\_ functions are treated, one should not use more than a single one
at the same time inside a term. At times multiple replace\_ functions may
lead to confusion inside \FORM.
%--#] replace_ :
%--#[ reverse_ :
\section{reverse\_}\index{reverse\_}\index{function!reverse\_}
\label{funreverse}
\noindent Can only occur as an argument of a function. Is replaced
by the reversed string of its own arguments.
%--#] reverse_ :
%--#[ root_ :
\section{root\_}\index{root\_}\index{function!root\_}
\label{funroot}
\noindent If we have \verb:root_(n,x): and \verb:n: is a positive
integer and \verb:x: is a rational number and \verb:y: is a rational number
with $y^n = x$ (no imaginary numbers are considered and negative numbers
are avoided if possible. Only one root is given) then \verb:root_(n,x): is
replaced by \verb:y:. This function was originally intended for internal
use. Do not hold it against the author that \verb:root_(2,1): is replaced
by \verb:1:. In the case that it is needed the user should manipulate the
sign or the complexity properties externally.
%--#] root_ :
%--#[ setfun_ :
\section{setfun\_}\index{setfun\_}\index{function!setfun\_}
\label{funsetfun}
\noindent Currently not active.
%--#] setfun_ :
%--#[ sig_ :
\section{sig\_}\index{sig\_}\index{function!sig\_}
\label{funsig}
\noindent Is replaced by the sign of the (numerical) argument, i.e. by -1
if there is a single negative argument and by +1 if there is a single
numerical argument that is greater or equal to zero.
%--#] sig_ :
%--#[ sign_ :
\section{sign\_}\index{sign\_}\index{function!sign\_}
\label{funsign}
\noindent \verb:sign_(n): is replaced by \verb:(-1)^n: if n is an
integer.
%--#] sign_ :
%--#[ sizeof_ :
\section{sizeof\_}\index{sizeof\_}\index{function!sizeof\_}
\label{funsizeof}
\noindent If there is a single argument and this argument is the name of an
active (or previously active during the current job) expression, the
function is replaced by the number\index{number of \FORM words} of \FORM
words in this expression. Stored expressions that were entered via a load
statement (see \ref{substaload}) are excluded from this because for them
this information is not readily available.
%--#] sizeof_ :
%--#[ sum_ :
\section{sum\_}\index{sum\_}\index{function!sum\_}
\label{funsum}
\noindent General purpose sum\index{sum} function. The first argument should
be the summation parameter (a symbol). The second argument is the starting
point of summation, the third argument the `upper' limit and a potential
fourth argument the increment. These numbers should all be integers.
Summation stops when the summation parameter obtains a value that has
passed the upper limit. The last argument is the summand, the object to be
summed over. It can be any subexpression. If it contains the summation
parameter, it will be replaced by its value for each generated term.
Examples:
\begin{verbatim}
sum_(j,1,4,sign_(j)*x^j/j)
sum_(i,1,9,2,sign_((i-1)/2)*x^i*invfac_(i))
\end{verbatim}
%--#] sum_ :
%--#[ sump_ :
\section{sump\_}\index{sump\_}\index{function!sump\_}
\label{funsump}
\noindent Special sum function. Its arguments are like for the
sum\_ function, but each new term is the product of the previously
generated term with the last argument in which the current value of the
summation parameter has been substituted. The first term is always one.
Example:
% THIS EXAMPLE IS PART OF THE TESTSUITE. CHANGES HERE SHOULD BE APPLIED THERE AS
% WELL!
\begin{verbatim}
Symbol i,x;
Local F = sump_(i,0,5,x/i);
Print;
.end
F =
1 + x + 1/2*x^2 + 1/6*x^3 + 1/24*x^4 + 1/120*x^5;
\end{verbatim}
This function is a leftover from the Schoonschip\index{Schoonschip} days.
The ordinary sum\_ function is much more readable.
%--#] sump_ :
%--#[ table_ :
\section{table\_}\index{table\_}\index{function!table\_}
\label{funtable}
\noindent For action the arguments should be the name of a table and then
either the name of a function or one symbol for each dimension of the
table. In the case of the list of symbols the return value will be a
monomial in the given symbols in which the powers of the symbols correspond
to the table indices of the defined table elements with the coefficients
the table contents corresponding to those indices. In the case of a
function name the return value will be a sum over terms in which the table
elements are indicated by arguments in the given function while these
functions are then multiplied by the corresponding table elements. This is
one way to put a complete table inside an expression and store it (with the
save statement of \ref{substasave}) in a binary way for a future run in
which the table can be filled again with the
fillexpression\index{fillexpression} (see \ref{substafillexpression})
statement. Note that for obvious reasons one should avoid using symbols or
functions that also occur inside the table definitions.
%--#] table_ :
%--#[ tbl_ :
\section{tbl\_}\index{tbl\_}\index{function!tbl\_}
\label{funtbl}
\noindent This function is the `table stub function' as used by the
tablebase\index{tablebase} construction. This is explained in chapter
\ref{tablebase}. It is mainly for internal use, but it could occur in the
output.
%--#] tbl_ :
%--#[ term_ :
\section{term\_}\index{term\_}\index{function!term\_}
\label{funterm}
\noindent This function has no arguments. It is replaced by the current
term. It can be used to load the current term into a dollar variable as in
\begin{verbatim}
$x = term_;
\end{verbatim}
%--#] term_ :
%--#[ termsin_ :
\section{termsin\_}\index{termsin\_}\index{function!termsin\_}
\label{funtermsin}
\noindent If there is a single argument and this argument is the name of an
active (or previously active during the current job) expression, the
function is replaced by the number\index{number of terms} of terms in this
expression. Stored expressions that were entered via a load statement (see
\ref{substaload}) are excluded from this because for them \FORM\ would have
to actually count the terms.
%--#] termsin_ :
%--#[ termsinbracket_ :
\section{termsinbracket\_}\index{termsinbracket\_}\index{function!termsinbracket\_}
\label{funtermsinbracket}
\noindent If there is no argument, or the single argument is zero, the
function is replaced by the number of terms in the current
bracket\index{bracket}, provided the expression has been bracketed at its
last sort and a keep brackets statement (see \ref{substakeep}) has been
used. Note that the terms have to be counted. Hence this is a relatively
expensive command. More options will be implemented in the future.
%--#] termsinbracket_ :
%--#[ theta_ :
\section{theta\_}\index{theta\_}\index{function!theta\_}
\label{funtheta}
\noindent If there is a single numerical argument x the function is
replaced by one if $x \ge 0$ and by zero if $x < 0$. If there are two
numerical arguments $x_1$ and $x_2$ the function is replaced by one if $x_1
= x_2$ or if the arguments are in natural order (if theta\_ would be a
symmetric function there would be no reason to exchange the arguments) and
by zero if the arguments are not in natural order (they would be exchanged
in a symmetric function). In all other cases nothing is done.
%--#] theta_ :
%--#[ thetap_ :
\section{thetap\_}\index{thetap\_}\index{function!thetap\_}
\label{funthetap}
\noindent If there is a single numerical argument x the function is
replaced by one if $x > 0$ and by zero if $x \le 0$. If there are two
numerical arguments $x_1$ and $x_2$ the function is replaced by zero if $x_1
= x_2$ or if the arguments are not in natural order. If the arguments are
in natural order the function is replaced by one. In all other cases
nothing is done.
%--#] thetap_ :
%--#[ topologies_ :
\section{topologies\_}\index{topologies\_}\index{function!topologies\_}
\label{funtopologies}
\noindent For a description of this function, please see the section on
diagrams~\ref{diagrams}.
%--#] topologies_ :
%--#[ Reserved names :
\section{Extra reserved names}
\noindent In addition there are some names that have been reserved for
future use. At the moment these functions do not do very much. It is hoped
that in the future some simplifications of the arguments can be
implemented. These functions are:
\leftvitem{3cm}{sqrt\_}\index{sqrt\_}\index{function!sqrt\_}
\rightvitem{13cm}{The regular square root.}
\leftvitem{3cm}{ln\_}\index{ln\_}\index{function!ln\_}
\rightvitem{13cm}{The natural logarithm.}
\leftvitem{3cm}{sin\_}\index{sin\_}\index{function!sin\_}
\rightvitem{13cm}{The sine function.}
\leftvitem{3cm}{cos\_}\index{cos\_}\index{function!cos\_}
\rightvitem{13cm}{The cosine function.}
\leftvitem{3cm}{tan\_}\index{tan\_}\index{function!tan\_}
\rightvitem{13cm}{The tangent function.}
\leftvitem{3cm}{asin\_}\index{asin\_}\index{function!asin\_}
\rightvitem{13cm}{The inverse of the sine function.}
\leftvitem{3cm}{acos\_}\index{acos\_}\index{function!acos\_}
\rightvitem{13cm}{The inverse of the cosine function.}
\leftvitem{3cm}{atan\_}\index{atan\_}\index{function!atan\_}
\rightvitem{13cm}{The inverse of the tangent function.}
\leftvitem{3cm}{atan2\_}\index{atan2\_}\index{function!atan2\_}
\rightvitem{13cm}{Another inverse of the tangent function.}
\leftvitem{3cm}{sinh\_}\index{sinh\_}\index{function!sinh\_}
\rightvitem{13cm}{The hyperbolic sine function.}
\leftvitem{3cm}{cosh\_}\index{cosh\_}\index{function!cosh\_}
\rightvitem{13cm}{The hyperbolic cosine function.}
\leftvitem{3cm}{tanh\_}\index{tanh\_}\index{function!tanh\_}
\rightvitem{13cm}{The hyperbolic tangent function.}
\leftvitem{3cm}{asinh\_}\index{asinh\_}\index{function!asinh\_}
\rightvitem{13cm}{The inverse of the hyperbolic sine function.}
\leftvitem{3cm}{acosh\_}\index{acosh\_}\index{function!acosh\_}
\rightvitem{13cm}{The inverse of the hyperbolic cosine function.}
\leftvitem{3cm}{atanh\_}\index{atanh\_}\index{function!atanh\_}
\rightvitem{13cm}{The inverse of the hyperbolic tangent function.}
\leftvitem{3cm}{li2\_}\index{li2\_}\index{function!li2\_}
\rightvitem{13cm}{The dilogarithm function.}
\leftvitem{3cm}{lin\_}\index{lin\_}\index{function!lin\_}
\rightvitem{13cm}{The polylogarithm function.}
\noindent The user is allowed to use these functions, but it could be that
in the future they will develop a nontrivial behaviour. Hence caution is
required.
%--#] Reserved names :
form-master/doc/manual/gamma.tex 0000664 0000000 0000000 00000031461 13565763364 0017167 0 ustar 00root root 0000000 0000000
\chapter{Dirac algebra}
\label{gammaalgebra}
For its use in high\index{high energy physics} energy physics \FORM\ is
equipped with a built-in class of functions. These are the
gamma\index{gamma matrices} matrices of the Dirac\index{Dirac algebra}
algebra which are generically denoted by g\_\index{g\_}. The gamma matrices
fulfill the relations:
\begin{verbatim}
{g_(j1,mu),g_(j1,nu)} = 2 * d_(mu,nu)
[g_(j1,mu),g_(j2,nu)] = 0 j1 not equal to j2.
\end{verbatim}
The first argument is a so-called spin\index{spin line} line index. When
gamma matrices have the same spin line, they belong to the same Dirac
algebra and commute with the matrices of other Dirac algebra's. The indices
mu and nu are over space-time and are therefore usually running from 1 to 4
(or from 0 to 3 in Bjorken \& Drell metric\index{Bjorken \& Drell metric}).
The totally antisymmetric product e\_(m1,m2,...,mn)\*g\_(j,m1)\*...\*g\_(j,
mn)/n! is defined to be gamma5 or g5\_(j). The notation 5\index{g5\_} finds
its roots in 4 dimensional space-time. The unit matrix is denoted by
gi\_(j). In four dimensions a basis of the Dirac algebra can be given by:
\begin{verbatim}
gi_(j)
g_(j,mu)
[g_(j,mu),g_(j,nu)]/2
g5_(j)*g_(j,mu)
g5_(j)
\end{verbatim}
In a different number of dimensions this basis is correspondingly
different. We introduce the following notation for convenience:
\begin{verbatim}
g6_(j) = gi(j) + g5_(j) (from Schoonschip)
g7_(j) = gi(j) - g5_(j)
g_(j,mu,nu) = g_(j,mu)*g_(j,nu) (from Reduce)
g_(j,mu,nu,.....,ro,si) =
g_(j,mu,nu,.....,ro)*g_(j,si)
g_(j,5_) = g5_(j)
g_(j,6_) = g6_(j)
g_(j,7_) = g7_(j)
\end{verbatim}
The common operation on gamma matrices is to obtain the trace\index{trace}
of a string of gamma matrices. This is done with the statement:
\leftvitem{4cm}{trace4\index{trace4}, j}
\rightvitem{12cm}{Take the trace in 4 dimensions of the combination of all
gamma matrices with spin line j in the current term. Any non-commuting
objects that may be between some of these matrices are ignored. It is the
users responsibility to issue this statement only after all functions of
the relevant matrices are resolved. The four refers to special
tricks\index{tricks} that
can be applied in four dimensions. This allows for relatively compact
expressions. For the complete syntax, consult \ref{substatrace}.}
\leftvitem{4cm}{tracen\index{tracen}, j}
\rightvitem{12cm}{Take the trace in an unspecified number of dimensions.
This number of dimensions is considered to be even. The traces are
evaluated by only using the anticommutation properties of the matrices. As
the number of dimensions is not specified the occurrence of a g5\_(j) is a
fatal error. In general the expressions that are generated this way are
longer than the four dimensional expressions. For the complete syntax,
consult \ref{substatracen}.}
It is possible to alter the value of the trace of the
unit\index{unit matrix} matrix gi\_(j).\index{gi\_} Its
default value is 4, but by using the
statement (see \ref{substaunittrace})
\begin{verbatim}
unittrace value;
\end{verbatim}
it can be altered. Value may be any positive short number ($< 2^{15}$ on
32\index{32 bits} bit machines and $< 2^{31}$ on 64\index{64 bits} bit
machines) or a single symbol with the exception of the symbol
i\_.\index{i\_}
There are several options for the 4-dimensional traces. These options find
their origin in the Chisholm\index{Chisholm} relation that is valid in 4
dimensions but not in a general number of dimensions. This relation can be
found in the literature. It is given by:
\begin{equation}
\gamma_\mu Tr[\gamma_\mu S] = 2(S + S^R)
\end{equation}
\noindent in which S is a string of gamma matrices with an odd number of
matrices ($\gamma_5$ counts for an even number of matrices). $S^R$ is the
reversed string. This relation can be used to combine traces with common
indices. The use of this relation is the default for trace4\index{trace4}.
If it needs to be switched off, one should add the extra option
`nocontract':
\begin{verbatim}
trace4,nocontract,j;
\end{verbatim}
The option `contract'\index{contract} is the default but it can be used to
enhance the readability of the program. The second option that refers to
this relation is the option `symmetrize'\index{symmetrize}. Often it
happens that there are two or more common indices in two spin lines.
Without the symmetrize option (or with the
`nosymmetrize'\index{nosymmetrize} option) the first of these indices is
taken and the relation is applied to it. With the `symmetrize' option
the average over all possibilities is taken. This means of course that if
there are two common indices the amount of work is doubled. There is
however a potentially large advantage. In some traces that involve the use of
$\gamma_5$ the use of automatic algorithms results often in an avalanche of
terms with a single Levi-Civita tensor, while symmetry arguments can show
that these terms should add up to zero. By working out the traces in a more
symmetric fashion \FORM\ is often capable of eliminating all or nearly all of
these Levi-Civita tensors. Normally such an elimination is rather
complicated. It involves relations that have so far defied proper
implementation, even though people have been looking for such algorithms
already for a long time. Hence the use of the symmetry from the beginning
seems at the moment the best bet.
It is possible to only apply the Chisholm\index{Chisholm} identity without
taking the trace. This is done with the chisholm statement (see
\ref{substachisholm}).
The n dimensional traces can use a special feature, when the declaration
of the indices involved will allow it. When an index has been declared
as n-dimensional and the dimension is followed by a second symbol as in
\begin{verbatim}
symbols n,nn;
index mu=n:nn;
\end{verbatim}
and if the index \verb:mu: is a contracted index in a single
n-dimensional trace, then the formula for this trace can be shortened by
using \verb:nn: (one term) instead of the quantity $(n-4)$ (two terms).
This can make the taking of the n-dimensional traces significantly
faster.
\vspace{3mm}
\noindent Algorithms\index{algorithms}:
\FORM\ has been equipped with several built in rules to keep the
number of generated terms to a minimum during the evaluation of a
trace. These rules are:
\begin{description}
\item [rule 0]
Strings with an odd number of matrices (gamma5 counts for an even number
of matrices) have a trace that is zero, when using trace4 or tracen.
\item [rule 1]
A string of gamma matrices is first scanned for adjacent
matrices that have the same contractable index, or that are contracted with
the same vector. If such a pair is found, the relations
%\begin{eqnarray}
% \gamma^\mu\gamma^\nu & = & 1\times \delta^{\mu\nu} \nonumber \\
% \gamma^p\gamma^p & = & 1\times p\mydot p \nonumber
%\end{eqnarray}
\begin{verbatim}
g_(1,mu,mu) = gi_(1)*d_(mu,mu)
g_(1,p1,p1) = gi_(1)*p1.p1
\end{verbatim}
\noindent are applied.
\item [rule 2]
Next there is a scan for a pair of the same contractable
indices that has an odd number of other matrices in between. This is done
only for 4 dimensions (trace4) and the dimension of the indices must be 4.
If found, the Chisholm\index{Chisholm} identity is applied:
%\begin{eqnarray}
% \gamma^\mu\gamma^{m_1}\gamma^{m_2}\cdots\gamma^{m_n}\gamma^\mu & = &
% -2\gamma^{m_n}\cdots\gamma^{m_2}\gamma^{m_1} \nonumber \\
%\end{eqnarray}
\begin{verbatim}
g_(1,mu,m1,m2,...mn,mu) = -2*g_(1,mn,...,m2,m1)
\end{verbatim}
\item [rule 3]
Then (again only for trace4) there is a search for a pair
of matrices with the same 4 dimensional index and an even number of
matrices in between. If found, one of the following variations of the
Chisholm\index{Chisholm} identity is applied:
\begin{verbatim}
g_(1,mu,m1,m2,mu) = 4*gi_(1)*d_(m1,m2)
g_(1,mu,m1,m2,...,mj,mn,mu) =
2*g_(1,mn,m1,m2,...,mj)
+2*g_(1,mj,...,m2,m1,mn)
\end{verbatim}
\item [rule 4]
Then there is a scan for pairs of matrices that have the
same index or that are contracted with the same vector. If found, the
identity:
\begin{verbatim}
g_(1,mu,m1,m2,...,mj,mn,mu) =
2*d_(mu,mn)*g_(1,mu,m1,m2,...,mj)
-2*d_(mu,mj)*g_(1,mu,m1,m2,...,mn)
....
-/+2*d_(mu,m2)*g_(1,mu,m1,...,mj,mn)
+/-2*d_(mu,m1)*g_(1,mu,m2,...,mj,mn)
-/+2*d_(mu,mu)*g_(1,m1,m2,...,mj,mn)
\end{verbatim}
\noindent is used to 'anticommute'\index{anticommute} these identical
objects till they become adjacent and can be eliminated with the
application of rule 1. In the case of an n-dimensional trace and when
\verb:mu: is an index (it might also be a vector in the above formula) for
which the definition of the dimension involved two symbols, there is a
shorter formula. In that case the last three terms can be combined into two
terms:
\begin{verbatim}
-/+(n-4)*g_(1,m1,m2,...,mj,mn)
-/+4*d_(m1,m2)*g_(1,m3,m4,...,mj,mn)
\end{verbatim}
\noindent It should be clear now that this formula is only superior, when
there is a single symbol to represent $(n-4)$. After this all gamma
matrices that are left have a different index or are contracted with
different vectors. These are treated using:
\item [rule5]
Traces in 4 dimensions for which all gamma matrices have
a different index, or are contracted with a different four-vector are
evaluated using the reduction formula
\begin{verbatim}
g_(1,mu,nu,ro) =
g_(1,5_,si)*e_(mu,nu,ro,si)
+d_(mu,nu)*g_(1,ro)
-d_(mu,ro)*g_(1,nu)
+d_(nu,ro)*g_(1,mu)
\end{verbatim}
For tracen the generating algorithm is based on the generation of all
possible pairs of indices/vectors that occur in the gamma matrices in
combination with their proper sign. When the dimension is not specified,
there is no shorter expression.
\end{description}
\noindent Remarks:
When an index is declared to have dimension n and the command trace4 is
used, the special 4 dimensional rules 2 and 3 are not applied to this
index. The application of rule 1 or 4 will then give the correct
results. The result will nevertheless be wrong due to rule 5, when there
are at least 10 gamma matrices left after the application of the first 4
rules, as the two algorithms in rule 5 give a difference only, when
there are at least 10 gamma matrices. For counting gamma matrices the
$\gamma_5$ counts for 4 matrices with respect to this rule. The result
is unpredictable, when both indices in four dimensions and indices in n
dimensions occur in the same string of gamma matrices. Therefore one
should be very careful, when using the four dimensional trace under the
condition that the results need to be correct in n dimensions. This is
sometimes needed, when a $\gamma_5$ is involved. The tracen-statement
will not allow the presence of a $\gamma_5$. In general it is best to
emulate n-dimensional traces with a $\gamma_5$ separately. The eventual
trace, with all matrices with a different index, can be generated with
the use of the 'distrib\_' function:
% THIS EXAMPLE IS PART OF THE TESTSUITE. CHANGES HERE SHOULD BE APPLIED THERE AS
% WELL!
\begin{verbatim}
*
* Symmetric trace of a gamma5 and 12 regular matrices
*
I m1,...,m12;
F G5,g1,g2;
L F = G5(m1,...,m12);
id G5(?a) = distrib_(-1,4,g1,g2,?a);
id g1(?a) = e_(?a);
id g2(?a) = g_(1,?a);
tracen,1;
.end
Time = 1.07 sec Generated terms = 51975
F Terms in output = 51975
Bytes used = 919164
\end{verbatim}
This rather symmetric result is in contrast to the 4-dimensional result
which is much shorter, but it is very unsymmetric:
% THIS EXAMPLE IS PART OF THE TESTSUITE. CHANGES HERE SHOULD BE APPLIED THERE AS
% WELL!
\begin{verbatim}
*
* Regular trace of a gamma5 and 12 regular matrices
*
I m1,...,m12;
L F = g_(1,5_,m1,...,m12);
trace4,1;
.end
Time = 0.02 sec Generated terms = 1053
F Terms in output = 1029
Bytes used = 20284
\end{verbatim}
The precise workings of the distrib\_\index{distrib\_} function is given in
\ref{fundistrib}.
One should be careful when using projection operators of spinors. The
sloppy way is to write
\begin{verbatim}
(g_(1,p)+m)
\end{verbatim}
but technically this is not correct. The correct way is
\begin{verbatim}
(g_(1,p)+m*gi_(1))
\end{verbatim}
to avoid the possibility that in the end a trace will be taken over a term
that does not have any gamma matrix. If the projection operator is however
multiplied by other gamma matrices, it makes no difference whether the unit
matrix is present. That is why the sloppy notation will almost always give
the correct result. Almost always....
form-master/doc/manual/man.tex 0000664 0000000 0000000 00000005623 13565763364 0016661 0 ustar 00root root 0000000 0000000 \def\formmajorversion{4}
\def\formminorversion{2}
\def\formdate{4-oct-2018}
\documentclass[11pt]{report}
%\usepackage{index}
\makeatletter
\renewcommand*\l@section{\@dottedtocline{1}{1.5em}{3.0em}}
\makeatother
\usepackage{makeidx}
% Use hyperref package (hyperlinks) with correct option for pdflatex/latex:
\usepackage{ifpdf}
\ifpdf
\RequirePackage[pdftex]{hyperref}
\else
\RequirePackage[hypertex]{hyperref}
\fi
% and link indices back to text:
%\hypersetup{pdfpagemode={None},draft=false}
%\hypersetup{hyperindex,pagebackref,pdfpagemode={None},draft=false}
\newcommand{\pfill}{\hfill}
\newcommand{\emptypage}{\newpage \thispagestyle{empty} \tiny{.} \normalsize}
\newcommand{\lefttabitem}[1]{\noindent{\begin{minipage}[t]{4cm}{#1}
\end{minipage}}\vspace{1mm}}
\newcommand{\leftvitem}[2]{\noindent{\begin{minipage}[t]{#1}{#2}
\end{minipage}}\vspace{1mm}}
\newcommand{\tabitem}[1]{{\begin{minipage}[t]{12cm}{#1}\end{minipage}}
\vspace{1mm}}
\newcommand{\rightvitem}[2]{{\begin{minipage}[t]{#1}{#2}\end{minipage}}
\vspace{1mm}}
\newcommand{\clearemptydoublepage}{\newpage{\pagestyle{empty}\cleardoublepage}}
\textheight 655pt % Height of text (including footnotes and figures,
% excluding running head and foot).
% Note 1cm = 28.453pt
\textwidth 16.7cm % Width of text line.
%
% Need to move the origin on the page to centre the block of text:
%
\hoffset -2.15cm \voffset -1.7cm % settings good for IBM 3812 printer
\def\FORM{{\sc FORM}}
\def\TFORM{{\sc TFORM}}
\def\ParFORM{{\sc ParFORM}}
\def\Andre#1{{\sl #1}}
\def\Remark#1{{\sl #1}}
\def\Tr{{\rm Tr}}
\def\hash{\symbol{"23}}
\def\sign(#1){(\!-\!1)^{#1}}
\def\binom(#1,#2){ (\!\!
\begin{array}{c} #1 \\ #2 \end{array}\!\! ) }
\def\plus{\!+\!}
\def\minus{\!-\!}
\def\mydot{\!\!\cdot\!}
\def\nn{\nonumber \\ &&}
\def\nne{\nonumber \\ & = &}
\makeindex
\begin{document}
\begin{titlepage}
\title{\Huge FORM \\ \Large version \formmajorversion.\formminorversion \\
\huge Reference manual}
\date{\formdate}
\author{J.A.M.~Vermaseren, T.~Kaneko, J.~Kuipers, B.~Ruijl, M.~Tentyukov, T.~Ueda and J.~Vollinga}
\end{titlepage}
\maketitle
%\clearemptydoublepage
%\pagenumbering{roman}
%\setcounter{page}{1}
%\clearemptydoublepage
%\tableofcontents
%%\emptypage
%\clearemptydoublepage
%\pagenumbering{arabic}
\setcounter{page}{2}
\clearemptydoublepage
\emptypage
\clearemptydoublepage
\pagenumbering{roman}
\setcounter{page}{1}
\clearemptydoublepage
\tableofcontents
\clearemptydoublepage
\emptypage
\clearemptydoublepage
\pagenumbering{arabic}
\setcounter{page}{1}
%
\input{startup}
\input{variable}
\input{prepro}
\input{module}
\input{pattern}
\input{dollar}
\input{statements}
\input{functions}
\input{bracket}
\input{polynomials}
\input{optim}
\input{tablebas}
\input{dict}
\input{gamma}
\input{metric}
\input{sorting}
\input{setup}
\input{parallel}
\input{external}
\input{spectators}
\input{diagrams}
%
\printindex
\end{document}
form-master/doc/manual/manual.tex.in 0000664 0000000 0000000 00000006341 13565763364 0017766 0 ustar 00root root 0000000 0000000 \input{version.tex}
\def\formmajorversion{\repomajorversion}
\def\formminorversion{\repominorversion}
\def\formdate{\repodate}
%begin{latexonly} % To avoid latex2html/latex2html#37
\providecommand{\repodate}{\today}
%end{latexonly}
\documentclass[11pt]{report}
\usepackage{makeidx}
%begin{latexonly}
\makeatletter
\renewcommand*\l@section{\@dottedtocline{1}{1.5em}{3.0em}}
\makeatother
% Use hyperref package (hyperlinks) with correct option for pdflatex/latex:
\usepackage{ifpdf}
\ifpdf
\RequirePackage[pdftex]{hyperref}
\else
\RequirePackage[hypertex]{hyperref}
\fi
% and link indices back to text:
\hypersetup{hyperindex,pagebackref,pdfpagemode={None},draft=false}
%end{latexonly}
\providecommand{\texorpdfstring}[2]{#1}% htmlonly
\newcommand{\pfill}{\hfill}
\newcommand{\emptypage}{\newpage \thispagestyle{empty} \tiny{.} \normalsize}
\newcommand{\lefttabitem}[1]{\noindent{\begin{minipage}[t]{4cm}{#1}
\end{minipage}}\vspace{1mm}}
\newcommand{\leftvitem}[2]{\noindent{\begin{minipage}[t]{#1}{#2}
\end{minipage}}\vspace{1mm}}
\newcommand{\tabitem}[1]{{\begin{minipage}[t]{12cm}{#1}\end{minipage}}
\vspace{1mm}}
\newcommand{\rightvitem}[2]{{\begin{minipage}[t]{#1}{#2}\end{minipage}}
\vspace{1mm}}
\newcommand{\clearemptydoublepage}{\newpage{\pagestyle{empty}\cleardoublepage}}
\textheight 655pt % Height of text (including footnotes and figures,
% excluding running head and foot).
% Note 1cm = 28.453pt
\textwidth 16.5cm % Width of text line.
%
% Need to move the origin on the page to centre the block of text:
%
\hoffset -2.15cm \voffset -1.7cm % settings good for IBM 3812 printer
\def\FORM{{\sc FORM}}
\def\TFORM{{\sc TFORM}}
\def\ParFORM{{\sc ParFORM}}
\def\Andre#1{{\sl #1}}
\def\Remark#1{{\sl #1}}
\def\Tr{{\rm Tr}}
\def\hash{\symbol{"23}}
\def\sign(#1){(\!-\!1)^{#1}}
\def\binom(#1,#2){ (\!\!
\begin{array}{c} #1 \\ #2 \end{array}\!\! ) }
\def\plus{\!+\!}
\def\minus{\!-\!}
\def\mydot{\!\!\cdot\!}
\def\nn{\nonumber \\ &&}
\def\nne{\nonumber \\ & = &}
\makeindex
\begin{document}
\begin{titlepage}
\title{\Huge FORM \\ \Large version @VERSION@ \\ \huge Reference manual}
\date{\formdate}
\author{J.A.M.~Vermaseren, T.~Kaneko, J.~Kuipers, B.~Ruijl, M.~Tentyukov, T.~Ueda and J.~Vollinga}
\end{titlepage}
\maketitle
%\clearemptydoublepage
%\pagenumbering{roman}
%\setcounter{page}{1}
%\clearemptydoublepage
%\tableofcontents
%%\emptypage
%\clearemptydoublepage
%\pagenumbering{arabic}
\setcounter{page}{2}
\clearemptydoublepage
\emptypage
\clearemptydoublepage
\pagenumbering{roman}
\setcounter{page}{1}
\clearemptydoublepage
\tableofcontents
\clearemptydoublepage
\emptypage
\clearemptydoublepage
\pagenumbering{arabic}
\setcounter{page}{1}
\input{@srcdir@/startup}
\input{@srcdir@/variable}
\input{@srcdir@/prepro}
\input{@srcdir@/module}
\input{@srcdir@/pattern}
\input{@srcdir@/dollar}
\input{@srcdir@/statements}
\input{@srcdir@/functions}
\input{@srcdir@/bracket}
\input{@srcdir@/optim}
\input{@srcdir@/polynomials}
\input{@srcdir@/tablebas}
\input{@srcdir@/dict}
\input{@srcdir@/gamma}
\input{@srcdir@/metric}
\input{@srcdir@/sorting}
\input{@srcdir@/setup}
\input{@srcdir@/parallel}
\input{@srcdir@/external}
\input{@srcdir@/spectators}
\input{@srcdir@/diagrams}
\printindex
\end{document}
form-master/doc/manual/metric.tex 0000664 0000000 0000000 00000026132 13565763364 0017367 0 ustar 00root root 0000000 0000000
\chapter{A few notes on the use of a metric}
\label{metric}
\noindent When \FORM\ was designed, it was decided to make its syntax more or
less independent of a choice of the metric\index{metric}. Hence statements
and facilities that programs like Schoonschip\index{Schoonschip} or
REDUCE\index{REDUCE} provide but which depend on the choice of a metric
have been left out. Instead there are facilities to implement any choice of
the metric, when the need really arises. When one makes a proper study of
it, it turns out that one usually has to do very little or nothing. \hfill
\vspace{2mm}
\noindent First one should realize that \FORM\ does not know any specific
metric by itself. Dotproducts are just objects of manipulation. It is
assumed that when a common index of two vectors is contracted, this works
out properly into a scalar object. This means that if one has a metric with
upper and lower indices\index{indices!upper}\index{indices!lower}, one
index is supposed to be an upper index and the other is supposed to be a
lower index. If the user does not like this, it is his/her responsibility
to force the system into a different action. This is reflected in the fact
that \FORM\ does not have an internal metric tensor\index{tensor!metric}
$\eta_{\mu\nu}$. It has only a Kronecker\index{Kronecker}
delta\index{delta!Kronecker} $\delta_{\mu\nu} =$ \verb:d_(mu,nu): with
\verb:p(mu)*d_(mu,nu)*q(nu): $\rightarrow$ \verb:p.q: when mu and nu are
summable indices\index{indices!summable}. \hfill \vspace{2mm}
\noindent The dependency of a metric usually enters with statements like
$p^2 = \pm m^2$, which the user should provide anyway, because \FORM\ does
not have such knowledge. Connected to this is the choice of a
propagator\index{propagator} as either $\gamma_\mu p_\mu + m$ or
$\gamma_\mu p_\mu + i\ m$. This is also something the user should provide.
The only objects that \FORM\ recognizes and that could be considered as
metric-dependent are the gamma matrices\index{matrices!gamma} and the
Levi-Civita\index{Levi-Civita} tensor\index{tensor!Levi-Civita}
\verb:e_:. Because the trace of a $\gamma_5$ involves a Levi-Civita tensor,
the two are intimately connected. The anticommutator of two gamma matrices
is defined with the Kronecker delta. Amazingly enough that works out well,
provided that, if such Kronecker delta's survive in the output, they are
interpreted as a metric tensor. This should be done with great care,
because at such a point one does something that depends of the metric; one
may have to select whether the indices are upper or lower indices. One
should check carefully that the way the output is interpreted leads indeed
to the results that are expected. This is anyway coupled to how one should
interpret the input, because in such a case one would also have an input
with `open' indices and give them a proper interpretation. The rule is that
generally one does not have to do anything. The upper indices in the input
will be upper indices in the output and the same for lower indices. \hfill
\vspace{2mm}
\noindent The contraction\index{contraction} of two Levi-Civita tensors will give products of
Kronecker delta's. This means that formally there could be an error of the
sign of the determinant of the metric tensor, if one would like the
Kronecker delta to play the role of a metric tensor. Hence it is best to
try to avoid such a situation. \hfill \vspace{2mm}
\noindent In \FORM\ the $\gamma_5$ is an object that anticommutes with
the $\gamma_\mu$ and has $\gamma_5\gamma_5 = 1$. Its properties
in the trace are
\begin{eqnarray}
Tr[\gamma_5\gamma_{m_1}\gamma_{m_2}\gamma_{m_3}\gamma_{m_4}] & = &
4 \epsilon_{\mu_1\mu_2\mu_3\mu_4} \nonumber
\end{eqnarray}
This has a number of interesting consequences. The V-A and V+A currents are
represented by $\gamma_7\gamma_\mu = (1-\gamma_5)\gamma_\mu$ and
$\gamma_6\gamma_\mu = (1+\gamma_5)\gamma_\mu$ respectively. Under
conjugation we have to replace $\gamma_5$ by $-\gamma_5$ as is not
uncommon. \hfill \vspace{2mm}
\noindent There was a time that a conjugation\index{conjugation} operation
was planned in \FORM. As time progressed, it was realized that this would
introduce problems with some of the internal objects. Hence some objects
have the property that they are considered imaginary\index{imaginary}. In
practise \FORM\ does not do anything with this. Neither does it do anything
with the declarations real\index{real}, complex\index{complex} and
imaginary\index{imaginary}. If ever a way is found to implement a conjugation
operator that will make everybody happy, it may still be built in. \hfill
\vspace{2mm}
\noindent The above should give the user enough information to convert any
specific metric to what is needed to make \FORM\ do what is expected from it.
Afterwards one can convert back, provided no metric\index{metric} specific
operations are done. Such metric specific things are for instance needed in
some types of approximations in which one substitutes objects by
(vector)components halfway the calculation. In that case one cannot rely on
that the conversions at the beginning and the end will be compensating each
other. For this case \FORM\ allows the user to define a private metric. All
the tools exist to make this a success with the exception of a loss in
speed of course. Let us have a look at the contraction of two Levi-Civita
tensors in an arbitrary metric:
% THIS EXAMPLE IS PART OF THE TESTSUITE. CHANGES HERE SHOULD BE APPLIED THERE AS
% WELL!
\begin{verbatim}
Indices m1,m2,m3,n1,n2,n3,i1,i2,i3;
Cfunction eta(symmetric),e(antisymmetric);
Off Statistics;
*
* We have our own Levi-Civita tensor e
*
Local F = e(m1,m2,m3)*e(m1,m2,m3);
*
* We write the contraction as
*
id e(m1?,m2?,m3?)*e(n1?,n2?,n3?) =
e_(m1,m2,m3)*e_(i1,i2,i3)*
eta(n1,i1)*eta(n2,i2)*eta(n3,i3);
*
* Now we can use the internal workings of the contract:
*
Contract;
Print +s;
.sort
F =
+ eta(i1,i1)*eta(i2,i2)*eta(i3,i3)
- eta(i1,i1)*eta(i2,i3)^2
- eta(i1,i2)^2*eta(i3,i3)
+ 2*eta(i1,i2)*eta(i1,i3)*eta(i2,i3)
- eta(i1,i3)^2*eta(i2,i2)
;
*
* For specifying a metric we need individual components:
*
Sum i1,1,2,3;
Sum i2,1,2,3;
Sum i3,1,2,3;
Print +s;
.sort
F =
+ 6*eta(1,1)*eta(2,2)*eta(3,3)
- 6*eta(1,1)*eta(2,3)^2
- 6*eta(1,2)^2*eta(3,3)
+ 12*eta(1,2)*eta(1,3)*eta(2,3)
- 6*eta(1,3)^2*eta(2,2)
;
*
* And now we can provide the metric tensor
*
id eta(1,1) = 1;
id eta(2,2) = 1;
id eta(3,3) = -1;
id eta(1,2) = 0;
id eta(1,3) = 0;
id eta(2,3) = 0;
Print +s;
.end
F =
- 6
;
\end{verbatim}
This is the ultimate in flexibility\index{flexibility} of course. It can
also be worked out in a different way. In this case we try to change the
behaviour of the Kronecker\index{Kronecker} delta\index{delta!Kronecker} a
bit. This is dangerous\index{dangerous} and needs, in addition to a good
understanding of what is happening, good testing to make sure that what the
user wants is indeed what does happen. Here we use the
FixIndex\index{fixindex} (\ref{substafixindex}) statement. This one assigns
specific values to selected diagonal elements of the Kronecker delta. Of
course it is the responsibility of the user to make sure that the
calculation will indeed run into those elements. This is by no means
automatic, because when \FORM\ uses formal indices it never writes them out
in components. Moreover, it would not be defined what would be the
components connected to an index. The index could run over $0,1,2,3$ or
over $1,2,3,4$, or maybe even over $5,7,9,11$. And what does an
n-dimensional index run over? In the above example it is the sum
(\ref{substasum}) statement that determines this. Hence this is fully under
the control of the user. Therefore a proper way to deal with the above
example would be
% THIS EXAMPLE IS PART OF THE TESTSUITE. CHANGES HERE SHOULD BE APPLIED THERE AS
% WELL!
\begin{verbatim}
Indices i1,i2,i3;
FixIndex 1:1,2:1,3:-1;
Off Statistics;
*
Local F = e_(i1,i2,i3)*e_(i1,i2,i3);
Sum i1,1,2,3;
Sum i2,1,2,3;
Sum i3,1,2,3;
Print +s;
.sort
F =
+ 6*e_(1,2,3)*e_(1,2,3)
;
Contract;
Print +s;
.end
F =
- 6
;
\end{verbatim}
In the case that one would like to exchange the order of the summation and
the contraction, while using the FixIndex mechanism, one needs to be more
careful. In that case we have to prevent the indices from being summed over
while they are indices of a Kronecker delta, because as long as the indices
are symbolic, \FORM\ will replace \verb:d_(i1,i1): by the dimension of
\verb:i1:, and that is not what we want. Hence we have to declare the
indices to be non-summable by giving them dimension zero:
% THIS EXAMPLE IS PART OF THE TESTSUITE. CHANGES HERE SHOULD BE APPLIED THERE AS
% WELL!
\begin{verbatim}
Indices i1=0,i2=0,i3=0;
FixIndex 1:1,2:1,3:-1;
Off Statistics;
*
Local F = e_(i1,i2,i3)*e_(i1,i2,i3);
Contract;
Print +s;
.sort
F =
+ d_(i1,i1)*d_(i2,i2)*d_(i3,i3)
- d_(i1,i1)*d_(i2,i3)*d_(i2,i3)
- d_(i1,i2)*d_(i1,i2)*d_(i3,i3)
+ 2*d_(i1,i2)*d_(i1,i3)*d_(i2,i3)
- d_(i1,i3)*d_(i1,i3)*d_(i2,i2)
;
Sum i1,1,2,3;
Sum i2,1,2,3;
Sum i3,1,2,3;
Print +s;
.end
F =
- 6
;
\end{verbatim}
As we can see, the automatic summation over the indices is not performed
now and this gives us a chance to do the summation manually. After that the
fixindex statement can have its effect. \hfill \vspace{2mm}
\noindent It should be clear from the above examples that it is usually
much easier to manipulate the input in such a way that the terms with two
Levi-Civita tensors have the negative sign from the beginning. This would
give programs that are less complicated and much faster. \hfill \vspace{2mm}
\noindent Hence we are faced with the situation that in normal cases one
does not do anything. If one wants to go beyond this and wants to interfere
with the inner workings themselves by for instance inserting a factor $i$
in front of the $\gamma_5$ and emulating the upper and lower indices of a
favorite metric, this leads from one problem to the next. Extreme care is
needed. This is usually done by people who have first worked with other
programs in which things don't work as naturally as in \FORM. By the time
one has really figured out how to deal with the metric and how to make use
of the internal algorithms of \FORM, one usually does not have to do very
much again. \hfill \vspace{2mm}
\noindent As in the Zen\index{Zen} saying: \hfill \vspace{2mm}
\noindent To the beginning student mountains\index{mountains} are mountains
and water\index{water} is water. To the advanced student\index{student}
mountains stop being mountains and water stops being water. To the
master\index{master} mountains are mountains again and water is water
again. \hfill \vspace{2mm}
\noindent Of course the modern master also checks that what he expects the
system to do, is indeed what the system does.
form-master/doc/manual/module.tex 0000664 0000000 0000000 00000032747 13565763364 0017402 0 ustar 00root root 0000000 0000000
\chapter{Modules}
\label{modules}
Modules\index{module} are the basic execution\index{execution} blocks.
Statements\index{statements} are always part of a module, and they will be
executed only when the module is executed. This is directly opposite to
preprocessor instructions which are executed when they are encountered in
the input stream.
Modules are terminated by a line that starts with a period\index{period}.
Such a line is called the module\index{module instruction} instruction.
Once the module instruction has been recognized, the compilation of the
module is terminated and the module will be executed. All active
expressions will be processed one by one, term by term. When each term of
an expression has been through all statements of the module, the combined
results of all operations on all the terms of the expression will be sorted
and the resulting expression will be sent to the output. This can be an
intermediate file\index{file!intermediate}, or it can be some
memory\index{memory}, depending on the size of the output. If the combined
output of all active expressions is less than the parameter
``ScratchSize''\index{ScratchSize}, the results stay in memory. ScratchSize
is one of the setup parameters (see chapter \ref{setup}).
A module consists in general of several types of statements:
\begin{description}
\item [Declarations\index{declarations}] These are the declarations of
variables.
\item [Specifications\index{specifications}] These tell what to do with
existing expressions as a whole.
\item [Definitions\index{definitions}] These define new expressions.
\item [Executable\index{executable statements} statements] The operations
on all active expressions.
\item [OutputSpecifications\index{output specifications}] These specify the
output representation.
\item [End-of-module specifications\index{end of module specifications}]
Extra settings that are for this module only.
\item [Mixed statements\index{mixed statements}] They can occur in various
classes. Most notably the print statement.
\end{description}
Statements must occur in such an order that no statement follows a
statement of a later category. The only exception is formed by the mixed
statements, which can occur anywhere. This is different from earlier
versions of \FORM\ in which the order of the statements was not fixed. This
did cause a certain amount of confusion about the workings of \FORM.
There are several types of modules.
\begin{description}
\item[.sort\index{.sort}] \label{instrsort} The general end-of-module.
Causes execution of all active expressions, and prepares them for the next
module.
\item[.end\index{.end}] \label{instrend} Executes all active expressions
and terminates the program.
\item[.store\index{.store}] \label{instrstore} Executes all active
expressions. Then it writes all active global expressions to an
intermediate storage file\index{file!storage} and removes all other
non-global expressions. Removes all memory of declarations except for those
that were made before a .global instruction.
\item[.global\index{.global}] \label{instrglobal} No execution of
expressions. It just saves declarations made thus far from being erased by
a .store instruction.
\item[.clear\index{.clear}] \label{instrclear} Executes all active
expressions. Then it clears all buffers with the exception of the main
input stream. Continues execution in the main input stream as if the
program had started at this point. The only parameters that cannot be
changed at this point are the setup parameters. They remain. By default
also the clock\index{clock} is reset. If this is not desired this can be
changed by means of the ResetTimeOnClear\index{resettimeonclear} setup
variable (see chapter \ref{setup}).
\end{description}
Each program must be terminated by a .end instruction. If such an
instruction is absent and \FORM\ encounters an end-of-input it will issue a
warning and generate a .end instruction.
Module instructions can contain a special commentary that will be printed
in all statistics that are generated during the execution of the module.
This special commentary is restricted to 24 characters (the statistics have
a fixed format and hence there is only a limited amount of space
available). This commentary is initiated by a colon and terminated by a
semicolon. The characters between this colon and the semicolon are the
special message, also called advertisement. Example
\begin{verbatim}
.sort:Eliminate x;
\end{verbatim}
would give in the statistics something like
\begin{verbatim}
Time = 0.46 sec Generated terms = 360
F Terms in output = 360
Eliminate x Bytes used = 4506
\end{verbatim}
If the statistics are switched off, there will be no printing of this
advertisement either.
For backwards compatibility there is still an obsolete\index{obsolete}
mechanism to pass module options via the module instructions. This is a
feature which will probably disappear in future versions of \FORM. We do
give the syntax to allow the user to identify the option properly and
enable proper translation into the moduleoption\index{moduleoption}
statement (see \ref{substamoduleoption}).
\begin{verbatim}
.sort(PolyFun=functionname);
.sort(PolyFun=functionname):advertisement;
\end{verbatim}
causes the given function to be treated as a polynomial\index{polyfun}
function. This means that its (single) argument would be treated as the
coefficient of the terms. The action of \FORM\ on individual terms is
\begin{enumerate}
\item Ignore polynomial functions with more than one argument.
\item If there is no polynomial function with a single argument, generate
one with the argument 1.\item If there is more than one polynomial function
with a single argument, multiply the arguments and replace these functions
with a single polynomial function with the product of the arguments for a
single argument.
\item Multiply the argument of the polynomial function with the coefficient
of the term. Replace the coefficient itself by one.
\end{enumerate}
If, after this, two terms differ only in the argument of their polynomial
function \FORM\ will add the arguments and replace the two terms by a single
term which is identical to the two previous terms except for that the
argument of its polynomial function is the sum of their two arguments.
It should be noted that the proper placement of .sort\index{.sort}
instructions in a \FORM\ program is an art by itself. Too many .sort
instructions cause too much sorting, which can slow execution down
considerably. It can also cause the writing of intermediate expressions
which are much larger than necessary, if the next statements would cause
great simplifications. Not enough .sort instructions can make that
cancellations are postponed unnecessarily and hence much work will be done
double. This can slow down execution by a big factor. First an example of a
superfluous .sort:
\begin{verbatim}
S a1,...,a7;
L F = (a1+...+a7)^16;
.sort
Time = 31.98 sec Generated terms = 74613
F Terms in output = 74613
Bytes used = 1904316
id a7 = a1+a2+a3;
.end
Time = 290.34 sec
F Terms active = 87027
Bytes used = 2253572
Time = 295.20 sec Generated terms = 735471
F Terms in output = 20349
Bytes used = 538884
\end{verbatim}
Without the sort the same program gives:
\begin{verbatim}
S a1,...,a7;
L F = (a1+...+a7)^16;
id a7 = a1+a2+a3;
.end
Time = 262.79 sec
F Terms active = 94372
Bytes used = 2643640
Time = 267.81 sec Generated terms = 735471
F Terms in output = 20349
Bytes used = 538884
\end{verbatim}
and we see that the sorting in the beginning is nearly completely wasted.
Now a clear example of not enough .sort instructions. A common problem is
the substitution of one power\index{power series} series into another. If
one does this in one step one could have:
\begin{verbatim}
#define MAX "36"
S j,x(:`MAX'),y(:`MAX');
*
* Power series expansion of ln_(1+x)
*
L F = -sum_(j,1,`MAX',sign_(j)*x^j/j);
*
* Substitute the expansion of x = exp_(y)-1
*
id x = x*y;
#do j = 2,`MAX'+1
id x = 1+x*y/`j';
#enddo
Print;
.end
Time = 76.84 sec Generated terms = 99132
F Terms in output = 1
Bytes used = 18
F =
y;
\end{verbatim}
With an extra .sort inside the loop one obtains for the same program (after
suppressing some of the statistics:
\begin{verbatim}
#define MAX "36"
S j,x(:`MAX'),y(:`MAX');
*
* Power series expansion of ln_(1+x)
*
L F = -sum_(j,1,`MAX',sign_(j)*x^j/j);
*
* Substitute the expansion of x = exp_(y)-1
*
id x = x*y;
#do j = 2,`MAX'+1
id x = 1+x*y/`j';
.sort: step `j';
Time = 0.46 sec Generated terms = 360
F Terms in output = 360
step 2 Bytes used = 4506
#enddo
.
.
.
Time = 3.07 sec Generated terms = 3
F Terms in output = 1
step 37 Bytes used = 18
Print;
.end
Time = 3.07 sec Generated terms = 1
F Terms in output = 1
Bytes used = 18
F =
y;
\end{verbatim}
It is very hard to give general rules that are more specific than what has
been said above. The user should experiment with the placements of the .sort
before making a very large run.
\section{Checkpoints}
\label{checkpoints}
If\index{checkpoints} \FORM\ programs have to run for a long time, the
reliability of the hardware(computer system or network) or of the software
infrastructure becomes a critical issue. Program
termination\index{termination} due to unforeseen failures may waste days or
weeks of invested execution time. The checkpoint mechanism was introduced
to protect long running \FORM\ programs as good as possible from such
accidental interruptions. With activated checkpoints \FORM\ will save its
internal state and data from time to time on the hard disk. This data then
allows a recovery from a crash\index{crash}.
The checkpoint mechanism can be activated or deactivated by {\tt
On}\index{on} and {\tt Off}\index{off} statements. If the user has
activated checkpoints, recovery\index{recovery} data will be written to disk
at the end of a module execution. Options allow to influence the details of
the saving mechanism. If a program is terminated during execution, \FORM\ can
be restarted with the {\tt -R} option and it will continue its execution at
the last saved recovery point.
The syntax of the checkpoint activation and deactivation is
\begin{verbatim}
On checkpoint [];
Off checkpoint;
\end{verbatim}
If no options are given, the recovery data will be saved at the end of every
module\index{module}. If one gives a time\index{time}
\begin{verbatim}
On checkpoint [];
\end{verbatim}
the saving will only be done if the given time has passed after the last
saving. Possible unit specifiers are {\tt s, m, h, d} and the number will
then be interpreted as seconds, minutes, hours, or days, respectively. The
default unit is seconds.
If one needs to run a script\index{run a script} before or after the saving,
one can specify a script filename.
\begin{verbatim}
On checkpoint runbefore="";
On checkpoint runafter="";
On checkpoint run="";
\end{verbatim}
The option {\tt run}\index{run} sets both the scripts to be run before and
after saving.The scripts must have the executable flag set and they must
reside in the execution path of the shell\index{shell} (unless the filename
already contains the proper path).
The scripts receive the module number\index{module number} as an argument
(accessible as \$1 inside the script). The return value of the script
running before the saving will be interpreted. If the script returns an
error (non-zero return value), a message will be issued and the saving will
be skipped.
The recovery data will be written to files named {\tt FORMrecv.*} with
various name extensions. If a file {\tt FORMrecv.tmp} exists, \FORM\ will not
run unless one gives it the recovery option\index{recovery option}
{\tt -R}. This is to prevent the unintentional loss of recovery data. If
\FORM\ terminates successfully, all the additional data files will be removed.
The additional recovery files will be created in the directory containing
the scratch files. The extra files will occupy roughly as much space as
the scratch files\index{scratch files} and the save\index{save files} and
hide files\index{hide files} combined. This extra space must be made
available, of course.
If recovery data exists and \FORM\ is started with the {\tt -R} option, \FORM\
will continue execution after the last module that successfully wrote the
recovery data. All the command line parameters that have been given to the
crashed \FORM\ program\index{crashed \FORM\ program} must also be given to the
recovering \FORM\ program. The input files are not part of the recovery data
and will be read in anew when recovering. Therefore it is strongly
discouraged to change any of these files between saving and recovery.
form-master/doc/manual/online.tex 0000664 0000000 0000000 00000003277 13565763364 0017375 0 ustar 00root root 0000000 0000000 \def\formmajorversion{4}
\def\formminorversion{2}
\def\formdate{4-oct-2018}
\documentclass{report}
\usepackage{html}
\usepackage{graphics}
\usepackage{makeidx}
\providecommand{\texorpdfstring}[2]{#1}
%\makeatletter
%\renewcommand*\l@section{\@dottedtocline{1}{1.5em}{3.0em}}
%\makeatother
\newcommand{\pfill}{\hfill}
\newcommand{\lefttabitem}[1]{\noindent{\begin{minipage}[t]{4cm}{#1}
\end{minipage}}\vspace{1mm}}
\newcommand{\leftvitem}[2]{\noindent{\begin{minipage}[t]{#1}{#2}
\end{minipage}}\vspace{1mm}}
\newcommand{\tabitem}[1]{{\begin{minipage}[t]{12cm}{#1}\end{minipage}}
\vspace{1mm}}
\newcommand{\rightvitem}[2]{{\begin{minipage}[t]{#1}{#2}\end{minipage}}
\vspace{1mm}}
\def\FORM{{\sc FORM}}
\def\TFORM{{\sc TFORM}}
\def\ParFORM{{\sc ParFORM}}
\def\Andre#1{{\sl #1}}
\def\Remark#1{{\sl #1}}
\def\Tr{{\rm Tr}}
\def\hash{\symbol{"23}}
\def\sign(#1){(\!-\!1)^{#1}}
\def\binom(#1,#2){ (\!\!
\begin{array}{c} #1 \\ #2 \end{array}\!\! ) }
\def\plus{\!+\!}
\def\minus{\!-\!}
\def\mydot{\!\!\cdot\!}
\def\nn{\nonumber \\ &&}
\def\nne{\nonumber \\ & = &}
\makeindex
\begin{document}
\begin{center}
{\Huge FORM \\ \Large version \formmajorversion.\formminorversion
\\ \huge Reference manual} \\
{\formdate} \\
{J.A.M.~Vermaseren, T.~Kaneko, J.~Kuipers, B.~Ruijl, M.~Tentyukov, T.~Ueda and J.~Vollinga}
\end{center}
\tableofcontents
\input{startup}
\input{variable}
\input{prepro}
\input{module}
\input{pattern}
\input{dollar}
\input{statements}
\input{functions}
\input{bracket}
\input{polynomials}
\input{optim}
\input{tablebas}
\input{dict}
\input{gamma}
\input{metric}
\input{sorting}
\input{setup}
\input{parallel}
\input{external}
\input{spectators}
\input{diagrams}
\printindex
\end{document}
form-master/doc/manual/optim.tex 0000664 0000000 0000000 00000045573 13565763364 0017246 0 ustar 00root root 0000000 0000000
\chapter{Output optimization}
\label{optimization}
One of the uses of symbolic programs is to prepare formulas for further
numerical processing\index{numerical processing}. Technically speaking such
processing is not part of computer algebra, although some packages may
provide facilities for this. In \FORM\ such facilities, such as
Monte Carlo integration, do not exist at the moment, but, starting with
version 4.1, \FORM\ does provide statements to construct outputs in C or
Fortran that are highly optimized with respect to the number of arithmetic
operations\index{arithmetic operations} that are needed for their
evaluation. The algorithms used for this are described in the papers
\begin{itemize}
\item Code Optimization in FORM - \url{https://arxiv.org/abs/1310.7007}
\item Improving multivariate Horner schemes with Monte Carlo tree search - \url{https://arxiv.org/abs/1207.7079}
\item Combining Simulated Annealing and Monte Carlo Tree Search for Expression Simplification - \url{https://arxiv.org/abs/1312.0841}
\item Why Local Search Excels in Expression Simplification - \url{https://arxiv.org/abs/1409.5223}
\end{itemize}
In short, an optimal Horner scheme is constructed after
which common subexpressions are eliminated. The methods for finding the
optimal scheme can use a simple heuristic, Monte Carlo Tree Search,
or a Stochastic Local Search approach such as Simulated Annealing
In this section the precise
format of the commands that concern the optimizations will be described.
In optimized output \FORM\ needs temporary variables\index{temporary
variables}. In order to avoid conflicts with user defined objects \FORM\
uses the extra symbols \ref{substaextrasymbols}\index{extra symbols} for
these variables. This means that the user can control their output
representation in the standard way. In addition there are preprocessor
variables that tell how many of these extra symbols were needed:
\begin{description}
\item[optimminvar\_] The number of extra symbols before the optimization
process started\index{optimminvar\_}.
\item[optimmaxvar\_] The number of extra symbols after the optimization
process finished\index{optimmaxvar\_}.
\end{description}
Each new optimization will remove the old optimization results and start
the extra symbols from the number there were before the optimization
started. Because this may cause interference with the functioning of the
extrasymbol statement, regular printing with output optimization and the
extrasymbol statement cannot occur inside the same module. Such occurrence
would result in an error message.
Because the output optimization is done for expressions that contain only
symbols\index{symbols}, \FORM\ has to convert all non-symbols and negative
powers of symbols to extra symbols\index{extra symbols} before it starts
the optimization. This is another reason why interference between the
extrasymbol \ref{substaextrasymbols}\index{extra symbols} statement and
output optimizations is forbidden. When the results are printed, the
definition of the extra symbols that are introduced this way are printed as
well.
\FORM\ has two ways to perform optimizations. The first and easiest is in
the regular output. If one asks for optimization (by specifying the proper
format for this) and follows this by a print statement, the output printed
will be in optimized form. This is however just a representation of the
expression and the next module will obtain the original expression for its
input.
The more useful way to obtain an optimized output is with the \#optimize
instruction. To use this instruction properly one should understand what
\FORM\ does when it optimizes an expression. The whole process of
optimization takes place inside the memory. Hence, \FORM\ cannot optimize
expressions that do not fit inside the CPU memory. The notation is however
fairly compact and \FORM\ needs far less space than for instance the
compiler (and gives better results). The result of the optimization is
stored inside a buffer. There is only a single optimization
buffer\index{optimization buffer} and the preprocessor variables
optimminvar\_\index{optimminvar\_} and optimmaxvar\_\index{optimmaxvar\_}
refer to the contents of this buffer. When the \#optimize instruction is
used it loads this buffer and the contents stay around until either a
\#clearoptimize instruction is used or a new \#optimize instruction is
issued.
The \#optimize instruction changes the original expression to its optimized
shape in which it is usually a very short expression that refers to one or
more extra symbols. The optimization information is automatically erased,
and with it the expression that was optimized, when a second \#optimize
instruction is issued. Clearing the optimization buffer means that the
information of the first expression is irretrievably lost and the contents
of the first expression become meaningless, because its extra symbols have
been erased. Hence if the user still needs this expression it is necessary
to make a copy of it before optimization.
The optimization buffers, and the optimized expression, can be removed by
the user with the \#clearoptimize instruction. This is mandatory before the
use of a ToPolynomial \ref{substatopolynomial}\index{ToPolynomial}
statement, because that may introduce new extra symbols.
The contents of the optimization buffer\index{optimization buffer} can be
written with the \%O combination in the format string in the \#write
instruction. This means that it is easy to write this output to file.
Consider for instance the following program:
\begin{verbatim}
CF f;
S a,b,c;
L H = f(a)+f(b)+(a+b+c)^2;
L G = f(c)+(a+b+c)^3;
Format O2;
Print +f;
.sort
ExtraSymbols,array,w;
Format Fortran;
#optimize G
#write " REAL*8 w(`optimmaxvar_')"
#write "%O"
#write " G = %e",G
#clearoptimize
.sort
#optimize H
#write " REAL*8 w(`optimmaxvar_')"
#write "%O"
#write " H = %e",H
.end
\end{verbatim}
This program shows the two different methods and shows what is left of the
expressions G and H. It also shows that we have to deal with the
expressions one by one when we use the \#optimize instruction, while in the
regular printing of the output this is not needed because the expression
itself remains in its unoptimized version.
\subsection{Optimization options of the Format statement}
The \verb|Format| statement has a number of options to control the
code optimization. The easiest to use are the following:
\begin{description}
\item[O0] Switches off all optimizations and prints the output the
normal \FORM\ way. This is the default.
\item[O1] Activates the lowest level of optimization. It is very fast,
i.e., linear in the size of the expression, and gives reasonably
efficient code.
\item[O2] Activates the medium level of optimization. This is slower
than the previous setting, but usually gives better results.
\item[O3] Activates the highest level of optimization using MCTS. It can be
rather slow, but usually gives even better results.
\item[O4] Activates the highest level of optimization using Local Stochastic Search.
It is usually much faster than MCTS and may give better results.
\end{description}
Below we show how to use O4 and how it compares to O2:
\begin{verbatim}
#-
S a,b,c,d,e,f,g,h,i,j,k,l,m,n;
L G = (4*a^4+b+c+d + i^4 + g*n^3)^10 +
(a*h + e + f*i*j + g + h)^8 + (i + j + k + l + m + n)^12;
L H = G;
Format O2;
.sort
#optimize G
#write "Optimized with O2:"
#write "Optimized with Horner scheme: `optimscheme_'"
#write "Number of operations in output: `optimvalue_'"
#clearoptimize
.sort
Format O4,saIter=1000; * use 1000 iterations for optimization
#optimize H
#write "Optimized with O4:"
#write "Optimized with Horner scheme: `optimscheme_'"
#write "Number of operations in output: `optimvalue_'"
.end
\end{verbatim}
which gives the output:
\begin{verbatim}
Optimized with O2:
Optimized with Horner scheme: i,n,j,m,l,k,g,a,d,c,b,h,f,e
Number of operations in output: 2578
Optimized with O4:
Optimized with Horner scheme: m,h,k,a,l,e,n,g,j,c,f,b,i,d
Number of operations in output: 1937
\end{verbatim}
The preprocessor variable optimscheme\_ \index{optimscheme\_} gives the best Horner scheme that the
program found and the preprocessor optimvalue\_ \index{optimvalue\_} gives the number of
arithmetic operations in the resulting expression.
These levels of optimization refer to some default settings of all
controlling parameters. These default values are in
Tab.~\ref{tbl:defaults}. It is also possible to set each parameter
individually to fine-tune the optimization process. The parameters
that can be set are divided in several categories. First, it is
possible to set which Horner schemes\index{Horner scheme} are tried:
\begin{description}
\item[Horner=(Occurrence $|$ MCTS $|$ SA)] Determines whether an
occurrence order\index{occurrence order} Horner scheme is used, or
whether MCTS\index{MCTS}\index{Monte Carlo tree search}, or Stochastic Local Search is employed to
find Horner schemes.
\item[HornerDirection=(Forward $|$ Backward $|$ ForwardOrBackward $|$] \hfill
{\bf ForwardAndBackward)}
Forward makes that the MCTS search in the O3 option will
determine the outermost variables in the multivariate Horner scheme first
and then work its way inward.
In the case of backward, the tree search determines the innermost variable
first. In some cases this can give much better results when there are
many common subexpressions involving a limited number of variables.
ForwardOrBackward tries both of these
schemes. ForwardAndBackward fills the order from both sides
simultaneously, resulting in more options, but also a much larger
search tree. If there are many variables, it could make the search tree
too large to obtain good results. \hfill \\
When the option Horner=Occurrence is used the option backward will switch
to something called `anti-occurrence' which means that the most frequent
variable corresponds to the innermost brackets.
\end{description}
In the case of MCTS\index{MCTS}\index{Monte Carlo tree search} there are
various parameters that can control the search process:
\begin{description}
\item[MCTSConstant=$<$\emph{value}$>$]
This sets the constant $C_P$ in the UCT formula that governs
the Monte Carlo tree search. It is supposed to be given as a real number
with a decimal point (no floating point notation that includes powers).
\item[MCTSNumExpand=$<$\emph{value}$>$] The number of times the tree
is traversed and hence the number of times that a Horner scheme is
constructed.
\item[MCTSNumKeep=$<$\emph{value}$>$]
During the MCTS procedure \FORM\ only tries to construct
a proper ordering for the Horner scheme, followed by a common subexpression
elimination in the style of the O1 option. The best `value' schemes are
remembered and for those a common subexpression elimination in the style of
the O2 option is done afterward. This second style elimination is far more
costly. In nearly all cases the best O2-style scheme is in the very few top
O1-style schemes.
\item[MCTSNumRepeat=$<$\emph{value}$>$]
Sometimes it is more advantageous to run
a new tree search several times, each with a smaller number of
expansions. This parameter tells how many times we will run with a
new tree. The total number of tree traversals is the product of
MCTSNumRepeat and MCTSNumExpand.
\item[MCTSNumExpand=$<$\emph{value1*value2}$>$]
Makes \FORM\ to run `value1' trees, each with `value2' Horner scheme
constructions. Hence this option is equivalent to the combination \hfill \\
MCTSNumRepeat=$<$\emph{value1}$>$, MCTSNumExpand=$<$\emph{value2}$>$.
\item[MCTSTimeLimit=$<$\emph{value}$>$] The maximum time in seconds
that is used when searching through the tree.
\item[MCTSDecayMode=$<$\emph{value}$>$] Determines how the $C_P$ parameter
in the UCT formula decreases:
\begin{center}
\begin{tabular}{|c|l|}
\hline
value & effect\\
\hline
0 & no decay\\
1 & linear decay with iteration number\\
2 & faster decay for the final iterations\\
3 & decrease with iteration number and with node depth\\
\hline
\end{tabular}
\end{center}
% 0 means there
%is no decay, 1 means a linear decay with iteration number, 2 a more
%agressive decay for the final iterations, and MCTSDecayMode=3 .
%The default value is 1.
\end{description}
For Stochastic Local Search the following parameters can be set:
\begin{description}
\item[saIter=$<$\emph{value}$>$] Number of optimization steps that will be performed. This has the most influence on the quality of the simplification. The default value is 1000.
\item[saMaxT=$<$\emph{value}$>$] Maximum temperature used in Simulated Annealing. The higher the temperature,
the more exploration occurs. The default value is 2000.
\item[saMinT=$<$\emph{value}$>$] Minimum temperature used in Simulated Annealing. The lower the temperature,
the more exploitation occurs. The default value is 1.
\end{description}
The cooling rate from saMaxT to saMinT is exponential in saIter. More information can be
found in the research papers.
The Horner methods generate a number of Horner schemes: one or two in
the case of occurrence order schemes, depending of the direction
parameter, and a number equal to MCTSNumKeep in the case of
MCTS. Next, for each stored Horner scheme other optimizations are
performed as determined by the following parameter:
\begin{description}
\item[Method=(None $|$ CSE $|$ Greedy $|$ CSEGreedy)] Determines what
method is used for optimizing the generated Horner schemes.
CSE\index{CSE}\index{Common subexpression elimination} performs a simple
common subexpression elimination and Greedy performs greedy
optimizations\index{greedy optimizations} (see the paper for more
explanations) which are more sophisticated versions of CSE's. CSEGreedy
performs CSE followed by greedy optimizations; usually this is somewhat
faster than just greedy optimizations, but it gives slightly worse results.
The option None does nothing after applying the Horner scheme and is only
useful for debugging purposes.
\end{description}
When the method of greedy optimizations is used, repeatedly all
potential optimizations are determined and a few of them are performed. The
following parameters are used to tune the greedy method:
\begin{description}
\item[GreedyMaxPerc] The percentage of the possible optimizations that is
performed.
\item[GreedyMinNum] The minimum number of possible optimizations that
is performed.
\item[GreedyTimeLimit] The maximum time in seconds that is spent in
the process of greedy optimization.
\end{description}
There are also two more general settings:
\begin{description}
\item[Stats=(On $|$ Off)] This parameter determines whether statistics
of the optimization are shown. Statistics are printed in the format
{\tt *** STATS: original 1P 16M 5A : 23}
{\tt *** STATS: optimized 0P 10M 5A : 15}
in which P indicates power operations (at least a third power), M the
number of multiplications and A the number of additions/subtractions. The
last number is the total number of operations in which an $n$-th power counts
as $n-1$ operations.
\item[TimeLimit=$<$\emph{value}$>$] This set both the MCTSTimeLimit
and the GreedyTimeLimit to half of the given value.
\end{description}
Finally there are some parameters that are of a rather specialized nature.
They can be used for debugging\index{debugging} purposes or in the case
that one knows already what is the best Horner scheme. Their default values
are Off.
\begin{description}
\item[DebugFlag=(On $|$ Off)] \label{optimdebugflag}
In the case that the value is On, the list of temporary variables is
printed in reverse order with the string "id " in front. This makes
them into a set of \FORM\ substitutions that undo the optimizations. One
can use this for instance to make sure that the optimized code is identical
to the original.
\item[PrintScheme=(On $|$ Off)]
This option (when On) will print the Horner scheme. That is the order in
which the variables were taken outside parentheses.
\item[Scheme=(list of symbols)] The list should be enclosed by parentheses
and the symbols should be separated by either blanks or comma's. This
option will fix the Horner scheme\index{Horner scheme} to be used. One
could for instance use the output of the PrintScheme option for this to
avoid a lengthy search when a good order of the variables is already known.
Things become a bit tricky when extra symbols are involved. One should make
sure that their labelling is identical to when the scheme was created! When
extra symbols are used in their array/vector notation, one needs to
separate them by comma's, because blank spaces next to parentheses are
eliminated by the preprocessor. If one specifies the wrong number of
variables, the results can be quite unpredictable. At the moment of
compilation \FORM\ does not know the variables that are actually used. The
safe thing is to verify the actual variables with a testrun using the
PrintScheme option in the O1 mode.
\end{description}
{ \small
\begin{table}[!ht]
\centering
\begin{tabular}{|l|c|c|c|c|}
\hline
& O1 & O2 & O3 (default) & O4 (default) \\
\hline
Horner & occurrence & occurrence & MCTS & SA \\
HornerDirection & OR & OR & OR & OR \\
MCTSConstant & --- & --- & 1.0 & --- \\
MCTSNumExpand & --- & --- & 1000 & --- \\
MCTSNumKeep & --- & --- & 10 & --- \\
MCTSNumRepeat & --- & --- & 1 & --- \\
MCTSTimeLimit & --- & --- & 0 & --- \\
MCTSDecayMode & --- & --- & 1 & --- \\
saIter & --- & --- & --- & 1000 \\
saMinT & --- & --- & --- & 1 \\
saMaxT & --- & --- & --- & 2000 \\
Method & cse & greedy & greedy & greedy \\
GreedyMinNum & --- & 10 & 10 & 10 \\
GreedyMaxPerc & --- & 5 & 5 & 5 \\
GreedyTimeLimit & --- & 0 & 0 & 0 \\
Stats & off & off & off & off \\
TimeLimit & 0 & 0 & 0 & 0 \\
\hline
\end{tabular}
\caption{Values for the various parameters in the predefined
optimization levels. OR stands for ForwardOrBackward.}
\label{tbl:defaults}
\end{table}
}
All options should be specified in a single format statement and be
separated either by commas or blank spaces. When
\verb|Format Optimize| is used, first the default settings are taken
and then the options that are specified overwrite them. It is allowed
to have the O1, O2, O3, O4 optimization specifications followed by
options. In that case the program first sets the values of those
specifications and then modifies according to what it encounters in
the rest of the statement.
form-master/doc/manual/parallel.tex 0000664 0000000 0000000 00000052142 13565763364 0017700 0 ustar 00root root 0000000 0000000
\chapter{The parallel version}
\label{parallel}
%--#[ Introduction :
\FORM\ has two versions that can make use of several processors
simultaneously. Which version can be used profitably depends very much on
the architecture of the computer one is using. Each version has its own
control commands which are ignored by the other version and the sequential
version of \FORM. The parallel versions are:
\begin{itemize}
\item \ParFORM\index{ParFORM}: This version runs on processors that have
their own memory and preferably their own disk. Each processor gets a copy
of the complete program and MPI\index{MPI} is used for the
communication\index{communication}. When the network connections are very
fast one can also use \ParFORM\ on computer clusters. \ParFORM\ was
developed at the university of Karlsruhe\index{Karlsruhe}.
\item \TFORM\index{TFORM}: This version uses POSIX threads and runs on computers
which have several processors with a shared memory. Data is kept as common
data as much as possible and only when a worker thread gets a task a
minimal amount of data is copied to its private buffers. Currently it seems
to perform best on computers with two or four processors.
\end{itemize}
Both \ParFORM\ and \TFORM\ suffer from the same bottlenecks\index{bottleneck}.
At the beginning of a module there is a single expression, managed by a
master process which then has to distribute the terms over the workers. At
the end of the module the sorted results of the workers have to be gathered
in by the master\index{master} and merged into a single expression again.
Efficiency depends critically on how fast the terms can be given to the
workers\index{workers}, how well the load for the workers is balanced and
how much time the master has to spend in the final stages of the sorting.
Another factor is the complexity of the operations inside the module. If
the module has very few and simple statements, the gain in performance will
be much less than when the module has much work to do for each term.
The \ParFORM\ and \TFORM\ specific code is internally completely separated.
This offers the possibility that sooner or later the two can be combined to
allow efficient running on clusters of dual or quad processor machines.
Whether this would give significant extra benefits needs to be
investigated. When this project will be undertaken depends very much on the
availability of such computers.
Because \ParFORM{} uses MPI\index{MPI} and because different MPI
environments are normally not binary compatible, the port to a new machine
requires a recompilation of the source code and a relinking to the MPI
library. Hence we do not have executables in the distribution site.
One needs to build \ParFORM{} on one's computer.
For \TFORM\ the situation is much more favorable. Its treatment of the
parallelization follows the standard for POSIX\index{POSIX} threads (or
PThreads) for which the libraries are implemented on almost any
UNIX\index{UNIX} system and many other systems.
The ideal of a parallel version of \FORM\ is that it should execute nearly
any regular \FORM\ program, whether it was written for parallelization or
not. And it should execute much faster on several processors than the
sequential version on a single processor. The performance is given by the
improvement factor which is the execution time of the sequential version
divided by the execution time of the parallel version as measured in real
time (not CPU time) on a computer that has no other major tasks. The ideal
would of course be that a computer with N processors would give an
improvement factor of N. It should be easy to see that this ideal cannot be
reached, due to the bottlenecks described above. Also the compilation takes
place on a single processor and the instructions of the preprocessor are
typically also tasks for a single thread/processor. Yet for small numbers
of processors one can do rather well. Many old calculations, when repeated
with \TFORM\ would give improvement\index{improvement factor} factors above
1.7 on a dual pentium\index{pentium} machine and around 3 or a bit higher
on a quad opteron\index{opteron} machine. This was without modifying even a
single statement in the programs. Of course these numbers depend very much
on the type of the problem and the programming style used. As of yet there
is very little experience with parallel versions of \FORM. Hence people will
have to discover what are good ways of getting the most out of their
computer. It is expected that there will be much progress in the coming
years.
First we will now discuss the running of the two versions. After that we
will describe some common syntactic problems.
%--#] Introduction :
%--#[ TFORM :
\section{TFORM}
\label{tform}
Let us assume that the executable of \TFORM\index{TFORM} is called tform. It
is used exactly the same way as the sequential version of \FORM\ (named form)
is used with the exception of the possibility to specify the number of
worker\index{worker} threads with the -w option. The command
\begin{verbatim}
tform -w4 calcdia
\end{verbatim}
would execute the program in the file calcdia.frm, using 4 worker threads,
in addition to the one master thread. When the -w option is not given or
when only one worker thread is asked for, tform will run the whole program
inside the master\index{master} thread. Because tform always has some
overhead this is usually a little bit slower than using form. Strange
enough there are exceptions although this may have to do with the fact that
measuring the time of a program doesn't always give the same numbers.
It is also possible to specify the number of worker threads in the setup
file, using the line
\begin{verbatim}
Threads 4
\end{verbatim}
for 4 threads. And as with all setup parameters one can pass this
information also via the environment variable FORM\_threads or with the line
\begin{verbatim}
#: Threads 4
\end{verbatim}
at the beginning of the program file.
When the master passes terms to the workers, it has to signal\index{signal}
the workers that there is some data. In their turn, each worker has to send
the master a signal when it has completed its task and it is ready for
more. Such signals cost time. Hence it is usually best to send terms in
groups, called buckets\index{bucket}. The optimal number of terms in a
bucket depends very much on the problem and the size of the expression.
Bigger buckets mean less overhead in signals. If the buckets are too big
the workers may have to wait too much. Values between 100 and 1000 are
usually rather good. There is a default bucket size which is typically
around 500. The user can change this value in two ways: The first is with
the ThreadBucketSize\index{threadbucketsize} setup parameter in the
form.set file (or at the startup of the program file, or with the
FORM\_threadbucketsize environment variable) and the second is with the
ThreadBucketSize statement (see \ref{substathreadbucketsize}) which is a
declaration like Symbol or Dimension. The first terms in an expression will
be sent in smaller buckets to get the workers something to do as soon as
possible.
Usually the bigger buckets give a better performance, but they suffer from
a nasty side-effect. Complicated terms that need much execution time have a
tendency to stick together. Hence there can be one bucket with most of the
difficult terms and at the end of the module all workers and the master
have to wait for one worker to finish. This can be improved with a
load\index{load balancing} balancing mechanism. The current version will
take terms from the buckets of workers that take more time than the others.
By default this mechanism is on, but it can be switched on or off with the
`on ThreadLoadBalancing\index{threadloadbalancing};' and `off
ThreadLoadBalancing;' statements. It can also be set as one of the setup
parameters in the form.set file with
\begin{verbatim}
ThreadLoadBalancing OFF
\end{verbatim}
or
\begin{verbatim}
ThreadLoadBalancing ON
\end{verbatim}
or at the start of the program or in the environment.
The LINUX\index{LINUX} operating system tries to cache\index{cache} files
that are to be written to disk. Somehow, when several big files have to be
written it gets all confused (it is not known in what way). This means that
if tform produces 4 large sort files\index{file!sort} eventually the system
becomes intolerably slow. At one time a test program was 4.5 times slower
with 4 worker processors than with just the master running, even though the
master had a single even bigger sort file. This has been improved by having
the file-to-file sort of the threads changed into a
file-to-masterbuffers-to-combined-output. Yet the writing and subsequent
merging of the 4 files at the same time can be disastrous. Work is done to
improve this, but it may not be easy to circumvent facilities of the
operating system. Apparently the quality of the drivers is crucial here.
One can switch the parallel processing on or off (for the complete module)
at any moment in the program with the
statements\index{on!threads}\index{off!threads}
\begin{verbatim}
On Threads;
Off Threads;
\end{verbatim}
or using the moduleoption statement (\ref{substamoduleoption}) that
affects \TFORM{}'s behaviour for just the current module:
\begin{verbatim}
ModuleOption Parallel;
ModuleOption NoParallel;
\end{verbatim}
Additionally one can switch the statistics per thread on or off with
\begin{verbatim}
On ThreadStats;
Off ThreadStats;
\end{verbatim}
When the thread\index{on!threadstats}\index{on!threadstats} statistics are
switched off only the statistics of the master thread are printed which is
usually only the final statistics for each of the expressions.
The timing information in the statistics is the CPU\index{CPU time} time
spent by the thread that prints the statistics. Hence the total CPU time
spent is the sum of the time of all workers and the time of the master. In
good running the time of the master should be the smallest number. When the
statistics per thread are switched off, only the statistics of the master
process will be printed with this `small' number. Hence it may look like
the program isn't progressing very much.
For debugging purposes the term by term print\index{print} statement (see
\ref{substaprint}) is equipped with the \verb:%W: and \verb:%w: format
strings. The first will cause the printing of the number of the current
thread and the CPU-time used thus far in that thread. The second will only
print the number of the current thread. The thread with the number zero is
the master thread. Putting a statement like
\begin{verbatim}
Print +f "<%W> %t";
\end{verbatim}
would show which thread is processing which term and when.
These are all the commands that specifically concern \TFORM. When more
experience is gained using \TFORM, more parameters and commands may become
available.
The fact that the threads need private\index{private} data makes that \TFORM\
will use more memory than \FORM. Most of the buffers are not very large, but
of course there are some buffers which need to be large, like the sort
buffers and the scratch input\index{input}/hide\index{hide} buffers. The
sizes that the user specifies for these buffers are for the corresponding
buffers of the master. The workers get each 1/N times the size for these
buffers, when there are N workers. In the case that makes these buffers too
small because of for instance MaxTermSize, the buffers may become larger.
%--#] TFORM :
%--#[ ParFORM :
\section{ParFORM}
\label{parform}
Let us call the executable of \ParFORM\index{ParFORM} parform.
The user must execute parform as an MPI\index{MPI} application.
In many MPI implementations, this is done by using the mpirun\index{mpirun}
command:
\begin{verbatim}
mpirun -np 4 parform calcdia
\end{verbatim}
This example executes the program in the file calcdia.frm, using 4
processes,in which one process is the master process and the other 3
processes are the worker processes.
One has to keep in mind that in some MPI implementations environment
variables will not be passed to an MPI application. Alternatively extra
options are needed for passing them.
If one wants to run \ParFORM{} under a job scheduler on a computer cluster
environment, one may need to write a job script, which depends to a great
extent on the environment.
\ParFORM{} uses MPI for communications between the master and workers.
Actually terms are distributed by using point-to-point send/receive
operations of MPI. Since there is some latency for establishing a
connection between processes, especially between those running on different
computers, it is best to send terms in groups, like buckets in \TFORM{}.
The default number of terms in a bucket is currently 1000 in \ParFORM{}. It
can be changed with the ProcessBucketSize statement
(\ref{substaprocessbucketsize}\index{processbucketsize}) if this is deemed
necessary. It can also be changed for the current module with the statement
(\ref{substamoduleoption}\index{moduleoption!processbucketsize}).
\begin{verbatim}
ModuleOption ProcessBucketSize number;
\end{verbatim}
And finally it can also be changed in the setup, using the
ProcessBucketSize (\ref{setupprocessbucketsize}) setup parameter.
The first terms in an expression will be sent in smaller buckets to get the
workers something to do as soon as possible.
One can switch the parallel processing on or off (for the complete module)
at any moment in the program with the statements\index{on!parallel}%
\index{off!parallel}
\begin{verbatim}
On Parallel;
Off Parallel;
\end{verbatim}
or using the moduleoption statement (\ref{substamoduleoption}) that
affects \ParFORM{}'s behaviour for just the current module:
\begin{verbatim}
ModuleOption Parallel;
ModuleOption NoParallel;
\end{verbatim}
Additionally one can switch the statistics per process on or off with
\begin{verbatim}
On ProcessStats;
Off ProcessStats;
\end{verbatim}
When the process\index{on!processstats}\index{on!processstats} statistics
are switched off only the statistics of the master process are printed
which are usually only the final statistics for each of the expressions.
As in \TFORM{}, \verb:%W: and \verb:%w: in the term by term
print\index{print} statement (see \ref{substaprint}) are available in
\ParFORM{}. They print the number of the current process and the
CPU-time used thus far in that process.
In principle one can run all \FORM{} or \TFORM{} programs with \ParFORM{}.
In practice \ParFORM{} is not so efficient for some problems, in which
more data have to be synchronized between the master and the workers.
The cases for which \ParFORM{} needs to send data via MPI include:
\begin{itemize}
\item The redefine statements, which modify preprocessor variables
on the workers.
\item Modifying \$-variables in regular statements with a moduleoption
statement (see \ref{pardollars}, \ref{substamoduleoption}
and~\ref{dollars-in-parallel}).
\item Expression names appearing in right hand sides of definition or
substitution statements.
\end{itemize}
The last case may need more explanation.
Consider the following code:
\begin{verbatim}
Local G = F;
id a = F;
\end{verbatim}
where the expression F is supposed to be already defined. The point is that
these substitutions of the expression F are performed on the workers. The
workers, however, do not know the contents of the expression F because it
is stored on the master. Therefore, before executing this module \ParFORM{}
needs to make the master broadcast the expression F to the workers. This
may be quite time-consuming because the expression could be very large.
%--#] ParFORM :
%--#[ Some problems :
\section{Some problems}
\label{dollars-in-parallel}
Both parallel versions share a number of problems which are inherent to
running in an environment in which the order\index{order of terms} in which
terms are processed isn't deterministic\index{deterministic}. Most of these
problems concern \verb:$:-variables. They present a mix between private and
common information. Consider the code
\begin{verbatim}
id f(x?$xvar) = g(x);
id ......
id a^n? = b^n*h($var);
\end{verbatim}
Of course one could do this simple example differently, but we are
discussing the principle. What we have here is that each term that passes
the first statement will acquire its own value of \verb:$var:, to be used a
bit later. It is clear that if we have a common administration of
\verb:$:-variables we would have to `lock'\index{lock} the value for a
considerable amount of time, thereby spoiling much of the gains of parallel
processing. Hence in this case it would be best that each worker maintains
its own local value of \verb:$var:. But in the following example we have
the opposite:
\begin{verbatim}
#$xmax = -1;
if ( count(x,1) > $xmax ) $xmax = count_(x,1);
\end{verbatim}
Here we collect a maximum power in the variable \verb:$xmax:. If each
worker would have a local value of \verb:$xmax:, the question is what to do
with all these local values at the end of the module. A human will see that
here we are collecting a maximum, but the computer cannot and should not
see this. Hence the general rule in parallel processing is that when there
are \verb:$:-variables\index{\$-variable} obtaining a value during the
algebraic phase of a module the entire module is run sequentially, unless
\FORM\ has been helped with a moduleoption statement for each of the
variables involved. Hence in the last example
\begin{verbatim}
ModuleOption Maximum $xmax;
\end{verbatim}
would tell \FORM\ how to combine the local values in \ParFORM\ (\ParFORM\
maintains local values of all \verb:$:-variables). In \TFORM\ it
would put the value directly into the central administration, provided it
is bigger than the previous value. Only during the update the variable
would have to be locked.
There are several options in the moduleoption statement:
\begin{itemize}
\item Maximum\index{moduleoption!maximum}: The variable must have a
numerical value and the maximum is collected.
\item Minimum\index{moduleoption!minimum}: The variable must have a
numerical value and the minimum is collected.
\item Sum\index{moduleoption!sum}: The variable must have a numerical value
and the sum is collected.
\item Local\index{moduleoption!local}: The value will be kept privately and
no attempt is made to put it in the central administration, neither during
the execution of the module, nor at the end. If there was already a
variable by this name in the central administration it will keep the value
it had before the module started execution. At the end of the module, all
private values will be forgotten.
\end{itemize}
The redefine statement is a major inefficiency in a parallel environment.
It redefines a preprocessor variable and there is only a single bookkeeping
for such variables. This means that the variable has to be sent to the
master process (\ParFORM) or that a lock has to be placed to prevent other
workers to write to the same storage simultaneously (\TFORM). In addition
the final value in the preprocessor variable will be determined by the last
term processed in any of the workers. This may not be the same term in
different runs. It is up to the user to write programs that still give
correct results under such conditions. The best way around the inefficiency
is using \verb:$:-variables and preprocessor instructions. We show this in
an example in which we construct the equivalent of a conditional repeat
that includes a .sort instruction.
\begin{verbatim}
#do i = 1,1
statements
if ( count(x,1) > 0 ) redefine i "0";
.sort
#enddo
\end{verbatim}
To run this in parallel, it is better to use the following code.
\begin{verbatim}
#do i = 1,1
#$i = 1;
statements
if ( count(x,1) > 0 ) $i = 0;
ModuleOption minimum $i;
.sort
#redefine i "`$i'"
#enddo
\end{verbatim}
In this program the centrally stored value of \verb:$i: is updated at most
once. Admitedly it isn't as simple as the redefine statement, but it
works in all versions of \FORM\ starting with version 3.0.
It should be noted that when a new expression is defined in its defining
module it starts out as a single term. Hence it cannot benefit from
parallelization in that module. Therefore the code
\begin{verbatim}
#define MAX "200"
Symbols x0,...,x10;
Local F = (x0+...+x`MAX')^3;
id x1 = -x2-...-x`MAX';
.end
\end{verbatim}
will execute inside a single worker while
\begin{verbatim}
#define MAX "200"
Symbols x0,...,x10;
Local F = (x0+...+x`MAX')^3;
.sort
id x1 = -x2-...-x`MAX';
.end
\end{verbatim}
will make the first expansion inside a single worker and the more costly
substitution can be made in parallel. A better load\index{load balancing}
balancing algorithm in which at any node in the expansion tree tasks can be
given to idle workers would solve this problem, but due to some
complications this has not yet been implemented. The structure of \FORM\ will
however allow such an implementation.
%\footnote{In the year 1991 version 1 of FORM was parallelized on a
%computer at FNAL along these lines. It was however rather primitive and
%lack of access to suitable computers stopped further development at that
%moment.}
%--#] Some problems :
form-master/doc/manual/pattern.tex 0000664 0000000 0000000 00000026626 13565763364 0017571 0 ustar 00root root 0000000 0000000 \chapter{Pattern matching}
\label{pattern}
Substitutions\index{substitutions}\index{pattern matching} are made in \FORM\
by specifying a generic object that should be replaced by an expression.
This generic object is called a pattern\index{pattern}. Patterns that the
user may already be familiar with are the regular expressions in many
UNIX\index{UNIX} based systems or just a statement like \verb:ls *.frm: to
list only files of which the name ends in \verb:.frm:. In this case the
\verb:*: is called a wildcard\index{wildcard} that can take any string
value. In symbolic manipulation there will be wildcards also, but their
nature will be different. They are also indicated in a different way.
In \FORM\ wildcard variables are indicated by attaching a
question\index{question mark} mark (?) to the name of a variable. The type
of the variable indicates what type of object we are looking for. Assume
the following id\index{id} statements:
\begin{verbatim}
Functions f,g;
Symbol x;
id f(g?,x) = g(x,x);
\end{verbatim}
In this statement g will match any function and hence all occurrences
of f, in which the first argument is a function and the second argument is
the symbol x, will match. In the right hand side the function g will be
substituted by whatever identity g had to assume in the left hand side to
make the match. Hence \verb:f(f,x): will be replaced by \verb:f(x,x):.
In general function wildcards\index{wildcard!function} can only match
functions. Even though tensors are special functions, regular function
wildcards cannot match tensors, and tensor wildcards cannot match
functions. However commuting\index{commuting} function wildcards can match
noncommuting\index{noncommuting} functions {\sl et vice versa}.
Index\index{wildcard!index} wildcards can only match indices. The
dimension of the indices is not relevant. Hence:
\begin{verbatim}
id f(mu?,mu?) = 4;
\end{verbatim}
would match both \verb:f(ka,ka): and \verb:f(2,2):. We will see later
how to be more selective about such matches.
When the same wildcard occurs more than once in a pattern, it should be
matched by the same object in all its occurrences. Hence the above pattern
would not match \verb:f(mu,nu):.
There is one complication concerning the above rule of index wildcards only
matching indices. \FORM\ writes contractions with vectors in a special
shorthand notation called Schoonschip\index{Schoonschip} notation. Hence
\verb:f(mu)*p(mu): becomes \verb:f(p):. This means that the substitution
\begin{verbatim}
id f(mu?)*g(nu?) = fg(mu,nu);
\end{verbatim}
should also replace the term \verb:f(p)*g(q): by \verb:fg(p,q):. In this
case it looks like the wildcard indices matched the vectors. This is
however not the case, because if we take the previous pattern (with the
\verb:f(mu?,mu?):), it is not going to match the term \verb:f(p,p):,
because this term should be read as something of the type
\verb:f(mu,nu)*p(mu)*p(nu):
and that term does not fit the pattern \verb:f(mu?,mu?):.
Vector\index{wildcard!vector} wildcards can match vectors, but they can
also match vector-like expressions in function arguments. A vector-like
expression is an expression in which all terms contain one single vector
without indices, possibly multiplied by other objects like coefficients,
functions or symbols. Hence
\begin{verbatim}
id f(p?) = p.p;
\end{verbatim}
would match \verb:f(q):, \verb:f(2*q-r): and \verb:f(a*q+f(x)*r):, if p, q
and r are vectors, and a and x are symbols, and f is a function. It would
not match \verb:f(x): and neither would it match \verb:f(q*r):, nor
\verb:f(a*q+x):.
Wildcard\index{wildcard!symbol} symbols are the most flexible objects. They
can match symbols, numbers and expressions that do not contain loose
indices or vectors without indices. These last objects are called
scalar\index{scalar objects} objects. Hence wildcard symbols can match all
scalar objects. In
\begin{verbatim}
id x^n? = x^(n+1)/(n+1);
\end{verbatim}
the wildcard symbol n would normally match a numerical integer power. In
\begin{verbatim}
id f(x?) = x^2;
\end{verbatim}
there would be a match with \verb:f(y):, with \verb:f(1+2*y): and with
\verb:f(p.p):, but there would not be a match with \verb:f(p): if p is a
vector.
There is one extra type of wildcards. This type is rather special. It
refers to groups of function
arguments\index{wildcard!argument field}\index{argument field wildcard}.
The number of arguments is not specified. These variables are indicated by
a question mark followed by a name (just the opposite of the other wildcard
variables), and in the right hand side they are also written with the
leading question mark:
\begin{verbatim}
id f(?name) = g(1,?name);
\end{verbatim}
In this statement\index{?name} all occurrences of f with any number of
arguments (including no arguments) will match. Hence \verb:f(mu,nu): will
be replaced by \verb:g(1,mu,nu):. In the case that f is a regular function
and g is a tensor, it is conceivable that the arguments in \verb:?name:
will not fit inside a tensor. For instance \verb:f(x):, with x a symbol,
would match and \FORM\ would try to put the symbol inside the tensor g. This
would result in a runtime error. In general \FORM\ will only accept arguments
that are indices or single vectors for a substitution into a tensor. The
object \verb:?name: is called an {\bf argument field wildcard}.
One should realize that the use of multiple argument field wildcards can
make the pattern matching slow.
\begin{verbatim}
id f(?a,p1?,?b,p2?,?c,p3?,?d)*g(?e,p3?,?f,p1?,?g,p2?,?h) = ....
\end{verbatim}
may involve considerable numbers of tries, especially when there are many
occurrences of f and g in a term. One should be very careful with this.
A complication is the pattern matching in functions with symmetry
properties. In principle \FORM\ has to try all possible permutations before
it can conclude that a match does not occur. This can become rather time
consuming when many wildcards are involved. \FORM\ has a number of tricks
built in, in an attempt to speed this up, but it is clear that for many
cases these tricks are not enough. This type of pattern matching is one of
the weakest aspects of `artificial intelligence' in general. It is hoped
that in future versions it can be improved. For the moment the practical
consequence is that argument field wildcards cannot be used in symmetric
and antisymmetric functions. If one needs to make a generic replacement in
a symmetric function one cannot use
\begin{verbatim}
CFunction f(symmetric),g(symmetric);
id f(?a) = ....;
\end{verbatim}
but one could try something like
\begin{verbatim}
CFunction f(symmetric),ff,g(symmetric);
id f(x1?,...,x5?) = ff(x1,...,x5);
id ff(?a) = ...;
id ff(?a) = f(?a);
\end{verbatim}
if f has for instance 5 arguments. If different numbers of arguments are
involved, one may need more than one statement here or a statement with the
replace\_\index{replace\_} function:
\begin{verbatim}
Multiply replace_(f,ff);
\end{verbatim}
It just shows that one should at times be a bit careful with overuse of
(anti)symmetric functions. Cyclic functions do not have this restriction.
When there are various possibilities for a match, \FORM\ will just take the
first one it encounters. Because it is not fixed how \FORM\ searches for
matches (in future versions the order of trying may be changed without
notice) one should try to avoid ambiguities\index{ambiguity} as in
\begin{verbatim}
id f(?a,?b) = g(?a)*h(?b);
\end{verbatim}
Of course the current search method is fully consistent (and starts with
all arguments in \verb:?a: and none in \verb:?b: etc, but a future pattern
matcher may do it in a different order.
When two argument field wildcards in the left hand side have the same name,
a match will only occur, when they match the same objects. Hence
\begin{verbatim}
id f(?a,?a) = g(?a);
\end{verbatim}
will match \verb:f(a,b,a,b): or just \verb:f: (in which case \verb:?a: will
have zero arguments), but it will not match \verb:f(b,b,b):.
Sometimes it is useful when a search can be restricted to a limited set of
objects. For this \FORM\ knows the concept of sets\index{set}. If the name of
a set is attached after the question mark, this is an indication for \FORM\
to look only for matches in which the wildcard becomes one of the members
of the set:
\begin{verbatim}
Symbols a,a1,a2,a3,b,c;
Set aa:a1,a2,a3;
id f(a?aa) = ...
\end{verbatim}
would match \verb:f(a1): but not \verb:f(b):. Sets can also be defined
dynamically\index{set!dynamical} by enclosing the elements between curly
brackets\index{bracket!curly} as in:
\begin{verbatim}
Symbols a,a1,a2,a3,b,c;
id f(a?{a1,a2,a3}) = ...
\end{verbatim}
Sets\index{Set of symbols} of symbols can contain (small integer) numbers
as well. Similarly sets\index{set of indices} of indices can contain fixed
indices (positive numbers less than the value of fixindex\index{fixindex}
(see the chapter on the setup \ref{setup}). This means that some sets can
be ambiguous\index{set!ambiguous} in their nature.
Sometimes sets\index{sets!array} can be used as some type of
array\index{array}. In the case of
\begin{verbatim}
Symbols a,a1,a2,a3,b,c,n;
Set aa:a1,a2,a3;
id f(a?aa[n]) = ...
\end{verbatim}
not only does `a' have to be an element of the set aa, but if it is an
element of that set, n will become the number of the element that has been
matched. Hence for \verb:f(a2): the wildcard a would become \verb:a2: and
the wildcard n would become 2. These objects can be used in the
right-hand side. One can also use sets in the right-hand side with an index
like the n of the previous example:
\begin{verbatim}
Symbols a,a1,a2,a3,b1,b2,b3,c,n;
Functions f,g1,g2,g3;
Set aa:a1,a2,a3;
Set bb:b1,b2,b3;
Set gg:g1,g2,g3;
id f(a?aa[n]) = gg[n](bb[n]);
\end{verbatim}
which would replace \verb:f(a2): by \verb:g2(b2):. One cannot do
arithmetic\index{arithmetic} with the number of the array element.
Constructions like \verb:bb[n+1]: are not allowed.
There is one more mechanism by which the array nature of sets can be used.
In the statement (declarations as before)
\begin{verbatim}
id f(a?aa?bb) = a*f(a);
\end{verbatim}
a will have to be an element of the set aa, but after the matching it takes
the identity of the corresponding\index{set!corresponding element} element of
the set bb. Hence \verb:f(a2): becomes after this statement
\verb:b2*f(b2):.
Wildcards can also give their value directly to
\$-variables\index{wildcard!\$-variable}\index{\$-variable} (see chapter
\ref{dollars} about the \$-variables). If a \$-variable is attached to a
wildcard (if there is a set restriction, it should be after the set) the
\$-variable will obtain the same contents as the wildcard, provided a match
occurs. If there is more than one match, the last match will be in the
\$-variable.
\begin{verbatim}
id f(a?$w) = f(a);
\end{verbatim}
will put the match of a in \verb:$w:. Hence in the case of \verb:f(a2): the
\$-variable will have the value \verb:a2:. In the case of
\verb:f(a2)*f(a3): the eventual value of \verb:$w: depends on the order in
which \FORM\ does the matching. This is not specified and it would not be
a good strategy to make programs that will depend on it. A future pattern
matcher might do it differently! But one could do things like
\begin{verbatim}
while ( match(f(a?$w)) );
id f($w) = ....
id g($w) = ....
endwhile;
\end{verbatim}
just to make sure with which match one is working.
form-master/doc/manual/polynomials.tex 0000664 0000000 0000000 00000044517 13565763364 0020461 0 ustar 00root root 0000000 0000000
\chapter{Polynomials and Factorization}
\label{polynomials}
\noindent Starting with version 4, FORM is equipped with powerful handling
of rational polynomials and with factorization capabilities. Because this
creates many new possibilities, it brings a whole new category of commands
with it. We will list most of these here.
\noindent First there are the rational polynomials. These work a bit like
the PolyFun~\ref{substapolyfun}, but now with two arguments: a numerator and
a denominator. Instead of PolyFun the function is designated as
PolyRatFun~\ref{substapolyratfun} as in the example below:
\begin{verbatim}
Symbol x,y;
CFunction rat;
PolyRatFun rat;
L F = rat(x+y,x-y)+rat(x-y,x+y);
Print;
.end
F =
rat(2*x^2 + 2*y^2,x^2 - y^2);
\end{verbatim}
Dealing with a PolyRatFun can be very handy, but one should realize that
there is a limit to the size of the arguments, because the PolyRatFun with
its arguments is part of a term and hence is limited by the maximum size of
a term~\ref{setupmaxtermsize}. One should also take into account that the
manipulation of multivariate polynomials, and in particular the GCD
operation, can be rather time consuming.
\noindent The PolyRatFun has one limitation as compared to the regular
PolyFun: in its arguments one may use only symbols. Of course FORM is
equipped with a mechanism to replace other objects by extra internally
generated symbols~\ref{substaextrasymbols}. One could imagine FORM to
automatically convert these objects to symbols, do the polynomial
arithmetic and then convert back. This is done with factorization and the
gcd\_~\ref{fungcd}\index{gcd\_}\index{function!gcd\_},
div\_~\ref{fundiv}\index{div\_}\index{function!div\_}
and rem\_~\ref{funrem}\index{rem\_}\index{function!rem\_} functions. But
because the addition of PolyRatFun's is such a frequent event, this would
be very costly in time. Hence it is better that the user does this once
in a controlled way.
\noindent The PolyFun and PolyRatFun declarations are mutually exclusive.
The PolyRatFun is considered a special type of PolyFun and there can be
only one PolyFun at any moment. If one wants to switch back to a mode in
which there is neither a PolyFun nor a PolyRatFun one can use
\begin{verbatim}
PolyRatFun;
\end{verbatim}
to indicate that after this there is no function with that status.
\noindent When a PolyRatFun has only a single argument, this argument is
interpreted as the numerator of a fraction. FORM will add automatically a
second argument which has the value 1.
\noindent The second important polynomial facility is factorization. This
is not necessarily something trivial. First of all, with very lengthy
multivariate input, this can be unpractically slow. Second of all, there
are various types of objects that we may factorize and each has its special
needs. One of those needs is access to the factors, which is different for
the factors of function arguments, of \$-expressions or even complete
expressions. In addition \$-expressions should be factorizable either from
the preprocessor or on a term by term basis. Let us start with function
arguments.
\noindent One can factorize function arguments with the FactArg
statement~\ref{substafactarg}. The factors are each represented by a
separate argument as in
\begin{verbatim}
Symbol x,y;
CFunction f1,f2;
Local F = f1(x^4-y^4)+f2(3*y^4-3*x^4);
FactArg,f1,f2;
Print;
.end
F=
f1(y-x,y+x,y^2+x^2,-1)+f2(y-x,y+x,y^2+x^2,3);
\end{verbatim}
Overall constants and overall signs are taken separately as one can see. If
one wants the factors in separate functions one can use the
ChainOut~\ref{substachainout} command as in
\begin{verbatim}
Symbol x,y;
CFunction f1,f2;
Local F = f2(3*y^4-3*x^4);
FactArg,f2;
Print;
.sort
F=
f2(y-x,y+x,y^2+x^2,3);
ChainOut,f2;
id f2(x?number_) = x;
Print;
.end
F=
3*f2(y-x)*f2(y+x)*f2(y^2+x^2);
\end{verbatim}
\noindent Factorization of expressions is a bit more complicated. Clearly
this cannot be a command at the term level. Hence we had two options on how
to implement this. One would have been as a preprocessor instruction, which
we did not select, and the other is as some type of format statement, which
is what we did opt for. In the case we factorize an expression, the
original unfactorized expression is replaced by the factorized version.
After that we keep the factorized version only and that may bring some
restrictions with it. Of course, in the same way one can factorize an
expression, one can unfactorize it. The corresponding statements are
Factorize~\ref{substafactorize}, NFactorize~\ref{substanfactorize},
UnFactorize~\ref{substaunfactorize} and
NUnFactorize~\ref{substanunfactorize}. These statements are used at the end
of the module in the same place as one might use the bracket
statement~\ref{substabracket}. It should be noticed however that a
factorized expression will never apply the bracket mechanism. They are
mutually exclusive, because internally we use the bracket mechanism with a
built in symbol factor\_ to indicate the factors. Here is an example:
\begin{verbatim}
Symbol x,y;
Local F = x^4-y^4;
Print;
.sort
Time = 0.00 sec Generated terms = 2
F Terms in output = 2
Bytes used = 64
F=
-y^4+x^4;
Print;
Factorize F;
.end
Time = 0.00 sec Generated terms = 2
F Terms in output = 2
Bytes used = 64
Time = 0.00 sec Generated terms = 7
F Terms in output = 7
factorize Bytes used = 288
F=
(-1)
*(y-x)
*(y+x)
*(y^2+x^2);
\end{verbatim}
We have printed the statistics in this example to show that the
factorization prints its own statistics. This factorization is executed
after the expression has been completed and before manipulations on the
next expression start. This way it is possible to overwrite the first
output by the factorized output and we do not loose diskspace
unnecessarily.
\noindent The next question is of course how to find out how many factors
an expression has and how to access individual factors. There is a function
numfactors\_ which gives the number of factors in an expression:
\begin{verbatim}
Symbol x,y;
Local F1 = x^4-y^4;
Local F2 = 0;
Local F3 = 1;
Local F4 = x^4-y^4;
Print;
Factorize F1,F2,F3;
.sort
F1=
(-1)
*(y-x)
*(y+x)
*(y^2+x^2);
F2=0;
F3=
(1);
F4=
-y^4+x^4;
#do i = 1,4
#$n`i' = numfactors_(F`i');
#message expression F`i' has `$n`i'' factors
~~~expression F1 has 4 factors
#enddo
~~~expression F2 has 1 factors
~~~expression F3 has 1 factors
~~~expression F4 has 0 factors
.end
\end{verbatim}
As we see, an expression that is zero still gives one factor when it is
factorized. When the expression is not factorized it will return 0 in all
cases. The factors can be accessed easily once one knows that the factors
are stored by means of the bracket mechanism and the n-th factor is the
bracket with the n-th power of the symbol factor\_ outside the bracket:
\begin{verbatim}
Symbol x,y;
Local F = x^4-y^4;
Factorize F;
.sort
#$n = numfactors_(F);
#do i = 1,`$n'
Local F`i' = F[factor_^`i'];
#enddo
Print;
.end
F=
(-1)
*(y-x)
*(y+x)
*(y^2+x^2);
F1=
-1;
F2=
y-x;
F3=
y+x;
F4=
y^2+x^2;
\end{verbatim}
\noindent It is also possible to put an expression in the input in a
factorized format. For this we have the
LocalFactorized~\ref{substalfactorized} and
GlobalFactorized~\ref{substagfactorized} commands. These commands can
be abbreviated to LFactorized, GFactorized or even LF and GF. One should
notice that these commands do not execute a factorization. They accept the
factors as the user provides them:
\begin{verbatim}
Symbol x,y;
LocalFactorize E = -(x+1)*(x+2)*((x+3)*(x+4));
Print;
.end
E =
( - 1 )
* ( 1 + x )
* ( 2 + x )
* ( 12 + 7*x + x^2 );
\end{verbatim}
\noindent This can go to some extremes when we feed in expressions
containing powers and expressions that are potentially already factorized:
\begin{verbatim}
Symbol x,y;
LocalFactorize E = -(x+1)*(x+2)*((x+3)*(x+4));
Local F = -(x+1)*(x+2)*((x+3)*(x+4));
Print;
.sort
E=
(-1)
*(1+x)
*(2+x)
*(12+7*x+x^2);
F=
-24-50*x-35*x^2-10*x^3-x^4;
LF G = (x-1)*(x+2)^2*E^2*F^2;
Print G;
.end
G=
(-1+x)
*(2+x)
*(2+x)
*(-1)
*(1+x)
*(2+x)
*(12+7*x+x^2)
*(-1)
*(1+x)
*(2+x)
*(12+7*x+x^2)
*(-24-50*x-35*x^2-10*x^3-x^4)
*(-24-50*x-35*x^2-10*x^3-x^4);
\end{verbatim}
\noindent To put some order in this one may factorize the new expression
again:
\begin{verbatim}
Symbol x,y;
LocalFactorize E = -(x+1)*(x+2)*((x+3)*(x+4));
Local F = -(x+1)*(x+2)*((x+3)*(x+4));
.sort
LF G = (x-1)*(x+2)^2*E^2*F^2;
Print G;
Factorize G;
.end
G=
(-1+x)
*(1+x)
*(1+x)
*(1+x)
*(1+x)
*(2+x)
*(2+x)
*(2+x)
*(2+x)
*(2+x)
*(2+x)
*(3+x)
*(3+x)
*(3+x)
*(3+x)
*(4+x)
*(4+x)
*(4+x)
*(4+x);
\end{verbatim}
\noindent In this case all constants are multiplied, all factors are
factorized, and all factors in the new format are sorted.
\noindent The case that one or more factors are zero is special. In
principle the zero factors are kept as in:
\begin{verbatim}
Symbol x,y;
LocalFactorize E = -0*(x+1)*(x+2)*0*((x+3)*(x+4));
Print;
.end
E=
(-1)
*(0)
*(1+x)
*(2+x)
*(0)
*(12+7*x+x^2);
\end{verbatim}
\noindent This way one can see what has happened when a substitution makes
a factor zero. When we factorize this expression again however the whole
expression becomes zero. If this is not intended and one would like to
continue with the factors that are nonzero we have the keepzero option in
the factorize statement as in:
\begin{verbatim}
Symbol x,y;
Format Nospaces;
LocalFactorize E = -0*3*(x+1)*(x+2)/2*0*((x+3)*(x+4));
Print;
.sort
E=
(-1)
*(0)
*(3)
*(1+x)
*(2+x)
*(1/2)
*(0)
*(12+7*x+x^2);
Print;
Factorize(keepzero) E;
.end
E=
(0)
*(-3/2)
*(1+x)
*(2+x)
*(3+x)
*(4+x);
\end{verbatim}
\noindent We see here that first all constants are separate factors and the
new factorization combines them. The keepzero option does the same with the
factors that are zero. The zero factor will always be the first. Hence it
is rather easy to test for whether the total expression should actually be
zero. We just have to look whether \verb:E[factor_]: is zero.
\noindent The unfactorize~\ref{substaunfactorize} statement is the opposite
of the factorize statement. It takes the factorized expression and
multiplies out the factors. It also uses the current brackets for
formatting the output.
\begin{verbatim}
Symbol x,y;
LFactorized F = (x+1)*(x+y)*(y+1);
Print;
.sort
F=
(1+x)
*(y+x)
*(1+y);
Print;
Bracket x;
UnFactorize F;
.end
F=
+x*(1+2*y+y^2)
+x^2*(1+y)
+y+y^2;
\end{verbatim}
\noindent In principle there are various models by which the
unfactorization can be done in an efficient way. In addition it would be
less efficient when the master would do all the work as is the case with
the factorize statement. Currently this statement is still being developed
internally. It is possible to make ones own emulation of it. Here we give
the `brute force' way:
\begin{verbatim}
Symbol x,y;
LFactorized F = (x+1)*(x+y)*(y+1);
Print;
.sort
F=
(1+x)
*(y+x)
*(1+y);
#$num = numfactors_(F);
Local G = *...*;
Bracket x;
Print;
.end
F=
(1+x)
*(y+x)
*(1+y);
G=
+x*(1+2*y+y^2)
+x^2*(1+y)
+y+y^2;
\end{verbatim}
\noindent Factorization of \$-expressions is yet a different thing. The
\$-expressions do not have a bracket mechanism. Hence we need different
ways of storing the factors. In the case of expressions we have to work in
a way that is potentially disk based. With \$-expressions we work in
allocated memory. Hence we also store the factors in allocated memory. In
that case we can keep both the original and the factors. The factors are
accessed by referring to their number between braces. The number zero
refers to the number of factors:
\begin{verbatim}
Symbol x,y;
CFunction f;
Off Statistics;
#$a = x^4-y^4;
Local F = f(x^4-y^4)+f(x^6-y^6);
Print;
.sort
F=
f(-y^4+x^4)+f(-y^6+x^6);
#factdollar $a;
#do i = 1,`$a[0]'
#write <> "Factor `i' of `$a' is `$a[`i']'"
Factor 1 of -y^4+x^4 is -1
#enddo
Factor 2 of -y^4+x^4 is y-x
Factor 3 of -y^4+x^4 is y+x
Factor 4 of -y^4+x^4 is y^2+x^2
id f(x?$b) = f(x);
FactDollar $b;
do $i = 1,$b[0];
Print "Factor %$ of %$ is %$",$i,$b,$b[$i];
enddo;
Print;
.end
Factor 1 of -y^4+x^4 is -1
Factor 2 of -y^4+x^4 is y-x
Factor 3 of -y^4+x^4 is y+x
Factor 4 of -y^4+x^4 is y^2+x^2
Factor 1 of -y^6+x^6 is -1
Factor 2 of -y^6+x^6 is y-x
Factor 3 of -y^6+x^6 is y+x
Factor 4 of -y^6+x^6 is y^2-x*y+x^2
Factor 5 of -y^6+x^6 is y^2+x*y+x^2
F=
f(-y^4+x^4)+f(-y^6+x^6);
\end{verbatim}
\noindent We see here a variety of new features. The preprocessor can
factorize \$a with the \#FactDollar instruction. We do indeed pick up the
number of factors in the preprocessor as `\$a[0]' and the factors
themselves as `\$a[1]' etc. For the \$-variable that needs to be
manipulated during running time things as a bit more complicated. We define
\$b as part of a wildcard pattern matching. This is still rather normal.
Then we use the FactDollar statement. Notice that for each term we will
have a different \$b. To access the factors we cannot use the preprocessor
methods because those are only available at compile time. Hence we cannot
use the preprocessor \#do instruction and therefore we need an execution
time do statement. The loop parameter will have to be a \$-variable as
well. The do statement and the print statement show now how one can use the
factors. In the output one can see that indeed we had two different
contents for \$b. And the arguments of the function f remain unaffected.
\noindent One may also ask for the number of factors in a \$-expression
with the numfactors\_ function as in:
\begin{verbatim}
Symbol x,y;
CFunction f;
Format Nospaces;
#$a = x^4-y^4;
#factdollar $a;
Local F = f(numfactors_($a))
+f(<$a[1]>,...,<$a[`$a[0]']>);
Print;
.end
F=
f(-1,y-x,y+x,y^2+x^2)+f(4);
\end{verbatim}
\noindent Note that in the second case we need to use the construction
`\$a[0]' because the preprocessor needs to substitute the number
immediately in order to expand the triple dot operator. This cannot wait
till execution time.
\noindent Some remarks.
\noindent The time needed for a factorization depends strongly on the
number of variables used. For example factorization of $x^{60}-1$ is much
faster than factorization of $x^{60}-y^{60}$. One could argue that the
second formula can be converted into the first, but there is a limit to
what FORM should do and what the user should do.
\begin{verbatim}
Symbol x,y;
Format NoSpaces;
On ShortStats;
Local F1 = x^60-1;
Local F2 = y^60-x^60;
Factorize F1,F2;
Print;
.end
0.00s 1> 2--> 2: 52 F1
0.07s 1> 51--> 51: 1524 F1 factorize
0.07s 1> 2--> 2: 64 F2
1.17s 1> 51--> 51: 1944 F2 factorize
F1=
(-1+x)
*(1-x+x^2)
*(1-x+x^2-x^3+x^4)
*(1-x+x^3-x^4+x^5-x^7+x^8)
*(1+x)
*(1+x+x^2)
*(1+x+x^2+x^3+x^4)
*(1+x-x^3-x^4-x^5+x^7+x^8)
*(1-x^2+x^4)
*(1-x^2+x^4-x^6+x^8)
*(1+x^2)
*(1+x^2-x^6-x^8-x^10+x^14+x^16);
F2=
(y-x)
*(y+x)
*(y^2-x*y+x^2)
*(y^4-x*y^3+x^2*y^2-x^3*y+x^4)
*(y^4+x*y^3+x^2*y^2+x^3*y+x^4)
*(y^2+x*y+x^2)
*(y^2+x^2)
*(y^8-x*y^7+x^3*y^5-x^4*y^4+x^5*y^3-x^7*y+x^8)
*(y^8+x*y^7-x^3*y^5-x^4*y^4-x^5*y^3+x^7*y+x^8)
*(y^8-x^2*y^6+x^4*y^4-x^6*y^2+x^8)
*(y^4-x^2*y^2+x^4)
*(y^16+x^2*y^14-x^6*y^10-x^8*y^8-x^10*y^6+x^14*y^2+x^16);
\end{verbatim}
\noindent When one has a factorized expression and one uses the multiply
statement, all terms in the factorized expression are multiplied the
specified amount. This may lead to a counterintuitive result:
\begin{verbatim}
Symbols a,b;
LF F = (a+b)^2;
multiply 2;
Print;
.end
F =
( 2*b + 2*a )
* ( 2*b + 2*a );
\end{verbatim}
This is a consequence of the way we store the factors. This way each factor
will be multiplied by two. If one would like to add a factor one can do
this by the following simple mechanism:
\begin{verbatim}
Symbols a,b;
LF F = (a+b)^2;
.sort
LF F = 2*F;
Print;
.end
F =
( 2 )
* ( b + a )
* ( b + a );
\end{verbatim}
\noindent In version 3 there were some experimental polynomial functions
like polygcd\_\index{polygcd\_}\index{function!polygcd\_}. These have been
removed as their functionality has been completely taken over by the new
functions gcd\_~\ref{fungcd}, div\_~\ref{fundiv} and rem\_~\ref{funrem} and
some statements like normalize~\ref{substanormalize},
makeinteger~\ref{substamakeinteger} and factarg~\ref{substafactarg}. Unlike
regular functions, the functions gcd\_. div\_ and rem\_ have the
peculiarity that if one of the arguments is just an expression or a
\$-expression, this expression is not evaluated until the function is
evaluated. This means that the evaluated expression does not have to fit
inside the maximum size reserved for a single term. In some cases, when the
gcd\_ function is invoked with many arguments, the expression may not have
to be evaluated at all! The GCD of the other arguments may be one already.
%\begin{verbatim}
%\end{verbatim}
%\begin{verbatim}
%\end{verbatim}
%\begin{verbatim}
%\end{verbatim}
form-master/doc/manual/prepro.tex 0000664 0000000 0000000 00000250050 13565763364 0017411 0 ustar 00root root 0000000 0000000
\chapter{The preprocessor}
\label{preprocessor}
%--#[ General :
The preprocessor\index{preprocessor} is a program segment that reads and
edits\index{edit} the input, after which the processed input is offered to
the compiler\index{compiler} part of \FORM. When a module\index{module}
instruction is encountered by the preprocessor, the compilation is halted
and the module is executed. The compiler buffers are cleared and \FORM\
will continue with the next module. The preprocessor acts almost purely on
character strings. As such it does not know about the algebraic properties
of the objects it processes. Additionally the preprocessor also filters out
the commentary\index{commentary}.
The commands for the preprocessor are called instructions. Preprocessor
instructions start with the character \# as the first non-blank character
in a line. After this there are several possibilities.
\begin{description}
\item[\#:]\index{\#:} Special syntax for setup parameters at the beginning
of the program. See the chapter on the setup parameters.
\item[\#$-$, \#$+$]\index{\#$-$}\index{\#$+$} Turns the listing of the input off or
on.
\item[\#name]\index{\#name} Preprocessor command. The syntax of the various
commands will be discussed below.
\item[\#\$name]\index{\#\$name} Giving a value to a dollar variable in the
preprocessor. See chapter \ref{dollars} on dollar variables.
\end{description}
%--#] General :
%--#[ The preprocessor variables :
\section{The preprocessor variables}
\label{preprovariables}
In order to help in the edit\index{edit} function the preprocessor is
equipped with variables\index{preprocessor variables} that can be defined
or redefined by the user or by other preprocessor actions. Preprocessor
variables have regular names that are composed of strings of alphanumeric
characters of which the first one must be alphabetic. When they are defined
one just uses this name. When they are used the name should be enclosed
between a backquote\index{backquote} and a quote\index{quote} as if these
were some type of brackets. Hence `a2r' is the reference to a regular
preprocessor variable. Preprocessor variables contain strings of
characters. No interpretation is given to these strings. The
backquote/quote pairs can be nested. Hence `a`i'r' will result in the
preprocessor variable `i' to be substituted first. If this happens to be
the string "2", the result after the first substitution would be `a2r' and
then \FORM\ would look for its string value.
The use of the backquotes is different from the earlier versions of \FORM.
There the preprocessor variables would be enclosed in a pair of quotes and
no nesting\index{nesting} was possible. \FORM\ still understands this old
notation because it does not lead to ambiguities. The user is however
strongly advised to use the new notation with the backquotes, because in
future versions the old\index{old notation} notation may not be recognized
any longer.
\noindent \FORM\ has a number of built in preprocessor variables. They are:
\begin{description}
\item[VERSION\_] The current version\index{VERSION\_} as the \formmajorversion{} in
\formmajorversion.\formminorversion.
\item[SUBVERSION\_] The sub-version\index{SUBVERSION\_} as the \formminorversion{} in
\formmajorversion.\formminorversion.
\item[NAME\_] The name\index{NAME\_} of the program file.
\item[DATE\_] The date\index{DATE\_} of the current run.
\item[CMODULE\_] The number\index{CMODULE\_} of the current module.
\item[SHOWINPUT\_] If input listing\index{SHOWINPUT\_} is on: 1, if off: 0.
\item[EXTRASYMBOLS\_] The current number of extra symbols\index{EXTRASYMBOLS\_}
(see \ref{substaextrasymbols}).
\item[OLDNUMEXTRASYMBOLS\_] The number of extra symbols\index{OLDNUMEXTRASYMBOLS\_}
before the current optimization started (see chapter \ref{optimization}).
\item[OPTIMMINVAR\_] The number of the first extra symbol\index{OPTIMMINVAR\_} needed
for the current optimization (see chapter \ref{optimization}).
\item[OPTIMMAXVAR\_] The number of the last extra symbol\index{OPTIMMAXVAR\_} needed
for the current optimization (see chapter \ref{optimization}).
\item[OPTIMSCHEME\_] The best Horner scheme\index{OPTIMSCHEME\_} found
for the current optimization (see chapter~\ref{optimization}).
\item[OPTIMVALUE\_] The number of arithmetic operations\index{OPTIMVALUE\_}
in the resulting expression for the current optimization
(see chapter~\ref{optimization}).
\item[PID\_] The process identifier (PID) \index{PID} \index{PID\_} of
the running process. In \ParFORM{} (\ref{parform}), it represents
the PID of the master process in order to ensure that all the
processes in a job use the same number. A recovered session from
a checkpoint (\ref{checkpoints}) keeps using the PID of the
crushed session.
\item[STOPWATCH\_] Same as `TIMER\_'.
\item[TIME\_] The running time\index{time\_} till the moment of call in the string format
with a decimal point and two digits after the decimal point.
This is the same format as in the statistics.
\item[TIMER\_] The running time\index{timer\_} since the last reset in milliseconds. Hence,
unlike `time\_' this value can be used in the preprocessor
calculator and in numerical compares in \#if instructions.
See also the \#reset (see \ref{prereset}) instruction.
\item[NUMACTIVEEXPRS\_] The number of the current active expressions.
\item[ACTIVEEXPRNAMES\_] The list of the current active expression names
separated by commas. This can be passed to \#do lvar=\{...\}
instruction~(\ref{predo}) like:
\begin{verbatim}
#do e = {`activeexprnames_'}
#ifdef `e'
Local `e' = `e' + something;
#endif
#enddo
\end{verbatim}
\end{description}
\noindent If \FORM\ cannot find a preprocessor variable, because it has
neither been defined by the user, nor is it one of the built in variables,
it will look in the systems environment\index{environment} to see whether
there is an environment variable by that name. If this is the case its
string value will be substituted.
\noindent Preprocessor variables can have arguments and thereby become
macro's. One should consult the description of the \#define~\ref{predefine}
instruction about the delayed substitution feature to avoid the value of
the preprocessor variables in the macro would be substituted immediately
during the definition. Hence proper use is
\begin{verbatim}
#define EXCHANGE(x,y) "Multiply replace_(`~x',`~y',`~y',`~x');"
\end{verbatim}
\noindent \FORM{} has the following built in macro's:
\begin{description}
\item[TOLOWER\_(string)] in which the character string in the argument is
converted to lower case. After this it will become input.
\item[TOUPPER\_(string)] in which the character string in the argument is
converted to upper case. After this it will become input.
\end{description}
It is anticipated that some more macro's will become available to allow for
the editing of names of variables.
%--#] The preprocessor variables :
%--#[ Calculator :
\section{The preprocessor calculator}
\label{calculator}
Sometimes a preprocessor\index{preprocessor variable!numeric} variable
should be interpreted as a number and some arithmetic\index{arithmetic}
should be done with it. For this \FORM\ is equipped with what is called the
preprocessor calculator\index{calculator}. When the input reading device
encounters a left curly\index{curly bracket} bracket\index{bracket!curly}
\verb:{:, it will read till the matching right curly bracket \verb:}: and
then test whether the characters (after substitution of preprocessor
variables) can be interpreted as a numerical expression. If it is not a
valid numerical expression the whole string, including the curly brackets,
will be passed on to the later stages of the program. If it is a numerical
expression, it will be evaluated, and the whole string, including the curly
brackets, will be replaced by a textual representation of the result.
Example:
\begin{verbatim}
Local F`i' = F{`i'-1}+F{`i'-2};
\end{verbatim}
If the preprocessor variable i has the value 11, the calculator makes this
into
\begin{verbatim}
Local F11 = F10+F9;
\end{verbatim}
Valid numerical expressions can contain the characters
\begin{verbatim}
0 1 2 3 4 5 6 7 8 9 + - * / % ( ) { } & | ^ !
\end{verbatim}
The use of parentheses is as in regular arithmetic. The curly
brackets fulfil the same role, as one can nest these brackets of course.
Operators are:
\begin{description}
\item[$+$] Regular addition\index{addition}.
\item[$-$] Regular subtraction\index{subtraction}.
\item[$\ast$] Regular multiplication\index{multiplication}.
\item[$/$] Regular (integer) division\index{division}.
\item[$\%$] The remainder\index{remainder} after (integer) division as in
the language C\index{C}.
\item[$\&$] And\index{and} operator. This is a bitwise operator.
\item[$|$] Or\index{or} operator. This is a bitwise or.
\item[$\wedge$] Exponent\index{exponent} operator.
\item[$!$] Factorial\index{factorial}. This is a postfix operator.
\item[$\wedge\%$] A postfix ${}^2\!\log$. This means that it
takes\index{twolog} the ${}^2\!\log$ of the object to the left of it.
\item[$\wedge/$] A postfix square\index{square root} root. This means that
it takes the square root of the object to the left of it.
\end{description}
Note that all arithmetic\index{arithmetic} is done over the integers and
that there is a finite range. On 32\index{32 bits} bit systems this range
will be $2^{31}-1$ to $-2^{31}$, while on 64\index{64 bits} bit systems
this will be $2^{63}-1$ to $-2^{63}$. In particular this means that
\verb:{13^/}: becomes \verb:3:. The preprocessor calculator is only meant
for some simple counting and organization of the program flow. Hence there
is no large degree of sophistication. Very important is that the
comma\index{comma} character is not a legal character for the preprocessor
calculator. This can be used to avoid some problems. Suppose one needs to
make a substitution of the type:
\begin{verbatim}
id f(x?!{0}) = 1/x;
\end{verbatim}
in which the value zero should be excluded from the pattern matching (see
dynamical\index{set!dynamical} sets in chapter \ref{pattern} on pattern
matching). This would not work, because the preprocessor would make this
into
\begin{verbatim}
id f(x?!0) = 1/x;
\end{verbatim}
which is illegal syntax. Hence the proper trick is to write
\begin{verbatim}
id f(x?!{,0}) = 1/x;
\end{verbatim}
With the comma the preprocessor will leave this untouched, and hence now
the set is passed properly.
Good use of the preprocessor calculator can make life much easier for
\FORM. For example the following statements
\begin{verbatim}
id f(`i') = 1/(`i'+1);
id f(`i') = 1/{`i'+1};
\end{verbatim}
are quite different in nature. In the first statement the compiler gets an
expression with a composite denominator. The compiler never tries to
simplify expressions by doing algebra on them. Sometimes this may not be
optimal, but there are cases in which it would cause wrong results (in
particular when noncommuting and commuting functions are mixed and
wildcards are used). Hence the composite denominator has to be worked out
during run time for each term separately. The second statement has the
preprocessor work out the sum and hence the compiler gets a simple fraction
and less time will be needed during running. Note that
\begin{verbatim}
id f(`i') = {1/(`i'+1)};
\end{verbatim}
would most likely not produce the desired result, because the preprocessor
calculator works only over the integers. Hence, unless i is equal to zero
or -2, the result would be zero (excluding of course the fatal error when i
is equal to -1).
%--#] Calculator :
%--#[ ... :
\section{The triple dot operator}
\label{tripledot}
The last\index{...} stage of the actions of the preprocessor involves the
triple dot operator. It indicates a repeated pattern as in \verb:a1+...+a4:
which would expand into \verb:a1+a2+a3+a4:. This operator is used in two
different ways. First the most general way:
\begin{verbatim}
operator1...operator2
\end{verbatim}
in which the less\index{less than} than and greater\index{greater than}
than signs serve as boundaries for the patterns. The operators can be any
pair of the following:
\begin{description}
\item[+\ +]\index{+...+} Repetitions will be separated by plus signs.
\item[--\ --]\index{-...-} Repetitions will be separated by minus signs.
\item[+\ --]\index{+...-} Repetitions will be separated by alternating signs.
First will be plus.
\item[--\ +]\index{-...+} Repetitions will be separated by alternating signs.
First will be minus.
\item[$\ast\ \ast$]\index{*...*} Repetitions will be separated by $\ast$.
\item[/\ /]\index{/.../} Repetitions will be separated by /.
\item[,\ ,]\index{,...,} Repetitions will be separated by comma's.
\item[:\ :]\index{:...:} Repetitions will be separated by {\it single} dots.
%\item[+\ +]\index{.@$+\cdots+$} Repetitions will be separated by plus signs.
%\item[--\ --]\index{.@$-\cdots-$} Repetitions will be separated by minus signs.
%\item[+\ --]\index{.@$+\cdots-$} Repetitions will be separated by alternating signs.
%First will be plus.
%\item[--\ +]\index{.@$-\cdots+$} Repetitions will be separated by alternating signs.
%First will be minus.
%\item[$\ast\ \ast$]\index{.@$\ast\cdots\ast$} Repetitions will be separated by $\ast$.
%\item[/\ /]\index{.@$/\cdots/$} Repetitions will be separated by /.
%\item[,\ ,]\index{.@$,\cdots,$} Repetitions will be separated by comma's.
%\item[:\ :]\index{.@$:\cdots:$} Repetitions will be separated by {\it single} dots.
\end{description}
For such a pair of operators \FORM\ will inspect the patterns\index{pattern}
and see whether the differences between the two patterns are just numbers.
If the differences are numbers and the absolute value of the difference of
each matching pair is always the same (a difference of zero is allowed too;
it leads to no action for the pair), then \FORM\ will expand the pattern,
running from the first to the last in increments of one. For each pair the
counter can either run up or run down, depending on whether the number in
the first pattern is greater or less than the number in the second pattern.
Example:
\begin{verbatim}
Local F = -...+;
\end{verbatim}
leads to
\begin{verbatim}
Local F = a1b6(c3)-a2b5(c4)+a3b4(c5)-a4b3(c6);
\end{verbatim}
The second form is a bit simpler. It recognizes that there are special
cases that can be written in a more intuitive way. If there is only a
single number to be varied, and it is the end of the pattern, and the rest
of the patterns consists only of alphanumeric characters of which the first
is an alphabetic character, we do not need the less than/greater than
combination. This is shown in
\begin{verbatim}
Symbol a1,...,a12;
\end{verbatim}
There is one extra exception. The variables used this way may have a
question mark after them to indicate that they are wildcards:
\begin{verbatim}
id f(a1?,...,a4?) = g(a1,...,a4,a1+...+a4);
\end{verbatim}
This construction did not exist in earlier versions of \FORM\ (version 1 and
version 2). There one needed the \#do\index{\#do} instruction for many of
the above constructions, creating code that was very hard to read. The
\verb:...: operator should improve the readability of the programs very
much.
%--#] ... :
%--#[ add :
\section{\#add}
\label{preadd}
\noindent Syntax:
\#add object: "string"
\noindent See chapter \ref{dictionaries} on dictionaries.
\noindent Adds words to an open dictionary.
%--#] add :
%--#[ addseparator :
\section{\#addseparator}
\label{preaddseparator}
\noindent Syntax:
\#addseparator character
\noindent See also \#rmseparator (\ref{prermseparator}),
\#call (\ref{precall}), \#do (\ref{predo})
\noindent Adds a character\index{\#addseparator} to the list of permissible
separator characters for arguments of \#call or \#do instructions. By
default the two characters that are permitted are the comma and the
character \verb:|:. Blanks, tabs and double quotes are ignored. Note that
the comma must be specified between double quotes as in
\begin{verbatim}
#addseparator ","
\end{verbatim}
%--#] addseparator :
%--#[ append :
\section{\#append}
\label{preappend}
\noindent Syntax:
\#append $<$filename$>$
\noindent See also write (\ref{prewrite}),
close (\ref{preclose}), create (\ref{precreate}),
remove (\ref{preremove})
\noindent Opens\index{\#append} the named file for writing. The file will
be positioned at the end. The next \#write\index{\#write} instruction will
add to it.
%--#] append :
%--#[ appendpath :
\section{\#appendpath}
\label{preappendpath}
\noindent Syntax:
\#appendpath pathname
\noindent See also prependpath~(\ref{preprependpath})
\noindent Appends the given path relative to the current file to the end of
the FORM path\index{path}.
%--#] appendpath :
%--#[ break :
\section{\#break}
\label{prebreak}
\noindent Syntax:
\#break
\noindent See also switch (\ref{preswitch}),
endswitch (\ref{preendswitch}),
case (\ref{precase}),
default (\ref{predefault})
\noindent If the\index{\#break} lines before were not part of the control
flow ({\it i.e.} these lines are used for the later stages of the program),
this instruction is ignored. If they are part of the control flow, the flow
will continue after the matching \#endswitch\index{\#endswitch}
instruction. The \#break instruction must of course be inside the range of
a \#switch\index{\#switch}/\#endswitch construction.
%--#] break :
%--#[ breakdo :
\section{\#breakdo}
\label{prebreakdo}
\noindent Syntax:
\#breakdo [{\tt<}number{\tt>}]
\noindent See also \#do (\ref{predo}) and \#enddo (\ref{preenddo})
\noindent The \#breakdo\index{\#breakdo} instruction allows one to jump out
of a \#do loop. If a (nonzero integer) number is specified it indicates the
number of loops the program should terminate. Control will continue after
the \#enddo instruction of the number of loops indicated by `number'.
The default value is one. If the value is zero the statement has no effect.
%--#] breakdo :
%--#[ call :
\section{\#call}
\label{precall}
\noindent Syntax:
\#call procname(var1,...,varn)
\noindent See also procedure (\ref{preprocedure}), endprocedure
(\ref{preendprocedure})
\noindent This instruction\index{\#call} calls the
procedure\index{procedure} with the name procname. The result is that \FORM\
looks for this procedure, first in its procedure
buffers\index{buffer!procedure} (for procedures that were defined in the
regular text stream as explained under the \#procedure\index{\#procedure}
instruction), then it looks for a file by the name procname.prc in the
current directory, and if it still has not found the procedure, it looks in
the directories indicated by the path\index{path} variable in either the setup
file or at the start of the program (see chapter \ref{setup} on the setup
file). Next it looks for the -p option in the command that started \FORM\
(see the chapter on running \FORM). If this -p option has not been used \FORM\
will see whether there is an environment variable by the name
FORMPATH\index{FORMPATH}. The directories indicated there will be searched
for the file procname.prc. If \FORM\ cannot find the file, there will be an
error message and execution will be stopped immediately.
Once the procedure has been located, \FORM\ reads the whole file and then
determines whether the number of parameters is identical in the
\#call\index{\#call} instruction and the \#procedure\index{\#procedure}
instruction. A difference is a fatal error.
The parameter field consists of strings, separated by commas. If a string
contains a comma, this comma should be preceded by a
backslash\index{backslash} character (\verb:\:). If a string should contain
a linefeed\index{linefeed}, one should `escape' this linefeed by putting a
backslash and continue on the next line.
Before version 3 of \FORM\ the syntax was different. The parentheses
were curly brackets and the separators the symbol \verb:|:. This was made
to facilitate the use of strings that might contain commas. In practise
however, this turned out to be far from handy. In addition the new
preprocessor calculator is a bit more active and hence an instruction of
the type
\begin{verbatim}
#call test{1}
\end{verbatim}
will now be intercepted by the preprocessor calculator\index{calculator}
and changed into
\begin{verbatim}
#call test1
\end{verbatim}
Because there are many advantages to the preprocessor calculator treating
the parameters of the procedures before they are called (in the older
versions it did not do this), the notation has been changed. \FORM\ still
understands the old notation, provided that there is no conflict with the
preprocessor calculator. Hence
\begin{verbatim}
#call test{1|a}
#call test{1,a}
#call test(1|a)
#call test(1,a)
\end{verbatim}
are all legal and give the same result, but only the last notation will
work in future versions of \FORM.
Nowadays also the use of the argument field wildcard (see chapter
\ref{pattern} on pattern matching) is allowed as in the
regular functions:
% THIS EXAMPLE IS PART OF THE TESTSUITE. CHANGES HERE SHOULD BE APPLIED THERE AS
% WELL!
\begin{verbatim}
#define a "1"
#define bc2 "x"
#define bc3 "y"
#define b "c`~a'"
#procedure hop(c,?d);
#redefine a "3"
#message This is the call: `c',`?d'
#endprocedure
#redefine a "2"
#message This is b: `b'
~~~This is b: c2
#call hop(`b`!b''`!b'`b'`!b'`b',`~a',`b',`a')
~~~This is the call: xc2c3c2c3,3,c3,2
.end
\end{verbatim}
We also see here that the rules about delayed substitution (see also the
\#define\index{\#define} instruction in section \ref{predefine}) apply. The
use of `!b' cancels the delayed substitution that is asked for in the
definition of b.
The default extension for procedure files is .prc\index{.prc}, but it is
possible to change this. There are two different ways: One is with the
\#procedureExtension\index{\#procedureExtension} instruction in section
\ref{preprocedureextension}. The other is via the setup (see the chapter on
the setup file, chapter \ref{setup}).
%--#] call :
%--#[ case :
\section{\#case}
\label{precase}
\noindent Syntax:
\#case string
\noindent See also switch (\ref{preswitch}),
endswitch (\ref{preendswitch}),
break (\ref{prebreak}),
default (\ref{predefault})
\noindent The lines after the \#case\index{\#case} instruction will be used
if either this is the first \#case\index{\#case} instruction of which the
string matches the string in the \#switch\index{\#switch} instruction, or
the control flow was already using the lines before this \#case instruction
and there was no \#break\index{\#break} instruction (this is called
fall-through). The control flow will include lines either until the next
matching \#break instruction, or until the matching
\#endswitch\index{\#endswitch} instruction.
%--#] case :
%--#[ clearoptimize :
\section{\#clearoptimize}
\label{preclearoptimize}
\noindent Syntax:
\#clearoptimize
See the chapter about optimization \ref{optimization}
%--#] clearoptimize :
%--#[ close :
\section{\#close}
\label{preclose}
\noindent Syntax:
\#close $<$filename$>$
\noindent See also write (\ref{prewrite}), append (\ref{preappend}),
create (\ref{precreate}), remove (\ref{preremove})
\noindent This instruction closes\index{\#close} the file\index{file!close}
by the given name, if such a file had been opened by the previous
\#write\index{\#write} instruction. Normally \FORM\ closes all such files at
the end of execution. Hence the user would not have to worry about this.
The use of a subsequent \#write instruction with the same file name will
remove the old contents and hence start basically a new file. There are
times that this is useful.
%--#] close :
%--#[ closedictionary :
\section{\#closedictionary}
\label{preclosedictionary}
\noindent Syntax:
\#closedictionary
\noindent See chapter \ref{dictionaries} on dictionaries.
\noindent Either closes an open dictionary (\ref{preopendictionary}) or stops
using the dictionary (\ref{preusedictionary}) that is currently used for output
translation.
%--#] closedictionary :
%--#[ commentchar :
\section{\#commentchar}
\label{precommentchar}
\noindent Syntax:
\#commentchar character
\noindent The specified\index{\#commentchar} character should be a single
non-whitespace character. There may be white space (blanks and/or tabs)
before or after it. The character will take over the role of the comment
character. {\it i.e.} any line that starts with this character in column 1
will be considered commentary\index{commentary}. This feature was provided
because output of some other algebra programs could put the multiplication
sign in column 1 in longer expressions.
The default commentary character is $\ast$.
%--#] commentchar :
%--#[ create :
\section{\#create}
\label{precreate}
\noindent Syntax:
\#append $<$filename$>$
\noindent See also write (\ref{prewrite}),
close (\ref{preclose}), append (\ref{preappend}),
remove (\ref{preremove})
\noindent Opens the named\index{\#create} file for writing. If the file
existed already, its previous contents will be lost. The next
\#write\index{\#write} instruction will add to it. In principle this
instruction is not needed, because the \#write instruction would create the
file if it had not been opened yet at the moment of writing.
%--#] create :
%--#[ default :
\section{\#default}
\label{predefault}
\noindent Syntax:
\#default
\noindent See also switch (\ref{preswitch}),
endswitch (\ref{preendswitch}),
case (\ref{precase}),
break (\ref{prebreak})
\noindent Control\index{\#default} flow continues after this instruction if
there is no \#case\index{\#case} instruction of which the string matches
the string in the \#switch\index{\#switch} instruction. Control flow also
continues after this instruction, if the lines before were included and
there was no \#break\index{\#break} instruction to stop the control flow
(fall-through). Control flow will stop either when a matching \#break
instruction is reached, or when a matching \#endswitch\index{\#endswitch}
is encountered. In the last case of course control flow will continue after
the \#endswitch instruction.
%--#] default :
%--#[ define :
\section{\#define}
\label{predefine}
\noindent Syntax:
\#define name "string"
\noindent See also redefine (\ref{preredefine}), undefine
(\ref{preundefine})
\noindent in which name\index{\#define} refers to the name of the
preprocessor\index{preprocessor variable}
variable\index{variable!preprocessor} to be defined and the contents of the
string will form the value of the variable. The double quotes are mandatory
delimiters of the string.
The use of the \#define\index{\#define} instruction creates a new instance
of the preprocessor variable with the given name. This means that the old
instance\index{instance} remains. If for some reason the later instance
becomes undefined (see for instance \#undefine), the older instance will be
the one that is active. If the old definition is to be overwritten, one
should use the \#redefine\index{\#redefine} instruction.
As of version 3.2 preprocessor variables can also have arguments as in the
C\index{C} language. Hence
\#define var(a,b) "(`\verb:~:a'+`\verb:~:b'+`c')"
is allowed. The parameters should be referred to inside a pair of `' as
with all preprocessor variables. A special feature is the socalled
delayed\index{delayed substitution}
substitution\index{substitution!delayed}. With macro's like the above the
question is always {\sl when} a preprocessor variable will be substituted.
Take for instance
% THIS EXAMPLE IS PART OF THE TESTSUITE. CHANGES HERE SHOULD BE APPLIED THERE AS
% WELL!
\begin{verbatim}
#define c "3"
#define var1(a,b) "(`~a'+`~b'+`c')"
#define var2(a,b) "(`~a'+`~b'+`~c')"
#redefine c "4"
Local F1 = `var1(1,2)';
Local F2 = `var2(1,2)';
Print;
.end
F1 =
6;
F2 =
7;
\end{verbatim}
The parameter c will be substituted immediately when var1 is defined. In
var2 it will be only substituted when var2 is used. It should be clear that
a and b should also be used in the delayed fashion because they do not
exist yet at the moment of the definition of var1 and var2. Notice also
that the whole macro\index{macro}, with its arguments should be placed
between the backquote and the quote. Another example can be found with the
\#call\index{\#call} instruction. See section \ref{precall}
%--#] define :
%--#[ do :
\section{\#do}
\label{predo}
\noindent Syntax:
\#do lvar = i1,i2
\#do lvar = i1,i2,i3
\#do lvar = $\{$string1$|$...$|$stringn$\}$
\#do lvar = $\{$string1,...,stringn$\}$
\#do lvar = nameofexpression
\noindent See also enddo (\ref{preenddo})
\noindent The \#do\index{\#do} instruction\index{do loop} needs a matching
\#enddo\index{\#enddo} instruction. All code in-between these two
instructions will be read as many times as indicated in the parameter field
of the \#do instruction. The parameter lvar is a preprocessor variable of
which the value is determined by the other parameters. Inside the loop it
should be referred to by enclosing its name between a backquote/quote pair
as is usual for preprocessor variables. The various possible parameter
fields have the following meaning:
\begin{description}
\item[\#do lvar = i1,i2] The parameters i1 and i2 should be integers or
names of dollar expressions that evaluate into integers. The
first time in the loop lvar will get the value of i1 (as a string) and each
next time its value will be one greater (translated into a string again).
The last time in the loop the value of lvar will be the greatest integer
that is less or equal to i2. If i2 is less than i1, the loop is skipped
completely. If i2 is the name of a dollar variable, each time the control
reaches the end of the loop the dollar variable is evaluated and the
current value is used.
\item[\#do lvar = i1,i2,i3] The parameters i1,i2 and i3 should be integers
or names of dollar expressions that evaluate into integers.
The first time in the loop lvar will get the value of i1 (as a string) and
each next time its value will be incremented by adding i3 (translated into
a string again). If i3 is positive, the last value of lvar will be the one
for which lvar+i3 is greater than i2. If i2 is less than i1, the loop is
skipped completely. If i3 is negative the last value of lvar will be the
one for which lvar+i3 is less than i2. If i3 is zero there will be an
error. If i2 or i3 are the names of a dollar variable, each time the control
reaches the end of the loop the dollar variable(s) is/are evaluated and the
current value is used.
\item[\#do lvar = $\{$string1$|$...$|$stringn$\}$] The first time in the
loop the value of lvar is the string indicated by string1, the next time
will be string2 etc till the last time when it will be stringn. This is
called a listed\index{listed loop} loop\index{loop!listed}. The notation
with the $|$ is an old notation which is still accepted. The new notation
uses a comma instead.
\item[\#do lvar = $\{$string1,...,stringn$\}$] The first time in the loop
the value of lvar is the string indicated by string1, the next time will be
string2 etc till the last time when it will be stringn. This is called a
listed\index{listed loop} loop\index{loop!listed}.
\item[\#do lvar = expression] The loop variable will take one by one for
its value all the terms of the given expression. This is protected against
changing the expression inside the loop by making a copy of the expression
inside the memory. Hence one should be careful with very big expressions.
An expression that is zero gives a loop over zero terms, hence the loop is
never executed.
\end{description}
The first two types of \#do instructions are called
numerical\index{numerical loop} loops\index{loop!numerical}. In the
parameters of numerical loops the preprocessor calculator\index{calculator}
is invoked automatically. One should make sure not to use a leading $\{$
for the first numerical parameter in such a loop. This would be interpreted
as belonging to a listed loop.
After a loop has been finished, the corresponding preprocessor variable
will be undefined. This means that if there is a previous preprocessor
variable by the same name, the value of the \#do instruction will be used
inside the loop, and afterwards the old value will be active again.
It is allowed to overwrite the value of a preprocessor \#do instruction
variable. This can be very useful to create the equivalent of a repeat loop
that contains .sort instructions as in
\begin{verbatim}
#do i = 1,1
id,once,x = y+2;
if ( count(x,1) > 0 ) redefine i "0";
.sort
#enddo
\end{verbatim}
A few remarks are necessary here. The redefine\index{redefine} statement
(see section \ref{substaredefine}) should be before the last
.sort\index{.sort} inside the loop, because the \#do instruction is part of
the preprocessor. Hence the value of i is considered before the module is
executed. This means that if the redefine would be after the .sort, two
things would go wrong: First the loop would be terminated before the
redefine would ever make a chance of being executed. Second the statement
would be compiled in the expectation that there is a variable i, but then
the loop would be terminated. Afterwards, when the statement is being
executed it would refer to a variable that does not exist any longer.
If one wants to make a loop over the externals of the brackets of an
expression only, one needs to do some work. Assume we have the expression F
and we want to loop over the brackets in x and y:
\begin{verbatim}
L FF = F;
Bracket x,y;
.sort
CF acc,acc2;
Skip F;
Collect acc,acc2;
id acc(x?) = 1;
id acc2(x?)= 1;
B x,y;
.sort
Skip F;
Collect acc;
id acc(x?) = 1;
.sort
#do i = FF
L G = F[`i'];
.
.
#enddo
\end{verbatim}
Notice that we have to do the collect\index{collect} trick twice because
the first time the bracket could be too long for one term. The second time
that restriction doesn't exist because besides the x and the y there are
only integer coefficients.
%--#] do :
%--#[ else :
\section{\#else}
\label{preelse}
\noindent Syntax:
\#else
\noindent See also if (\ref{preif}),
endif (\ref{preendif}),
elseif (\ref{preelseif}),
ifdef (\ref{preifdef}),
ifndef (\ref{preifndef})
\noindent This instruction\index{\#else} is used inside a
\#if\index{\#if}/\#endif\index{\#endif} construction. The code that follows
it until the \#endif instruction will be read if the condition of the \#if
instruction (and of none of the corresponding \#elseif\index{\#elseif}
instructions) is not true. If any of these conditions is true, this code is
skipped. The reading is stopped after the matching \#endif is encountered
and continued after this matching \#endif instruction.
%--#] else :
%--#[ elseif :
\section{\#elseif}
\label{preelseif}
\noindent Syntax:
\#elseif ( condition )
\noindent See also if (\ref{preif}),
endif (\ref{preendif}),
else (\ref{preelse})
\noindent The syntax\index{\#elseif} of the condition is identical to the
syntax for the condition in the \#if\index{\#if} instruction. The \#elseif
instruction can occur between an \#if and an \#endif\index{\#endif}
instruction, before a possible matching \#else\index{\#else} instruction.
The code after this condition till the next \#elseif instruction, or till a
\#else instruction or till a \#endif instruction, whatever comes first,
will be read if the condition in the \#elseif instruction is true and none
of the conditions in matching previous \#if or \#elseif instructions were
true. The reading is stopped after the matching \#elseif/\#else/\#endif is
encountered and continued after the matching \#endif instruction.
Example
\begin{verbatim}
#if ( `i' == 2 )
some code
#elseif ( `i' == 3 )
more code
#elseif ( `j' >= "x2y" )
more code
#else
more code
#endif
\end{verbatim}
%--#] elseif :
%--#[ enddo :
\section{\#enddo}
\label{preenddo}
\noindent Syntax:
\#enddo
\noindent See also do (\ref{predo})
\noindent Used to\index{\#enddo} terminate\index{terminate} a preprocessor
do\index{do loop} loop. See the \#do\index{\#do} instruction.
%--#] enddo :
%--#[ endif :
\section{\#endif}
\label{preendif}
\noindent Syntax:
\#endif
\noindent See also if (\ref{preif}),
else (\ref{preelse}),
elseif (\ref{preelseif}),
ifdef (\ref{preifdef}),
ifndef (\ref{preifndef})
\noindent Used to terminate\index{\#endif} a \#if\index{\#if},
\#ifdef\index{\#ifdef} or \#ifndef\index{\#ifndef} construction.
Reading will continue after it.
%--#] endif :
%--#[ endinside :
\section{\#endinside}
\label{preendinside}
\noindent Syntax:
\#endinside
\noindent See also \#inside (\ref{preinside})
\noindent Used to\index{\#endinside} terminate a \#inside construction in
the preprocessor. For more details, see the \#inside\index{\#inside}
instruction.
%--#] endinside :
%--#[ endprocedure :
\section{\#endprocedure}
\label{preendprocedure}
\noindent Syntax:
\#endprocedure
\noindent See also procedure (\ref{preprocedure}), call
(\ref{precall})
\noindent Each procedure\index{procedure} must be terminated by an
\#endprocedure\index{\#endprocedure} instruction. If the procedure resides
in its own file, the \#endprocedure will cause the closing of the file.
Hence any text that is in the file after the \#endprocedure instruction
will be ignored.
When control reaches the \#endprocedure instruction, all (local)
preprocessor variables\index{variables!preprocessor} that were defined
inside the procedure and all parameters of the call of the procedure will
become undefined.
%--#] endprocedure :
%--#[ endswitch :
\section{\#endswitch}
\label{preendswitch}
\noindent Syntax:
\#endswitch
\noindent See also switch (\ref{preswitch}),
case (\ref{precase}),
break (\ref{prebreak}),
default (\ref{predefault})
\noindent This instruction marks the end\index{\#endswitch} of a
\#switch\index{\#switch} construction. After none or one of the cases of
the \#switch construction has been included in the control flow, reading
will continue after the matching \#endswitch instruction. Each \#switch
needs a \#endswitch, unless a .end instruction is encountered first.
%--#] endswitch :
%--#[ exchange :
\section{\#exchange}
\label{preexchange}
\noindent Syntax:
\#exchange expr1,expr2
\#exchange \$var1,\$var2
\noindent Exchanges\index{\#exchange} the names of two
expressions\index{expression}. This means that the contents of the
expressions remain where they are. Hence the order in which the expressions
are processed remains the same, but the name under which one has to refer
to them has been changed.
In the variety with the dollar variables\index{\$-variable} the contents of
the variables are exchanged. This is not much work, because dollar
variables reside in memory and hence only two pointers to the contents have
to be exchanged (and some extra information about the contents).
This instruction can be very useful when sorting expressions or dollar
variables by their contents.
%--#] exchange :
%--#[ external :
\section{\#external}
\label{preexternal}
\noindent Syntax:
\#external ["prevar"] systemcommand
\noindent Starts the command\index{\#external} in the background,
connecting to its standard\index{standard output}\index{standard input}
input\index{input!standard} and output\index{output!standard}. By default,
the \#external command has no controlling terminal, the standard error stream
is redirected to \verb|/dev/null| and the command is run in a subshell in a
new session and in a new process group (see the preprocessor instruction
\verb|#setexternalattr|).
The optional parameter ``prevar'' is the name of a preprocessor variable
placed between double quotes. If it is present, the ``descriptor'' (small
positive integer number) of the external command is stored into this
variable and can be used for references to this external command (if there
is more than one external command running simultaneously).
The external command that is started last becomes the ``current'' (active)
external command. All further instructions
\#fromexternal\index{\#fromexternal} and \#toexternal\index{\#toexternal}
deal with the current external command.
%--#] external :
%--#[ factdollar :
\section{\#factdollar}
\label{prefactdollar}
\noindent Syntax:
\#factdollar \$-variable
\noindent See also the chapters on polynomials \ref{polynomials} and
\$-variables \ref{dollars}
\noindent The \#factdollar\index{\#factdollar} instruction causes the
factorization of the indicated \$-variable. After this instruction and
until the \$-variable is redefined there will be two versions of the
variable: one is the original unfactorized version and the other is a list
of factors. If the name of the variable is \$a the factors can be accessed
as $\$a[1],\cdots,\$a[n]$. The total number of factors is given by
$\$a[0]$. These factors can also be treated as preprocessor variables by
putting them between quotes as in `$\$a[2]$'.
%--#] factdollar :
%--#[ fromexternal :
\section{\#fromexternal}
\label{prefromexternal}
\noindent Syntax:
\#fromexternal[$+-$] ["[\$]varname" [maxlength]]
\noindent Appends\index{\#fromexternal} the output of the current external
command to the \FORM\ program. The semantics differ depending on the optional
arguments. After the external command sends the prompt\index{prompt}, \FORM\
will continue with a next line after the line containing the \#fromexternal
instruction. The prompt string is not appended. The optional $+$ or $-$ sign
after the name has influence on the listing of the content. The varieties
are:
\#fromexternal[$+-$]
\noindent The semantics is similar to the \#include\index{\#include}
instruction but folders are not supported.
\#fromexternal[$+-$] "[\$]varname"
\noindent is used to read the text from the running external command into
the preprocessor variable varname, or into the dollar variable \$varname if
the name of the variable starts with the dollar sign ``\$''.
\#fromexternal[$+-$] "[\$]varname" maxlength
\noindent is used to read the text from the running external command into
the preprocessor (or dollar) variable varname. Only the first maxlength
characters are stored.
%--#] fromexternal :
%--#[ if :
\section{\#if}
\label{preif}
\noindent Syntax:
\#if ( condition )
\noindent See also endif (\ref{preendif}),
else (\ref{preelse}),
elseif (\ref{preelseif}),
ifdef (\ref{preifdef}),
ifndef (\ref{preifndef})
\noindent The \#if\index{\#if} instruction should be accompanied by a
matching \#endif\index{\#endif} instruction. In addition there can be
between the \#if and the \#endif some \#elseif\index{\#elseif} instructions
and/or a single \#else\index{\#else} instruction. The condition is a
logical variable that is true if its value is not equal to zero, and false
if its value is zero. Hence it is allowed to use
\begin{verbatim}
#if `i'
statements
#endif
\end{verbatim}
provided that i has a value which can be interpreted as a number. If there
is just a string that cannot be seen as a logical\index{logical} condition
or a number it will be interpreted as false. The regular syntax of the
simple condition is
\begin{verbatim}
#if `i' == st2x
statements
#endif
\end{verbatim}
or
\begin{verbatim}
#if ( `i' == st2x )
statements
#endif
\end{verbatim}
in which the compare is a numerical compare if both strings can be seen as
numbers, while it will be a string compare if at least one of the two
cannot be seen as a numerical object. One can also use more complicated
conditions as in
\begin{verbatim}
#if ( ( `i' > 5 ) && ( `j' > `i' ) )
\end{verbatim}
These are referred to as composite conditions. The possible operators are
\begin{description}
\item[$>$] Greater than, either in numerical or in lexicographical sense.
\item[$<$] Less than, either in numerical or in lexicographical sense.
\item[$>=$] Greater than or equal to, either in numerical or in
lexicographical sense.
\item[$<=$] Less than or equal to, either in numerical or in
lexicographical sense.
\item[$==$ or $=$] Equal to.
\item[$!=$] Not equal to.
\item[$\&\&$] Logical and operator to combine conditions.
\item[$||$] Logical or operator to combine conditions.
\end{description}
If the condition evaluates to true, the lines after the \#if instruction
will be read until the first matching \#elseif instruction, or a \#else
instruction or a \#endif instruction, whatever comes first. After such an
instruction is encountered input reading stops and continues after the
matching \#endif instruction.
Like with the regular if-statement (see \ref{substaif}), there are some special
functions that allow the asking of questions about objects. These are
\leftvitem{3cm}{exists()}
\rightvitem{13cm}{The argument of exists\index{exists} is the name of an
expression or a \$-variable. This function then returns one if this object
exists, cq. has been defined. Otherwise it returns zero. }
\leftvitem{3cm}{isdefined()}
\rightvitem{13cm}{The argument of isdefined\index{isdefined} is the name of a
preprocessor variable. This function then returns one if this object
has been defined. Otherwise it returns zero. Technically \texttt{\#ifdef `VAR'}
and
\texttt{\#if ( isdefined(VAR) )} are the same. The isdefined function
allows for greater flexibility in composite conditions.}
\leftvitem{3cm}{isfactorized()}
\rightvitem{13cm}{The argument of isfactorized\index{isfactorized} is the
name of an expression or a \$-variable. This function then returns one if
the object has been factorized. Otherwise it returns zero. }
\leftvitem{3cm}{isnumerical()}
\rightvitem{13cm}{The argument of isnumerical\index{isnumerical} is the
name of an expression or a \$-variable. This function then returns one if
the object contains a single term that is purely numerical in nature.
Otherwise it returns zero. }
\leftvitem{3cm}{maxpowerof()}
\rightvitem{13cm}{The argument of maxpowerof\index{maxpowerof} is the name
of a symbol. This function then evaluates into the maximum power of that
symbol as it has been declared. If no maximum power has been set in the
declaration of the symbol, the general maximum power for symbols is
returned (see \ref{substasymbols}).}
\leftvitem{3cm}{minpowerof()}
\rightvitem{13cm}{The argument of minpowerof\index{minpowerof} is the name
of a symbol. This function then evaluates into the minimum power of that
symbol as it has been declared. If no minimum power has been set in the
declaration of the symbol, the general minimum power for symbols is
returned (see \ref{substasymbols}).}
\leftvitem{3cm}{sizeof()}
\rightvitem{13cm}{The argument of termsin\index{termsin} is the name of an
expression or a \$-variable. This function then evaluates into the number
of \FORM words in that expression or variable.}
\leftvitem{3cm}{termsin()}
\rightvitem{13cm}{The argument of termsin\index{termsin} is the name of an
expression or a \$-variable. This function then evaluates into the number
of terms in that expression.}
%--#] if :
%--#[ ifdef :
\section{\#ifdef}
\label{preifdef}
\noindent Syntax:
\#ifdef `prevar'
\noindent See also if (\ref{preif}),
endif (\ref{preendif}),
else (\ref{preelse}),
ifndef (\ref{preifndef})
\noindent If the named\index{\#ifdef} preprocessor variable has been
defined the condition is true, else it is false. For the rest the
instruction behaves like the \#if\index{\#if} instruction.
An alternative is to use the isdefined object inside the \#if instruction.
%--#] ifdef :
%--#[ ifndef :
\section{\#ifndef}
\label{preifndef}
\noindent Syntax:
\#ifndef `prevar'
\noindent See also if (\ref{preif}),
endif (\ref{preendif}),
else (\ref{preelse}),
ifdef (\ref{preifdef})
\noindent If the named\index{\#ifndef} preprocessor variable has been
defined the condition is false, else it is true. For the rest the
instruction behaves like the \#if\index{\#if} instruction.
%--#] ifndef :
%--#[ include :
\section{\#include}
\label{preinclude}
\noindent Syntax:
\#include[$-+$] filename
\#include[$-+$] filename \# foldname
\noindent The named\index{\#include} file is searched for and opened.
Reading\index{reading} continues from this file until its end. Then the
file will be closed and reading continues after the \#include instruction.
If a foldname\index{foldname} is specified, \FORM\ will only read the
contents of the first fold\index{fold} it encounters in the given file that
has the specified name.
The file is searched for in the current directory, then in the path
specified in the path\index{path} variable in the setup file or at the
beginning of the program (see chapter \ref{setup} on the setup file). Next
it will look in the path specified in the -p option when \FORM\ is started
(see the chapter on running \FORM). If this option has not been used, \FORM\
will look for the environment variable FORMPATH\index{FORMPATH}. If this
variable exists it will be interpreted as a path and \FORM\ will search the
indicated directories for the given file. If none is found there will be an
error message and execution will be halted.
The optional $+$ or $-$ sign after the name has influence on the listing of the
contents of the file. A $-$ sign will have the effect of a \#$-$ instruction
during the reading of the file. A plus sign will have the effect of a \#$+$
instruction during the reading of the file.
A fold is defined by a starting line of the format:
\begin{verbatim}
*--#[ name :
\end{verbatim}
and a closing line of the format
\begin{verbatim}
*--#] name :
\end{verbatim}
in which the first character is actually the current
commentary\index{commentary} character (see the \#commentchar instruction).
All lines between two such lines are considered to be the contents of the
fold. If \FORM\ decides that it needs this fold, it will read these contents
and put them in its input stream. More about folds is explained in the
manual of the STedi editor which is also provided in the \FORM\
distribution.
%--#] include :
%--#[ inside :
\section{\#inside}
\label{preinside}
\noindent Syntax:
\#inside \$var1 [more \$variables]
\noindent See also \#endinside (\ref{preendinside})
\noindent Used to\index{\#inside} execute a few statements on the contents
of one or more dollar variables (see \ref{dollars}) during compilation time.
Although this is a preprocessor instruction one can use the
triple dot operator provided one uses the generic version with the $<>$.
\noindent The statements in the scope of the \#inside / \#endinside
construction must be regular executable statements. They may not contain
end-of-module instructions like the .sort instruction. It is allowed to use
dollar variables, procedures and preprocessor do loops and if's, but it is
not allowed to nest the \#inside / \#endinside constructions.
%--#] inside :
%--#[ message :
\section{\#message}
\label{premessage}
\noindent Syntax:
\#message themessagestring
\noindent This instruction places a message\index{\#message} in the output
that is clearly marked as such. It is printed with an initial three
characters in front as in
\begin{verbatim}
Symbols a,b,c;
#message Simple example;
~~~Simple example;
Local F = (a+b+c)^10;
.end
Time = 0.00 sec Generated terms = 66
F Terms in output = 66
Bytes used = 1138
\end{verbatim}
Note that the semicolon\index{semicolon} is not needed and if present is
printed as well. If one needs messages without this clear marking, one
should use the \#write\index{\#write} instruction.
%--#] message :
%--#[ opendictionary :
\section{\#opendictionary}
\label{preopendictionary}
\noindent Syntax:
\#opendictionary name
\noindent See chapter \ref{dictionaries} on dictionaries.
\noindent Opens a dictionary and makes it ready for adding words to it. If
the dictionary does not exist yet, it will be created.
%--#] opendictionary :
%--#[ optimize :
\section{\#optimize}
\label{preoptimize}
\noindent Syntax:
\#optimize nameofoneexpression
See the chapter about optimization \ref{optimization}
%--#] optimize :
%--#[ pipe :
\section{\#pipe}
\label{prepipe}
\noindent Syntax:
\#pipe systemcommand
\noindent See also system (\ref{presystem})
\noindent This\index{\#pipe} forces a system command to be executed by the
operating system. The complete string (excluding initial blanks or tabs) is
passed to the operating system. Next \FORM\ will intercept the output of
whatever is produced and read that as input. Hence, whenever output is
produced \FORM\ will take action, and it will wait when no output is ready.
After the command has been finished, \FORM\ will continue with the next line.
This instruction has only been implemented on systems that support
pipes\index{pipe}. This is mainly UNIX\index{UNIX} and derived systems.
Note that this instruction also introduces operating system dependent code.
Hence it should be used with great care.
%--#] pipe :
%--#[ preout :
\section{\#preout}
\label{prepreout}
\noindent Syntax:
\#preout ON
\#preout OFF
\noindent Turns\index{\#preout} listing of the output of the preprocessor
to the compiler on or off. Example:
% THIS EXAMPLE IS PART OF THE TESTSUITE. CHANGES HERE SHOULD BE APPLIED THERE AS
% WELL!
\begin{verbatim}
#PreOut ON
S a1,...,a4;
S,a1,a2,a3,a4
L F = (a1+...+a4)^2;
L,F=(a1+a2+a3+a4)^2
id a4 = -a1;
id,a4=-a1
.end
Time = 0.00 sec Generated terms = 10
F Terms in output = 3
Bytes used = 52
\end{verbatim}
%--#] preout :
%--#[ prependpath :
\section{\#prependpath}
\label{preprependpath}
\noindent Syntax:
\#prependpath pathname
\noindent See also appendpath~(\ref{preappendpath})
\noindent Prepends the given path relative to the current file to the beginning
of the FORM path\index{path}.
%--#] prependpath :
%--#[ printtimes :
\section{\#printtimes}
\label{preprinttimes}
\noindent Syntax:
\#printtimes
\noindent Prints\index{\#printtimes} the current execution time and real
time in the same way as done at the end of the program. Helps in monitoring
the real time passed in TFORM jobs.
Example:
\begin{verbatim}
#Printtimes
423.59 sec + 5815.88 sec: 6239.47 sec out of 1215.29 sec
\end{verbatim}
%--#] printtimes :
%--#[ procedure :
\section{\#procedure}
\label{preprocedure}
\noindent Syntax:
\#procedure name(var1,...,varn)
\noindent See also endprocedure (\ref{preendprocedure}), call
(\ref{precall})
\noindent Name\index{\#procedure} is the name of the
procedure\index{procedure}. It will be referred to by this name. If the
procedure resides in a separate file the name of the file should be
name.prc and the \#procedure instruction should form the first line of the
file. The \# should be the first character of the file. The parameter field
is optional. If there are no parameters, the procedure should also be
called without parameters (see the \#call instruction). The parameters
(here called var1 to varn) are preprocessor variables and hence they should
be referred to between a backquote\index{backquote}/quote\index{quote} pair
as in `var1' to `varn'. If there exist already variables with such names
when the procedure is called, the new definition comes on top of the old
one. Hence in the procedure (and procedures called from it, unless the same
problems occurs there too, as would be the case with recursions) the new
definition is used, and it is released again when control returns from the
procedure. After that the old definition will be in effect again.
If the procedure is included in the regular input stream, \FORM\ will read
the text of the procedure until the \#endprocedure\index{\#endprocedure}
instruction and store it in a special buffer. When the procedure is called,
\FORM\ will read the procedure from this buffer, rather than from a file. In
systems where file transfer is slow (very busy server with a slow network)
this may be faster, especially when many small procedures are called.
One way to make libraries\index{library!making a}\index{library} that
contain many procedures and maybe more code is to put all procedures into
one header (.h) file and include this file at the beginning of the program
with a \#include\index{\#include} instruction. This way one has all
procedures load and one knows for sure that it are the proper procedures as
it guards against the inadvertently picking up of procedures from other
directories. It also makes for fewer files and hence makes for better
housekeeping.
%--#] procedure :
%--#[ procedureextension :
% NEW@@@
\section{\#procedureextension}
\label{preprocedureextension}
\noindent Syntax:
\#procedureextension string
\noindent See also \#call (\ref{precall})
\noindent The default\index{\#procedureextension} extension of procedures
is .prc\index{.prc} in \FORM. It is however possible that this clashes with
the extensions used by other programs like the Grace\index{Grace} system
(Yuasa et al, Prog. Theor. Phys. Suppl. 138(2000)18 ). In that case it is
possible to change the extension of the procedures in the current program.
This is either done via the setup (page \ref{setup}) or by the
\#procedureextension instruction of the preprocessor. The new string
replaces the string prc, used by default. For the new string the following
restrictions hold:
\begin{enumerate}
\item The first character must be alphabetic
\item No whitespace characters (blanks and/or tabs) are allowed
\end{enumerate}
For the rest any characters can be used.
\noindent The new extension will remain valid either till the next
\#procedureextension instruction or to the next .clear\index{.clear}
instruction (page \ref{instrclear}), whatever comes first.
%--#] procedureextension :
%--#[ prompt :
\section{\#prompt}
\label{preprompt}
\noindent Syntax:
\#prompt [newprompt]
\noindent Sets a new prompt\index{\#prompt} for the current external
command (if present) and all further (newly started) external commands.
If newprompt is an empty string, the default prompt (an empty line) will be
used.
The prompt\index{prompt} is a line consisting of a single prompt string. By
default, this is an empty string.
%--#] prompt :
%--#[ redefine :
\section{\#redefine}
\label{preredefine}
\noindent Syntax:
\#redefine name "string"
\noindent See also define (\ref{predefine}), undefine
(\ref{preundefine})
\noindent in which\index{\#redefine} name refers to the name of the
preprocessor\index{preprocessor variable}
variable\index{variable!preprocessor} to be redefined. The contents of the
string will be its new value. If no variable of the given name exists yet,
the instruction will be equivalent to the \#define\index{\#define}
instruction.
%--#] redefine :
%--#[ remove :
\section{\#remove}
\label{preremove}
\noindent Syntax:
\#remove $<$filename$>$
\noindent See also write (\ref{prewrite}), append (\ref{preappend}),
create (\ref{precreate}), close (\ref{preclose})
\noindent Deletes\index{\#remove} the named file from the system. Under
UNIX\index{UNIX} this would be equivalent to the instruction
\begin{verbatim}
#system rm filename
\end{verbatim}
and under MS-DOS\index{MS-DOS} oriented systems like Windows\index{Windows}
it would be equivalent to
\begin{verbatim}
#system del filename
\end{verbatim}
The difference with the \#system\index{\#system} instruction is that the
\#remove\index{\#remove} instruction does not depend on the particular
syntax of the operating system. Hence the \#remove instruction can always
be used.
%--#] remove :
%--#[ reset :
\section{\#reset}
\label{prereset}
\noindent Syntax:
\#reset [{\tt<}keyword{\tt>}]
\noindent See also `TIMER\_' preprocessor variable.
\noindent Currently the only keywords that are allowed are timer and
stopwatch. They have the same effect, which is to reset the timer for the
`timer\_' (or `stopwatch\_) preprocessor variable (see \ref{preprovariables}).
%--#] reset :
%--#[ reverseinclude :
\section{\#reverseinclude}
\label{prereverseinclude}
\noindent Syntax:
\#reverseinclude[$-+$] filename
\#reverseinclude[$-+$] filename \# foldname
\noindent This instruction is identical to the \#include \ref{preinclude}
instruction, with the exception that the statements and instructions in the
file are read in reverse order. This can be useful at times when code is
generated in a particular order in a file and one would like to 'undo' this
code. It is somewhat related to the effects of the debugflag option
(\ref{optimdebugflag}) in the optimization options of the format statement
\ref{optimization}.
There are a few limitations. If, for instance, linefeeds or semicolons
occur inside preprocessor variables, the reading routines cannot see this.
Additionally unfinished strings (unmatched double quotes) will result in
a fatal error. On the other hand the fold structure remains preserved.
%--#] reverseinclude :
%--#[ rmexternal :
\section{\#rmexternal}
\label{prermexternal}
\noindent Syntax:
\#rmexternal [n]
\noindent Terminates\index{\#rmexternal} an external command. The integer
number n must be either the descriptor of a running external command, or 0.
If n is 0, then all external programs will be terminated.
If n is not specified, the current external command will be terminated.
The action of this instruction depends on the attributes of the external
channel (see the \#setexternalattr\index{\#setexternalattr} (section
\ref{setexternalcommunication}) instruction). By default, the instruction
closes the commands' IO channels, sends a KILL\index{KILL signal} signal to
every process in its process group and waits for the external command to be
finished.
%--#] rmexternal :
%--#[ rmseparator :
\section{\#rmseparator}
\label{prermseparator}
\noindent Syntax:
\#rmseparator character
\noindent See also \#addseparator (\ref{preaddseparator}),
\#call (\ref{precall}), \#do (\ref{predo})
\noindent Removes a character\index{\#rmseparator} from the list of permissible
separator characters for arguments of \#call or \#do instructions. By
default the two characters that are permitted are the comma and the
character \verb:|:. Blanks, tabs and double quotes are ignored. Note that
the comma must be specified between double quotes as in
\begin{verbatim}
#rmseparator ","
\end{verbatim}
%--#] rmseparator :
%--#[ setexternal :
\section{\#setexternal}
\label{presetexternal}
\noindent Syntax:
\#setexternal n
\noindent Sets\index{\#setexternal} the ``current'' external command. The
instructions \#toexternal\index{\#toexternal} and
\#fromexternal\index{\#fromexternal} deal with the current external
command. The integer number n must be the descriptor of a running external
command.
%--#] setexternal :
%--#[ setexternalattr :
\section{\#setexternalattr}
\label{presetexternalattr}
\noindent Syntax:
\#setexternalattr list\_of\_attributes
\noindent sets\index{\#setexternalattr} attributes for {\em newly started}
external commands. Already running external commands are not affected. The
list of attributes is a comma separated list of pairs attribute=value,
e.g.:
\begin{verbatim}
#setexternalattr shell=noshell,kill=9,killall=false
\end{verbatim}
Possible attributes are:
\begin{description}
\item[kill\index{kill}]
specifies the signal to be sent to the external command
either before the termination of the \FORM\ program or by the preprocessor
instruction \verb|#rmexternal|. By default this is 9 (
SIGKILL\index{SIGKILL signal}). Number 0 means that no signal will be sent.
\item[killall\index{killall}] Indicates whether the kill signal will be sent to the whole
group or only to the initial process. Possible values are ``\verb|true|''
and ``\verb|false|''. By default, the kill signal will be sent to the
whole group.
\item[daemon\index{daemon}]
Indicates whether the command should be ``daemonized'', i.e.
the initial process will be passed to the init process and will belong
to the new process group in the new session.
Possible values are ``\verb|true|'' and ``\verb|false|''. By default,
``\verb|true|''.
\item[shell\index{shell}]
specifies which shell\index{shell} is used to run a
command. (Starting an external command in a subshell permits to
start not only executable files but also scripts\index{script} and
pipelined\index{pipelined job} jobs. The disadvantage is that there is no
way to detect failure upon startup since usually the shell is started
successfully.) By default this is ``\verb|/bin/sh -c|''. If set
\verb|shell=noshell|, the command will be stared by the instruction
\#external\index{\#external} directly but not in a subshell, so the command
should be a name of the executable file rather than a system command. The
instruction \#external will duplicate the actions of the shell in searching
for an executable file if the specified file name does not contain a slash
(/) character. The search path is the path specified in the environment by
the PATH\index{PATH} variable. If this variable isn't specified, the
default path ``\verb|:/bin:/usr/bin|''
is used.
\item[stderr\index{stderr}]
specifies a file to redirect the standard\index{standard error} error
stream to. By default it is ``\verb|/dev/null|''. If set
\verb|stderr=terminal|, no redirection occurs.
\end{description}
Only attributes that are explicitly mentioned are changed, all others remain
unchanged. Note, changing attributes should be done with care. For example,
\begin{verbatim}
#setexternalattr daemon=false
\end{verbatim}
starts a command in the subshell within the current process group with
default attributes kill=9 and killall=true.
The instruction \#rmexternal\index{\#rmexternal} sends the
KILL\index{KILL signal} signal to the wholegroup, which means that also
\FORM\ itself will be killed.
%--#] setexternalattr :
%--#[ setrandom :
\section{\#setrandom}
\label{presetrandom}
\noindent Syntax:
\#setrandom number
\noindent See also random\_ (\ref{funrandom}) and ranperm\_ (\ref{funranperm})
\noindent The \#setrandom\index{\#setrandom} instruction initializes the
random number generator
random\_~\ref{funrandom}\index{random\_}\index{function!random\_}. The
number that is used as a seed can have the length of two words in FORM.
This means that on a 32-bits computer it can be an (unsigned) 32-bits
integer and on a 64-bits computer it can be an (unsigned) 64 bits integer.
If there is no \#setrandom instruction the random number generator is
initialized in a built in standard way. The \#setrandom instruction also
initializes the random number generators of the workers when one uses TFORM
or ParFORM. They are initialized with different seeds that are derived in a
non-trivial way from the seed given by the user and the number of the
worker.
%--#] setrandom :
%--#[ show :
\section{\#show}
\label{preshow}
\noindent Syntax:
\#show [preprocessorvariablename[s]]
\noindent If no names\index{\#show} are present, the contents of all
preprocessor variables\index{variable!preprocessor} will be printed to the
regular output. If one or more preprocessor variables are specified
(separated by comma's), only their contents will be printed. The
preprocessor variables should be represented by their name only. No
enclosing backquote/quote should be used, because that would force a
substitution of the preprocessor variable before the instruction gets to
see the name. Example:
\begin{verbatim}
#define MAX "3"
Symbols a1,...,a`MAX';
L F = (a1+...+a`MAX')^2;
#show
#The preprocessor variables:
0: VERSION_ = "3"
1: SUBVERSION_ = "2"
2: NAMEVERSION_ = ""
3: DATE_ = "Wed Feb 28 08:43:20 2007"
4: NAME_ = "testpre.frm"
5: CMODULE_ = "1"
6: MAX = "3"
.end
Time = 0.00 sec Generated terms = 6
F Terms in output = 6
Bytes used = 102
\end{verbatim}
We see that the variable MAX has indeed the value 3. There are six
additional variables which have been defined by \FORM\ itself. Hence the
trailing underscore which cannot be used in user defined names. The current
version of \FORM\ is shown in the variable VERSION\_\index{VERSION\_} and the
name of the current program is given in the variable NAME\_\index{NAME\_}.
For more about the system defined preprocessor variables see
\ref{preprovariables}.
There is another preprocessor variable that does not show in the listings.
Its name is SHOWINPUT\_\index{SHOWINPUT\_}. This variable has the value one
if the listing of the input is on and the value zero if the listing of the
input is off.
%--#] show :
%--#[ skipextrasymbols :
\section{\#skipextrasymbols}
\label{preskipextrasymbols}
\noindent Syntax:
\#skipextrasymbols positivenumber
\noindent See also ExtraSymbols~(\ref{substaextrasymbols}) and the chapter
on optimization~(\ref{optimization}).
\noindent This instructions adds a number of dummy extra
symbols\index{extra symbols} to the list of extra
symbols~(\ref{substaextrasymbols}). This can be used when several
optimizations are done on an expression in such a way that the extra
symbols of previous optimizations are still present. Normally the number
space for them is erased in a \#clearoptimize instruction. This can be
avoided with a sequence like
\begin{verbatim}
#skipextrasymbols,{`optimmaxvar_'-`optimminvar_'+1}
\end{verbatim}
In this case the numbering of the next optimization will start after the
last extra symbol of the previous optimization.
One should realize however that the definitions of the extra symbols are
not kept once the new optimization is started or once a \#clearoptimize
instruction is issued. Example:
\begin{verbatim}
#-
S a,b,c,d,e;
L F = (a+b+c+d+3*e)^3;
B b;
.sort
ExtraSymbols,array,w;
Format O3,stats=ON;
#optimize F
#write <> " %4O"
.sort
#SkipExtraSymbols,{`optimmaxvar_'-`optimminvar_'+1}
id b = b+1;
Print +f;
B b;
.end
\end{verbatim}
Because the O3 format is still active, the final printing uses the
optimization as well. If the \#SkipExtraSymbols instruction would have been
omitted, the numbering would start again from one, while the rhs. of their
definitions would contain the old extra symbols. The result would be
incorrect.
%--#] skipextrasymbols :
%--#[ switch :
\section{\#switch}
\label{preswitch}
\noindent Syntax:
\#switch string
\noindent See also endswitch (\ref{preendswitch}),
case (\ref{precase}),
break (\ref{prebreak}),
default (\ref{predefault})
\noindent the\index{\#switch} string could for instance be a preprocessor
variable as in
\begin{verbatim}
#switch `i'
\end{verbatim}
The \#switch\index{\#switch} instruction, together with
\#case\index{\#case}, \#break\index{\#break}, \#default\index{\#default}
and \#endswitch\index{\#endswitch}, allows the user to conveniently make
code for a number of cases that are distinguished by the value of a
preprocessor variable. In the past this was only possible with the use of
folds\index{folds} in the \#include\index{\#include} instruction and the
corresponding include file\index{file!include} (see \ref{preinclude}).
Because few people have an editor like STedi (see the \FORM\ distribution
site) that can handle the folds in a proper way, it was judged that the
more common switch mechanism might be friendlier. The proper syntax of a
complete construction would be
\begin{verbatim}
#switch `par'
#case 1
some statements
#break
#case ax2
other statements
#break
#default
more statements
#break
#endswitch
\end{verbatim}
The number of cases is not limited. The compare between the strings in the
\#switch instruction and in the \#case instructions is as a text string.
Hence numerical strings have no special meaning. If a \#break instruction
is omitted, control may go into another case. This is called
fall-through\index{fall-through}.
This is a way in which one can have the same statements for several cases.
The \#default instruction is not mandatory.
\FORM\ will look for the first case of which the string matches the string
in the \#switch instruction. Input reading (control flow) starts after this
\#case instruction, and continues till either a \#break instruction is
encountered, or the \#endswitch is met. After that input reading continues
after the \#endswitch instruction. If no case has a matching string, input
reading starts after the \#default instruction. If no \#default instruction
is found, input reading continues after the matching \#endswitch
instruction.
\#switch constructions can be nested\index{nested}. They can be combined
with \#if\index{\#if} constructions, \#do\index{\#do} instructions, etc.
but they should obey normal nesting rules (as with nesting of
brackets\index{bracket} of different types).
%--#] switch :
%--#[ system :
\section{\#system}
\label{presystem}
\noindent Syntax:
\#system systemcommand
\noindent See also pipe (\ref{prepipe})
\noindent This forces a system\index{\#system} command to be executed by
the operating system. The complete string (excluding initial blanks or
tabs) is passed to the operating system. \FORM\ will then wait until control
is returned. Note that this instruction introduces operating system
dependent code. Hence it should be used with great care.
%--#] system :
%--#[ terminate :
\section{\#terminate}
\label{preterminate}
\noindent Syntax:
\#terminate [exitcode]
\noindent This forces \FORM\ to terminate\index{\#terminate} execution
immediately. If an exit code is given (an integer number), this will be the
return value that \FORM\ gives to the shell program from which it was run. If
no return value is specified, the value -1 will be returned.
%--#] terminate :
%--#[ timeoutafter :
\section{\#timeoutafter}
\label{pretimeoutafter}
\noindent Syntax:
\#timeoutafter $<$Number of seconds$>$
\noindent This instruction starts a timer. When the given time expires the
current program will be terminated, unless the timer is reset before this
time. Resetting the timer is dome with the "\#timeoutafter 0" instruction.
The purpose of this instruction is to prevent runaway programs, because a
given subpart takes much more time than it should. Example:
\begin{verbatim}
.sort
#timeoutafter 1000
#call problematicprocedure
.sort
#timeoutafter 0
\end{verbatim}
If one runs many diagrams with a make-like facility like minos, diagrams
that behave in an unexpected way can be killed this way and minos can
continue with the next diagram. Later one can see which diagrams caused
problems and one may study what the problem was.
%--#] timeoutafter :
%--#[ toexternal :
\section{\#toexternal}
\label{pretoexternal}
\noindent Syntax:
\#toexternal "formatstring" $<$,variables$>$
\noindent Sends\index{\#toexternal} the output to the current external
command. The semantics of the \verb|"formatstring"| and the
\verb|[,variables]| is the same as for the \#write\index{\#write}
instruction, except for the trailing end-of-line symbol. In contrast to the
\#write instruction, the \#toexternal instruction does not append any new
line symbol to the end of its output.
%--#] toexternal :
%--#[ undefine :
\section{\#undefine}
\label{preundefine}
\noindent Syntax:
\#undefine name
\noindent See also define (\ref{predefine}), redefine
(\ref{preredefine})
\noindent \index{\#undefine} Name refers to the name of the
preprocessor variable\index{variable!preprocessor} to be undefined. This
statement causes the given preprocessor variable to be removed from the
stack of preprocessor variables. If an earlier instance of this variable
existed (other variable with the same name), it will become active again.
There are various other ways by which preprocessor variables can become
undefined. All variables belonging to a procedure are undefined at the end
of a procedure, and so are all other preprocessor variables that were
defined inside this procedure. The same holds for the preprocessor variable
that is used as a loop parameter in the \#do\index{\#do} instruction.
%--#] undefine :
%--#[ usedictionary :
\section{\#usedictionary}
\label{preusedictionary}
\noindent Syntax:
\#usedictionary name
\#usedictionary name (options)
\noindent See chapter \ref{dictionaries} on dictionaries.
\noindent Starts using a dictionary for output translation.
%--#] usedictionary :
%--#[ write :
\section{\#write}
\label{prewrite}
\noindent Syntax:
\#write [$<$filename$>$] "formatstring" [,variables]
\noindent See also append (\ref{preappend}),
create (\ref{precreate}), remove (\ref{preremove}),
close (\ref{preclose})
\noindent If there\index{\#write} is no file specified, the output will be
to the regular output\index{output channel} channel. If a file is
specified, \FORM\ will look whether this file is open already. If it is open
already, the specified output will be added to the file. If it is not open
yet it will be opened. Any previous contents will be lost. This would be
equivalent to using the \#create\index{\#create} instruction first. If
output has to be added to an existing file, the \#append\index{\#append}
instruction should be used first.
The format\index{format string} string is like a format string in the
language C\index{C}. This means
that it is placed between double quotes. It will contain text that will be
printed, and it will contain special character sequences for special
actions. These sequences and the corresponding actions are:
\begin{description}
\item[$\backslash$n] A newline\index{newline} character.
\item[$\backslash$t] A tab\index{tab} character.
\item[$\backslash$"] A double\index{double quote} quote character.
\item[$\backslash$b] A backslash\index{backslash} character.
\item[\%\%] The character \%\index{\%}.
\item[\%] If the last character in the string, it causes the omission of a
linefeed\index{linefeed} at the end of the printing. Note that if this
happens in the regular output (as opposed to a file) there may be
interference with the listing of the input.
\item[\%\$] A dollar variable\index{\$-variable}. The variable should be
indicated in the list of variables. Each occurrence of \%\$ will look for
the next variable.
\item[\%e] An active expression\index{expression}. The expression should be
indicated in the list of variables. Each occurrence of \%e will look for
the next variable. Unlike the output caused by the print statement the
expression will be printed without its name and there will also be no
\verb:=: sign unless there is one in the format string of course. If the
current output format is fortran\index{fortran} output there is an extra option. After the
name of the expression one should put between parentheses the name to be
used when there are too many continuation cards.
\item[\%+e] Like \%e, but like the +s option in the Print
statement\ref{substaprint} where each term starts on a new line.
\item[\%E] Like \%e, but whereas the \%e terminates the expression with a
;, the \%E does not give this trailing semicolon\index{semicolon}.
\item[\%+E] Like \%E, but like the +s option in the Print
statement\ref{substaprint} where each term starts on a new line.
\item[\%s] A string\index{string}. The string should be
given in the list of variables and be enclosed between double quotes. Each
occurrence of \%s will look for the next variable in the list.
\item[\%f] A file\index{file}. The name of the file will be expected in the
list of variables. The file is searched for in the current directory, then
in path indicated by the path variable in the setup file or at the
beginning of the file (see chapter \ref{setup} on the setup file), then in
the path specified in the -p option when \FORM\ is started (see the chapter
on running \FORM). If this option has not been used, \FORM\ will look for the
environment variable FORMPATH\index{FORMPATH}. If this variable exists it
will be interpreted as a path and \FORM\ will search the indicated
directories for the given file. If none is found there will be an error
message and execution will be halted.
\item[\%X] Forces the printing of the list of extra symbols
(\ref{sect-extrasymbols}) and their definitions\index{extrasymbols}.
\item[\%O] Forces the printing of the definitions of the extra symbols in
the buffer with the temporary variables from the previous optimization (see
the chapter on optimizations \ref{optimization}).
\end{description}
If no special variables are asked for (by means of \%\$, \%e, \%E or \%s)
the list of variables will be ignored (if present). Example:
% THIS EXAMPLE IS PART OF THE TESTSUITE. CHANGES HERE SHOULD BE APPLIED THERE AS
% WELL!
\begin{verbatim}
Symbols a,b;
L F = a+b;
#$a1 = a+b;
#$a2 = (a+b)^2;
#$a3 = $a1^3;
#write " One power: %$\n Two powers: %$\n Three powers: %$\n%s"\
,$a1,$a2,$a3," The end"
One power: b+a
Two powers: b^2+2*a*b+a^2
Three powers: b^3+3*a*b^2+3*a^2*b+a^3
The end
.end
Time = 0.00 sec Generated terms = 2
F Terms in output = 2
Bytes used = 32
\end{verbatim}
We see that the writing occurs immediately after the \#write\index{\#write}
instruction, because it is done by the preprocessor. Hence the output comes
before the execution of the expression F.
% THIS EXAMPLE IS PART OF THE TESTSUITE. CHANGES HERE SHOULD BE APPLIED THERE AS
% WELL!
\begin{verbatim}
S x1,...,x10;
L MyExpression = (x1+...+x10)^4;
.sort
Format Fortran;
#write " FUNCTION fun(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10)"
#write " REAL x1,x2,x3,x4,x5,x6,x7,x8,x9,x10"
#write " fun = %e",MyExpression(fun)
#write " RETURN"
#write " END"
.end
\end{verbatim}
Some remarks are necessary here. Because the \#write is a preprocessor
instruction, the .sort\index{.sort} is essential. Without it, the
expression has not been worked out at the moment we want to write. The name
of the expression is too long for fortran\index{fortran}, and hence the
output file will use a different name (in this case the name `fun' was
selected). The output file looks like
% THIS EXAMPLE IS PART OF THE TESTSUITE. CHANGES HERE SHOULD BE APPLIED THERE AS
% WELL!
\begin{verbatim}
FUNCTION fun(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10)
REAL x1,x2,x3,x4,x5,x6,x7,x8,x9,x10
fun = 24*x1*x2*x3*x4 + 24*x1*x2*x3*x5 + 24*x1*x2*x3*x6 + 24*x1*x2
& *x3*x7 + 24*x1*x2*x3*x8 + 24*x1*x2*x3*x9 + 24*x1*x2*x3*x10 + 12*
.....
& x8 + 4*x6**3*x9 + 4*x6**3*x10 + x6**4 + 24*x7*x8*x9*x10 + 12*x7*
& x8*x9**2
fun = fun + 12*x7*x8*x10**2 + 12*x7*x8**2*x9 + 12*x7*x8**2*x10 +
& 4*x7*x8**3 + 12*x7*x9*x10**2 + 12*x7*x9**2*x10 + 4*x7*x9**3 + 4*
& x7*x10**3 + 12*x7**2*x8*x9 + 12*x7**2*x8*x10 + 6*x7**2*x8**2 +
& 12*x7**2*x9*x10 + 6*x7**2*x9**2 + 6*x7**2*x10**2 + 4*x7**3*x8 +
& 4*x7**3*x9 + 4*x7**3*x10 + x7**4 + 12*x8*x9*x10**2 + 12*x8*x9**2
& *x10 + 4*x8*x9**3 + 4*x8*x10**3 + 12*x8**2*x9*x10 + 6*x8**2*
& x9**2 + 6*x8**2*x10**2 + 4*x8**3*x9 + 4*x8**3*x10 + x8**4 + 4*x9
& *x10**3 + 6*x9**2*x10**2 + 4*x9**3*x10 + x9**4 + x10**4
RETURN
END
\end{verbatim}
and each time after 19 continuation lines we have to break the expression
and use the \verb:fun = fun +: trick to continue.
%--#] write :
%--#[ Some remarks :
\section{Some remarks}
It should be noted that the various constructions like
\#do\index{\#do}/\#enddo\index{\#enddo},
\#procedure\index{\#procedure}/\#endprocedure\index{\#endprocedure},
\#switch\index{\#switch}/\#endswitch\index{\#endswitch} and
\#if\index{\#if}/\#endif\index{\#endif} all
create a certain environment. These environments cannot be interweaved. This
means that one cannot make code of the type
\begin{verbatim}
#do i = 1,5
#if ( `MAX' > `i' )
id f(`i') = g`i'(x);
#enddo
some statements
#do i = 1,5
#endif
#enddo
\end{verbatim}
whether this could be considered useful or not. Similarly one cannot make a
construction that might be very useful:
\begin{verbatim}
#do i = 1,5
#do j`i' = 1,3
#enddo
some statements
#do i = 1,5
#enddo
#enddo
\end{verbatim}
Currently the syntax does not allow this. This may change in the future.
%--#] Some remarks :
form-master/doc/manual/setup.tex 0000664 0000000 0000000 00000073266 13565763364 0017256 0 ustar 00root root 0000000 0000000
\chapter{The setup}
\label{setup}
When \FORM\ is started, it has a number of settings\index{setup} built in
that were determined during its installation\index{installation}. If the
user would like to alter these settings, it is possible to either specify
their desired values in a setup file\index{file!setup} or to do so at the
beginning of the program file\index{file!program}. There are two ways in
which \FORM\ can find a setup file. The first way is by having a file named
`form.set'\index{form.set} in the current directory. If such a file is
present, \FORM\ will open it and interpret its contents as setup parameters.
If this file is not present, one may specify a setup file with the -s
option in the command tail. This option must precede the name of the input
file. After the -s follow one or more blanks or tabs and then the full name
of the setup file. \FORM\ will try to read startup parameters from this file.
If a file `form.set' is present, \FORM\ will ignore the -s option and its
corresponding file name. This order of interpretation allows the user to
define an alias with a standard setup file which can be overruled by a
local setup file. If, in the beginning of the program file, before any
other statements with the exception of the \#- instruction and commentary
statements, there are lines that start with \#: the remaining contents of
these lines are interpreted exactly like the lines in the setup file. The
specifications in the program file take precedence\index{precedence} over
all other specifications. If neither of the above methods is used, \FORM\
will use a built in set of parameters. Their values may depend on the
installation and are given below.
The following is a list of parameters that can be set. The syntax is rather
simple: The full word must be specified (case insensitive), followed by one
or more blanks or tabs and the desired number, string or character.
Anything after this is considered to be commentary. In the setup file lines
that do not start with an alphabetic character are seen as commentary. The
sizes of the buffers are given in bytes, unless mentioned otherwise. A word
is 2 bytes for 32\index{32 bits} bit machines and 4 bytes for 64\index{64
bits} bit machines.
In \FORM\ version 3.3 and later, it is also allowed to define
preprocessor variables\index{preprocessor variables} (see also
\ref{preprovariables}) in the setup file. In addition one can use
preprocessor variables in the setup, provided it is not in the name of the
parameter/keyword.
\leftvitem{4.0cm}{bracketindexsize\index{setup!bracketindexsize}\index{bracketindexsize}}
\rightvitem{12.6cm}{Maximum size in bytes of any individual index of a
bracketted expression. Each expression will have its own index. The index
starts with a relatively small size and will grow if needed. But it will
never grow beyond the specified size. If more space is needed, \FORM\ will
start skipping brackets and find those back later by linear search. See
also chapter~\ref{brackets} and section~\ref{substabracket}.}
\leftvitem{4.0cm}{CommentChar\index{setup!commentchar}\index{commentchar}}
\rightvitem{12.6cm}{This should be followed by one or more blanks and a
single non-blank character. This character will be used to indicate
commentary, instead of the regular $*$ in column 1.}
\leftvitem{4.0cm}{CompressSize\index{setup!compresssize}\index{compresssize}}
\rightvitem{12.6cm}{When compressing output terms, \FORM\ needs a compression
buffer. This buffer deals recursively with compression and decompression of
terms that are either written or read. Its size will be at least
MaxTermSize but when there is heavy use of expressions in the right hand
side of definitions or substitution it would have to be considerably
longer. It is hoped that in the future this parameter can be eliminated.
CompressSize should be given in bytes.}
\leftvitem{4.0cm}{ConstIndex\index{setup!constindex}\index{constindex}}
\rightvitem{12.6cm}{This is the number of indices that are considered to be
constant indices like in fixed vector components (the so-called fixed
indices). The size of this parameter is not coupled to any array space, but
it should not go much beyond 1000 on a 32\index{32 bits} bit machine. On a
64\index{64 bits} bit machine it can go considerably further.}
\leftvitem{4.0cm}{ContinuationLines\index{setup!continuationlines}\index{continuationlines}}
\rightvitem{12.6cm}{The number of continuation lines that the local Fortran
compiler will allow. This limits the number of continuation lines, when the
output option `Format Fortran' (see \ref{substaformat}) is selected.}
\leftvitem{4.0cm}{Define\index{setup!define}\index{define}}
\rightvitem{12.6cm}{The syntax is as in the \#define instruction in the
preprocessor (see \ref{preprovariables}), with the remark that in the setup
file there should be no leading \# character as that would make the line
into commentary. Example: \hfill \\
{\tt\ \ \ \ define MODULUS "31991"} \hfill \\
which could be used
at a later point in the program to activate a modulus statement (see
\ref{substamodulus}).}
\leftvitem{4.0cm}{DotChar\index{setup!dotchar}\index{dotchar}}
\rightvitem{12.6cm}{There should be a single character following this name
(and the blank(s) after it). This character will be used instead of the \_,
when dotproducts\index{dotproducts} are printed in Fortran\index{Fortran}
output. This option is needed because some Fortran compilers do not
recognize the underscore as a valid character. In the olden days one could
use here the dollar character but nowadays many Fortran compilers do not
recognize this character as belonging to a variable name.}
\leftvitem{4.0cm}{FunctionLevels\index{setup!functionlevels}\index{functionlevels}}
\rightvitem{12.6cm}{The maximum number of levels that may occur, when
functions have functions in their arguments.}
\leftvitem{4.0cm}{HideSize\index{setup!hidesize}\index{hidesize}}
\rightvitem{12.6cm}{The size of the hide buffer. The size of this buffer is
normally set equal to scratchsize (see below). If one uses the setting of
HideSize after the setting of ScratchSize, one can give the hide buffer its
own size. There are cases that this can make the program faster.}
\leftvitem{4.0cm}{IncDir\index{setup!incdir}\index{incdir}}
\rightvitem{12.6cm}{Directory (or path of directories) in which \FORM\ will
look for files if they are not to be found in the current directory. This
involves files for the \#include\index{\#include} and \#call\index{\#call}
instructions. This variable takes precedence over the
Path\index{setup!path}\index{path} variable.}
%\leftvitem{4.0cm}{IndentSpace\index{setup!indentspace}\index{indentspace}}
%\rightvitem{12.6cm}{}
\leftvitem{4.0cm}{InsideFirst\index{setup!insidefirst}\index{insidefirst}}
\rightvitem{12.6cm}{Not having any effect at the moment.}
\leftvitem{4.0cm}{JumpRatio\index{setup!jumpratio}\index{jumpratio}}
\rightvitem{12.6cm}{See the endswitch (\ref{substaendswitch}) statement.}
\leftvitem{4.0cm}{MaxNumberSize\index{setup!maxnumbersize}\index{maxnumbersize}}
\rightvitem{12.6cm}{Allows the setting of the maximum size of the numbers
in \FORM. The number should be given in words. For 32\index{32 bits} bit
systems a word is two bytes and for 64\index{64 bits} bit systems a word is
4 bytes. The number size is always limited by the maximum size of the terms
(see MaxTermSize). Actually it has to be less than half of MaxTermSize
because a coefficient contains both a numerator and a denominator. It is
not always a good idea to have the number size at its maximum value,
especially when MaxTermSize is large. In that case it could be very long
before a runaway algorithm runs into limitations of size (arithmetic for
very long fractions is not very fast due to the continuous need for
computing GCD's)}
\leftvitem{4.0cm}{MaxTermSize\index{setup!maxtermsize}\index{maxtermsize}}
\rightvitem{12.6cm}{This\label{setupmaxtermsize}
is the maximum size that an individual term may occupy in words. This
size does not affect any allocations. One should realize however that the
larger this size is the heavier the demand can be on the workspace, because
the workspace acts as a heap during the execution and sometimes allocations
have to be made in advance, before \FORM\ knows what the actual size of the
term will be. Consequently the evaluation tree cannot be very deep, when
WorkSpace / MaxTermSize is not very big. MaxTermSize controls mainly how
soon \FORM\ starts complaining about terms that are too complicated. Its
absolute maximum is 32568 on 32\index{32 bits} bit systems and about $10^9$
on 64\index{64 bits} bit systems (of course the workspace would have to be
considerably larger than that....).}
\leftvitem{4.0cm}{MaxWildCards\index{setup!maxwildcards}\index{maxwildcards}}
\rightvitem{12.6cm}{The maximum number of wildcards that
can be active in a single matching of a pattern. Under normal circumstance
the default value of 100 should be more than enough.}
\leftvitem{4.0cm}{NoSpacesInNumbers\index{setup!nospacesinnumbers}\index{nospacesinnumbers}}
\rightvitem{12.6cm}{Long\label{nospacesinnumbers} numbers are usually spread over several lines
by placing a backspace character at the end of each line and then
continuing at the next line. For cosmetic purposes \FORM\ puts usually a few
blank spaces at the beginning of the new line. \FORM\ itself can read this but
some programs cannot. Hence one can put \FORM\ in a mode in which these
blanks are omitted. The values of the variable are ON or OFF. There is also
a command to change this behaviour at runtime. See the on and off commands
in sections \ref{staonnospacesinnumbers} and \ref{staoffnospacesinnumbers}.}
\leftvitem{4.0cm}{NumStoreCaches\index{setup!numstorecaches}\index{numstorecaches}}
\rightvitem{12.6cm}{This number determines how many store caches (see
the description of the SizeStoreCache setup parameter below) there will
be. In the case of parallel processing this will be the number of caches
per processor.}
\leftvitem{4.0cm}{NwriteStatistics\index{setup!nwritestatistics}\index{nwritestatistics}}
\rightvitem{12.6cm}{When this word is mentioned, the default setting for the
statistics is that no run time statistics will be shown. Ordinarily they
will be shown.}
\leftvitem{4.0cm}{NwriteThreadStatistics\index{setup!nwritethreadstatistics}
\index{nwritethreadstatistics}}
\rightvitem{12.6cm}{\vspace{1ex}This variable has the values ON or OFF. It controls for
\TFORM{} whether the statistics of the individual threads will be printed. The
default value is ON.}
\leftvitem{4.0cm}{OldOrder\index{setup!oldorder}\index{oldorder}}
\rightvitem{12.6cm}{A special flag (values ON/OFF) by which one can still
select the old option of not checking for the order of statements inside a
module. This should be used only in the case that it is nearly impossible
to change a program to the new mode in which the order of the statements
(declarations etc) is relevant. In the future this old mode may not exist.}
\leftvitem{4.0cm}{Parentheses\index{setup!parentheses}\index{parentheses}}
\rightvitem{12.6cm}{The maximum number of nestings of parentheses or
functions inside functions. The variable may be eliminated in a later
version.}
\leftvitem{4.0cm}{Path\index{setup!path}\index{path}}
\rightvitem{12.6cm}{Directory (or path of directories) in which \FORM\ will
look for files if they are not to be found in the current directory. This
involves files for the \#include\index{\#include} and \#call\index{\#call}
instructions. \FORM\ will test this path after a potential path specified as
IncDir\index{setup!incdir}\index{incdir}.}
%\leftvitem{4.0cm}{PolyGCDchoice\index{setup!polygcdchoice}\index{polygcdchoice}}
%\rightvitem{12.6cm}{}
\leftvitem{4.0cm}{ProcedureExtension\index{setup!procedureEetension}\index{procedureextension}}
\rightvitem{12.6cm}{The extension that will be used by \FORM\ for finding the
procedures that are in separate files. Restrictions on the strings used are
as explained in the preprocessor
\#procedureextension\index{\#procedureextension} instruction in section
\ref{preprocedureextension}.}
\leftvitem{4.0cm}{ProcessBucketSize\index{setup!processbucketsize}\index{processbucketsize}}
\rightvitem{12.6cm}{\label{setupprocessbucketsize} For the parallel version
\ParFORM. It is ignored in other versions. Tells \ParFORM\ how many terms
there should be in the buckets that are being distributed over the
secondary processors. See also \ref{substaprocessbucketsize}.}
\leftvitem{4.0cm}{ResetTimeOnClear\index{setup!resettimeonclear}\index{resettimeonclear}}
\rightvitem{12.6cm}{The value is ON or OFF. The default value is ON. This
means that by default the clock is reset after each .clear\index{.clear}
(see chapter \ref{modules} on modules) instruction at the end of a module.}
\leftvitem{4.0cm}{ScratchSize\index{setup!scratchsize}\index{scratchsize}}
\rightvitem{12.6cm}{The size of the input and the output buffers for the
regular algebra processing. Terms are read in in chunks this size and are
written to the output file using buffers of this size. There are either two
or three of these buffers, depending on whether the hide\index{hide}
facility is being used (see \ref{substahide}). These buffers must have a
size that is at least as large as the MaxTermSize\index{maxtermsize}. These
buffers act as caches for the files with the extension .sc1\index{.sc1},
.sc2\index{.sc2} and .sc3\index{.sc3}. See also the HideSize parameter
above for the independent setting of the size of the hide buffer.}
\leftvitem{4.0cm}{SizeStoreCache\index{setup!sizestorecache}\index{sizestorecache}}
\rightvitem{12.6cm}{The size of the caches\index{caches} that are used for
reading terms when stored expressions are used in the r.h.s.\ of a
statement. Typically there are several such caches and they make the
reading much faster. In the case of parallel processing these caches become
very important because without them the different processes may all want to
read from the .str\index{.str} file\index{file!store} at the same time and
execution speed will suffer badly. The number of store caches is determined
by the NumStoreCaches\index{numstorecaches} setup parameter which is
described above. The size of these caches doesn't have to be very large as
compared to some of the other buffers. It is recommended though to have
them at least as large as MaxTermSize\index{maxtermsize} (see above).}
\leftvitem{4.0cm}{SortType\index{setup!sorttype}\index{sorttype}}
\rightvitem{12.6cm}{Possible values are "lowfirst"\index{lowfirst},
"highfirst"\index{highfirst} and "powerfirst"\index{powerfirst}. "lowfirst"
is the default. Determines the order in which the terms are placed during
sorting. In the case of lowfirst, lower powers of symbols and dotproducts
come before higher powers. In the case of highfirst it is the opposite. In
the case of powerfirst the combined powers of all symbols together are
considered and the highest combined powers come first. See also the
on\index{on} statement in \ref{substaon}.}
\leftvitem{4.0cm}{TempDir\index{setup!tempdir}\index{tempdir}}
\rightvitem{12.6cm}{This variable should contain the name of a directory
that is the directory in which \FORM\ should make its temporary files. If the
-t option is used when \FORM\ is started, the TempDir variable in the
setup file is ignored. \FORM\ can create a number of different temporary
files.}
\leftvitem{4.0cm}{TempSortDir\index{setup!tempsortdir}\index{tempsortdir}}
\rightvitem{12.6cm}{This variable should contain the name of a directory
that is the directory in which \FORM{} should make its temporary sort files.
If the -ts option is used when \FORM{} is started, the TempSortDir variable in
the setup file is ignored. If TempSortDir is not specified, then the value of
TempDir is used also for sort files.}
\leftvitem{4.0cm}{ThreadBucketSize\index{setup!threadbucketsize}\index{threadbucketsize}}
\rightvitem{12.6cm}{Only relevant for \TFORM. The size of the number of
terms sent to the workers simultaneously.
For details see the chapter on the parallel version (\ref{parallel}).}
\leftvitem{4.0cm}{ThreadLoadBalancing\index{setup!threadloadbalancing}\index{threadloadbalancing}}
\rightvitem{12.6cm}{\indent Only relevant for \TFORM. Possible values are ON
or OFF. For details see the chapter on the parallel version (\ref{parallel}).}
\leftvitem{4.0cm}{Threads\index{setup!threads}\index{threads}}
\rightvitem{12.6cm}{Only relevant for \TFORM\ (see chapter on the parallel
version). Specifies the default number of worker threads to be used. The
values 0 and 1 will indicate that running will only be done by the master
thread (\ref{parallel}).}
\leftvitem{4.0cm}{ThreadScratchOutSize\index{setup!threadscratchoutsize}\index{threadscratchoutsize}}
\rightvitem{12.6cm}{The size of the output scratch buffers for each of the
worker threads. These buffers will be used when the InParallel
statement~\ref{substainparallel} is active. They are used to catch the
output of the expressions as processed by the individual workers before
they are copied to the output scratch buffer/file of the master. The output
scratch buffer/file of each worker will never contain more than one
expression at a time.}
\leftvitem{4.0cm}{ThreadScratchSize\index{setup!threadscratchsize}\index{threadscratchsize}}
\rightvitem{12.6cm}{The size of the input scratch buffers for each of the
worker threads. These buffers are only used when the main scratch buffers
of the master process aren't sufficient and scratch files have been made.
When the buffers of the master are big enough, the workers only use
pointers to the buffer of the master. Once there are scratch files the
buffer is used for caching the input from those files. In that case each
worker has its own cache. For reading purposes it can actually be counter
productive if these buffers are very large. This parameter sets the value
for the input and the hide\index{hide} scratch files. The output scratch
size for the workers is set with the ThreadScratchOutSize parameter.}
%\leftvitem{4.0cm}{ThreadSortFileSynch\index{setup!threadsortfilesynch}\index{threadsortfilesynch}}
%\rightvitem{12.6cm}{\indent Only relevant for \TFORM. Possible values are ON
%or OFF. For details see the chapter on the parallel version (\ref{parallel}).}
\leftvitem{4.0cm}{TotalSize\index{setup!totalsize}\index{totalsize}}
\rightvitem{12.6cm}{Puts \FORM\ in a mode in which it tries to determine
the maximum space occupied by all expressions at any given moment during
the execution of the program. This space is the sum of the
input/output/hide scratch files, the sort file(s) and the .str file. This
maximum is printed at the end of the program. The same can be obtained with
the "On TotalSize" statement (see \ref{ontotalsize}) or the -T option
in the command tail when \FORM\ is started (see \ref{running}).}
\leftvitem{4.0cm}{WorkSpace\index{setup!workspace}\index{workspace}}
\rightvitem{12.6cm}{The size of the heap that is used by the algebra
processor when it is evaluating the substitution tree. It will contain
terms, half finished terms and other information. The size of the workspace
may be a limitation on the depth of a substitution tree.}
\leftvitem{4.0cm}{WTimeStats\index{setup!wtimestats}\index{wtimestats}}
\rightvitem{12.6cm}{Turns on the wall-clock time mode in the statistics.
See the `\texttt{On wtimestats}' statement~\ref{substaon}.}
Variables that take a path\index{path} for their value expect a sequence of
directories, separated by colon characters as in the UNIX\index{UNIX} way
to define such objects.
The above parameters are conceptually relatively easy. The parameters that
are still left are more complicated and are often restricted in their
size by some relationships. Hence it is necessary to understand the
sorting inside \FORM\ a little bit before using them. On the other hand
these parameters can influence the performance noticeably. See also chapter
\ref{sorting} for more details.
When terms are send to `output' by the main algebra engine, they are put
inside a buffer. This buffer is called the `small\index{small buffer}
buffer\index{buffer!small}'. Its size is given by the variable {\sl
SmallSize\index{smallsize}}. When this buffer is full, or when the number
of terms in this buffer exceeds a given maximum, indicated by the variable
{\sl TermsInSmall\index{termsinsmall}}, the contents of the buffer are
sorted. The sorting is done by pointers, hence it is important that the
small buffer resides inside the physical memory. During the sorting it may
happen that coefficients are added. The sum of two rational numbers can
take more space than any of the individual numbers, so there will be a
space problem. This has been solved by the construction of an extension to
the small buffer. The variable {\sl SmallExtension\index{smallextension}}
is the size of the small buffer together with this extension. The value for
SmallExtension will always be at least 7/6 times the value of SmallSize.
The result of the sorting of the small buffer is written to the
`large\index{large buffer} buffer\index{buffer!large}' (with the size {\sl
LargeSize\index{largesize}}) as a single object and the filling of the
small buffer can resume. Whenever there is not enough room in the large
buffer for the result of sorting the small buffer, or whenever there are
already a given number of these sorted `patches' in it (controlled by the
variable {\sl LargePatches\index{largepatches}}) the buffer will be sorted
by merging the patches\index{patch} to make room for the new results. The
output is written to the sort file as a single patch. Then the results from
the small buffer can be written to the large buffer. This game can continue
till no more terms are generated. In the end it will be necessary to sort
the results in the intermediate sort file\index{file!sort}. This can be
done with up to {\sl FilePatches\index{filepatches}} at a time. Because
file operations are notoriously slow the combination of the small buffer,
the small extension and the large buffer is used for caching\index{cache}
purposes. Hence this space can be split in `FilePatches' caches. The
limitation is that each cache should be capable to contain at least two
terms of maximal size. This means that the sum of SmallExtension and
LargeSize must be at least FilePatches times 2*MaxTermSize*(bytes in short
integer). It is possible to set the size of these caches directly with the
variable {\sl SortIOsize\index{sortiosize}}. If the variable is too large,
the variable FilePatches may be adjusted by \FORM. If there are more than
FilePatches patches in the sort file, a second sort file is needed for the
output of each `superpatch'\index{superpatch}. When the first sort file has
been treated, the second sort file can be treated in exactly the same way
as its predecessor. This process will finish eventually. When there are at
most FilePatches patches in a sort file, the output of their merging can be
written directly to the regular output. For completeness we give a list of
all these variables:
\leftvitem{3cm}{FilePatches\index{setup!filepatches}\index{filepatches}}
\rightvitem{13cm}{The maximum number of patches that can be merged
simultaneously, when the intermediate sort file is involved.}
\leftvitem{3cm}{LargePatches\index{setup!largepatches}\index{largepatches}}
\rightvitem{13cm}{The maximum number of patches that is allowed in the
large buffer. The large buffer may reside in virtual memory, due to the
nature of the sort that is applied to it.}
\leftvitem{3cm}{TermsInSmall\index{setup!termsinsmall}\index{termsinsmall}}
\rightvitem{13cm}{The maximum number of terms that is allowed in the small
buffer before it is sorted. The sorted result is either copied to the large
buffer or written to the intermediate sort file (when LargeSize is too
small).}
\leftvitem{3cm}{SmallSize\index{setup!smallsize}\index{smallsize}}
\rightvitem{13cm}{The size of the small buffer in bytes.}
\leftvitem{3cm}{SmallExtension\index{setup!smallextension}\index{smallextension}}
\rightvitem{13cm}{The size of the small buffer plus its extension.}
\leftvitem{3cm}{LargeSize\index{setup!largesize}\index{largesize}}
\rightvitem{13cm}{The size of the large buffer.}
\leftvitem{3cm}{SortIOsize\index{setup!sortiosize}\index{sortiosize}}
\rightvitem{13cm}{The size of the buffer that is used to write to the
intermediate sorting file and to read from it. It should be noted that if
this buffer is not very large, the sorting of large files may become rather
slow, depending on the operating system. Hence we recommend a potential
fourth stage in the sorting over having this number too small to fit more
filepatches in the combined small and large buffer. Setting the small and
large buffers to a decent size may avoid all problems by a: making more
space for the caching, b: creating fewer file patches to start with.}
There is a second set of the above setup parameters for sorts of
subexpressions\index{subexpressions} as in function arguments or in the
term environment (see \ref{substaterm}). Because these things can happen
with more than one level, whatever allocations have to be made (during
runtime when needed) may have to be made several times. Hence one should be
far more conservative here than with the global allocations. Anyway, those
sorts should rarely involve anything very big. With the function arguments
the condition is that the final result will fit inside a single term, but
with the term environment no such restriction exists. The relevant
variables here are subfilepatches, sublargepatches, sublargesize,
subsmallextension, subsmallsize, subsortiosize and subtermsinsmall. Their
meanings are the same as for the variables without the sub in front.
When \FORM\ is running in parallel mode (either \TFORM\ or \ParFORM) each worker
will need its own buffers. In \ParFORM\ in which the processors each control
their own memory, the size of each of these buffers are the same as for the
master process. In \TFORM\ with its shared memory the above sizes refer to
the buffers of the master thread. The workers each get basically buffers
with 1/N times the size of the buffer of the master. This may get made a
bit bigger when potential conflicts with MaxTermSize occur.
The default settings are
\begin{center}
\begin{tabular}{lrr}
Variable & 32-bits & 64-bits \\ \hline
bracketindexsize & 200000 & 200000 \\
commentchar & $*$ & $*$ \\
compresssize & 90000 & 90000 \\
constindex & 128 & 128 \\
continuationlines & 15 & 15 \\
dotchar & . & . \\
filepatches & 256 & 256 \\
functionlevels & 30 & 30 \\
hidesize & 50000000 & 50000000 \\
incdir & . & . \\
%indentspace & & \\
insidefirst & ON & ON \\
largepatches & 256 & 256 \\
largesize & 50000000 & 50000000 \\
maxnumbersize & 200 & 200 \\
maxtermsize & 10000 & 40000 \\
maxwildcards & 100 & 100 \\
nospacesinnumbers & OFF & OFF \\
numstorecaches & 4 & 4 \\
nwritefinalstatistics & OFF & OFF \\
nwritestatistics & OFF & OFF \\
nwritethreadstatistics &OFF & OFF \\
oldorder & OFF & OFF \\
parentheses & 100 & 100 \\
path & . & . \\
%polygcdchoice & 0 & 0 \\
processbucketsize & 1000 & 1000 \\
scratchsize & 50000000 & 50000000 \\
sizestorecache & 32768 & 32768 \\
smallextension & 20000000 & 20000000 \\
smallsize & 10000000 & 10000000 \\
sortiosize & 100000 & 100000 \\
sorttype & lowfirst & lowfirst \\
subfilepatches & 64 & 64 \\
sublargepatches & 64 & 64 \\
sublargesize & 4000000 & 4000000 \\
subsmallextension & 800000 & 800000 \\
subsmallsize & 500000 & 500000 \\
subsortiosize & 32768 & 32768 \\
subtermsinsmall & 10000 & 10000 \\
tempdir & . & . \\
tempsortdir & . & . \\
termsinsmall & 100000 & 100000 \\
threadbucketsize & 500 & 500 \\
threadloadbalancing & ON & ON \\
threads & 0 & 0 \\
threadsortfilesynch & OFF & OFF \\
threadscratchoutsize & 2500000 & 2500000 \\
threadscratchsize & 100000 & 100000 \\
workspace & 10000000 & 40000000
%zipsize & 32768 & 32768
\end{tabular}
\end{center}
If one compares these numbers with the corresponding numbers for older
versions one will notice that here we assume that the standard computer
will have much more memory available than in the `old time'. Basically we
expect that a serious \FORM\ user has at least 64 Mbytes available. If it is
considerably less one should define a setup file with smaller settings.
More recently a new notation for large numbers has been allowed. One can
use the characters K, M, G and T to indicate kilo (three zeroes), mega (6
zeroes), giga (9 zeroes) and tera (12 zeros) as in 10M for 10000000.
To find out what the setup values are, one can use the `ON,setup;'
statement (\ref{substaon}).
In version 3.3 and later one may use environment\index{environment}
variables for the values of the setup parameters, either in the setup file
or at the beginning of the .frm file. The environment variable is used as a
preprocessor variable in the sense that its name is enclosed in a
backquote-quote pair as in \verb:`VARNAME':. The variable will be looked
for and if found it will be substituted. This can however not be done in a
recursive way, because the regular routines that take care of the
preprocessor variables are not active yet when the setups are read.
form-master/doc/manual/sorting.tex 0000664 0000000 0000000 00000033337 13565763364 0017576 0 ustar 00root root 0000000 0000000
\chapter{Sorting and statistics}
\label{sorting}
The sorting system is a vital part of \FORM\ and one of the main reasons why
the speed of \FORM\ compares so favorably with other systems.
A good understanding of what happens during the sorting\index{sorting} of
expressions is essential if one wants to write efficient\index{efficient}
programs. In essence the sorting is done by a tree\index{tree sort} sort.
However due to the nature of mathematical expressions there is a
complication. When two terms are identical with the possible exception of
their coefficient, we will add their coefficients, put this new coefficient
in the place of the coefficient of the first term, and drop the second
term. If the new coefficient happens to be zero, both terms are dropped.
Hence the number of terms during the sort is not fixed. For a tree sort
this is not a major complication\index{complication}. What is more annoying
though is that the new coefficient may take more space inside the storage
than either of the old coefficients. Let us have a look now at what happens
in a \FORM\ program. Much can be seen from the statistics.
\begin{verbatim}
S x1,...,x4;
L F = (x1+...+x4)^4;
.end
Time = 0.01 sec Generated terms = 35
F Terms in output = 35
Bytes used = 628
\end{verbatim}
In this case the program generated 35 terms. Whenever a term is generated
and \FORM\ is done with it (no more statements will act on it), \FORM\
will write it into a buffer which is called the small buffer. Additionally
it stores a pointer to the location of this term inside the small buffer.
Next it will continue generating terms. This process will be stopped by
either of three conditions:
\begin{enumerate}
\item \FORM\ is finished generating terms.
\item The last generated term does not fit inside the space remaining in
the small buffer.
\item There is no space for a pointer to the last generated term inside the
array of pointers.
\end{enumerate}
In either of these three cases \FORM\ will sort the contents of the
small\index{small buffer} buffer\index{buffer!small}. This sorting is done
`by pointers' and hence it is important that the whole small buffer fits
inside the physical memory of the computer. If this would not be the case,
some very inefficient swapping of memory might be the result. During this
sorting \FORM\ may run into the problem that the coefficient of two combined
terms does not fit in the place of one of the two old coefficients. This
means that the combined term will need more space, but because the old
terms might be enclosed by other terms, this space may not be available
locally. To this end \FORM\ has some spare space in the small buffer which is
called the small\index{small extension} extension\index{extension!small}.
Actually the term SmallExtension\index{smallextension} is used for the
combination of the small buffer and its extra space. The extra space is at
least $1/6$ times the size of the small buffer, but typically it will be
about $1/3$ the size of the small buffer. In some exceptional cases (with
heavy use of a polynomial coefficient via the PolyFun\index{polyfun}
command) bigger sizes might be useful.
In the case that the new combined term needs more space than each of the
old terms, the new term is placed in the extension space. If, during the
sort, the extension space becomes exhausted, \FORM\ will make a
garbage\index{garbage collection}
collection of the entire extended small buffer. This will always result in
the extension space becoming empty again, because the notation of the terms
in \FORM\ is such the new combined term will at most occupy an amount
of space equal to the sum of the spaces of the original two terms. In older
versions of \FORM\ this garbage collection was executed by means of a
temporary disk file. In the new version it is done inside the memory by
temporarily allocating a new buffer. Anyway such garbage collections are
relatively rare.
In the above example, the sorting occurred because the generation of terms
was finished. Hence the sorted output is written away in such a way that it
can be used as input for a potential next module (or to be printed).
Hence let us change the size of the small buffer:
\begin{verbatim}
#: SmallSize 300
S x1,...,x4;
L F = (x1+...+x4)^4;
.end
Time = 0.00 sec Generated terms = 13
F 1 Terms left = 13
Bytes used = 236
Time = 0.00 sec Generated terms = 26
F 1 Terms left = 26
Bytes used = 476
Time = 0.00 sec Generated terms = 35
F 1 Terms left = 35
Bytes used = 632
Time = 0.00 sec Generated terms = 35
F Terms in output = 35
Bytes used = 628
\end{verbatim}
Now the size of the small buffer will be only 300 bytes. As a result the
13-th term does not fit. We can see this in the statistics: the 13-th term
has been generated and \FORM\ sorts the small buffer. The output of the 12
sorted terms is written to another buffer, called the
large\index{large buffer} buffer\index{buffer!large}. Inside the large
buffer the terms are lightly compressed. This compression is related to the
fact that in each `patch'\index{patch} the terms are already sorted and
hence we may not have to repeat the identical beginnings of each term.
Hence the amount of space used after this sort is less than the 300 bytes
of the small buffer, even though the 13-th term gave an overflow for these
300 bytes. The small buffer fills up again at the 26-th term and again it
is sorted and the results written to the large buffer. Finally, after 35
terms, the generation is finished. Hence the remains in the small buffer
are also sorted and written as a third `patch' into the large buffer. Then
the large buffer is sorted. For this a different sort technique is used. It
is assumed that the large buffer is not always residing inside the physical
memory. Hence parts of it may be swapped out temporarily. With the size of
current days memories this may not happen very often, unless one sets the
size of the buffer to something comparable to the memory size of the
computer and several programs are running at the same time. Anyway,
swapping will not affect the large buffer very much. \FORM\ will merge the
`patches' by going sequentially through them with a method called
`tree\index{tree of losers} of losers' in the book by Knuth\index{Knuth}
(the art of computer programming, vol. 3). Because it goes sequentially
through the patches, uses all the information it reads and never needs it
again, this method is indeed rather well resistant to swapping.
The next complication is of course when the large buffer is full. This can
be either because its byte space is full, or because the maximum number of
patches is exceeded. Because the sorting method uses quite a few variables
for each patch, there is a space allocated for them and hence there is a
maximum number of patches. If we set this to 2 (just for demonstration
purposes) we obtain:
\begin{verbatim}
#: SmallSize 200
#: LargePatches 2
S x1,...,x4;
L F = (x1+...+x4)^4;
.end
Time = 0.00 sec Generated terms = 9
F 1 Terms left = 9
Bytes used = 164
Time = 0.00 sec Generated terms = 17
F 1 Terms left = 17
Bytes used = 312
Time = 0.00 sec Generated terms = 26
F 1 Terms left = 26
Bytes used = 478
Time = 0.00 sec
F Terms active = 26
Bytes used = 474
Time = 0.00 sec Generated terms = 35
F 1 Terms left = 35
Bytes used = 630
Time = 0.00 sec
F Terms active = 35
Bytes used = 786
Time = 0.00 sec Generated terms = 35
F Terms in output = 35
Bytes used = 628
\end{verbatim}
We see that after the third small buffer has been sorted, the third patch
cannot be written to the large buffer. Hence the large buffer is sorted
(indicated by the special statistics involving the phrase `Terms active').
The result of this is written as a sorted patch to the
sort\index{sort file} file. This file is one of the
temporary\index{temporary files} files that \FORM\ can create. It has the
extension .sor\index{.sor extension}. Now the third patch can be written
into the --by now empty-- large buffer. At the end of term generation, the
last small buffer is sorted, its results written into the large buffer,
then that is sorted and its results written as the final patch into the
sort file. Then, finally the patches in the sort file are merged in a
method similar to the way the large buffer is sorted. This final sort is a
disk\index{disk to disk sort} to disk sort. Hence it can use the disk
rather intensely and the use of the CPU may drop temporarily, although it
is nothing so dramatic as when the computer is involved in heavy
inefficient swapping as can be the case with many other algebra programs.
Also, this is usually only a small fraction of the running time of the
program. The exception may be when \FORM\ is running several processes and
they are all using disk sorts simultaneously. In that case some file
systems may not be very good at handling the ensuing
traffic\index{traffic jam} jams.
Also the disk to disk sort will have a maximum number of patches that can
be sorted simultaneously. If this number is exceeded there will be one or
more extra stages\index{stages in the sorting} in the sorting, all of which
will be disk to disk sorts. It is advisable to tune the setup parameters in
such a way that one can prevent this, because it involves usually needless
use of resources. One can try to increase the parameter
FilePatches\index{filepatches}, but the problem is that \FORM\ uses a
caching\index{caching} system to buffer the inputs from the sort file. The
cache buffers have to have a size that is at least twice the maximum size
of a term. For each patch it needs a buffer and all buffers together should
fit inside the combination of the large buffer and the small extended
buffer. This puts an upper limit on the number of file patches.
Additionally this buffer (SortIOsize\index{sortiosize}) should not be very
small, because otherwise the disk IO operations are very inefficient. Hence
it helps often to increase the size of the small buffer and the large
buffer first. That gives fewer patches. Additionally it in turn can allow
for more file patches that are not too small.
One thing that one can see now is that if terms are to cancel or to add, it
is advantageous if this happens already in an early stage of the sorting.
This means that it is most efficient if these terms will end up in the
small buffer at the same time. This should explain the example given in the
section on brackets\index{brackets}. This way fewer terms are written to
the large buffer and/or the sort file, which means that less disk space
will be used.
The sizes of buffers involved can all be tuned to a given hardware. How
this is done is explained in the chapter on the setup\index{setup} \ref{setup}.
When \FORM\ is dealing with the arguments\index{arguments of functions} of
functions and if an argument is a multiterm subexpression, also such
subexpressions need to be sorted. In older versions of \FORM\ this was done
inside the at that moment remaining space of the small buffer and its
extension. The reason was that such subexpressions would be rather short
(they would have to fit inside a function argument and were hence limited
by the maximum size of a term) and buffer space was hard to come by in
computers with small memories. In the new version of \FORM\ other
subexpression sorts were added: the sorting in the term environment (see
\ref{substaterm}) and the sorting of \$-expressions. Both sorts do not have
the restriction of the maximum size of a term. They can result in
expressions that are arbitrarily long (although that might not give
efficient programs). Hence the sorting of subexpressions have now their own
buffers. And more than one such set may be needed if for instance the term
environment is used in a nested fashion. Of course the settings for the
buffers of this `subsort' are not quite as large as for the main buffers.
And the user can of course also influence their settings as explained in
the chapter on the setup \ref{setup}. This chapter gives also all default
values.
There is one restriction on the sorting of function arguments and
\$-expressions: They are not allowed to go into the stage4 sorting. Any
such attempt will result in an error message and the suggestion to raise
the size of the buffers for this type of sorting.
When \FORM\ is running in parallel mode (either \TFORM\ or \ParFORM) each worker
will need its own buffers. In \ParFORM\ in which the processors each control
their own memory, the size of each of these buffers are the same as for the
master process. In \TFORM\ with its shared memory the sizes that the user
selects for the sort buffers and the scratch file caches refer to the
buffers of the master thread. The workers each get basically buffers with
1/N times the size of the buffer of the master. They may be made a bit
bigger when potential conflicts with MaxTermSize occur.
form-master/doc/manual/spectators.tex 0000664 0000000 0000000 00000014503 13565763364 0020272 0 ustar 00root root 0000000 0000000
\chapter{Spectators}
\label{spectators}
At times expressions contain many terms that will not be treated for many
modules to come. For the actions in those modules they are considered
spectator terms. and they may consume much computer time due to their
presence during the sorting. For this we have the spectator system in which
we can send those terms to a special file, named a spectator file, in such
a way that they can be picked up at a convenient time in the future. In
short:
\noindent
Spectators are expressions together with a \index{filename}filename. The
file is for storage when the spectator becomes too big. Create a spectator
with the statement\index{createspectator}
\begin{verbatim}
CreateSpectator Exprname,"filename";
\end{verbatim}
The file will be made in the same directory where the temporary files are made.
Example:
\begin{verbatim}
CreateSpectator Yintegrals,"Yfile.spec";
\end{verbatim}
One may send terms to a spectator with the executable
statement\index{tospectator}
\begin{verbatim}
ToSpectator Exprname;
\end{verbatim}
An example would be
\begin{verbatim}
if ( count(Z,1) == 0 ) ToSpectator Yintegrals;
\end{verbatim}
The terms are dumped into the file as they are at the moment the
ToSpectator statement is executed. No brackets etc.
In the future they may be compressed. At the moment they are not.
Recovery of the contents of the spectator is done with the CopySpectator
statement as in\index{copyspectator}
\begin{verbatim}
CopySpectator NewExp = Yintegrals;
\end{verbatim}
Currently you can only read the spectators this way. You cannot make more
complicated constructions. You can only do things with the terms of the
spectator expression after the contents have been put in your new
expression. The spectator file remains in existence. In later modules you
can still add to it. You cannot read from and add to the same spectator in
the same module. The CopySpectator command can be followed by executable
statements in the same module. This may not be economical because the
contents of the spectator have not been sorted. There could be identical
terms that occur many times or even cancel. Better sort them first.
A spectator can be removed from the system with the
statement\index{removespectator}
\begin{verbatim}
RemoveSpectator Yintegrals;
\end{verbatim}
It is also possible to truncate a spectator down to zero length
with\index{emptyspectator}
\begin{verbatim}
EmptySpectator Yintegrals;
\end{verbatim}
You can have as many spectators as you like, but they all have some cache
buffers. There may also be limitations in the file system on the maximum
number of open files. The filename for the spectator is purely for the sake
of the users administration and recognition. You cannot carry the file over
to other programs. There is no variable administration in it as in the
saved files.
The .global instruction makes a spectator file survive a .store. It is up
to the user to make sure that all the variables in it also survive the
.store. There is no checking!
One use of the spectator system would be when integrating many different
terms by means of a very prolonged recursion system in which integrals of a
given complexity are reduced to integrals of lower complexity, but each
such reduction may take quite a few steps. One could have:
\begin{verbatim}
#do i = `MAXCOMPLEXITY'-1,0,-1
CreateSpectator complex`i',"complex`i'.spec";
#enddo
Local F`MAXCOMPLEXITY' = ....;
#do i = `MAXCOMPLEXITY'-1,1,-1
#do ii = 1,1;
* routine for doing a part of the recursion level `i'
....
#do j = `i'-1,0,-1
if ( complexityofterm == `i' ) ToSpectator complex`i';
#enddo
if ( notyetfinished ) redefine ii "0";
.sort
#enddo
Drop F{`i'+1};
CopySpectator F`i' = complex`i';
.sort
RemoveSpectator complex`i';
#enddo
*
* and finally, assuming that complexity zero means finished:
*
Drop F1;
CopySpectator F0 = complex0;
.sort
RemoveSpectator complex0;
\end{verbatim}
Some remarks are called for here. If one works with the
polyratfun\index{polyratfun} concept and the rational
polynomials\index{rational polynomials} become rather complicated, the
sorting after the CopySpectator statement can have serious bottleneck
problems in \index{TFORM}\TFORM{} and \index{ParFORM}\ParFORM{} because the
addition of those polynomials can be rather expensive and much of it ends
up in the master processor. This can be made better with the following
construction (assuming the function rat was declared as polyratfun at the
moment of all the ToSpectator statements that wrote to the spectator):
\begin{verbatim}
PolyRatFun;
Drop F1;
CopySpactator F0 = complex0;
ABracket+ rat;
.sort
PolyRatFun rat;
RemoveSpectator complex0;
.sort
\end{verbatim}
First we remove the declaration of rat as polyratfun. Then we read the
spectator and sort it such that all terms that should be added eventually
are grouped together. This sorting is very cheap as only identical terms
are combined. Then we declare the polyratfun again and because of the way
the terms are sorted nearly all additions take place inside the workers,
hence at maximum parallelization efficiency.
The above method still contains one inefficiency: because the polyratfun is
declared again, the contents of the rat function need to be 'normalized'
again, while they were already normalized. This involves calculating a gcd
of the numerator and the denominator, which is an expensive operation
and is useless in this case. For this we have a special option in the
polyratfun declaration:
\begin{verbatim}
PolyRatFun rat-;
\end{verbatim}
This will skip the normalization on the input of the module. One should
note however that if one uses this option under different conditions in
which the input rat function might not be normalized, the program might
crash or even give wrong answers. Hence this option should only be used
with the highest degree of caution! This is an option for very experienced
users only. No support is given concerning programs that run correctly
without the use of this option and fail when using it.
It should be noted that in the sequential version of \FORM{} this
construction is not needed at all, because there is only one processor
anyway.
form-master/doc/manual/startup.tex 0000664 0000000 0000000 00000015545 13565763364 0017614 0 ustar 00root root 0000000 0000000 \chapter{Running FORM}
\label{running}
The proper way to invoke the running\index{running \FORM} of \FORM\ depends on
the operating system that is being used. Here we will consider the
UNIX\index{UNIX} operating system and its derivatives. The version for
computers with the Windows operating system use Cygwin\index{Cygwin}, which
is a UNIX derivative as well and hence it functions similarly. In all cases
a proper call of \FORM\ is
\begin{verbatim}
form [options] inputfile
\end{verbatim}
The input file\index{file!input} should have a name that ends in the
extension \verb:.frm:. It is however not needed to specify this extension.
If this extension is absent, \FORM\ will add it. Example:
\begin{verbatim}
form myformprogram
\end{verbatim}
and \FORM\ will look for the file \verb:myformprogram.frm:. The options are
separated by blanks and start with a minus sign, followed by one or more
alphabetic characters. They are:
\begin{description}
\item[-c] Error checking only. Notice that this will not work
properly if there are conditionals in the preprocessor phase that
depend on results obtained at earlier stages of the program.
\item[-d] Next argument/option is the name of a preprocessor
variable that will be defined before the run starts. A specific value can be assigned with the
syntax {\tt -d VARIABLENAME=VALUE}. The default value is 1.
\item[-D] Same as -d.
\item[-f] Output goes only to log file.
\item[-F] Output only to log file. Further like -L or -ll.
\item[-h] Wait for some key to be touched before finishing the run.
Basically only for some old window based systems.
\item[-I] Next argument/option is the path of a directory for
include, procedure and subroutine files.
\item[-l] Make a regular log file.
\item[-ll] Make a log file without intermediate statistics.
\item[-L] Same as -ll.
\item[-M] Put the PID (process identifier) in the name of the temporary
files. This makes for longer names, but gives a better guarantee of
uniqueness. If a file with the created name exists already it will be
overwritten. This option is for when several instances of \FORM\ are
started at nearly the same time as can happen from minos or make (with
the make -j option).
\item[-p] Next argument/option is the path of a directory for
input, include, procedure and subroutine files.
\item[-{pipe}] Indicates that \FORM\ is started up as the receiving
end of a pipe. Action will be taken to set up the proper communication
channels.
\item[-q] Quiet option. Only output expressions are printed.
\item[-R] Recover from a crash. See the checkpoint mechanism in
\ref{checkpoints}.
\item[-s] Next argument/option is the path of a directory for a
setup file.
\item[-si] Same as -q.
\item[-S] Next argument/option is the name of a setup file.
\item[-t] Next argument/option is the path of a directory for temporary files.
\item[-ts] Next argument/option is the path of a directory for temporary sort
files.
\item[-T] Puts\index{totalsize} \FORM\ in a mode in which the maximum
totalsize is measured and printed at the end of the program. For more
information see the "On TotalSize;" statement~\ref{ontotalsize}.
\item[-v] Only the version will be printed. The program terminates
immediately after it.
\item[-w] This should be followed immediately by a number. The
number indicates the number of worker threads for \TFORM. All other
versions of \FORM\ ignore this parameter. It should be noted that \TFORM\
is a different program. For more information, please consult
chapter~\ref{parallel}.
\item[-W] Turn on the wall-clock time mode in the statistics.
See the `\texttt{On wtimestats}' statement~\ref{substaon}.
\item[-y] Run only the preprocessor and dump its output.
\item[-z] The number following is a timelimit for the program in second.
\item[-Z] Removes the .str file on crash, whatever its contents. Under
ordinary circumstances at a crash a .str file will not be removed if
it has a nonzero content.
\end{description}
\noindent The log\index{log} file\index{file!log} is a file in which all
output is collected, even when the output appears on the screen already.
This makes it possible to follow the progress of the program and have a
record of everything at the same time. The name of the log file is
identical to the name of the program without the extension \verb:.frm: but
with the extra extension \verb:.log:.
Example:
\begin{center}
\begin{verbatim}
form -t /LocalDisk/mydir -l myformprogram
\end{verbatim}
\end{center}
\FORM\ will run the program in the file \verb:myformprogram.frm:. Its output
will both be written to the screen and into the file
\verb:myformprogram.log:. The temporary files (if any) will be made in the
directory \verb:/LocalDisk/mydir:. This last feature is very useful,
because writing temporary files across a network can sometimes slow things
down considerably.
The second way to pass parameters to \FORM\ during startup is by means of
environment\index{environment variables} variables, assuming of course that
the system supports them. The following variables are supported:
\begin{description}
\item[FORMPATH]\index{FORMPATH} The directory in which \FORM\ will look for
procedures and header files, assuming it cannot find them in the current
directory.
\item[FORMTMP]\index{FORMTMP} The directory in which \FORM\ will make its
temporary files\index{file!temporary}.
\item[FORMTMPSORT]\index{FORMTMPSORT} The directory in which \FORM{} will make
its temporary sort files.
\item[FORMSETUP]\index{FORMSETUP} The full path and name of a setup
file\index{file!setup}.
\end{description}
It should be noted that when a parameter is specified both in the command
tail and in the environment the value of the command tail will be used.
The third way to pass parameters at startup is by means of a setup
file\index{file!setup}.
One of the first things \FORM\ does is to locate such a startup file. The
procedure that is being followed for this is:
\begin{itemize}
\item If the command tail specifies a setup file, \FORM\ will use this file,
ignoring all other indications with respect to the setup file. This assumes
of course that this file exists. If it does not exist \FORM\ passes on to the
next option.
\item If the command tail specifies a path for the setup file, \FORM\ will
try to open the file "form.set" in this directory. If this cannot be done
(by lack of rights or because the file does not exist) \FORM\ passes on to
the next option.
\item Next \FORM\ tries to open the file "form.set"\index{form.set} in the
current directory.\item If all else fails, \FORM\ will look for the
environment parameter FORMSETUP and use its value as the name of a setup
file.
\end{itemize}
If all the above attempts fail, \FORM\ will not use a setup file. For more
information about the setup file one should consult the corresponding
chapter on page \ref{setup}.
form-master/doc/manual/statements.tex 0000664 0000000 0000000 00000743005 13565763364 0020300 0 ustar 00root root 0000000 0000000
\chapter{Statements}
\label{statements}
%--#[ abrackets :
\section{abrackets, antibrackets}
\label{substaabrackets}
\noindent \begin{tabular}{ll}
Type & Output control statement\\
Syntax & ab[rackets][+][-] {\tt<}list of names{\tt>}; \\
& antib[rackets][+][-] {\tt<}list of names{\tt>}; \\
See also & bracket (\ref{substabracket}) and the chapter on brackets
(\ref{brackets})
\end{tabular} \vspace{4mm}
\noindent
This statement\index{abrackets}\index{antibrackets} does the opposite of
the bracket statement (see \ref{substabracket}). In the bracket statement
the variables that are mentioned are placed outside brackets and inside the
brackets are all other objects. In the antibracket statement the variables
in the list are the only objects that are not placed outside the brackets.
For the rest of the syntax, see the bracket statement (section
\ref{substabracket}).
\vspace{10mm}
%--#] abrackets :
%--#[ also :
\section{also}
\label{substaalso}
\noindent \begin{tabular}{ll}
Type & Executable Statement \\
Syntax & a[lso] [options] {\tt<}pattern{\tt>} =
{\tt<}expression{\tt>}; \\
See also & identify (\ref{substaidentify}), idold (\ref{substaidold})
\end{tabular} \vspace{4mm}
\noindent The also\index{also} statement should follow either an
id\index{id} statement or another also statement. The action is that the
pattern matching in the also statement takes place immediately after the
pattern matching of the previous id statement (or also statement) and after
possible matching patterns have been removed, but before the r.h.s.
expressions are inserted. It is identical to the idold statement (see
\ref{substaidold}). Example:
\begin{verbatim}
id x = cosphi*x-sinphi*y;
also y = sinphi*x+cosphi*y;
\end{verbatim}
\noindent The options are explained in the section on the id statement (see
\ref{substaidentify}). \vspace{10mm}
%--#] also :
%--#[ antiputinside :
\section{antiputinside}
\label{substaantiputinside}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & antiputinside {\tt<}name of function{\tt>} [,$<$antibracket information$>$];
\\ See also & PutInside (\ref{substaputinside})
\end{tabular}\vspace{4mm}
\noindent This statement\index{antiputinside} puts all parts of the term
with the exception of the variables in the antibracket information inside a
function argument. The function must be a regular function (hence no tensor
or table which are special types of functions). The
antibracket\index{antibracket} information should adhere to the syntax of
the bracket statement (\ref{substabracket}, \ref{substaabrackets}) and all
occurrences of all variables with the exception of the antibracket
variables will be put inside the function. The coefficient will also be put
inside the function.
\vspace{10mm}
%--#] antiputinside :
%--#[ antisymmetrize :
\section{antisymmetrize}
\label{substaantisymmetrize}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & an[tisymmetrize] \verb:{:{\tt<}name of function/tensor{\tt>}
[{\tt<}argument specifications{\tt>}];\verb:}: \\
See also & symmetrize (\ref{substasymmetrize}), cyclesymmetrize
(\ref{substacyclesymmetrize}), rcyclesymmetrize (\ref{substarcyclesymmetrize})
\end{tabular} \vspace{4mm}
\noindent The argument specifications are explained in the section on the
symmetrize statements (see \ref{substasymmetrize}).\medskip
\noindent The action of this statement\index{antisymmetrize} is to
anti-symmetrize the (specified) arguments of the functions that are
mentioned. This means that the arguments are brought to `natural order' in
the notation of \FORM\ and each exchange of arguments or groups of arguments
results in a minus sign in the coefficient of the term. The `natural order'
may depend on the order of declaration of the variables. If two arguments
or groups of arguments that are part in the anti-symmetrization are
identical, the function is replaced by zero. \vspace{10mm}
%--#] antisymmetrize :
%--#[ apply :
\section{apply}
\label{substaapply}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & apply ["{\tt<}tablename(s){\tt>}"];
\\ See also & tablebases (\ref{tablebase}), apply (\ref{tblapply})
\end{tabular} \vspace{4mm}
\noindent This statement\index{apply} is explained in the chapter on
tablebases.\vspace{10mm}
%--#] apply :
%--#[ argexplode :
\section{argexplode}
\label{substaargexplode}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & argexplode [{\tt<}list of functions{\tt>}] \\
See also & argimplode (\ref{substaargimplode})
\end{tabular} \vspace{4mm}
\noindent See the description of the ArgImplode~\ref{substaargimplode}
statement.
\vspace{10mm}
%--#] argexplode :
%--#[ argimplode :
\section{argimplode}
\label{substaargimplode}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & argimplode [{\tt<}list of functions{\tt>}] \\
See also & argexplode (\ref{substaargexplode})
\end{tabular} \vspace{4mm}
\noindent This is a rather specialized statement. It converts one notation
of indices, used for harmonic sums\index{sums!harmonic}\index{harmonic
sums}, harmonic
polylogarithms\index{polylogarithms!harmonic}\index{harmonic
polylogarithms} and multiple zeta values\index{multiple zeta values} into
its alternative notation. The two notations are:
\begin{verbatim}
Z(0,0,0,1,0,0,-1)
Z(4,-3)
\end{verbatim}
In the first notation the indices can only be 0, 1 and -1. In the second
notation there can be no zeroes. The `ArgImplode,Z;'
statement\index{argimplode} would be
equivalent to the statement
% THIS EXAMPLE IS PART OF THE TESTSUITE. CHANGES HERE SHOULD BE APPLIED THERE AS
% WELL!
\begin{verbatim}
repeat id Z(?a,0,x?!{0,0},?b) = Z(?a,x+sig_(x),?b);
\end{verbatim}
and takes one from the first notation to the second. The `ArgExplode,Z;'
statement\index{argexplode} is equivalent to the statement
% THIS EXAMPLE IS PART OF THE TESTSUITE. CHANGES HERE SHOULD BE APPLIED THERE AS
% WELL!
\begin{verbatim}
repeat id Z(?a,x?!{1,0,-1},?b) = Z(?a,0,x-sig_(x),?b);
\end{verbatim}
and takes one from the second notation to the first. The reason that these
statements have been built in lies in the fact that for many indices the
repeat statements started to become very time-consuming.
\noindent For the harmonic sums, the harmonic polylogarithms and the
multiple zeta values one can use the summer6 and the harmpol packages in
the \FORM\ distribution. They are described in the papers
J.~A.~M. Vermaseren, {\it Harmonic sums, Mellin transforms and integrals},
{\em Int. J. Mod. Phys.} {\bf A14} (1999) 2037,
http://arxiv.org/abs/hep-ph/9806280.
E.~Remiddi and J.~A.~M. Vermaseren, {\it Harmonic polylogarithms}, {\em
Int. J. Mod. Phys.} {\bf A15} (2000) 725,
http://arxiv.org/abs/hep-ph/9905237.
\vspace{10mm}
%--#] argimplode :
%--#[ argtoextrasymbol :
\section{argtoextrasymbol}
\label{substaargtoextrasymbol}
\noindent
\begin{tabular}{ll}
Type &
Executable statement \\
Syntax &
argtoextrasymbol [tonumber] [{\tt<}argument specifications{\tt>}]; \\
See also &
topolynomial (\ref{substatopolynomial}) and
extrasymbols (\ref{substaextrasymbols}, \ref{sect-extrasymbols}).
\end{tabular}
\vspace{4mm}
\noindent
Converts function arguments into extra symbols.
An argument will be replaced with an extra symbol.
The arguments that have been encountered before are replaced with the same
extra symbols.
Unlike the \texttt{topolynomial} statement (\ref{substatopolynomial}), the
replacement occurs even for arguments consisting only of numbers and symbols
(including extra symbols).
\vspace{4mm}
\noindent
The \texttt{tonumber} option requests that function arguments are converted to
positive integers corresponding to extra symbols. This provides an efficient
mapping from any expression (stored as a function argument) to a number.
\vspace{4mm}
\noindent
The function arguments to be converted can be specified in the same way as the
\texttt{argument} statement (see \ref{substaargument}).
\vspace{10mm}
%--#] argtoextrasymbol :
%--#[ argument :
\section{argument}
\label{substaargument}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & argument [{\tt<}argument specifications{\tt>}] \\ &
\ \ \ \ \ \ \ \ \ \ \ \
\verb:{:{\tt<}name of function/set{\tt>}
[{\tt<}argument specifications{\tt>}]\verb:}:; \\
See also & endargument (\ref{substaendargument})
\end{tabular} \vspace{4mm}
\noindent This statement starts an argument\index{argument}
environment\index{environment!argument}. Such an environment is terminated
by an endargument statement (see \ref{substaendargument}). The statements
between the argument and the endargument\index{endargument} statements will
be applied only to the function arguments as specified by the remaining
information in the argument statement. This information is given by:
\begin{itemize}
\item No further information: the statements are applied to all arguments
of all functions.
\item A series of numbers: the statements are applied to the given
arguments of all functions.
\item A function name (or a set of functions), possibly followed by a
series of numbers: the statements are applied to the numbered arguments of
the function specified. If a set of functions was specified, all the
functions in the set will be taken. If no numbers are specified, all
arguments of the function (or elements of the set) are taken.
\end{itemize}
The combination of a function (or set) possibly followed by numbers of
arguments, can occur as many times as needed. The generic numbers of
arguments that refer to all functions work in addition to the numbers
specified for individual functions. Example\vspace{1mm}
\begin{verbatim}
Argument 2,f,1,{f,f1},3,4;
\end{verbatim}
This specifies the second argument of all functions. In addition the first
argument of \verb:f: will be taken and then also the third and fourth
arguments of \verb:f: and \verb:f1: will be taken. \vspace{4mm}
\noindent Argument/endargument constructions can be nested. \vspace{10mm}
%--#] argument :
%--#[ autodeclare :
\section{auto, autodeclare}
\label{substaautodeclare}
\noindent \begin{tabular}{ll}
Type & Declaration statement\\
Syntax & autodeclare {\tt<}variable type{\tt>} {\tt<}list of variables to be declared{\tt>}; \\
& auto {\tt<}variable type{\tt>} {\tt<}list of variables to be declared{\tt>};
\end{tabular} \vspace{4mm}
\noindent The variable\index{auto}\index{autodeclare} types are
\vspace{1mm}
\lefttabitem{s[ymbol]}
\tabitem{Declaration of symbols\index{symbols}. For options see \ref{substasymbols}.}
\lefttabitem{v[ector]}
\tabitem{Declaration of vectors\index{vectors}. For options see \ref{substavectors}.}
\lefttabitem{i[ndex]}
\tabitem{Declaration of indices\index{index}. For options see \ref{substaindex}.}
\lefttabitem{i[ndices]}
\tabitem{Declaration of indices\index{indices}. For options see \ref{substaindex}.}
\lefttabitem{f[unctions]}
\tabitem{Declaration of noncommuting\index{noncommuting}
functions\index{functions!noncommuting}. For options see
\ref{substanfunctions}.}
\lefttabitem{nf[unctions]}
\tabitem{Declaration of noncommuting functions. For options see
\ref{substanfunctions}.}
\lefttabitem{cf[unctions]}
\tabitem{Declaration of commuting\index{commuting}
functions\index{functions!commuting}. For options see
\ref{substacfunctions}.}
\lefttabitem{co[mmuting]}
\tabitem{Declaration of commuting functions. For options see
\ref{substacfunctions}.}
\lefttabitem{t[ensors]}
\tabitem{Declaration of commuting tensors\index{tensors!commuting}. For options see
\ref{substatensors}.}
\lefttabitem{nt[ensors]}
\tabitem{Declaration of noncommuting tensors\index{tensors!noncommuting}. For options see
\ref{substantensors}.}
\lefttabitem{ct[ensors]}
\tabitem{Declaration of commuting tensors\index{tensors!commuting}. For options see
\ref{substactensors}.}
\noindent The action of the autodeclare statement is to set a default for
variable types. In a statement of the type
\begin{verbatim}
AutoDeclare Symbol a,bc,def;
\end{verbatim}
all undeclared variables of which the name starts with the character a, the
string bc or the string def will be interpreted as symbols and entered in
the name tables as such. In the case there are two statements as in
\begin{verbatim}
AutoDeclare CFunction b,d;
AutoDeclare Symbol a,bc,def;
\end{verbatim}
all previously undeclared variables of which the name starts with a, bc or
def will be declared as symbols. All other previously undeclared variables
of which the name starts with a b or a d will be declared as commuting
functions. This is independent of the order of the autodeclare statements.
{\FORM} starts looking for the most detailed matches
first. Hence the variable defi will match with the string def first.
\vspace{4mm}
\noindent It is also allowed to use the properties of the various variables
in the autodeclare statement:
\begin{verbatim}
AutoDeclare Index i=4,i3=3,i5=5;
\end{verbatim}
This declares all previously undeclared variables of which the name starts
with an i to be four dimensional indices, unless their names start with i3 in
which case they will be three dimensional indices, or their names start
with i5 in which case they will be five dimensional indices. \vspace{10mm}
%--#] autodeclare :
%--#[ bracket :
\section{bracket}
\label{substabracket}
\noindent \begin{tabular}{ll}
Type & Output control statement\\
Syntax & b[rackets][+][-] {\tt<}list of names{\tt>}; \\
See also & antibracket (\ref{substaabrackets}), keep (\ref{substakeep}),
collect(\ref{substacollect}) and the chapter on brackets
(\ref{brackets})
\end{tabular} \vspace{4mm}
\noindent This statement causes the output to be reorganized in such a way
that all objects in the `list of names' are placed outside
brackets\index{bracket} and all remaining objects inside
brackets\index{brackets}. This grouping will remain till the next time that
the expression is active and is being manipulated. Hence the brackets can
survive skip (see \ref{substaskip}), hide (see \ref{substahide}) and even
save (see \ref{substasave}) and load (see \ref{substaload}) statements. The
bracket information can be used by the collect (see \ref{substacollect})
and keep (see \ref{substakeep}) statements, as well in r.h.s. expressions
when the contents of individual brackets of an expression can be picked up
(see \ref{brackets}). \vspace{4mm}
\noindent The list of names can contain names of symbols, vectors,
functions, tensors and sets. In addition it can contain dotproducts. There
should be only one bracket or antibracket (see \ref{substaabrackets})
statement in each module. If there is more than one, only the last one has
an effect. The presence of a set has the same effect as having all the
symbolic elements of the set declared in the (anti)bracket
statement.\vspace{4mm}
\noindent The presence of a $+$ or $-$ after the bracket (or anti bracket)
refers to potential indexing of the brackets\index{brackets!indexing}.
Usually {\FORM} has the information inside the terms in an expression. If
it needs to search for a particular bracket it does so by starting at the
beginning of that expression. This can be slow. If one likes to access
individual brackets, it may be faster to tell {\FORM} to make an index by
putting the $+$ after the bracket or antibracket keyword. For more
information, see the chapter on brackets (see \ref{brackets}). A $-$
indicates that no index should be made. Currently this is the default and
hence there is no need to use this option. It is present just in case the
default might be changed in a future version of {\FORM} (in which {\FORM}
might for instance try to determine by itself what seems best. This option
exists for case that the user would like to overrule such a mechanism).
\vspace{4mm}
\noindent See also the antibracket statement in \ref{substaabrackets}.
\vspace{10mm}
%--#] bracket :
%--#[ break :
%
\section{break}
\label{substabreak}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & break; \\
\\ See also & case (\ref{substacase}), switch (\ref{substaswitch}),
default(\ref{substadefault}), endswitch (\ref{substaendswitch}).
\end{tabular} \vspace{4mm}
\noindent When a break statement is reached in a switch construction the
next statement to be executed is the first statement after the
corresponding endswitch statement.
\vspace{10mm}
%
%--#] break :
%--#[ case :
%
\section{case}
\label{substacase}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & case,number; \\
\\ See also & switch (\ref{substaswitch}), break (\ref{substabreak}),
default(\ref{substadefault}), endswitch (\ref{substaendswitch}).
\end{tabular} \vspace{4mm}
\noindent The cases in a switch construction are marked by a number. This
number must be an interger that can be represented inside a {\FORM} word.
On a 64-bit processor this would be an integer in the range $-2^{31}$ to
$2^{31}-1$. If the dollar variable in the switch statement has the same
value as the integer in the case statement, the next statement to be
executed is the first statement after the case statement. Usually cases are
terminated by break statements, but if there is no break statement 'fall
through' may occur in which execution continues with the first statement
after the next case statement or default statement.
\vspace{10mm}
%
%--#] case :
%--#[ cfunctions :
\section{cfunctions}
\label{substacfunctions}
\noindent \begin{tabular}{ll}
Type & Declaration statement\\
Syntax & c[functions] {\tt<}list of functions to be declared{\tt>}; \\
See also & functions (\ref{substafunctions}), nfunctions (\ref{substanfunctions})
\end{tabular} \vspace{4mm}
\noindent This statement declares commuting\index{commuting}
functions\index{functions!commuting}. The name of a
function can be followed by some information that specifies additional
properties of the preceding function. These can be (name indicates the
name of the function to be declared): \vspace{4mm}
\leftvitem{4.1cm}{name{\hash}r}
\rightvitem{12cm}{The function is considered to be a real\index{real} function (default).}
\leftvitem{4.1cm}{name{\hash}c}
\rightvitem{12cm}{The function is considered to be a complex\index{complex} function. This means
that internally two spaces are reserved. One for the variable name and one
for its complex conjugate name{\hash}.}
\leftvitem{4.1cm}{name{\hash}i}
\rightvitem{12cm}{The function is considered to be imaginary\index{imaginary}.}
\leftvitem{4.1cm}{name(s[ymmetric])}
\rightvitem{12cm}{The function is totally symmetric\index{symmetric}. This means that during
normalization {\FORM} will order the arguments according to its internal
notion of order by trying permutations. The result will depend on the order
of declaration of variables.}
\leftvitem{4.1cm}{name(a[ntisymmetric])}
\rightvitem{12cm}{The function is totally antisymmetric\index{antisymmetric}. This means that
during normalization {\FORM} will order the arguments according to its
internal notion of order and if the resulting permutation of arguments is
odd the coefficient of the term will change sign. The order will depend on
the order of declaration of variables.}
\leftvitem{4.1cm}{name(c[yclesymmetric])}
\rightvitem{12cm}{The function is cycle\index{cycle symmetric} symmetric in
all its arguments. This means that during normalization {\FORM} will order
the arguments according to its internal notion of order by trying cyclic
permutations. The result will depend on the order of declaration of
variables.}
\leftvitem{4.1cm}{name(r[cyclesymmetric)
name(r[cyclic])
name(r[eversecyclic])}
\rightvitem{12cm}{The function is reverse\index{reverse cycle symmetric}
cycle symmetric in all its arguments. This means that during normalization
{\FORM} will order the arguments according to its internal notion of order
by trying cyclic permutations and/or a complete reverse order of all
arguments. The result will depend on the order of declaration of
variables.}
\noindent The complexity properties and the symmetric properties can be
combined. In that case the complexity properties should come first as in
\begin{verbatim}
CFunction f1#i(antisymmetric);
\end{verbatim}
\vspace{10mm}
%--#] cfunctions :
%--#[ chainin :
\section{chainin}
\label{substachainin}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & Chainin,name of function;
\\ See also & chainout (\ref{substachainout})
\end{tabular} \vspace{4mm}
\noindent Has\index{chainin} the same effect as the statement
\begin{verbatim}
repeat id f(?a)*f(?b) = f(?a,?b);
\end{verbatim}
if f is the name of the function specified. The chainin statement is just a
faster shortcut. \vspace{10mm}
%--#] chainin :
%--#[ chainout :
\section{chainout}
\label{substachainout}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & Chainout,name of function;
\\ See also & chainin (\ref{substachainin})
\end{tabular} \vspace{4mm}
\noindent Has\index{chainout} the same effect as the statement
\begin{verbatim}
repeat id f(x1?,x2?,?a) = f(x1)*f(x2,?a);
\end{verbatim}
if f is the name of the function specified. The chainout statement is just a
much faster shortcut. \vspace{10mm}
%--#] chainout :
%--#[ chisholm :
\section{chisholm}
\label{substachisholm}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & chisholm [options] {\tt<}spinline indices{\tt>}; \\
See also & trace4 (\ref{substatrace}) and the chapter on gamma algebra
(\ref{gammaalgebra})
\end{tabular} \vspace{4mm}
\noindent This statement\index{chisholm} applies the identity
\begin{eqnarray}
\gamma_a\gamma_\mu\gamma_b \Tr[\gamma_\mu S] & = &
2\gamma_a( S + S^R ) \gamma_b \nonumber
\end{eqnarray}
\setcounter{equation}{2}
in order to contract traces. $S$ is here a string of
gamma\index{gamma matrices} matrices and $S^R$ is the reverse string. This
identity is particularly useful when the matrices $\gamma_6 = 1+\gamma_5$
and/or $\gamma_7 = 1-\gamma_5$ are involved. The spinline\index{spinline} index refers to
which trace should be eliminated this way. The options are \vspace{1mm}
\lefttabitem{symmetrize}
\tabitem{If there is more than one contraction with other gamma matrices,
the answer will be the sum of the various contractions, divided by the
number of different contractions. This will often result in a minimization
of the number of $\gamma_5$ matrices left in the final results.}
\lefttabitem{nosymmetrize}
\tabitem{The first contraction encountered will be taken. No attempt is
made to optimize with respect to the number of $\gamma_5$ matrices left.}
\noindent IMPORTANT: the above identity is only valid in 4 dimensions. For
more details, see chapter \ref{gammaalgebra} on gamma\index{gamma algebra} algebra. \vspace{10mm}
%--#] chisholm :
%--#[ cleartable :
\section{cleartable}
\label{substacleartable}
\noindent \begin{tabular}{ll}
Type & Declaration statement\\
Syntax & ClearTable [{\tt<}list of tables{\tt>}]
\end{tabular} \vspace{4mm}
\noindent This statement clears the tables that are mentioned. Sometimes
(sparse) tables can take so much space that there is no room for new
elements, while old elements are not needed any longer. In that case one
can clear the table and start all over again with filling it. It is also
useful when one wants to reuse a table, but now with a different content.
\vspace{10mm}
%--#] cleartable :
%--#[ collect :
\section{collect}
\label{substacollect}
\noindent \begin{tabular}{ll}
Type & Specification statement\\
Syntax & collect {\tt<}name of function{\tt>}; \\
& collect {\tt<}name of function{\tt>}
{\tt<}name of other function{\tt>}; \\
& collect {\tt<}name of function{\tt>}
{\tt<}name of other function{\tt>} {\tt<}percentage{\tt>};
\\ See also & bracket (\ref{substabracket}), antibracket
(\ref{substaabrackets}) and the chapter on brackets
(\ref{brackets})
\end{tabular} \vspace{4mm}
\noindent Upon processing\index{collect} the expressions (hence expressions
in hide as well as skipped expressions do not take part in this) the
contents of the brackets\index{brackets} (if there was a bracket or
antibracket\index{antibracket} statement in the preceding module) are
collected and put inside the argument of the named function. Hence if the
expression \verb:F: is given by
\begin{verbatim}
F =
a*(b^2+c)
+ a^2*(b+6)
+ b^3 + c*b + 12;
\end{verbatim}
the statement
\begin{verbatim}
Collect cfun;
\end{verbatim}
will change \verb:F: into
\begin{verbatim}
F = a*cfun(b^2+c)+a^2*cfun(b+6)+cfun(b^3+c*b+12);
\end{verbatim}
The major complication\index{complication} occurs if the content of a
bracket is so long that it will not fit inside a single term. The maximum
size of a term is limited by the setup parameter
maxtermsize\index{maxtermsize} (see \ref{setupmaxtermsize}). If this size
is exceeded, {\FORM} will split the bracket contents over more than one term,
in each of which it will be inside the named function. It will issue a
warning that it has done so. \vspace{4mm}
\noindent If a second function is specified (the
alternative\index{alternative} collect function) and if a bracket takes
more space than can be put inside a single term, the bracket contents will
be split over more than one term, in each of which it will be inside the
alternative collect function. In this case there is no need for a
warning\index{warning}
as the user can easily check whether this has occurred by checking whether
the alternative function is present in the expression. \vspace{4mm}
\noindent If additionally a percentage\index{percentage} is specified (an
integer in the range of 1 to 99) this determines how big the argument must
be as compared to MaxTermSize (see chapter \ref{setup} on the setup) before
use is made of the alternate collect function. \vspace{10mm}
%--#] collect :
%--#[ commuteinset :
\section{commuteinset}
\label{substacommuteinset}
\noindent \begin{tabular}{ll}
Type & Declaration statement\\
Syntax & commuteinset {\tt<}$\{$list of noncommuting functions/tensors$\}${\tt>}; \\
See also & functions (\ref{substafunctions})
\end{tabular} \vspace{4mm}
\noindent This statement\index{commuteinset} allows one or more sets of
noncommuting functions and or tensors for its argument(s). The functions
inside each set will commute with each other. It is allowed to have the
same function inside more than one set. For a function to commute with
itself (with for instance different arguments) it needs to be specified
twice inside the same set. In that case it is more efficient to have a
separate set with only two arguments. Example:
\begin{verbatim}
I i1,...,i10;
F A1,...,A10;
CommuteInSet{A1,A3,A5},{A1,g_},{A1,A1};
L F = A5*A1*A5*A1*A5*A2*A3*A5*A1*A5*A3*A1;
L G = g_(2,i1)*g_(2,i2,i3)*A1(i2)*g_(1,i4)*g_(1,5_,i5,i6)
*A1(i1)*A1(i3)*g5_(1)*A3(i5)*A3(i4)*g5_(1);
Print +f +s;
.end
F =
+ A1*A1*A5*A5*A5*A2*A1*A1*A3*A3*A5*A5;
G =
+ g_(1,i4,i5,i6)*g_(2,i1,i2,i3)*A1(i1)*A1(i2)*A1(i3)*
A3(i5)*A3(i4)*g_(1,5_);
\end{verbatim}
\vspace{10mm}
%--#] commuteinset :
%--#[ commuting :
\section{commuting}
\label{substacommuting}
\noindent \begin{tabular}{ll}
Type & Declaration statement\\
Syntax & co[mmuting] {\tt<}list of functions to be declared{\tt>}; \\
See also & cfunctions (\ref{substacfunctions}), functions (\ref{substafunctions})
\end{tabular} \vspace{4mm}
\noindent This statement\index{commuting} is completely identical to the
cfunction statement (see \ref{substacfunctions}). \vspace{10mm}
%--#] commuting :
%--#[ compress :
\section{compress}
\label{substacompress}
\noindent \begin{tabular}{ll}
Type & Declaration statement\\
Syntax & comp[ress] {\tt<}on/off{\tt>};
\\ See also & on (\ref{substaon}), off (\ref{substaoff})
\end{tabular} \vspace{4mm}
\noindent This statement\index{compress} is obsolete. The user should try
to use the compress option of the on (see \ref{substaon}) or the off (see
\ref{substaoff}) statements. \vspace{10mm}
%--#] compress :
%--#[ contract :
\section{contract}
\label{substacontract}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & contract [{\tt<}argument specifications{\tt>}];
\end{tabular} \vspace{4mm}
\noindent Statement\index{contract} causes the contraction of pairs of
Levi-Civita\index{Levi-Civita} tensors\index{tensor!Levi-Civita} \verb:e_:
(see also \ref{functions}) into combinations of Kronecker\index{Kronecker}
delta's\index{delta!Kronecker}. If there are contracted indices, and if
their dimension is identical to the number of indices of the Levi-Civita
tensors, the regular shortcuts are taken. If there are contracted indices
with a different dimension, the contraction treats these indices
temporarily as different and lets the contraction be ruled by the
contraction mechanism of the Kronecker delta's. In practise this means that
the dimension will enter via $\delta^{\mu}_{\mu} \rightarrow {\rm
dim}(\mu)$. \vspace{4mm}
\noindent In {\FORM} there are no upper\index{upper} and lower\index{lower}
indices\index{indices!lower}\index{indices!upper}. Of course the user can
emulate those. The contract statement always assumes that there is a proper
distribution of upper and lower indices if the user decided to work in a
metric in which this makes a difference. Note however that due to the fact
that the Levi-Civita tensor is considered to be imaginary, there is usually
no need to do anything special. This is explained in the chapter on
functions (see \ref{functions}). \vspace{4mm}
\noindent There are several options to control which contractions will be
taken. They are \vspace{1mm}
\lefttabitem{Contract;}
\tabitem{Here only a single pair of Levi-Civita tensors will be contracted.
The pair that is selected by {\FORM} is the pair that will give the smallest
number of terms in their contraction.}
\leftvitem{4cm}{Contract {\tt <}number{\tt>};}
\rightvitem{12cm}{This tells {\FORM} to keep contracting pairs of Levi-Civita tensors
until there are {\tt <}number{\tt>} or {\tt <}number{\tt>}$+1$
Levi-Civita tensors left. A common example is
Contract 0;
which will contract as many pairs as possible.}
\leftvitem{4cm}{Contract:{\tt<}number{\tt>};}
\rightvitem{12cm}{Here the number indicates the number of indices in the
Levi-Civita tensors to be contracted. Only a single pair will be
contracted and it will be the pair that gives the smallest number of
terms.}
\leftvitem{4cm}{Contract:{\tt<}number{\tt>}
\hfill {\tt<}number{\tt>};}
\rightvitem{12cm}{The First number refers to the number of indices in the
Levi-Civita tensors to be contracted. The second number refers to the
number of Levi-Civita tensors that should be left (if possible) after
contraction.}
\noindent Note that the order in which {\FORM} selects the contractions is by
looking at which pair will give the smallest number of terms. This means
that usually the largest buildup of terms is at the end. This is not always
the case, because there can be a complicated network of contracted indices.
\vspace{10mm}
%--#] contract :
%--#[ copyspectator :
\section{copyspectator}
\label{substacopyspectator}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & copyspectator {\tt<}exprname = spectator;{\tt>};
\end{tabular} \vspace{4mm}
\noindent See chapter\ref{spectators} on spectators.
\vspace{10mm}
%--#] copyspectator :
%--#[ createspectator :
\section{createspectator}
\label{substacreatespectator}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & createspectator {\tt<}spectatorname, "filename";{\tt>};
\end{tabular} \vspace{4mm}
\noindent See chapter\ref{spectators} on spectators.
\vspace{10mm}
%--#] createspectator :
%--#[ ctable :
\section{ctable}
\label{substactable}
\noindent \begin{tabular}{ll}
Type & Declaration statement\\
Syntax & ctable {\tt<}options{\tt>} {\tt<}table to be
declared{\tt>}; \\
See also & functions (\ref{substafunctions}), table (\ref{substatable}),
ntable (\ref{substantable})
\end{tabular} \vspace{4mm}
\noindent This statement declares a commuting\index{commuting}
table\index{table!commuting} and is identical to the table command (see
\ref{substatable}) which has the commuting property as its default.
\vspace{10mm}
%--#] ctable :
%--#[ ctensors :
\section{ctensors}
\label{substactensors}
\noindent \begin{tabular}{ll}
Type & Declaration statement\\
Syntax & ct[ensors] {\tt<}list of tensors to be declared{\tt>}; \\
See also & functions (\ref{substafunctions}), tensors
(\ref{substatensors}), ntensors (\ref{substantensors})
\end{tabular} \vspace{4mm}
\noindent This statement declares commuting\index{commuting}
tensors\index{tensor!commuting}. It is equal to the tensor statement (see
\ref{substatensors}) which has the commuting property as its default.
\vspace{10mm}
%--#] ctensors :
%--#[ cyclesymmetrize :
\section{cyclesymmetrize}
\label{substacyclesymmetrize}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & cy[clesymmetrize] \verb:{:{\tt<}name of function/tensor{\tt>}
[{\tt<}argument specifications{\tt>}];\verb:}: \\
See also & symmetrize (\ref{substasymmetrize}), antisymmetrize
(\ref{substaantisymmetrize}), rcyclesymmetrize (\ref{substarcyclesymmetrize})
\end{tabular} \vspace{4mm}
\noindent The argument\index{cyclesymmetrize} specifications are explained
in the section on the symmetrize statements (see \ref{substasymmetrize}).
\medskip
\noindent The action of this statement is to cycle-symmetrize the (specified)
arguments of the functions that are mentioned. This means that the
arguments are brought to `natural order' in the notation of \FORM\ by trying
cyclic permutations of the arguments or groups of arguments. The `natural
order' may depend on the order of declaration of the variables.
\vspace{10mm}
%--#] cyclesymmetrize :
%--#[ deallocatetable :
\section{deallocatetable}
\label{substadeallocatetable}
\noindent \begin{tabular}{ll}
Type & Declaration\\
Syntax & DeallocateTable,name(s) of sparse table(s);
\\ See also & table (\ref{substatable}), fill (\ref{substafill}),
table bases (\ref{tablebase})
\end{tabular} \vspace{4mm}
\noindent Works\index{deallocatetable} only for sparse\index{sparse}
tables\index{table!sparse}. Deallocates all definitions of elements as
obtained with `Fill'\index{fill} statements as if there never were any
`Fill' statements for the given tables.
This statement exists because sometimes cleaning up big tables is needed
when they take too much memory. This can be the case when a big tablebase
has been used. \vspace{10mm}
%--#] deallocatetable :
%--#[ default :
%
\section{default}
\label{substadefault}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & default; \\
\\ See also & case (\ref{substacase}), break (\ref{substabreak}),
switch(\ref{substaswitch}), endswitch (\ref{substaendswitch}).
\end{tabular} \vspace{4mm}
\noindent This is the default case in a switch construction.
\vspace{10mm}
%
%--#] default :
%--#[ delete :
\section{delete}
\label{substadelete}
\noindent \begin{tabular}{ll}
Type & Specification statement\\
Syntax & delete storage; \\
See also & save (\ref{substasave}), load (\ref{substaload}) \\
Syntax & delete extrasymbols; \\
Syntax & delete extrasymbols\textgreater{}number; \\
See also & extrasymbols (\ref{substaextrasymbols}) \\
\end{tabular} \vspace{4mm}
\noindent This statement has currently two varieties. The delete
storage\index{delete} clears the complete storage\index{storage file}
file\index{file!storage} and reduces it to zero size. The effect is that
all stored expressions are removed from the system. Because it is
impossible to remove individual expressions from the store file (there is
no mechanism to fill the resulting holes) it is the only way to clean up
the storage file. If some expressions should be excluded from this
elimination process, one should copy them first into active global
expressions, then delete the storage file, after which the expressions can
be written to storage again with a .store\index{.store} instruction.
\noindent The delete extrasymbols\index{delete}\index{} variety removes
extra symbols\index{extra symbols} from the list. The default is that all
extra symbols are removed, but one can also remove the symbols above a
given number as in
\begin{verbatim}
#$es = `extrasymbols_';
ToPolynomial;
....some code....
.sort
* now the new extra symbols are not needed anylonger
Delete extrasymbols>`$es';
\end{verbatim}
\vspace{10mm}
%--#] delete :
%--#[ denominators :
\section{denominators}
\label{substadenominators}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & denominators functionname;
\end{tabular} \vspace{4mm}
\noindent This statement\index{denominators} allows the user to rename all
occurrences of the built-in denominator function. This built-in function is
kind of an oddity inside \FORM. Denominators are presented by a very special
function which doesn't really have a name and hence is rather hard to
address. In addition there are special rules connected to denominators.
Hence it is usually better to collect denominators inside functions that
have been defined by the user and hence allow the user to manipulate them
at will. Yet, objects can end up inside denominator functions, especially
when output from other programs is read in. Hence this statement allows all
occurrences of the denominator function to be renamed into the function
that is given in the statement. This function will work well together with
the PolyRatFun statement in which we define a PolyFun with two arguments of
which the second acts as a denominator and the first as a numerator:
\begin{verbatim}
PolyRatFun,rat;
Denominators,den;
id den(x?) = rat(1,x);
\end{verbatim}
For more about this one should consult the part on the
PolyRatFun\index{polyratfun} statement
(\ref{substapolyratfun}) and the chapter on polynomials (still to be
included because the current version can handle only polynomials in a
single variable and is also not optimized for many occurrences that have
identical denominators).
\vspace{10mm}
%--#] denominators :
%--#[ dimension :
\section{dimension}
\label{substadimension}
\noindent \begin{tabular}{ll}
Type & Declaration statement\\
Syntax & d[imension] {\tt<}number or symbol{\tt>};
\\ See also & index (\ref{substaindex})
\end{tabular} \vspace{4mm}
\noindent Sets the default dimension\index{dimension!default}. This default
dimension determines the dimension of the indices\index{indices} that are
being declared without dimension specification as well as the dimension of
all dummy indices\index{indices!dummy}. At the moment an index is declared
and there is no dimension specification, {\FORM} looks for the default
dimension and uses that. This index will then have this dimension, even
when the default dimension is changed at a later moment. The dummy indices
always have the dimension of the current default dimension. If the default
dimension is changed the dimension of all dummy indices changes with it.
Varieties: \vspace{1mm}
\leftvitem{4cm}{Dimension {\tt<}number{\tt>};}
\rightvitem{12cm}{Declares the number to be the default dimension. The number must be smaller than
32768 on 32bit architectures or 2147483648 on 64bit architectures. Negative numbers are not allowed.
If one wants to work with negative dimensions, the practical workaround is to use a symbolic
dimension and later replace that symbol appropriately.}
\leftvitem{4cm}{Dimension {\tt<}symbol{\tt>};}
\rightvitem{12cm}{Symbol must be the name of a symbol, either previously
declared or declarable because of an auto-declaration (see
\ref{substaautodeclare}). Declares the symbol to be the default dimension.}
\leftvitem{4cm}{Dimension
\hfill {\tt<}symbol{\tt>}:{\tt<}symbol{\tt>};}
\rightvitem{12cm}{The symbols\index{symbols} must be the names of symbols,
either previously declared or declarable because of an auto-declaration
(see \ref{substaautodeclare}). The first symbol will be the default
dimension. The second symbol will be the first symbol minus 4. It will
be used as such in the trace\index{trace contractions}
contractions\index{contractions!trace}. See also \ref{substatracen} and
\ref{substaindex}.}
\noindent Examples:
\begin{verbatim}
Dimension 3;
Dimension n;
Dimension n:[n-4];
\end{verbatim}
The default dimension in {\FORM} is 4. \vspace{10mm}
%--#] dimension :
%--#[ discard :
\section{discard}
\label{substadiscard}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & dis[card];
\end{tabular} \vspace{4mm}
\noindent This statement discards\index{discard} the current term. It can
be very useful in statements of the type
\begin{verbatim}
if ( count(x,1) > 5 ) Discard;
\end{verbatim}
which eliminates all terms that have more than five powers of x.
\vspace{10mm}
%--#] discard :
%--#[ disorder :
\section{disorder}
\label{substadisorder}
\noindent \begin{tabular}{ll}
Type & Executable statement \\
Syntax & disorder {\tt<}pattern{\tt>} = {\tt<}expression{\tt>};
\\ See also & identify (\ref{substaidentify})
\end{tabular} \vspace{4mm}
\noindent This statement is identical to the disorder\index{disorder}
option\index{option!disorder} of the id\index{id statement}\index{id}
statement (see \ref{substaidentify}). It is just a shorthand notation for
`id disorder'. \vspace{10mm}
%--#] disorder :
%--#[ do :
\section{do}
\label{substado}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & do \$loopvar = lowvalue,highvalue\verb:{:,increment\verb:}:;
\\ See also & enddo (\ref{substaenddo})
\end{tabular} \vspace{4mm}
\noindent The syntax is the typical syntax for do-loops. The loop variable
has to be a dollar variable. For parallel performance this variable can be
declared local in a moduleoption (see \ref{substamoduleoption}) statement,
unless it is also used in other ways in the current module. The loop
parameters should either be (short) integers or dollar variables or factors
of dollar variables provided they evaluate at run time to (short) integers.
The enddo statement should be in the same module as the do statement. In
addition it should be properly nested with if, repeat, while and argument
constructions.
\noindent The do-loop facility is in principle superfluous, because the
repeat~(\ref{substarepeat}), if~(\ref{substaif}) and the pattern matcher can
basically do everything the do-loop can do. Sometimes however the do-loop
is easier to program and gives more readable code as shown here:
\begin{verbatim}
do $i = 1,5;
id,only,x^$i = f(F[factor_^$i]);
enddo;
\end{verbatim}
\noindent versus
\begin{verbatim}
id,only,x^n?{1,2,3,4,5} = ff(n);
repeat id ff(n?pos_) = ff(n-1)*f(F[factor_^n]);
id ff(n?neg0_) = 1;
\end{verbatim}
\noindent One should note that the do-loop is evaluated at run time. Hence
the dollar variables need to be evaluated at run time as well. Therefore,
if it is possible, the preprocessor variety (see \ref{predo}) is almost
always faster in execution as in
\begin{verbatim}
#do i = 1,5
id,only,x^`i' = f(F[factor_^`i']);
#enddo
\end{verbatim}
\noindent This can of course not be done in constructions like
\begin{verbatim}
id f1(x?$x) = f2(x);
FactDollar,$x;
Do $i = 1,$x[0];
Multiply f($i,$x[$i]);
Enddo;
\end{verbatim}
\noindent because here \verb:$x: and its factors are only known at run time
and may be different for each term.
\vspace{10mm}
%--#] do :
%--#[ drop :
\section{drop}
\label{substadrop}
\noindent \begin{tabular}{ll}
Type & Specification statement\\
Syntax & drop; \\
& drop {\tt<}list of expressions{\tt>};
\\ See also & ndrop (\ref{substandrop})
\end{tabular} \vspace{4mm}
\noindent In the first variety this statement\index{drop} eliminates all
expressions\index{expression} from the system. In the second variety it
eliminates only the expressions that are mentioned from the system. All
expressions that are to be dropped can still be used in the r.h.s. of other
expressions inside the current module. Basically the expressions to be
dropped are not treated for execution and after the module has finished
completely they are removed. See also the ndrop
statement~\ref{substandrop}. \vspace{10mm}
%--#] drop :
%--#[ dropcoefficient :
\section{dropcoefficient}
\label{substadropcoefficient}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & DropCoefficient;
\end{tabular} \vspace{4mm}
\noindent This statement replaces the coefficient of the current term by
one. In principle it has the same effect as
\begin{verbatim}
Multiply 1/coeff_;
\end{verbatim}
but there is always the philosophical issue what is the coefficient once
one enters function arguments. Inside an
Argument/EndArgument\index{argument}\index{endargument} environment this
statement would drop the coefficient of the terms inside the argument.
\vspace{10mm}
%--#] dropcoefficient :
%--#[ dropsymbols :
\section{dropsymbols}
\label{substadropsymbols}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & DropSymbols;
\end{tabular} \vspace{4mm}
\noindent This statement removes all symbols from a term.
It has the same effect as
\begin{verbatim}
id,many,x?^n? = 1;
\end{verbatim}
(x and n are symbols) except for that it is much faster.
\vspace{10mm}
%--#] dropsymbols :
%--#[ else :
\section{else}
\label{substaelse}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & else;
\\ See also & if (\ref{substaif}),
elseif (\ref{substaelseif}),
endif (\ref{substaendif})
\end{tabular} \vspace{4mm}
\noindent To be used in combination with an if statement (see
\ref{substaif}). The statements following the
else\index{else statement}\index{else} statement until the matching
endif\index{endif statement}\index{endif}
statement (see \ref{substaendif}) will be executed for the current term if
the conditions of the matching proceeding if\index{if statement}\index{if}
statement and/or all corresponding elseif\index{elseif} statements (see
\ref{substaelseif}) are false. If any of the conditions of the matching
proceeding if or elseif statements are true the statements following the
else statement will be skipped. \vspace{10mm}
%--#] else :
%--#[ elseif :
\section{elseif}
\label{substaelseif}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & elseif ( {\tt<}condition{\tt>} );
\\ See also & if (\ref{substaif}),
else (\ref{substaelse}),
endif (\ref{substaendif})
\end{tabular} \vspace{4mm}
\noindent Should be proceeded by an if\index{if statement}\index{if}
statement (see \ref{substaif}) and followed at least by a matching
endif\index{endif statement}\index{endif}
statement (see \ref{substaendif}). If the conditions of the proceeding
matching if statement and all proceeding matching
elseif\index{elseif statement}\index{elseif} statements are false the
condition of this elseif statement will be evaluated. If it is true, the
statements following it until the next matching elseif,
else\index{else statement}\index{else} or endif statement will be executed.
If not, control is passed to this next elseif, else or endif statement. The
syntax for the condition is exactly the same as for the condition in the if
statement. \vspace{10mm}
%--#] elseif :
%--#[ emptyspectator :
\section{emptyspectator}
\label{substaemptyspectator}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & emptyspectator {\tt<}spectator;{\tt>};
\end{tabular} \vspace{4mm}
\noindent See chapter\ref{spectators} on spectators.
\vspace{10mm}
%--#] emptyspectator :
%--#[ endargument :
\section{endargument}
\label{substaendargument}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & endargument; \\
See also & argument (\ref{substaargument})
\end{tabular} \vspace{4mm}
\noindent Terminates an argument environment\index{environment!argument}
(see \ref{substaargument}). The argument\index{argument} statement and its
corresponding endargument\index{endargument} statement must belong to the
same module. Argument environments can be nested with all other
environments. \vspace{10mm}
%--#] endargument :
%--#[ enddo :
\section{enddo}
\label{substaenddo}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & enddo;
\\ See also & do (\ref{substado})
\end{tabular} \vspace{4mm}
See the do statement (\ref{substado}).
\vspace{10mm}
%--#] enddo :
%--#[ endif :
\section{endif}
\label{substaendif}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & endif;
\\ See also & if (\ref{substaif}),
elseif (\ref{substaelseif}),
else (\ref{substaelse})
\end{tabular} \vspace{4mm}
\noindent Terminates an if\index{if statement}\index{if} construction (see \ref{substaif},
\ref{substaelseif} and \ref{substaelse}). If should be noted that
if\index{endif statement}\index{endif}
constructions can be nested.
\vspace{10mm}
%--#] endif :
%--#[ endinexpression :
\section{endinexpression}
\label{substaendinexpression}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & endinexpression;
\\ See also & inexpression(\ref{substainexpression})
\end{tabular} \vspace{4mm}
\noindent Only to be used in combination with the
inexpression\index{endinexpression}\index{inexpression} statement. The
combination
\begin{verbatim}
InExpression,expr;
Statements;
EndInExpression;
\end{verbatim}
is a more readable version of the construction
\begin{verbatim}
if ( expression(expr) );
Statements;
endif;
\end{verbatim}
\vspace{10mm}
%--#] endinexpression :
%--#[ endinside :
\section{endinside}
\label{substaendinside}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & endinside;
\\ See also & inside (\ref{substainside}) and the chapter on \$-variables
(\ref{dollars})
\end{tabular}\vspace{4mm}
\noindent Terminates an `inside'\index{inside}
environment\index{environment!inside} (see \ref{substainside}) which is
used to operate on the contents of \$-variables\index{\$-variable} (see
\ref{dollars}).\vspace{10mm}
%--#] endinside :
%--#[ endrepeat :
\section{endrepeat}
\label{substaendrepeat}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & endrepeat;
\\ See also & repeat (\ref{substarepeat}), while (\ref{substawhile})
\end{tabular} \vspace{4mm}
\noindent Ends the repeat\index{repeat}
environment\index{environment!repeat}. The repeat environment is started
with a repeat statement (see \ref{substarepeat}). The repeat and its
matching endrepeat\index{endrepeat} should be inside the same module.
Repeat environments can be nested with all other environments (and other
repeat environments). \vspace{10mm}
%--#] endrepeat :
%--#[ endswitch :
%
\section{endswitch}
\label{substaendswitch}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & endswitch; \\
\\ See also & case (\ref{substacase}), break (\ref{substabreak}),
default(\ref{substadefault}), switch (\ref{substaswitch}).
\end{tabular} \vspace{4mm}
\noindent Ends a switch construction. It collects the various cases, puts
them in order and decides whether the lookup of cases should be done by
means of a jumptable, or by binary searching. The ratio (spread in
cases)/(number of cases) determines whether a jumptable is constructed. The
default value below which a jumptable is constructed is 4. This value can
be changed in the setups (see the section on the setups \ref{setup}) with
the variable jumpratio.
\vspace{10mm}
%
%--#] endswitch :
%--#[ endterm :
\section{endterm}
\label{substaendterm}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & endterm;
\\ See also & term (\ref{substaterm}), sort (\ref{substasort})
\end{tabular} \vspace{4mm}
\noindent Terminates a term\index{term} environment\index{environment!term}
(see \ref{substaterm}). Term environments\index{endterm} can be nested with
other term environments and with other environments in general. The whole
environment should be part of one single module. See also \ref{substasort}.
\vspace{10mm}
%--#] endterm :
%--#[ endwhile :
\section{endwhile}
\label{substaendwhile}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & endwhile;
\\ See also & while (\ref{substawhile}), repeat (\ref{substarepeat})
\end{tabular} \vspace{4mm}
\noindent Terminates a while\index{while} environment\index{environment!while} (see \ref{substawhile}). The while
statement and its corresponding endwhile\index{endwhile} statement must be part of the same
module. \vspace{10mm}
%--#] endwhile :
%--#[ exit :
\section{exit}
\label{substaexit}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & exit ["{\tt<}string{\tt>}"];
\\ See also & setexitflag (\ref{substasetexitflag})
\end{tabular} \vspace{4mm}
\noindent Causes execution to be aborted\index{exit}\index{aborted}
immediately. The string will be printed in the output. This can be used to
indicate where \FORM\ ran into the exit statement. \vspace{10mm}
%--#] exit :
%--#[ extrasymbols :
\section{extrasymbols}
\label{substaextrasymbols}
\noindent \begin{tabular}{ll}
Type & Declaration statement\\
Syntax & extrasymbols,array\textbar{}vector\textbar{}underscore,name;
\\ See also & ToPolynomial (\ref{substatopolynomial}), FromPolynomial
(\ref{substafrompolynomial}), ArgToExtraSymbol (\ref{substaargtoextrasymbol})
\\& and extra symbols
(\ref{sect-extrasymbols}).
\end{tabular} \vspace{4mm}
\noindent Starting with version 4.0 of \FORM{} some built in operations or
statements can only deal with symbols and numbers. Examples of this are
factorization~(\ref{substafactarg}) (which uses the topolynomial facilities
automatically) and output simplification (see the Format
statement \ref{substaformat}).
The ToPolynomial statement\index{topolynomial} takes each term, looks for
objects that are not symbols to positive powers and replaces them by
symbols. If the object has been encountered before the same symbol will be
used, otherwise a new symbol will be defined. The object represented by the
`extra symbol'\index{extra symbols} is stored internally and can be printed
if needed with the \%X option in the \#write instruction (\ref{prewrite}).
The representation of the extra symbols is by default the name Z followed
by a number and an underscore character. If another name is desired this
should be specified in an `ExtraSymbols' statement. The name given may
contain only alphabetic characters! Because some compilers do not like the
underscore character, there is an alternative notation for the extra
symbols. This is just for cosmetic reasons and one cannot feed these
symbols into the compiler this way. This is with an array notation. The
statement
\begin{verbatim}
ExtraSymbols,array,Ab;
\end{verbatim}
would cause the second extra symbol to be printed as {\tt Ab(2)}. The total
number of defined extra symbols is given by the built in symbol
extrasymbols\_.
The option vector in the ExtraSymbols statement is identical to the option
array and the option underscore reverts the notation back to the default
notation with the trailing underscore.
\vspace{10mm}
%--#] extrasymbols :
%--#[ factarg :
\section{factarg}
\label{substafactarg}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & factarg options \verb:{:{\tt<}name of function/set{\tt>}
[{\tt<}argument specifications{\tt>}]\verb:}:;
\\ See also & splitarg (\ref{substasplitarg})
\end{tabular} \vspace{4mm}
\noindent Splits\index{factarg} the indicated function\index{function
arguments} arguments into individual factors. The argument specifications
are as in the splitarg\index{splitarg} statement (see
\ref{substasplitarg}). There are a few extra options:
\leftvitem{2cm}{(0)}
\rightvitem{14cm}{Eliminates the coefficient\index{coefficient} of the term
in the argument. Similar to Normalize,(0),....}
\leftvitem{2cm}{(1)}
\rightvitem{14cm}{The coefficient of the term and its sign are pulled out
separately.}
\leftvitem{2cm}{(-1)}
\rightvitem{14cm}{The coefficient is pulled out with its sign.}
\noindent In the case of the above options only the coefficient is treated.
When these options are not used the whole term is treated as in:
% THIS EXAMPLE IS PART OF THE TESTSUITE. CHANGES HERE SHOULD BE APPLIED THERE AS
% WELL!
\begin{verbatim}
Symbols a,b,c;
CFunctions f,f1,f2,f3;
Local F = f(-3*a*b)+f(3*a*b)
+f1(-3*a*b)+f1(3*a*b)
+f2(-3*a*b)+f2(3*a*b)
+f3(-3*a*b)+f3(3*a*b);
FactArg,f;
Factarg,(0),f1;
Factarg,(1),f2;
Factarg,(-1),f3;
Print;
.end
F =
f(a,b,-1,3) + f(a,b,3) + 2*f1(a*b) + f2(a*b,-1,3) + f2(a*b,3)
+ f3(a*b,-3) + f3(a*b,3);
\end{verbatim}
When no extra options are used, starting with version 4.0, the whole
argument is factorized over the rationals. This means that
\begin{verbatim}
f(x^2+2*x*y+y^2) --> f(y + x,y + x,1)
\end{verbatim}
It should be noticed that \FORM{} can although the internal algorithms can
only factorize expressions with numbers and symbols, \FORM{} redefines all
non-symbol objects temporarily into symbols and at the end substitutes them
back. This is done with a mechanism that is similar to that of the
ToPolynomial statement.
See also the On OldfactArg; and Off OldFactArg statements for a
compatibility mode with versions before version 4.0.
\vspace{10mm}
%--#] factarg :
%--#[ factdollar :
\section{factdollar}
\label{substafactdollar}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & factdollar {\tt<}name of dollar variable{\tt>};
\\ See also & the chapter on polynomials~\ref{polynomials}.
\end{tabular} \vspace{4mm}
\noindent The FactDollar statement will factorize a dollar expression. If
the dollar expression was already factorized the old factors will be
removed first. Unlike expressions (see \ref{substafactorize}) where only
either the expanded or the factorized version exists, with dollar
expressions we have both versions simultaneously. This means that one can
refer to the complete dollar in its unfactorized form and its factors. The
factors are indicated between braces as in \verb:$x[1]: which would be the
first factor. The number of factors of \verb:$x: is given by \verb:$x[0]:.
One can also obtain the number of factors of a dollar variable with the
numfactors\_ function (see \ref{funnumfactors}).
\noindent The index indicating the number of the factor can be a nonzero
integer, no greater than the number of factors, or (a factor of) a dollar
variable that evaluates into such a number. Composite expressions are not
allowed. They should be worked out first in a separate dollar variable,
after which this dollar variable can then be used as a factor indicator.
\vspace{10mm}
%--#] factdollar :
%--#[ factorize :
\section{factorize}
\label{substafactorize}
\noindent \begin{tabular}{ll}
Type & Output control statement\\
Syntax & factorize \verb:{:{\tt<}name of expression(s){\tt>}\verb:}:;
\\ See also & the chapter on polynomials~\ref{polynomials}.
\end{tabular} \vspace{4mm}
\noindent If no expressions are mentioned all expressions will be affected
by the action of this statement. One may exclude certain expressions with
the nfactorize statement (see \ref{substanfactorize}). If one or more
expressions are mentoned they will be added to the list of expressions that
will be affected.
\noindent The statement causes the output expression(s) that is/are marked
as such to be factorized after they have been processed and already written
to the output. This means that each expression, after having been written,
is read again and factorized. Then the factorized result is written over
the original output. After that FORM will start executing the statements of
the current module on the next expression, sort it, write it to output, and
if necessary read it again and factorize it.
\noindent Expressions never exists in two varieties as the dollar variable
that have been factorized. It is either unfactorized (default) or
factorized. An expression remains factorized untill an UnFactorize
statement is encoutered that mentions that this expression should be
brought to unfactorized representation (see also
UnFactorize~\ref{substaunfactorize} and
NunFactorize~\ref{substanunfactorize}).
\noindent One should realize that factorization of complicated expressions
can be a rather costly operation.
\vspace{10mm}
%--#] factorize :
%--#[ fill :
\section{fill}
\label{substafill}
\noindent \begin{tabular}{ll}
Type & Declaration statement\\
Syntax & fill {\tt<}tableelement{\tt>} = {\tt<}expression{\tt>} [,{\tt<}moreexpressions{\tt>}];
\\ See also & table (\ref{substatable}),
fillexpression (\ref{substafillexpression}),
printtable (\ref{substaprinttable})
\end{tabular} \vspace{4mm}
\noindent The standard\index{fill} way to define elements of a
table\index{table}. In the left hand
side one specifies the table element without the extra function arguments
that could potentially occur (see \ref{substatable}). In the right hand
side one specifies what the table element should be substituted by.
Example:
\begin{verbatim}
Table tab(1:2,1:2,x?);
Fill tab(1,1) = x+y;
Fill tab(2,1) = (x+y)^2;
Fill tab(1,2) = tab(1,1)+y;
Fill tab(2,2) = tab(2,1)+y^2;
\end{verbatim}
The first fill statement is a bit like a continuous attempt to try the
substitution
\begin{verbatim}
id tab(1,1,x?) = x+y;
\end{verbatim}
The last two fill statements show that one could use the table
recursively\index{recursively}.
If a real loop occurs the program may terminate due to
stack\index{stack overflow} overflow.
\noindent It is possible to define several table elements in one statement.
In that case the various elements are separated by commas. The last index
is the first one to be raised. This means that in the above example one
could have written:
\begin{verbatim}
Table tab(1:2,1:2,x?);
Fill tab(1,1) = x+y,tab(1,1)+y,(x+y)^2,tab(2,1)+y^2;
\end{verbatim}\vspace{10mm}
\noindent One warning\index{warning} is called for. One should avoid using
expressions in the right hand side of fill statements:
\begin{verbatim}
Table B(1:1);
Local dummy = 1;
.sort
Fill B(1) = dummy;
Drop dummy;
.sort
Local F = B(1);
Print;
.end
\end{verbatim}
In the example a crash will result, because when we use the table element
the expression dummy doesn't exist anymore. In a fill statement the r.h.s.
is not expanded. Hence it keeps the reference to the expression dummy. When
the table element is used the reference to the expression dummy is inserted
and expanded. Hence one obtains the contents of dummy that exist at the
moment of use. This is illustrated in the following example:
\begin{verbatim}
Table B(1:1);
Local dummy = 1;
.sort
Fill B(1) = dummy;
.sort
Local F = B(1);
Print;
.sort
Drop;
.sort
Local dummy = 2;
.sort
Local F = B(1);
Print;
.end
\end{verbatim}
The final value of F will be 2, not 1.
\noindent A way to get around this problem is to force the evaluation of
the table definition by using dollar\index{dollar}
variables\index{variable!dollar}:
\begin{verbatim}
Table B(1:1);
Local dummy = 1;
.sort
#$value = dummy;
Fill B(1) = `$value';
Drop dummy;
.sort
Local F = B(1);
Print;
.end
\end{verbatim}
Here we use the character representation of the contents of the dollar
variable to obtain an expression that doesn't need any further evaluation.
If we would put
\begin{verbatim}
fill B(1) = $value;
\end{verbatim}
a reference to the dollar variable would be inserted and it would only be
evaluated at use again. In principle this could cause similar problems.
\noindent Not dropping the expression dummy can sometimes give the correct
result, but is potentially still unsafe.
\begin{verbatim}
Table B(1:1);
Local u = 2;
Local dummy = 1;
.sort
Fill B(1) = dummy;
Drop dummy;
.sort
Local v = 5;
Local F = B(1);
Print;
.end
\end{verbatim}
Here the answer will be 5, because after u has been dropped the expressions
will be renumbered. Hence now dummy becomes the first expression, and
eventually v becomes the second expression. The references in the table
elements are not renumbered. Hence the r.h.s. of B(1) keeps pointing at the
second expression, which at the moment of application has the value 5. One
can see now also why the original example crashes. First dummy was the
first expression and at the moment of application F is the first (existing)
expression. Hence the substitution of B(1) causes a self reference and
hence an infinite loop. Eventually some buffer will
overflow\index{overflow}.
\vspace{10mm}
%--#] fill :
%--#[ fillexpression :
\section{fillexpression}
\label{substafillexpression}
\noindent \begin{tabular}{ll}
Type & Declaration statement\\
Syntax & fillexpression {\tt<}table{\tt>} = {\tt<}expression{\tt>}({\tt<}x1{\tt>},...,{\tt<}xn{\tt>});
\\ & fillexpression {\tt<}table{\tt>} = {\tt<}expression{\tt>}({\tt<}funname{\tt>});
\\ See also & table (\ref{substatable}),
fill (\ref{substafill}) and the table\_ function
(\ref{funtable})
\end{tabular}\vspace{4mm}
\noindent Used\index{fillexpression} to dynamically\index{dynamical loading}
load\index{loading dynamically} a table\index{table} during runtime. When
there are n symbols (here called x1 to xn) it is assumed that the table is
n-dimensional. The expression must previously have been bracketed in these
symbols and each of the brackets\index{brackets} has the effect of a
fill\index{fill} statement in which the powers of the x1 to xn refer to the
table elements. Brackets that do not have a corresponding table element are
skipped.
\noindent In the case that only a function name is specified the arguments
of the function refer to the table elements.
\vspace{10mm}
%--#] fillexpression :
%--#[ fixindex :
\section{fixindex}
\label{substafixindex}
\noindent \begin{tabular}{ll}
Type & Declaration statement\\
Syntax & fi[xindex] \verb:{:{\tt<}number{\tt>}:{\tt<}value{\tt>}\verb:}:;
\\ See also & index (\ref{substaindex}) and chapter \ref{metric}.
\end{tabular} \vspace{4mm}
\noindent Defines \verb:d_(number,number) = value: in which number is the
number\index{fixindex} of a fixed\index{fixed index} index\index{index}
(hence a positive short integer with a value less than
ConstIndex\index{constindex} (see \ref{setup}). The value should be a
short\index{short integer} integer, i.e. its absolute value should be less
than $2^{15}$ on 32\index{32 bits} bit computers and less than $2^{31}$ on
64\index{64 bits} bit
computers. One can define more than one fixed index in one statement.
Before one would like to solve problems involving the choice of a metric
with this statement, one should consult the chapter on the use of a
metric\index{metric}
(chapter \ref{metric}).
\vspace{10mm}
%--#] fixindex :
%--#[ format :
\section{format}
\label{substaformat}
\noindent \begin{tabular}{ll}
Type & Output control statement\\
Syntax & fo[rmat] {\tt<}option{\tt>};
\\ See also & print (\ref{substaprint})
\end{tabular} \vspace{4mm}
\noindent Controls the format\index{format} for the
printing\index{printing} of expressions. There is a variety of options.
\leftvitem{3.5cm}{$<$number$>$}
\rightvitem{13cm}{Output will be printed using the indicated number of
characters per line. The default is 72. Numbers outside the range 1-255 are
corrected to 72. Positive numbers less than 39 are corrected to 39.}
\leftvitem{3.5cm}{float\index{float}\index{format!float} \hfill \\ \null\quad{\tt[}$<$number$>${\tt]}}
\rightvitem{13cm}{Numbers are printed in floating\index{floating point}
point notation, even though internally they remain fractions. This is
purely cosmetic. If no number is specified the precision of the output will
be 10 digits. If a number is specified it indicates the number of digits to
be used for the precision.}
\leftvitem{3.5cm}{rational\index{rational}\index{format!rational}}
\rightvitem{13cm}{Output format is switched back to rational numbers (in
contrast to floating point output). This is the default.}
\leftvitem{3.5cm}{nospaces\index{nospaces}\index{format!nospaces}}
\rightvitem{13cm}{The output is printed without the spaces that make the
output slightly more readable. This gives a more compact output.}
\leftvitem{3.5cm}{spaces\index{spaces}\index{format!spaces}}
\rightvitem{13cm}{The output is printed with extra spaces between the terms
and around certain operators to make it slightly more readable. This is the
default.}
\leftvitem{3.5cm}{O0\index{optimize}\index{format!optimize}}
\rightvitem{13cm}{\FORM\ will turn off output optimization. See the section
on output optimization \ref{optimization}}
\leftvitem{3.5cm}{O1[options]\index{optimize}\index{format!optimize}}
\rightvitem{13cm}{\FORM\ will use level 1 output optimization. See the section
on output optimization \ref{optimization}}
\leftvitem{3.5cm}{O2[options]\index{optimize}\index{format!optimize}}
\rightvitem{13cm}{\FORM\ will use level 2 output optimization. See the section
on output optimization \ref{optimization}}
\leftvitem{3.5cm}{O3[options]\index{optimize}\index{format!optimize}}
\rightvitem{13cm}{\FORM\ will use level 3 output optimization. See the section
on output optimization \ref{optimization}.}
\leftvitem{3.5cm}{fortran\index{fortran}\index{format!fortran}}
\rightvitem{13cm}{The output is printed in a way that is readable by a
fortran compiler. This includes continuation characters and the splitting
of the output into blocks of no more than 15 continuation lines. This
number can be changed with the setup parameter ContinuationLines (see
\ref{setup}). In addition dotproducts are printed with the `dotchar'
in the place of the period between the vectors. This dotchar can be set in
the setup file (see \ref{setup}). Its default is the underscore character.}
\leftvitem{3.5cm}{doublefortran\index{doublefortran}\index{format!doublefortran}}
\rightvitem{13cm}{Same as the fortran mode, but fractions are printed with
double floating point numbers, because some compilers convert numbers like
1. into 1.E0. With this format \FORM\ will force double precision by using
1.D0.}
\leftvitem{3.5cm}{quadruplefortran\index{quadruplefortran}\index{format!quadruplefortran}}
\rightvitem{13cm}{Same as the fortran mode, but fractions are printed with
quadruple floating point numbers, because some compilers convert numbers like
1. into 1.E0. With this format \FORM\ will force quadruple precision by using
1.Q0.}
\leftvitem{3.5cm}{quadfortran\index{quadfortran}\index{format!quadfortran}}
\rightvitem{13cm}{Same as quadruplefortran.}
\leftvitem{3.5cm}{fortran90\index{fortran90}\index{format!fortran90}}
\rightvitem{13cm}{Similar to the fortran option, but prints the
continuation lines according to the syntax of Fortran 90. If the fortran90
option is followed by a comma and a string that does not contain white space
or other comma's, this string is attached to all numbers in coefficients of
terms. Example: \hfill \\
{\tt\ \ \ \ \ \ Format Fortran90,.0\_ki;} \hfill \\
%\begin{verbatim}
% Format Fortran90,.0_ki;
%\end{verbatim}
which would give in the printout: \hfill \\
{\tt\ \ \ \ \ \ +23.0\_ki/32.0\_ki*a**2\& } \hfill \\
{\tt\ \ \ \ \&\ +34.0\_ki/1325.0\_ki*a**3} \hfill \\
%\begin{verbatim}
% +23.0_ki/32.0_ki*a**2&
% & +34.0_ki/1325.0_ki*a**3
%\end{verbatim}
When there is no string attached it defaults to a period as in the regular
Fortran option.
}
\leftvitem{3.5cm}{C\index{C}\index{format!C}}
\rightvitem{13cm}{Output will be C compatible. The
exponent\index{exponent operator} operator ($\wedge$) is represented by the
function pow\index{pow}. It is the responsibility of the user that this
function will be properly defined. Dotproducts are printed with the
`dotchar'\index{dotchar} in the place of the period between the vectors.
This dotchar can be set in the setup file (see \ref{setup}). Its default is
the underscore\index{underscore character} character.}
\leftvitem{3.5cm}{maple\index{maple}\index{format!maple}}
\rightvitem{13cm}{Output will be as much as possible compatible with Maple
format. It is not guaranteed that this is perfect.}
\leftvitem{3.5cm}{mathematica\index{mathematica}\index{format!mathematica}}
\rightvitem{13cm}{Output will be as much as possible compatible with
Mathematica format. It is not guaranteed that this is perfect.}
\leftvitem{3.5cm}{reduce\index{reduce}\index{format!reduce}}
\rightvitem{13cm}{Output will be as much as possible compatible with
Reduce format. It is not guaranteed that this is perfect.}
\noindent The last few formats have not been tried out extensively. The
author is open for suggestions.
\leftvitem{3.5cm}{normal\index{normal}\index{format!normal}}
\rightvitem{13cm}{Will return to the regular \FORM\ formatting mode.}
\noindent If the statement has no arguments the formatting will be reset to
the mode it was in when the program started.\vspace{4mm}
%\leftvitem{3.5cm}{}
%\rightvitem{13cm}{}
%\leftvitem{3.5cm}{}
%\rightvitem{13cm}{}
\vspace{10mm}
%--#] format :
%--#[ frompolynomial :
\section{frompolynomial}
\label{substafrompolynomial}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & frompolynomial
\\ See also & factarg (\ref{substafactarg}), ToPolynomial
(\ref{substatopolynomial}) and ExtraSymbols (\ref{substaextrasymbols},
\ref{sect-extrasymbols}).
\end{tabular} \vspace{4mm}
\noindent Starting with version 4.0 of \FORM{} some built in operations or
statements can only deal with symbols and numbers. Examples of this are
factorization~(\ref{substafactarg}) and output simplification (still to be
implemented). Whereas the ToPolynomial statement takes each term, looks for objects
that are not symbols to positive powers and replaces them by symbols the
FromPolynomial does the opposite: it replaces the newly defined extra
symbols and replaces them back by their original meaning.
\vspace{10mm}
%--#] frompolynomial :
%--#[ functions :
\section{functions}
\label{substafunctions}
\noindent \begin{tabular}{ll}
Type & Declaration statement\\
Syntax & f[unctions] {\tt<}list of functions to be declared{\tt>}; \\
See also & cfunctions (\ref{substacfunctions}),
tensors (\ref{substatensors}),
ntensors (\ref{substantensors}), \\ &
table (\ref{substatable}),
ntable (\ref{substantable}),
ctable (\ref{substactable})
\end{tabular} \vspace{4mm}
\noindent Used to declare one or more functions\index{functions}. The functions declared
with this statement will be noncommuting\index{noncommuting}. For
commuting\index{commuting} functions one
should use the cf[unctions] statement (see \ref{substacfunctions}).
Functions can have a number of properties that can be set in the
declaration. This is done by appending the options to the name of the
function. These options are:
\leftvitem{4.1cm}{name{\hash}r}
\rightvitem{12cm}{The function is considered to be a real\index{real} function (default).}
\leftvitem{4.1cm}{name{\hash}c}
\rightvitem{12cm}{The function is considered to be a complex\index{complex} function. This means
that internally two spaces are reserved. One for the variable name and one
for its complex conjugate name{\hash}.}
\leftvitem{4.1cm}{name{\hash}i}
\rightvitem{12cm}{The function is considered to be imaginary\index{imaginary}.}
\leftvitem{4.1cm}{name(s[ymmetric])}
\rightvitem{12cm}{The function is totally symmetric\index{symmetric}. This means that during
normalization {\FORM} will order the arguments according to its internal
notion of order by trying permutations. The result will depend on the order
of declaration of variables.}
\leftvitem{4.1cm}{name(a[ntisymmetric])}
\rightvitem{12cm}{The function is totally antisymmetric\index{antisymmetric}. This means that
during normalization {\FORM} will order the arguments according to its
internal notion of order and if the resulting permutation of arguments is
odd the coefficient of the term will change sign. The order will depend on
the order of declaration of variables.}
\leftvitem{4.1cm}{name(c[yclesymmetric])}
\rightvitem{12cm}{The function is cycle\index{cycle symmetric}
symmetric\index{symmetric!cycle} in all its arguments.
This means that during normalization {\FORM} will order the arguments
according to its internal notion of order by trying cyclic permutations.
The result will depend on the order of declaration of variables.}
\leftvitem{4.1cm}{name(r[cyclesymmetric)
name(r[cyclic])
name(r[eversecyclic])}
\rightvitem{12cm}{The function is reverse\index{reverse cycle symmetric}
cycle symmetric\index{symmetric!reverse cycle} in all its arguments. This
means that during normalization {\FORM} will order the arguments according
to its internal notion of order by trying cyclic permutations and/or a
complete reverse order of all arguments. The result will depend on the
order of declaration of variables.}
\leftvitem{4.1cm}{namenumber
name>=number}
\rightvitem{12cm}{The function has a restriction on the number of
arguments. If the number of arguments of an occurrence of the function is
not fulfilling the condition during normalization {\FORM} will set the term
equal to zero.}\vspace{2mm}
\noindent The complexity properties, the symmetric properties and the
number of arguments restrictions can be
combined. In that case the complexity properties should come first and the
argument restrictions should come last as in
\begin{verbatim}
Function f1#i(symmetric)>=4<8;
Function f1#i<=8;
\end{verbatim}
\vspace{10mm}
%--#] functions :
%--#[ funpowers :
\section{funpowers}
\label{substafunpowers}
\noindent \begin{tabular}{ll}
Type & Declaration statement\\
Syntax & funpowers {\tt<}on/off{\tt>};
\\ See also & on (\ref{substaon}), off (\ref{substaoff})
\end{tabular} \vspace{4mm}
\noindent This statement\index{funpowers} is obsolete\index{obsolete}. The
user should try to use the funpowers option of the on\index{on} (see
\ref{substaon}) or the off\index{off} (see \ref{substaoff}) statements.
\vspace{10mm}
%--#] funpowers :
%--#[ gfactorized :
\section{gfactorized}
\label{substagfactorized}
\noindent \begin{tabular}{ll}
Type & Definition statement\\
Syntax & g[lobal]factorized {\tt<}option{\tt>};
\\ See also & the chapter on polynomials~\ref{polynomials}, the
factorize statement~\ref{substafactorize} and the LocalFactorized \\ &
statement~\ref{substalfactorized}.\hfill
\end{tabular}
\smallskip
\noindent The syntax is like the syntax of the LocalFactorized (or
LFactorized) statement~\ref{substalfactorized}. The only difference is that
now the expression defined by the statement will become a global
expression (see the Global statement~\ref{substaglobal}).
\vspace{10mm}
%--#] gfactorized :
%--#[ global :
\section{global}
\label{substaglobal}
\noindent \begin{tabular}{ll}
Type & Definition statement\\
Syntax & g[lobal] {\tt<}name{\tt>} = {\tt<}expression{\tt>}; \\
& g[lobal] {\tt<}names of expressions{\tt>};
\\ See also & local (\ref{substalocal})
\end{tabular} \vspace{4mm}
\noindent Used to define a global\index{global}
expression\index{expression}. A global expression is an expression that
remains active until the first .store\index{.store} instruction. At that
moment it is stored into the `storage file'\index{storage
file}\index{file!storage} and stops being manipulated. After this it can
still be used in the right hand side of expressions and id\index{id}
statements (see \ref{substaidnew}). Global expressions that have been put
in the storage file can be saved to a disk file\index{file!disk} with the
save statement (see \ref{substasave}) for use in later programs.
\noindent There are two versions of the global statement. In the first the
expression is defined and filled with a right hand side expression. The left
hand side and the right hand side are separated by an = sign. In this case
the expression can have arguments which will serve as
dummy\index{dummy arguments} arguments after the global expression has been
stored with a .store instruction. Note that this use of arguments can often
be circumvented with the replace\_ function (see \ref{funreplace}) as in
\begin{verbatim}
Global F(a,b) = (a+b)^2;
.store
Local FF = F(x,y);
Local GG = F*replace_(a,x,b,y);
\end{verbatim}
because both definitions give the same result.
\noindent The second version of the global statement has no = sign and no
right hand side. It can be used to change a local\index{local} expression
into a global expression. \vspace{10mm}
%--#] global :
%--#[ goto :
\section{goto}
\label{substagoto}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & go[to] {\tt<}label{\tt>}; \\
See also & label (\ref{substalabel})
\end{tabular} \vspace{4mm}
\noindent Causes\index{goto} processing to proceed at the indicated
label\index{label} statement
(see \ref{substalabel}). This label statement must be in the same module.
\vspace{10mm}
%--#] goto :
%--#[ hide :
\section{hide}
\label{substahide}
\noindent \begin{tabular}{ll}
Type & Specification statement\\
Syntax & hide; \\
& hide {\tt<}list of expressions{\tt>};
\\ See also & nhide (\ref{substanhide}),
unhide (\ref{substaunhide}),
nunhide (\ref{substanunhide}),
pushhide (\ref{substapushhide}),
pophide (\ref{substapophide})
\end{tabular} \vspace{4mm}
\noindent In the first variety this statement marks all currently active
expressions for being put in hidden\index{hide} storage. In the second variety it marks
only the specified active\index{active expressions} expressions as such. \vspace{4mm}
\noindent If an expression is marked for being hidden, it will be copied to
the `hide\index{hide file} file'\index{file!hide}, a storage which is
either in memory or on file depending on the combined size of all
expressions being hidden. If this size exceeds the size of the setup
parameter scratchsize\index{scratchsize} (see \ref{setup}) the storage will
be on file. If it is less, the storage will be in memory. An expression
that has been hidden is not affected by the statements in the modules as
long as it remains hidden, but it can be used inside other expressions in
the same way skipped\index{skipped expressions} expressions (see
\ref{substaskip}) or active expressions can be used. In particular all its
bracket\index{bracket} information (see \ref{substabracket}) is retained
and can be accessed, including possible bracket\index{bracket index}
indexing. \vspace{4mm}
\noindent The hide mechanism is particularly useful if an expression is not
needed for a large number of modules. It has also advantages over the
storing of global expressions after a .store\index{.store} instruction (see
\ref{instrstore}), because the substitution of global expressions is slower
(name definitions may have changed and have to be checked) and also a
possible bracket index is not maintained by the .store instruction.
\vspace{4mm}
\noindent Expressions can be returned from a hidden status into active
expressions with the unhide\index{unhide} statement (see
\ref{substaunhide}). One might want to consult the nhide\index{nhide}
statement (\ref{substahide}) as well. \vspace{4mm}
\noindent When an expression is marked to be hidden it will remain just
marked until execution starts in the current module. When it is the turn of
the expression to be executed, it is copied to the hide file instead.
\vspace{4mm}
\noindent Note that a .store instruction will simultaneously remove all
expressions from the hide system. \vspace{10mm}
%--#] hide :
%--#[ identify :
\section{identify}
\label{substaidentify}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & id[entify] [{\tt<}options{\tt>}] {\tt<}pattern{\tt>} = {\tt<}expression{\tt>};
\\ See also & also (\ref{substaalso}),
idnew (\ref{substaidnew}),
idold (\ref{substaidold})
\end{tabular}\vspace{4mm}
\noindent The statement\index{id}\index{identify} tries to match the
pattern\index{pattern}. If the pattern matches one or more times, it will
be replaced by the expression in the r.h.s. taking the possible
wildcard\index{wildcard} substitutions into account. For the description of
the patterns, see chapter \ref{pattern}.
\noindent The options are \vspace{1mm}
\lefttabitem{multi\index{multi}}
\tabitem{This option is for combinations of symbols and dotproducts only
and it does not use wildcard powers. \FORM\ determines how many times the
pattern fits in one pattern matching action. Then the r.h.s. is substituted
to that power. It is the default for these kinds of patterns.}
\lefttabitem{many\index{many}}
\tabitem{This is the default for patterns that contain other objects
than symbols and dotproducts. The pattern is matched and taken out. Then
\FORM\ tries again to match the pattern in the remainder of the term. This
is repeated until there is no further match. Then for each match the r.h.s.
is substituted (with its own wildcard substitutions).}
\lefttabitem{select\index{select}}
\tabitem{This option should be followed by one or more sets\index{set}. After
the sets the pattern can be specified. The pattern will only be substituted
if none of the objects mentioned in the sets will be left after the pattern
has been taken out. This holds only for objects 'at ground level'; i.e. the
pattern matcher will not look inside function arguments for this. Note
that this is a special case of the option 'only'.}
\lefttabitem{once\index{once}}
\tabitem{The pattern is matched only once, even if it occurs more than once
in the term. The first match that \FORM\ encounters is taken. When wildcards
are involved, this may depend on the order of declaration of variables. It
could also be installation dependent. Also the setting of
properorder\index{properorder} (see \ref{substaon} and \ref{substaoff})
could be relevant. Try to write programs in such a way that the outcome
does not depend on which match is taken.}
\lefttabitem{only\index{only}}
\tabitem{The pattern will match only if there is an exact match in
the powers of the symbols and dotproducts present.}
\lefttabitem{ifmatch$-\!\!>$\index{ifmatch}}
\tabitem{This option should be followed by the name (or number) of a
label\index{label}. If the pattern matches, the replacement will be made
after which the execution continues at the label.}
\lefttabitem{ifnomatch$-\!\!>$\index{ifmatch}}
\tabitem{This option should be followed by the name (or number) of a
label\index{label}. If the pattern does not match,
execution continues at the label.}
\lefttabitem{disorder\index{disorder}}
\tabitem{This option is used for products of
noncommuting\index{noncommuting} functions\index{functions!noncommuting} or
tensors\index{tensors!noncommuting}. The match will only take place if the
order of the functions in the match is different from what \FORM\ would have
made of it if the functions would be commuting\index{commuting}. Hence if
the functions in the term are in the order that \FORM\ would give them if
they would be commuting (which depends on the order of declaration) there
will be no match. This can be rather handy when using wildcards as in {\tt
F(a?)*F(b?)}.}
\lefttabitem{all\index{all}}
\tabitem{This option is rather special in that it generates all possible
matches one by one. Normally, when there are many possible matches, \FORM\
takes the first one it encounters. In the case of the all option it will
run through all possible matches and produce all of them. There are however
severe restrictions. First of all, other options are not allowed
simultaneously, although ifmatch$-\!\!>$ and ifnomatch$-\!\!>$ are allowed
because technically they are no options that concern the pattern matching.
In addition it is not allowed to be in an idold/also statement, and it
cannot be followed by such a statement. Most severely: it can have only
functions in the left hand side. These functions can have all kinds of
arguments, but outside the functions symbols, vectors, dotproducts etc. are
not allowed. This is due to the fact that the backtracking when a wildcard
combination fails, does not include such objects and it is this
backtracking mechanism that is used to generate all matches. For the
purpose of the all option tensors and unsubstituted tables count as
functions. It should also be known that the all option cannot be used in
the if(match()) construction. It would not make sense there anyway.}
\noindent Example:
\begin{verbatim}
Vector Q,p1,...,p5,q1,...,q5;
Cfunction V(s),replace;
Format 60;
* This is a t1 topology:
L F = V(Q,p1,p4)*V(p1,p2,p5)*
V(p2,p3,Q)*V(p3,p4,p5);
$t = term_;
id,all,$t*replace_(,...,) =
$t*replace(,...,);
Print +s;
.end
F =
+ V(Q,p1,p4)*V(Q,p2,p3)*V(p1,p2,p5)*V(p3,p4,p5)*
replace(p1,q1,p2,q2,p3,q3,p4,q4,p5,q5)
+ V(Q,p1,p4)*V(Q,p2,p3)*V(p1,p2,p5)*V(p3,p4,p5)*
replace(p2,q1,p1,q2,p4,q3,p3,q4,p5,q5)
+ V(Q,p1,p4)*V(Q,p2,p3)*V(p1,p2,p5)*V(p3,p4,p5)*
replace(p3,q1,p4,q2,p1,q3,p2,q4,p5,q5)
+ V(Q,p1,p4)*V(Q,p2,p3)*V(p1,p2,p5)*V(p3,p4,p5)*
replace(p4,q1,p3,q2,p2,q3,p1,q4,p5,q5)
;
\end{verbatim}
This program produces all renumberings of the momenta in the t1 topology
that produce the same topology. The interesting thing here is that one does
not have to know the topology to produce all topologically equivalent
terms.
There are two options in the id,all statement: \hfill \\
\lefttabitem{all(n[ormalize])}
\tabitem{Here the final answer is divided by the number of matches. In the
example above that would be 4.}
\lefttabitem{all($<$number$>$)}
\tabitem{The number between the parentheses will be the maximum number of
matches allowed. This means that once this number is reached, no further
matches are produced.}
\vspace{10mm}
%--#] identify :
%--#[ idnew :
\section{idnew}
\label{substaidnew}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & idn[ew] [{\tt<}options{\tt>}] {\tt<}pattern{\tt>} = {\tt<}expression{\tt>};
\\ See also & identify (\ref{substaidentify}),
also (\ref{substaalso}),
idold (\ref{substaidold})
\end{tabular} \vspace{4mm}
\noindent This statement\index{idnew} and its options are completely
identical to the regular id\index{id} or identify\index{identify} statement
(see \ref{substaidentify}). \vspace{10mm}
%--#] idnew :
%--#[ idold :
\section{idold}
\label{substaidold}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & ido[ld] [{\tt<}options{\tt>}] {\tt<}pattern{\tt>} = {\tt<}expression{\tt>};
\\ See also & identify (\ref{substaidentify}),
also (\ref{substaalso}),
idnew (\ref{substaidnew})
\end{tabular}\vspace{4mm}
\noindent This statement\index{idold} and its options are completely
identical to the regular also\index{also} statement (see \ref{substaalso}).
The options are described with the id\index{id} or identify\index{identify}
statement (see \ref{substaidentify}).
\vspace{10mm}
%--#] idold :
%--#[ if :
\section{if}
\label{substaif}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & if ( {\tt<}condition{\tt>} ); \\
& if ( {\tt<}condition{\tt>} ) {\tt<}executable statement{\tt>}
\\ See also & elseif (\ref{substaelseif}),
else (\ref{substaelse}),
endif (\ref{substaendif})
\end{tabular} \vspace{4mm}
\noindent Used\index{if} for executing parts of code only when certain
conditions\index{condition} are met. Works together with the
else\index{else} statement (see \ref{substaelse}), the elseif\index{elseif}
statement (see \ref{substaelseif}) and the endif\index{endif} statement
(see \ref{substaendif}). There are two versions. In the first the if
statement must be accompanied by at least an endif statement. In that case
the statements between the if statement and the endif statement will be
executed if the condition is met. It is also possible to use elseif and
else statements to be more flexible. This is done in the same way as in
almost all computer languages.
\noindent In the second form the if statement does not terminate with a
semicolon\index{semicolon}. It is followed by a single regular statement.
No endif statement should be used. The single statement will be executed if
the condition is met.
\noindent The condition in the if statement should be enclosed by
parentheses. Its primary components are:
\leftvitem{3.5cm}{count()\index{count}}
\rightvitem{13cm}{Returns an integer power counting value for the current
term. Should have arguments that come in pairs. The first element of the
pair is a variable. The second is its integer weight\index{weight}. The
types of variables that are allowed are symbols, dotproducts, functions,
tensors, tables and vectors. The weights can be positive as well as
negative. They have to be short integers (Absolute value $< 2^{15}$ on
32\index{32 bits} bit computers and $< 2^{31}$ on 64\index{64 bits} bit
computers). The vectors can have several options appended to their name.
This is done by putting a + after the name of the vector and have this
followed by one or more of the following letters:
\noindent \begin{tabular}{ll}
v & Loose vectors with an index are taken into account. \\
d & Vectors inside dotproducts are taken into account. \\
f & Vectors inside tensors are taken into account. \\
?set &
\begin{minipage}[t]{11cm}{The set should be a set of functions. Vectors inside
the functions that are members of the set are taken into account. It is
assumed that those functions are linear in the given vector}\end{minipage}
\end{tabular} \vspace{1mm}
When no options are specified the result is identical to +vfd.}
\leftvitem{3.5cm}{match()\index{match}}
\rightvitem{13cm}{The argument of the match condition can be any left hand
side of an id statement, including options as once\index{once},
only\index{only}, multi\index{multi}, many\index{many} and
select\index{select} (see \ref{substaidnew}). The id of the id statement
should not be included. \FORM\ will invoke the pattern\index{pattern matcher}
matcher and see how many times the pattern matches. This number is
returned. In the case of once or only this is of course at most one.}
\leftvitem{3.5cm}{expression()\index{expression}}
\rightvitem{13cm}{The argument(s) of this condition is/are a list of
expressions. In the case that the current term belongs to any of the given
expressions the return value is 1. If it does not belong to any of the
given expressions the return value is 0.}
\leftvitem{3.5cm}{occurs()\index{expression}}
\rightvitem{13cm}{The argument(s) of this condition is/are a list of
variables. In the case that any of the variables occurs inside the current
term (including inside function arguments) the
return value is 1. Otherwise the return value is zero.}
\leftvitem{3.5cm}{findloop()\index{findloop}}
\rightvitem{13cm}{The arguments are as in the
replaceloop\index{replaceloop} statement (see \ref{substareplaceloop}) with
the exception of the outfun which should be omitted. If \FORM\ detects an
index\index{index loop} loop in the current term that fulfils the specified
conditions the return value is 1. It is 0 otherwise.}
\leftvitem{3.5cm}{multipleof()\index{multipleof}}
\rightvitem{13cm}{The argument should be a positive integer. This object is
to be compared with a number (could be obtained from a condition) and if
this number is an integer multiple of the argument there will be a match.
If should be obvious that such a compare only makes sense for the == and !=
operators.}
\leftvitem{3.5cm}{$<$integer$>$}
\rightvitem{13cm}{To be compared either with another number, the result of a
condition or a multipleof object.}
\leftvitem{3.5cm}{coefficient\index{coefficient}}
\rightvitem{13cm}{Represents the coefficient of the current term.}
\leftvitem{3.5cm}{\$-variable}
\rightvitem{13cm}{Will be evaluated at runtime when the if statement is
encountered. Should evaluate into a numerical value. If it does not, an
error will result.}
\noindent All the above primary components result in numerical objects.
Such objects can be compared to each other in structures of the type
$<$obj1$>$ $<$operator$>$ $<$obj2$>$. The result of such a compare is
either true (or 1) or false (or 0). The operators are:
\leftvitem{2cm}{$>$}
\rightvitem{14cm}{Results in true if object 1 is greater than object 2.}
\leftvitem{2cm}{$<$}
\rightvitem{14cm}{Results in true if object 1 is less than object 2.}
\leftvitem{2cm}{$=$}
\rightvitem{14cm}{Same as ==.}
\leftvitem{2cm}{$==$}
\rightvitem{14cm}{Results in true if both objects have the same value.}
\leftvitem{2cm}{$>=$}
\rightvitem{14cm}{Results in true if object 1 is greater than or equal to object 2.}
\leftvitem{2cm}{$<=$}
\rightvitem{14cm}{Results in true if object 1 is less than or equal to object 2.}
\leftvitem{2cm}{$!=$}
\rightvitem{14cm}{Results in true if object 1 does not have the same value
as object 2.}
If the condition for true is not met, false is returned. Several of the
above compares can be combined with logical operators. For this it is
necessary to enclose the above compares within parentheses. This forces
\FORM\ to interpret the hierarchy\index{hierarchy} of the operators
properly. The extra logical operators are
\leftvitem{2cm}{$||$}
\rightvitem{14cm}{The or operation. True if at least one of the objects 1
and 2 is true (or nonzero). False or zero if both are false or zero.}
\leftvitem{2cm}{$\&\&$}
\rightvitem{14cm}{The and operation. True if both the objects 1
and 2 are true (or nonzero). False or zero if at least one is false or zero.}
\noindent Example:
\begin{verbatim}
if ( ( match(f(1,x)*g(?a)) && ( count(x,1,v+d,1) == 3 ) )
|| ( expression(F1,F2) == 0 ) );
some statements
endif;
if ( ( ( match(f(1,x)*g(?a)) == 0 ) && ( count(x,1,v+d,1) == 3 ) )
|| expression(F1,F2) );
some statements
endif;
\end{verbatim}
We see that \verb:match(): is equivalent to \verb:( match() != 0 ): and
something similar for \verb:expression():. This shorthand\index{shorthand}
notation can make a program slightly more readable.
{\bf Warning! } The if-statement knows only logical values as the result of
operations. Hence the answer to anything that contains parenthesis (which
counts as the evaluation of an expression) is either true (1) or false (0).
Hence the object (5) evaluates to true. \vspace{10mm}
%--#] if :
%--#[ ifmatch :
\section{ifmatch}
\label{substaifmatch}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & ifmatch$-\!\!>$ {\tt<}label{\tt>} {\tt<}pattern{\tt>} = {\tt<}expression{\tt>};
\\ See also & identify (\ref{substaidentify})
\end{tabular} \vspace{4mm}
\noindent This statement\index{ifmatch} is identical to the ifmatch option
of the id statement (see \ref{substaidentify}). Hence
\begin{verbatim}
ifmatch-> ....
\end{verbatim}
is just a shorthand notation for
\begin{verbatim}
id ifmatch-> ....
\end{verbatim}
\vspace{10mm}
%--#] ifmatch :
%--#[ ifnomatch :
\section{ifnomatch}
\label{substaifnomatch}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & ifnomatch$-\!\!>$ {\tt<}label{\tt>} {\tt<}pattern{\tt>} = {\tt<}expression{\tt>};
\\ See also & identify (\ref{substaidentify})
\end{tabular} \vspace{4mm}
\noindent This statement\index{ifnomatch} is identical to the ifnomatch option
of the id statement (see \ref{substaidentify}). Hence
\begin{verbatim}
ifnomatch-> ....
\end{verbatim}
is just a shorthand notation for
\begin{verbatim}
id ifnomatch-> ....
\end{verbatim}
\vspace{10mm}
%--#] ifnomatch :
%--#[ index :
\section{index, indices}
\label{substaindex}
\noindent \begin{tabular}{ll}
Type & Declaration statement\\
Syntax & i[ndex] {\tt<}list of indices to be declared{\tt>}; \\
& i[ndices] {\tt<}list of indices to be declared{\tt>};
\\ See also & dimension (\ref{substadimension}),
fixindex (\ref{substafixindex})
\end{tabular} \vspace{4mm}
\noindent Declares one or more indices\index{index}\index{indices}. In the
declaration of an index one can specify its dimension\index{dimension}.
This is done by appending one or two options to the name of the index to be
declared:\vspace{4mm}
\leftvitem{3.5cm}{name=dim}
\rightvitem{13cm}{The dimension is either a nonnegative integer or a
previously declared symbol. If the dimension is zero\index{zero!dimension}
this means that no dimension is attached to the index. The consequence is
that the index cannot be summed over and index contractions are not
performed for this index. If no dimension is specified the default
dimension will be assumed (see the dimension statement
\ref{substadimension}).}
\leftvitem{3.5cm}{name=dim:ext}
\rightvitem{13cm}{The dimension is a symbol as above. Ext is an extra
symbol which indicates the value of dim-4. This option is useful when
traces over gamma matrices are considered (see \ref{substatrace} and
\ref{substatracen}).} \vspace{10mm}
%--#] index :
%--#[ inexpression :
\section{inexpression}
\label{substainexpression}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & inexpression,name(s) of expression(s);
\\ See also & endinexpression~(\ref{substaendinexpression})
\end{tabular} \vspace{4mm}
\noindent The combination\index{inexpression}
\begin{verbatim}
InExpression,expr;
Statements;
EndInExpression;
\end{verbatim}
is a more readable version of the construction
\begin{verbatim}
if ( expression(expr) );
Statements;
endif;
\end{verbatim}
\vspace{10mm}
%--#] inexpression :
%--#[ inparallel :
\section{inparallel}
\label{substainparallel}
\noindent \begin{tabular}{ll}
Type & Specification statement\\
Syntax & inparallel; \\
& inparallel {\tt<}list of expressions{\tt>};
\\ See also & NotInParallel (\ref{substanotinparallel}),
ModuleOption (\ref{substamoduleoption})
\end{tabular} \vspace{4mm}
\noindent This statement is only active in the context of
\TFORM\index{TFORM}. It causes
(small) expressions to be executed side by side. Normally the terms of
expressions are distributed over the processors and the expressions are
executed one by one. This isn't very efficient for small expressions
because there is a certain amount of overhead. When there are many small
expressions, this statement can cause each expression to be executed by its
own processor. A consequence is that the expressions now can finish in a
semi-random order and hence may end up in the output in a order that is
different from when this statement isn't used. The proper order is restored
in the first module that comes after and that doesn't use this option. One
should be careful using this statement for big expressions, because in that
case the sorting may need sort files and the output may temporarily need
scratch files and the simultaneous use of many files can slow execution
down significantly.
\noindent In the case that no expressions are mentioned, all active
expressions will be affected. When there is a list of expressions, only
those mentioned will be affected, provided they are active. Several of
these statements will work cumulatively. This statement doesn't affect
expressions that are still to be defined inside the current module. If it
is needed to affect such expressions inside the current module, one should
use the InParallel option of the
ModuleOption~\ref{substamoduleoption}\index{ModuleOption}
statement. This statement works independently of the `On
Parallel;'~\ref{substaon} and `Off Parallel;'~\ref{substaoff} statements.
\vspace{10mm}
%--#] inparallel :
%--#[ inside :
\section{inside}
\label{substainside}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & inside {\tt<}list of \$-variables{\tt>};
\\ See also & endinside (\ref{substaendinside}) and the chapter on \$-variables
(\ref{dollars})
\end{tabular} \vspace{4mm}
\noindent works\index{inside} a bit like the argument\index{argument}
statement (see \ref{substaargument}) but with
\$-variables\index{\$-variable} instead of with functions. An inside
statement should be paired with an endinside\index{endinside} statement
(see \ref{substaendinside}) inside the same module. The statements
in-between will then be executed on the contents of the \$-variables that
are mentioned. One should pay some attention to the order of the action.
The \$-variables are treated sequentially. Hence, after the first one has
been treated its contents are substituted by the new value. Then the second
one is treated. If it uses the contents of the first variable, it will use
the new value. If the first variable uses the contents of the second
variable it will use its old value. Redefining any of the listed
\$-variables in the range of the `inside-environment' is very dangerous. It
is not specified what \FORM\ will do. Most likely it will be
unpleasant\index{unpleasant}.
\vspace{10mm}
%--#] inside :
%--#[ insidefirst :
\section{insidefirst}
\label{substainsidefirst}
\noindent \begin{tabular}{ll}
Type & Declaration statement\\
Syntax & insidefirst {\tt<}on/off{\tt>};
\\ See also & on (\ref{substaon}), off (\ref{substaoff})
\end{tabular} \vspace{4mm}
\noindent This statement\index{insidefirst} is obsolete\index{obsolete}.
The user should try to use the insidefirst option of the on (see
\ref{substaon}) or the off (see \ref{substaoff}) statements. \vspace{10mm}
%--#] insidefirst :
%--#[ intohide :
\section{intohide}
\label{substaintohide}
\noindent \begin{tabular}{ll}
Type & Specification statement\\
Syntax & intohide; \\
& intohide {\tt<}list of expressions{\tt>};
\\ See also & hide (\ref{substahide})
\end{tabular} \vspace{4mm}
\noindent In the first variety this statement marks all currently active
expressions for being put in hidden\index{hide} storage at the end of the
module, after it has been processed. In the second variety it marks only
the specified active\index{active expressions} expressions as such.
\vspace{4mm}
\noindent The difference with the hide (\ref{substahide}) statement is
that in the hide statement the expression is copied immediately into the
hide system and it will not be processed in the current module, while in
the intohide statement the expression is first processed and its final
output in this module is sent to the hide system rather than to the regular
scratch system. The effect is the same as not putting the intohide
statement in the current module and putting a hide statement in the next,
but it saves one copy operation and it is possibly a bit more economical
with the disk space.
\vspace{4mm}
\noindent Note that a .store instruction will simultaneously remove all
expressions from the hide system. \vspace{10mm}
%--#] intohide :
%--#[ keep :
\section{keep}
\label{substakeep}
\noindent \begin{tabular}{ll}
Type & Specification statement\\
Syntax & keep brackets; \\
See also & bracket (\ref{substabracket}), antibracket
(\ref{substaabrackets}) and the chapter on brackets
(\ref{brackets})
\end{tabular} \vspace{4mm}
\noindent The effect\index{keep brackets}\index{keep}\index{brackets!keep}
of this statement is that during execution of the current module the
contents of the brackets are not considered. The statements only act on the
`outside' of the brackets. Only when the terms are considered finished and
are ready for the sorting are they multiplied by the contents of the
brackets. At times this can save much computer time as complicated pattern
matching and multiplications of function arguments with large fractions
have to be done only once, rather than for each complete term separately
(assuming that each bracket contains a large number of terms).
\noindent There can be some nasty side effects. Assume an expression like:
% THIS EXAMPLE IS PART OF THE TESTSUITE. CHANGES HERE SHOULD BE APPLIED THERE AS
% WELL!
\begin{verbatim}
F = f(i1,x)*(g(i1,y)+g(i1,z));
B f;
.sort
Keep Brackets;
sum i1;
\end{verbatim}
the result will be
\begin{verbatim}
F = f(N1_?,x)*g(i1,y)+f(N1_?,x)*g(i1,z);
\end{verbatim}
because at the moment of summing over i1 \FORM\ is not looking inside the
brackets and hence it never sees the second occurrence of i1. There are
some beneficial applications of the keep statement in the
`mincer'\index{mincer} package that comes with the \FORM\ distribution. In
this package the most costly step was made faster by a significant factor
(depending on the problem) due to the keep brackets statement.
\vspace{10mm}
%--#] keep :
%--#[ label :
\section{label}
\label{substalabel}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & la[bel] {\tt<}name of label{\tt>};
\\ See also & goto (\ref{substagoto})
\end{tabular} \vspace{4mm}
\noindent Places a label\index{label} at the current location. The name of
the label can be any name or positive number. Control can be transfered to the
position of the label by a goto\index{goto} statement (see
\ref{substagoto}) or the ifmatch\index{ifmatch} option of an id statement
(see \ref{substaidentify}). The only condition is that the goto statement
and the label must be inside the same module. Once the module is terminated
all existing labels are forgotten. This means that in a later module a
label with the same name can be used again (this may not improve
readability though but it is a good thing when third party libraries are
used). \vspace{10mm}
%--#] label :
%--#[ lfactorized :
\section{lfactorized}
\label{substalfactorized}
\noindent \begin{tabular}{ll}
Type & Definition statement\\
Syntax & l[ocal]factorized {\tt<}name{\tt>} = {\tt<}expression{\tt>};
\\ See also & the chapter on polynomials~\ref{polynomials} and the
factorize statement~\ref{substafactorize}.
\end{tabular} \vspace{4mm}
\noindent Used to define a local\index{local} expression in factorized
notation and keep it that way. The factors are recognized by multiplication
and division signs at lowest bracket level. For the rest the expression is
treated as a regular local expression. Example:
\begin{verbatim}
Symbols x,y,z;
LocalFactorized F1 = 3*(x+y)*(y+z)*((x+z)*(2*x+1));
LocalFactorized F2 = 3*(x+y)*(y+z)+((x+z)*(2*x+1));
Print;
.end
F1 =
( 3 )
* ( y + x )
* ( z + y )
* ( z + x + 2*x*z + 2*x^2 );
F2 =
( z + 3*y*z + 3*y^2 + x + 5*x*z + 3*x*y + 2*x^2 );
\end{verbatim}
\noindent As one can see in the second expression, the plus at ground level
makes that there is only one factor. In the first expression the last
factor is seen as a single factor and not two factor2 because of the extra
parentheses. Only parentheses at ground level are used to recognize
factors. If one needs those factors anyway, one should either leave away
those parentheses or use an extra Factorize statement to have FORM
refactorize the expression.
\vspace{10mm}
%--#] lfactorized :
%--#[ load :
\section{load}
\label{substaload}
\noindent \begin{tabular}{ll}
Type & Declaration statement\\
Syntax & loa[d] {\tt<}filename{\tt>} [{\tt<}list of expressions{\tt>}];
\\ See also & save (\ref{substasave}), delete (\ref{substadelete})
\end{tabular} \vspace{4mm}
\noindent Loads\index{load} a previously saved\index{saved file}
file\index{file!saved} (see \ref{substasave}). If no expressions are
specified all expressions in the file are put in the storage
file\index{file!storage} and obtain the status of stored global
expressions. If a list of expressions is specified all those expressions
are loaded and possible other expressions are ignored. If a specified
expression is not present, an error will result. If one does not know
exactly what expressions are present in a file one could load the file
without a list of expressions, because \FORM\ will list all expressions that
it encountered. \vspace{10mm}
%--#] load :
%--#[ local :
\section{local}
\label{substalocal}
\noindent \begin{tabular}{ll}
Type & Definition statement\\
Syntax & l[ocal] {\tt<}name{\tt>} = {\tt<}expression{\tt>}; \\
& l[ocal] {\tt<}names of expressions{\tt>};
\\ See also & global (\ref{substaglobal})
\end{tabular} \vspace{4mm}
\noindent Used to define a local\index{local} expression. A local
expression is an expression that will be dropped\index{drop} when a
.store\index{.store} instruction is encountered. If this is not what is
intended one should use global\index{global} expressions (see
\ref{substaglobal}). The statement can also be used to change the status of
a global expression into that of a local expression. In that case there is
no = sign and no right hand side. \vspace{10mm}
%--#] local :
%--#[ makeinteger :
\section{makeinteger}
\label{substamakeinteger}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & makeinteger [{\tt<}argument specifications{\tt>}] \\ &
\ \ \ \ \ \ \ \ \ \ \ \ \ \ \
\verb:{:{\tt<}name of function/set{\tt>}
[{\tt<}argument specifications{\tt>}]\verb:}:; \\
See also & normalize (\ref{substanormalize})
\end{tabular} \vspace{4mm}
\noindent Normalizes\index{makeinteger} the indicated
argument\index{argument} of the indicated functions(s) in such a way that
all terms in this argument have integer
coefficients\index{coefficients!integer} with a their greatest common
divider being one. This still leaves the possibility that the first term of
this argument may be negative. If this is not desired one can first
normalize\index{normalize} the argument and then make its coefficients
integer. The overall factor that is needed to make the coefficients like
described is taken from the overall factor of the complete term. Example:
% THIS EXAMPLE IS PART OF THE TESTSUITE. CHANGES HERE SHOULD BE APPLIED THERE AS
% WELL!
\begin{verbatim}
S a,b,c;
CF f;
L F = f(22/3*a+14/5*b+18/7*c);
MakeInteger,f;
Print +f;
.end
F =
2/105*f(135*c + 147*b + 385*a);
\end{verbatim}
\noindent Note that this feature can be used to make outputs look much more
friendly. It can be used in combination with the
AntiBracket\index{antibracket} statement (\ref{substaabrackets}) and the
function dum\_\index{dum\_} (\ref{fundum}) to imitate a smart extra level
of brackets and make outputs shorter.
It is possible to introduce a scale factor when extracting the coefficient
and multiplying it into the complete term.
\leftvitem{4cm}{MakeInteger,$\wedge$,f;}
\rightvitem{12cm}{The number n must be an integer (may be negative) and if
the coefficient that is extracted is c the whole term is multiplied by the
factor $c^n$.}
\vspace{10mm}
%--#] makeinteger :
%--#[ many :
\section{many}
\label{substamany}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & many {\tt<}pattern{\tt>} = {\tt<}expression{\tt>};
\\ See also & identify (\ref{substaidentify})
\end{tabular} \vspace{4mm}
\noindent This statement\index{many} is identical to the many option of the
id\index{id} statement (see \ref{substaidentify}). Hence
\begin{verbatim}
many ....
\end{verbatim}
is just a shorthand notation for
\begin{verbatim}
id many ....
\end{verbatim}
\vspace{10mm}
%--#] many :
%--#[ merge :
%
\section{merge}
\label{substamerge}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & merge,functionname; \\
& merge,once,functionname;
\\ See also & shuffle (\ref{substashuffle})
\end{tabular} \vspace{4mm}
\noindent This statement is exactly the same as the shuffle\index{shuffle}
statement (see \ref{substashuffle}).
\vspace{10mm}
%
%--#] merge :
%--#[ metric :
\section{metric}
\label{substametric}
\noindent \begin{tabular}{ll}
Type & Declaration statement\\
Syntax & metric {\tt<}option{\tt>};
\end{tabular}
\smallskip
\noindent Remark: statement\index{metric} is inactive\index{inactive}.
Should have no effect.
\vspace{10mm}
%--#] metric :
%--#[ moduleoption :
\section{moduleoption}
\label{substamoduleoption}
\noindent \begin{tabular}{ll}
Type & Module control statement\\
Syntax & moduleoption {\tt<}option{\tt>}[,{\tt<}value{\tt>}];
\\ See also & polyfun (\ref{substapolyfun}),
processbucketsize (\ref{substaprocessbucketsize}),
dollar variables (\ref{pardollars})
\end{tabular} \vspace{4mm}
\noindent Used\index{moduleoption} to set a mode for just the current
module. It overrides the normal setting and will revert to this normal
setting after this module. The settings are:
\leftvitem{3.5cm}{parallel\index{moduleoption!parallel}}
\rightvitem{13cm}{Allows parallel\index{parallel} execution of the current module if all
other conditions are right. This is the default.}
\leftvitem{3.5cm}{noparallel\index{moduleoption!noparallel}}
\rightvitem{13cm}{Vetoes parallel\index{parallel} execution of the current module.}
\leftvitem{3.5cm}{inparallel\index{moduleoption!inparallel}}
\rightvitem{13cm}{This option is more or less equivalent to the
InParallel~\ref{substainparallel} statement. The difference is that because
this statement comes at the end of the module, its effects include also the
expressions that have been defined inside the current module. This is not
the case for the InParallel statement. The InParallel option can be
followed by the names of expressions. If no such names are present, all
active expressions are affected. Otherwise only the expressions that are
mentioned are affected. Once this option is mentioned no more options can
be used inside the same ModuleOption statement. This is to avoid potential
confusion that could arise when expressions are used with a name identical
to the name of one of the options.}
\leftvitem{3.5cm}{notinparallel\index{moduleoption!notinparallel}}
\rightvitem{13cm}{This option is more or less equivalent to the
NotInParallel~\ref{substanotinparallel} statement. The difference is that
because this statement comes at the end of the module, its effects include
also the expressions that have been defined inside the current module. This
is not the case for the NotInParallel statement. The NotInParallel option
can be followed by the names of expressions. If no such names are present,
all active expressions are affected. Otherwise only the expressions that
are mentioned are affected. Once this option is mentioned no more options
can be used inside the same ModuleOption statement. This is to avoid
potential confusion that could arise when expressions are used with a name
identical to the name of one of the options.}
\leftvitem{3.5cm}{polyfun\index{moduleoption!polyfun}}
\rightvitem{13cm}{Possibly followed by the name of a
`polyfun'\index{polyfun}. Is similar to the polyfun statement (see
\ref{substapolyfun}) but only valid for the current module.}
\leftvitem{3.5cm}{polyratfun\index{moduleoption!polyfun}}
\rightvitem{13cm}{Possibly followed by the name of a
`polyratfun'\index{polyratfun}. Is similar to the polyfun statement (see
\ref{substapolyratfun}) but only valid for the current module. If there is
second name, it refers to the inverse polyratfun. More complicated options
of the polyratfun statement cannot be used here.}
\leftvitem{3.5cm}{processbucketsize\index{moduleoption!processbucketsize}}
\rightvitem{13cm}{Followed by a number. Similar to the
processbucketsize\index{processbucketsize}
statement (see \ref{substaprocessbucketsize}) but only valid for the current
module.}
\leftvitem{3.5cm}{local\index{moduleoption!local}}
\rightvitem{13cm}{Should be followed by a list of \$-variables. Indicates
that the contents of the indicated \$-variables\index{\$-variable} are not
relevant once the module has been finished and neither is the term by term
order in which the \$-variables obtain their value. In practise each
processor\index{processor}/thread\index{thread} will work with its own copy
of this variable.}
\leftvitem{3.5cm}{maximum\index{moduleoption!maximum}}
\rightvitem{13cm}{Should be followed by a list of
\$-variables\index{\$-variable}. Indicates that of the contents of the
indicated \$-variables the maximum is the only thing that is relevant once
the module has been finished. The term by term order in which the
\$-variables obtain their value is not relevant.}
\leftvitem{3.5cm}{minimum\index{moduleoption!minimum}}
\rightvitem{13cm}{Should be followed by a list of
\$-variables\index{\$-variable}. Indicates that of the contents of the
indicated \$-variables the minimum is the only thing that is relevant once
the module has been finished. The term by term order in which the
\$-variables obtain their value is not relevant.}
\leftvitem{3.5cm}{sum\index{moduleoption!sum}}
\rightvitem{13cm}{Should be followed by a list of
\$-variables\index{\$-variable}. Indicates that the indicated \$-variables
are representing a sum. The term by term order in which the \$-variables
obtain their value is not relevant.}
\noindent The options `local', `maximum', `minimum' and `sum' are for
parallel versions of \FORM. The presence of \$-variables can be a problem
when the order of processing of the terms is not well defined. These
options tell \FORM\ what these \$-variables are used for. In the above
cases \FORM\ can take the appropriate action when gathering information
from the various processors. This will allow
parallel\index{parallel execution} execution of the current module. If
\$-variables are used in a module and they are defined on a term by term
basis, the normal action of \FORM\ will be to veto parallel execution unless
it is clear that no confusion can occur. See also chapter \ref{parallel} on
the parallel version and section \ref{pardollars} on the dollar variables.\vspace{10mm}
%--#] moduleoption :
%--#[ modulus :
\section{modulus}
\label{substamodulus}
\noindent \begin{tabular}{ll}
Type & Declaration statement\\
Syntax & m[odulus] [option(s)] {\tt<}value{\tt>};
\end{tabular} \vspace{4mm}
\noindent Defines all calculus to be modulus\index{modulus} the given
integer value, provided this number is positive.
% If this number is less than the
%(installation dependent but at least 10000) maximum power for symbols and
%dotproducts the powers of symbols and dotproducts are reduced with the
%relation $x^{value} = x$.
\noindent The modulus calculus extends itself to
fractions\index{fractions}. This means that if the value is not a prime
number division by zero could result. It is the responsibility of the user
to avoid such problems.
\noindent When the value in the modulus statement is either 0 or 1 the
statement would be meaningless. It is used as a signal to \FORM\ that modulus
calculus should be switched off again.
The options are
\begin{description}
\item[NoFunctions] Modulus calculus is not performed inside function
arguments.
\item[AlsoFunctions] Modulus calculus is also performed inside function
arguments.
\item[CoefficientsOnly] Modulus calculus is neither performed inside function
arguments nor on powers of symbols.
\item[PlusMin] The values of numbers are reduced to the range
$(-value+1)/2$ to $(value-1)/2$.
\item[Positive] The values of numbers are reduced to the range $0$ to
$value-1$.
\item[NoDollars] The modulus calculus is not performed inside dollar
variables.
\item[AlsoDollars] The modulus calculus is performed also inside dollar
expressions.
\item[InverseTable] To speed up calculations all inverses are computed by
means of a table. If the modulus value is very big, this table may be too
big for the memory. That would result in an error message.
\item[NoInverseTable] No Table of Inverses is constructed. They are
calculated whenever needed.
\item[AlsoPowers] Reduction is also used on powers of symbols with the
relation $x^mod = x$ if mod is the given value
\item[NoPowers] No reduction on powers is done.
\item[PrintPowersOf] The proper syntax is here printpowersof(generator) in
which generator is supposed to be a generator for calculus modulus the
given value, which means that all numbers will be written as a power of the
generator. If the number turns out not to be a proper generator an error
will be given. Note that finding the powers is done by means of the
construction of a table. Hence, if the modulus value is very big the table
might not fit inside memory. This will result in an error message.
\end{description}
The default mode is NoFunctions, Positive, NoInverseTable, NoDollars,
NoPowers.
The current syntax (version 4.0 and later) differs slightly from the
previous syntax. As however there were many bugs in the old implementation
we suspect that a slight change of the options does not inconvenience any
many users.
%--#] modulus :
%--#[ multi :
\section{multi}
\label{substamulti}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & multi {\tt<}pattern{\tt>} = {\tt<}expression{\tt>};
\\ See also & identify (\ref{substaidentify})
\end{tabular} \vspace{4mm}
\noindent This statement is identical to the multi\index{multi} option of
the id\index{id} statement (see \ref{substaidentify}). Hence
\begin{verbatim}
multi ....
\end{verbatim}
is just a shorthand notation for
\begin{verbatim}
id multi ....
\end{verbatim}
\vspace{10mm}
%--#] multi :
%--#[ multibracket : ????????????
%
%\section{multibracket}
%\label{substamultibracket}
%
%\noindent \begin{tabular}{ll}
%Type & Output control statement\\
%Syntax & multibracket ??????????????
%\\ See also & bracket (\ref{substabracket})
%\end{tabular} \vspace{4mm}
%
%\vspace{10mm}
%
%--#] multibracket :
%--#[ multiply :
\section{multiply}
\label{substamultiply}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & mu[ltiply] [{\tt<}option{\tt>}] {\tt<}expression{\tt>};
\end{tabular} \vspace{4mm}
\noindent Statement multiplies\index{multiply} all terms by the given
expression. It is advisable to use the options when noncommuting variables
are involved. They are:\vspace{1mm}
\lefttabitem{left\index{multiply!left}}
\tabitem{Multiplication is from the left.}
\lefttabitem{right\index{multiply!right}}
\tabitem{Multiplication is from the right.}
\noindent There is no guarantee\index{guarantee} as to what the default is
with respect to multiplication from the left or from the right. It is up to
{\FORM} to decide what it considers to be most efficient when neither
option is present. \vspace{4mm}
\noindent Note that one should not abbreviate this command to `multi',
because there is a separate multi\index{multi} command (see
\ref{substamulti}). \vspace{10mm}
%--#] multiply :
%--#[ ndrop :
\section{ndrop}
\label{substandrop}
\noindent \begin{tabular}{ll}
Type & Specification statement\\
Syntax & ndrop; \\
& ndrop {\tt<}list of expressions{\tt>};
\\ See also & drop (\ref{substadrop})
\end{tabular} \vspace{4mm}
In the first variety\index{ndrop} this statement cancels all
drop\index{drop} plans. This means that all expressions scheduled for being
dropped will be restored to their previous status of local or global
expressions. In the second variety this happens only to the expressions
that are specified. Example:
\begin{verbatim}
Drop;
Ndrop F1,F2;
\end{verbatim}
This drops all expressions, except for the expressions \verb:F1: and
\verb:F2:. \vspace{10mm}
%--#] ndrop :
%--#[ nfactorize :
\section{nfactorize}
\label{substanfactorize}
\noindent \begin{tabular}{ll}
Type & Output control statement\\
Syntax & nfactorize \verb:{:{\tt<}name of expression(s){\tt>}\verb:}:;
\\ See also & the chapter on polynomials~\ref{polynomials} and
\ref{substafactorize}.
\end{tabular} \vspace{4mm}
\noindent When one uses a factorize (see \ref{substafactorize}) statement
without arguments all expressions will be marked for factorization. If one
would like to exclude a few expressions this can be done with the
NFactorize statement. There should be at least one expression mentioned as
in:
\begin{verbatim}
Factorize;
NFactorize expr12,expr29;
\end{verbatim}
One can also use the Factorize statement with a number of expressions after
which the NFactorize statement can remove some from the list again as in:
\begin{verbatim}
Factorize expr1,...,expr100;
NFactorize expr12,expr29;
\end{verbatim}
\vspace{10mm}
%--#] nfactorize :
%--#[ nfunctions :
\section{nfunctions}
\label{substanfunctions}
\noindent \begin{tabular}{ll}
Type & Declaration statement\\
Syntax & n[functions] {\tt<}list of functions to be declared{\tt>}; \\
See also & functions (\ref{substafunctions}), cfunctions (\ref{substacfunctions})
\end{tabular} \vspace{4mm}
\noindent This statement\index{nfunction} declares
noncommuting\index{noncommuting} functions. It is equal to the
function\index{function} statement (see \ref{substafunctions}) which has
the noncommuting property as its default. \vspace{10mm}
%--#] nfunctions :
%--#[ nhide :
\section{nhide}
\label{substanhide}
\noindent \begin{tabular}{ll}
Type & Specification statement\\
Syntax & nhide; \\
& nhide {\tt<}list of expressions{\tt>};
\\ See also & hide (\ref{substahide}),
unhide (\ref{substaunhide}),
nunhide (\ref{substanunhide}),
pushhide (\ref{substapushhide}),
pophide (\ref{substapophide})
\end{tabular} \vspace{4mm}
\noindent In its first variety\index{nhide} this statement undoes all
hide\index{hide} plans that exist thus far in the current module. In the
second variety it does this only for the specified active\index{active}
expressions. See the hide statement in \ref{substahide}. Example:
\begin{verbatim}
Hide;
Nhide F1,F2;
\end{verbatim}
Here all active expressions will be transferred to the hide file except for
the expressions \verb:F1: and \verb:F2:. \vspace{10mm}
%--#] nhide :
%--#[ normalize :
\section{normalize}
\label{substanormalize}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & normalize options \verb:{:{\tt<}name of function/set{\tt>}
[{\tt<}argument specifications{\tt>}]\verb:}:;
\\ See also & argument (\ref{substaargument}), splitarg
(\ref{substasplitarg}), makeinteger (\ref{substamakeinteger})
\end{tabular} \vspace{4mm}
\noindent Normalizes\index{normalize} the indicated
arguments\index{argument} of the indicated functions. Normalization means
that the argument will be multiplied by the inverse of its
coefficient\index{coefficient}
(provided it is not zero). This holds for single term arguments. For
multiple term arguments the inverse of the coefficient of the first term of
the argument is used. The options and the argument specifications are as in
the SplitArg\index{splitarg} statement (see \ref{substasplitarg}). Under normal
circumstances the coefficient that is removed from the argument(s) is
multiplied into the coefficient of the term. This can be avoid with the
extra option
\verb:(0):. Hence
\leftvitem{4cm}{Normalize,f;}
\rightvitem{12cm}{changes {\tt f(2*x+3*y)} into {\tt 2*f(x+3/2*y)} but}
\leftvitem{4cm}{Normalize,(0),f;}
\rightvitem{12cm}{changes {\tt f(2*x+3*y)} into {\tt f(x+3/2*y)}.}
A more flexible way to extract the coefficient of the (first) term is by
providing a scale factor as in
\leftvitem{4cm}{Normalize,$\wedge$,f;}
\rightvitem{12cm}{The number n must be an integer (may be negative) and if
the coefficient of the first term was c the whole term is multiplied by the
factor $c^n$.}
\vspace{10mm}
%--#] normalize :
%--#[ notinparallel :
\section{notinparallel}
\label{substanotinparallel}
\noindent \begin{tabular}{ll}
Type & Specification statement\\
Syntax & notinparallel; \\
& notinparallel {\tt<}list of expressions{\tt>};
\\ See also & InParallel (\ref{substainparallel}),
ModuleOption (\ref{substamoduleoption})
\end{tabular} \vspace{4mm}
\noindent This statement is only active in the context of
\TFORM\index{TFORM}. It vetoes (small) expressions to be executed side by
side. For a complete explanation of this type of running one should look at
the InParallel~\ref{substainparallel} statement. Because the default is
that expressions are executed one by one, the major use of this statement
is in constructions like:
\begin{verbatim}
InParallel;
NotInParallel F1,F25;
\end{verbatim}
which would first mark all expressions to be executed in simultaneous mode
and then make an exception for {\tt F1} and {\tt F25}.
\vspace{10mm}
%--#] notinparallel :
%--#[ nprint :
\section{nprint}
\label{substanprint}
\noindent \begin{tabular}{ll}
Type & Output control statement\\
Syntax & np[rint] {\tt<}list of names of expressions{\tt>};
\\ See also & print (\ref{substaprint})
\end{tabular} \vspace{4mm}
\noindent Statement\index{nprint} is used to take expressions from the list
of expressions to be printed. When a print\index{print} statement is used
(see \ref{substaprint}) without specification of expressions, all active
expressions are marked for printing. With this statement one can remove a
number of them from the list. \vspace{10mm}
%--#] nprint :
%--#[ nskip :
\section{nskip}
\label{substanskip}
\noindent \begin{tabular}{ll}
Type & Specification statement\\
Syntax & nskip; \\
& nskip {\tt<}list of expressions{\tt>};
\\ See also & skip (\ref{substaskip})
\end{tabular} \vspace{4mm}
\noindent In the first variety\index{nskip} it causes the cancellation of
all skip\index{skip} plans (see \ref{substaskip}) for expressions. The
status of these expressions is restored to their previous status (active
local or global expressions). In the second variety this is done for the
specified expressions only. Example:
\begin{verbatim}
Skip;
Nskip F1,F2;
\end{verbatim}
This causes all active expressions to be skipped except for the expressions
\verb:F1: and \verb:F2:. \vspace{10mm}
%--#] nskip :
%--#[ ntable :
\section{ntable}
\label{substantable}
\noindent \begin{tabular}{ll}
Type & Declaration statement\\
Syntax & ntable {\tt<}options{\tt>} {\tt<}table to be
declared{\tt>}; \\
See also & functions (\ref{substafunctions}), table (\ref{substatable}),
ctable (\ref{substactable})
\end{tabular} \vspace{4mm}
\noindent This statement\index{ntable} declares a
noncommuting\index{noncommuting} table\index{table!noncommuting}. For the
rest it is identical to the table\index{table} command (see
\ref{substatable}) which has the commuting property as its default.
\vspace{10mm}
%--#] ntable :
%--#[ ntensors :
\section{ntensors}
\label{substantensors}
\noindent \begin{tabular}{ll}
Type & Declaration statement\\
Syntax & nt[ensors] {\tt<}list of tensors to be declared{\tt>}; \\
See also & functions (\ref{substafunctions}), tensors
(\ref{substatensors}), ctensors (\ref{substactensors})
\end{tabular} \vspace{4mm}
\noindent This statement\index{ntensor} declares
noncommuting\index{noncommuting} tensors\index{tensor!noncommuting}. For
the rest it is equal to the tensor\index{tensor} statement (see
\ref{substatensors}) which has the commuting property as its default.
\noindent The options that exist for properties of tensors are the same as
those for functions (see \ref{substafunctions}). \vspace{10mm}
%--#] ntensors :
%--#[ nunfactorize :
\section{nunfactorize}
\label{substanunfactorize}
\noindent \begin{tabular}{ll}
Type & Output control statement\\
Syntax & nunfactorize \verb:{:{\tt<}name of expression(s){\tt>}\verb:}:;
\\ See also & the chapter on polynomials~\ref{polynomials} and
\ref{substaunfactorize}.
\end{tabular} \vspace{4mm}
\noindent When one uses an UnFactorize (see \ref{substaunfactorize})
statement without arguments all expressions will be marked for being
unfactorized. If one would like to exclude a few expressions this can be
done with the NUnFactorize statement. There should be at least one expression
mentioned as in:
\begin{verbatim}
UnFactorize;
NUnFactorize expr12,expr29;
\end{verbatim}
One can also use the UnFactorize statement with a number of expressions after
which the NUnFactorize statement can remove some from the list again as in:
\begin{verbatim}
UnFactorize expr1,...,expr100;
NUnFactorize expr12,expr29;
\end{verbatim}
\vspace{10mm}
%--#] nunfactorize :
%--#[ nunhide :
\section{nunhide}
\label{substanunhide}
\noindent \begin{tabular}{ll}
Type & Specification statement\\
Syntax & nunhide; \\
& nunhide {\tt<}list of expressions{\tt>};
\\ See also & hide (\ref{substahide}),
nhide (\ref{substanhide}),
unhide (\ref{substaunhide}),
pushhide (\ref{substapushhide}),
pophide (\ref{substapophide})
\end{tabular} \vspace{4mm}
\noindent In its first variety\index{nunhide} this statement undoes all
unhide\index{unhide} (see \ref{substaunhide} and \ref{substahide}) plans
that the system has in the current module. In its second variety this
happens only with the specified expressions. Example:
\begin{verbatim}
Unhide;
Nunhide F1,F2;
\end{verbatim}
All expressions are taken from the hide\index{hide} system, except for the
expressions \verb:F1: and \verb:F2:. \vspace{10mm}
%--#] nunhide :
%--#[ nwrite :
\section{nwrite}
\label{substanwrite}
\noindent \begin{tabular}{ll}
Type & Declaration statement\\
Syntax & nw[rite] {\tt<}keyword{\tt>};
\\ See also & on (\ref{substaon}), off (\ref{substaoff})
\end{tabular} \vspace{4mm}
\noindent This statement\index{nwrite} is considered
obsolete\index{obsolete}. All its varieties have been taken over by the
off\index{off} statement (see \ref{substaoff}) and the on\index{on}
statement (see \ref{substaon}). The current version of {\FORM} will still
recognize it, but the user is advised to avoid its usage. In future
versions of {\FORM} it is scheduled to be used for a different kind of
writing and hence its syntax may change considerably. The conversion
program conv2to3\index{conv2to3} should help in the conversion of programs
that have been written for version 2. For completeness we still give the
syntax and how it should be converted.
The keywords are: \vspace{4mm}
\leftvitem{3.5cm}{stats\index{nwrite!stats}}
\rightvitem{13cm}{Same as: Off stats;}
\leftvitem{3.5cm}{statistics\index{nwrite!statistics}}
\rightvitem{13cm}{Same as: Off statistics;}
\leftvitem{3.5cm}{shortstats\index{nwrite!shortstats}}
\rightvitem{13cm}{Same as: Off shortstats;}
\leftvitem{3.5cm}{shortstatistics\index{nwrite!shortstatistics}}
\rightvitem{13cm}{Same as: Off shortstatistics;}
\leftvitem{3.5cm}{warnings\index{nwrite!warnings}}
\rightvitem{13cm}{Same as: Off warnings;}
\leftvitem{3.5cm}{allwarnings\index{nwrite!allwarnings}}
\rightvitem{13cm}{Same as: Off allwarnings;}
\leftvitem{3.5cm}{setup\index{nwrite!setup}}
\rightvitem{13cm}{Same as: Off setup;}
\leftvitem{3.5cm}{names\index{nwrite!names}}
\rightvitem{13cm}{Same as: Off names;}
\leftvitem{3.5cm}{allnames\index{nwrite!allnames}}
\rightvitem{13cm}{Same as: Off allnames;}
\leftvitem{3.5cm}{shortstats\index{nwrite!shortstats}}
\rightvitem{13cm}{Same as: Off shortstats;}
\leftvitem{3.5cm}{highfirst\index{nwrite!highfirst}}
\rightvitem{13cm}{Same as: Off highfirst;}
\leftvitem{3.5cm}{lowfirst\index{nwrite!lowfirst}}
\rightvitem{13cm}{Same as: Off lowfirst;}
\leftvitem{3.5cm}{powerfirst\index{nwrite!powerfirst}}
\rightvitem{13cm}{Same as: Off powerfirst;}
\vspace{10mm}
%--#] nwrite :
%--#[ off :
\section{off}
\label{substaoff}
\noindent \begin{tabular}{ll}
Type & Declaration statement\\
Syntax & off {\tt<}keyword{\tt>}; \\
& off {\tt<}keyword{\tt>} {\tt<}option{\tt>};
\\ See also & on (\ref{substaon})
\end{tabular} \vspace{4mm}
\noindent Statement\index{off} to control settings\index{settings} during
execution. Many of these settings replace older statements. The settings
and their keywords are:
\leftvitem{3.5cm}{allnames\index{off!allnames}}
\rightvitem{13cm}{Turns the allnames mode off. The default.}
\leftvitem{3.5cm}{allwarnings\index{off!allwarnings}}
\rightvitem{13cm}{Turns off the printing of all warnings.}
\leftvitem{3.5cm}{checkpoint\index{off!checkpoint}}
\rightvitem{13cm}{Deactivates the checkpoint mechanism. See
\ref{checkpoints}.}
\leftvitem{3.5cm}{compress\index{off!compress}}
\rightvitem{13cm}{Turns compression mode off.}
\leftvitem{3.5cm}{finalstats\index{off!finalstats}}
\rightvitem{13cm}{Turns off the last line of statistics that is normally
printed at the end of the run (introduced in version 3.2).}
\leftvitem{3.5cm}{highfirst\index{off!highfirst}}
\rightvitem{13cm}{Puts the sorting in a low first mode.}
\leftvitem{3.5cm}{insidefirst\index{off!insidefirst}}
\rightvitem{13cm}{Not active at the moment.}
\leftvitem{3.5cm}{lowfirst\index{off!lowfirst}}
\rightvitem{13cm}{Leaves the default low first mode and puts the sorting in
a high first mode.}
\leftvitem{3.5cm}{names\index{off!names}}
\rightvitem{13cm}{Turns the names mode off. This is the default.}
\leftvitem{3.5cm}{nospacesinnumbers\index{off!nospacesinnumbers}}
\rightvitem{13cm}{\label{staoffnospacesinnumbers}\vspace{1ex}Allows very
long numbers to be printed with leading blank spaces at the beginning of a new
line. The numbers are usually broken up by placing a backslash character at
the end of the line and then continuing at the next line. For cosmetic
purposes \FORM\ puts usually a few blank spaces at the beginning of the line.
\FORM\ itself can read this but some programs cannot. This option can be turned
off by the `on nospacesinnumbers;' statement. The printing of the blank
characters can be restored by turning this variable off. See also page
\ref{nospacesinnumbers} for a corresponding variable in the setup file.}
\leftvitem{3.5cm}{oldfactarg\index{off!oldfactarg}}
\rightvitem{13cm}{\label{staoffoldfactarg}Switches the use of the FactArg
statement~\ref{substafactarg}\index{factarg} to the new mode of version 4 or
later in which expressions in the argument of the mentioned function are
completely factored over the rationals. The default is off.}
\leftvitem{3.5cm}{parallel\index{off!parallel}}
\rightvitem{13cm}{Disallows the running of the program in parallel mode
(only relevant for parallel versions of \FORM).}
\leftvitem{3.5cm}{powerfirst\index{off!powerfirst}}
\rightvitem{13cm}{Puts the sorting back into `highfirst' mode.}
\leftvitem{3.5cm}{processstats\index{off!processstats}}
\rightvitem{13cm}{Turns the process by process printing of the statistics
in \ParFORM{} off. Only the master process will be printing statistics.
Other versions of \FORM{} will ignore this option.}
\leftvitem{3.5cm}{propercount\index{off!propercount}}
\rightvitem{13cm}{Turns the propercounting mode off. This means that for the
generated terms in the statistics not only the `ground level' terms are
counted but also terms that were generated inside function arguments.}
\leftvitem{3.5cm}{properorder\index{off!properorder}}
\rightvitem{13cm}{Turns the properorder mode off. This is the default.}
\leftvitem{3.5cm}{setup\index{off!setup}}
\rightvitem{13cm}{Switches off the mode in which the setup parameters are
printed. This is the default.}
\leftvitem{3.5cm}{stats\index{off!stats}}
\rightvitem{13cm}{Same as `Off statistics'.}
\leftvitem{3.5cm}{statistics\index{off!statistics}}
\rightvitem{13cm}{Turns off the printing of statistics.}
\leftvitem{3.5cm}{shortstats\index{off!shortstats}}
\rightvitem{13cm}{Same as `Off shortstatistics'.}
\leftvitem{3.5cm}{shortstatistics\index{off!shortstatistics}}
\rightvitem{13cm}{Takes the writing of the statistics back from shorthand
mode to the regular statistics mode in which each statistics messages takes
three lines of text and one blank line.}
\leftvitem{3.5cm}{threadloadbalancing\index{off!threadloadbalancing}}
\rightvitem{13cm}{\vspace{1.5ex}Disables the loadbalancing mechanism of
\TFORM\ in parallel mode. In other versions of \FORM\ this option is
ignored.}
\leftvitem{3.5cm}{threads\index{off!threads}}
\rightvitem{13cm}{Disallows multithreaded running in \TFORM.
In other versions of \FORM\ this option is ignored.}
\leftvitem{3.5cm}{threadstats\index{off!threadstats}}
\rightvitem{13cm}{Turns off the thread by thread printing of the statistics
in \TFORM. Only the master thread will be printing statistics. Other
versions of \FORM\ will ignore this option.}
\leftvitem{3.5cm}{totalsize\index{off!totalsize}}
\rightvitem{13cm}{Switches the totalsize mode off. For a more detailed
description of the totalsize mode, see the "On TotalSize;"
command~\ref{ontotalsize}.}
\leftvitem{3.5cm}{warnings\index{off!warnings}}
\rightvitem{13cm}{Turns off the printing of warnings.}
\leftvitem{3.5cm}{wtimestats\index{off!wtimestats}}
\rightvitem{13cm}{Disables the wall-clock time in the timing information in the
statistics on the master.}
\noindent If a description is too short, one should also consult the
description in the on statement (see \ref{substaon}). \vspace{10mm}
%--#] off :
%--#[ on :
\section{on}
\label{substaon}
\noindent \begin{tabular}{ll}
Type & Declaration statement\\
Syntax & on {\tt<}keyword{\tt>}; \\
& on {\tt<}keyword{\tt>} {\tt<}option{\tt>};
\\ See also & off (\ref{substaoff})
\end{tabular} \vspace{4mm}
\noindent New statement to control settings during execution. Many of these
settings replace older statements. The settings and their keywords are:
\leftvitem{3.5cm}{allnames\index{on!allnames}}
\rightvitem{13cm}{Same as `On names' but additionally all system variables
are printed as well. Default is off. }
\leftvitem{3.5cm}{allwarnings\index{on!allwarnings}}
\rightvitem{13cm}{Puts the printing of warnings in a mode in which all
warnings, even the very unimportant warnings are printed.}
\leftvitem{3.5cm}{checkpoint\index{on!checkpoint}}
\rightvitem{13cm}{Activates the checkpoint mechanism that allows for
the recovery of a crashed \FORM\ session. See \ref{checkpoints} for
detailed information.}
\leftvitem{3.5cm}{compress\index{on!compress}}
\rightvitem{13cm}{Turns compression mode on. This compression is a
relatively simple compression that hardly costs extra computer time but
saves roughly a factor two in disk storage. The old statement was `compress
on' but this should be avoided in the future. This setting is the default.}
\leftvitem{3.5cm}{compress,gzip\index{gzip}}
\rightvitem{13cm}{This option should be followed by a comma or a space and
a single digit. It activates the gzip compression for the sort file. This
compression can make the intermediate sort file considerably shorter at the
cost of some CPU time. This option can be used when disk space is at a
premium. The digit indicates the compression level. Zero means no
compression and 9 is the highest level. The default level is 6. Above that
the compression becomes very slow and doesn't gain very much extra.}
\leftvitem{3.5cm}{fewerstatistics\index{on!fewerstatistics}}
\rightvitem{13cm}{Determines how many of the statistics \FORM\ prints when a
small buffer is full. The keyword can be followed by a positive integer in
which case one out of that many of these statistics will be printed. If no
number is given the default value of 10 is used. When the number that
follows is zero, this feature is turned off (same effect as the value one).}
\leftvitem{3.5cm}{fewerstats\index{on!fewerstats}}
\rightvitem{13cm}{Same as the above fewerstatistics.}
\leftvitem{3.5cm}{finalstats\index{on!finalstats}}
\rightvitem{13cm}{Determines whether \FORM\ prints a final line of run time
statistics at the end of the run. Default is on.}
\leftvitem{3.5cm}{highfirst\index{on!highfirst}}
\rightvitem{13cm}{In this mode polynomials are sorted in a way that high
powers come before low powers.}
%\leftvitem{3.5cm}{indentspace\index{on!indentspace}}
%\rightvitem{13cm}{Not active at the moment.}
\leftvitem{3.5cm}{insidefirst\index{on!insidefirst}}
\rightvitem{13cm}{Not active at the moment.}
\leftvitem{3.5cm}{lowfirst\index{on!lowfirst}}
\rightvitem{13cm}{In this mode polynomials are sorted in a way that low
powers come before high powers. This is the default.}
\leftvitem{3.5cm}{names\index{on!names}}
\rightvitem{13cm}{Turns on the mode in which at the end of each module the
names of all variables that have been defined by the user are printed. This
is an inspection mode for debugging by the user. Default is off.}
\leftvitem{3.5cm}{nospacesinnumbers\index{on!nospacesinnumbers}}
\rightvitem{13cm}{\label{staonnospacesinnumbers}\vspace{1ex}Makes that very
long numbers are printed with no leading blank spaces at the beginning of a
new line. The numbers are usually broken up by placing a backspace character
at the end of the line and then continuing at the next line. For cosmetic
purposes \FORM\ puts usually a few blank spaces at the beginning of the line.
\FORM\ itself can read this but some programs cannot. Hence this printing of the
blank characters can be omitted by turning this variable on. See also page
\ref{nospacesinnumbers} for a corresponding variable in the setup file.}
\leftvitem{3.5cm}{oldfactarg\index{on!oldfactarg}}
\rightvitem{13cm}{\label{staonoldfactarg}Switches the use of the FactArg
statement~\ref{substafactarg}\index{factarg} to the old mode from before
version 4. This is a compatibility mode to allow oldprograms that rely on a
specific working of the FactArg statement to still run. The default is
off.}
\leftvitem{3.5cm}{parallel\index{on!parallel}}
\rightvitem{13cm}{Allows the running of the program in parallel mode unless
other problems prevent this. This is of course only relevant for parallel
versions of \FORM. The default is on.}
\leftvitem{3.5cm}{powerfirst\index{on!powerfirst}}
\rightvitem{13cm}{In this mode polynomials are sorted in a way that high
powers come before low powers. The most relevant is however the combined
power of all symbols.}
\leftvitem{3.5cm}{processstats\index{on!processstats}}
\rightvitem{13cm}{Only active for \ParFORM{}. It determines whether all
processes print their run time statistics or only the master process does so.
Default is on.}
\leftvitem{3.5cm}{propercount\index{on!propercount}}
\rightvitem{13cm}{Sets the counting of the terms during generation into
`propercount' mode. This means that only terms at the `ground level' are
counted and terms inside functions arguments are not counted in the
statistics. This setting is the default.}
\leftvitem{3.5cm}{properorder\index{on!properorder}}
\rightvitem{13cm}{Turns the properorder mode on. The default is off. In the
properorder mode \FORM\ pays particular attention to function arguments when
bringing terms and expressions to normal form. This may cost a considerable
amount of extra time. In normal mode \FORM\ is a bit sloppy (and much
faster) about this, resulting sometimes in an ordering that appears without
logic. This concerns only function arguments! This mode is mainly intended
for the few moments in which the proper ordering is important.}
\leftvitem{3.5cm}{setup\index{on!setup}}
\rightvitem{13cm}{Causes the printing of the current setup parameters for
inspection. Default is off.}
\leftvitem{3.5cm}{shortstatistics\index{on!shortstatistics}}
\rightvitem{13cm}{Puts the writing of the statistics in a shorthand mode in
which the complete statistics are written on a single line only.}
\leftvitem{3.5cm}{shortstats\index{on!shortstats}}
\rightvitem{13cm}{Same as `On shortstatistics'.}
\leftvitem{3.5cm}{statistics\index{on!statistics}}
\rightvitem{13cm}{Turns the writing of runtime statistics on. This is the
default. It is possible to change this default with one of the setup
parameters in the setup file (see \ref{setup}).}
\leftvitem{3.5cm}{stats\index{on!stats}}
\rightvitem{13cm}{Same as `On statistics'.}
\leftvitem{3.5cm}{threadloadbalancing\index{on!threadloadbalancing}}
\rightvitem{13cm}{\vspace{1.5ex}Causes the load balancing mechanism in \TFORM
to be turned on or off. Default is on. Ignored by other versions of \FORM.}
\leftvitem{3.5cm}{threads\index{on!threads}}
\rightvitem{13cm}{Allows the running of the program in multithreaded mode
unless other problems prevent this. This is of course only relevant for
\TFORM. Other versions of \FORM\ ignore this. The default is on.}
\leftvitem{3.5cm}{threadstats\index{on!threadstats}}
\rightvitem{13cm}{Only active for \TFORM. It determines whether all threads
print their run time statistics or only the master thread does so. Default
is on.}
\leftvitem{3.5cm}{totalsize\index{on!totalsize}}
\rightvitem{13cm}{\label{ontotalsize} Puts \FORM\ in a
mode\index{totalsize} in which it tries to determine
the maximum space occupied by all expressions at any given moment during
the execution of the program. This space is the sum of the
input/output/hide scratch files, the sort file(s) and the .str file. This
maximum is printed at the end of the program. The same can be obtained with
the "TotalSize ON" command in the setup (see \ref{setup}) or the -T option
in the command tail when \FORM\ is started (see \ref{running}).}
\leftvitem{3.5cm}{warnings\index{on!warnings}}
\rightvitem{13cm}{Turns on the printing of warnings in regular mode. This
is the default.}
\leftvitem{3.5cm}{wtimestats\index{on!wtimestats}}
\rightvitem{13cm}{Prints the wall-clock time in the timing information in the
statistics. The wall-clock time is indicated by `\texttt{WTime}' instead of
`\texttt{Time}' in the normal statistics with `\texttt{shortstatistics}' turned
off. For parallel versions, it affects the statistics only on the master, and
does not change those on the workers. The same can be obtained with the
\texttt{-W} option in the command line options of \FORM{} (see \ref{running})
or `\texttt{WTimeStats ON}' in the setup (see \ref{setup}). Default is off.}
\vspace{10mm}
%--#] on :
%--#[ once :
\section{once}
\label{substaonce}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & once {\tt<}pattern{\tt>} = {\tt<}expression{\tt>};
\\ See also & identify (\ref{substaidentify})
\end{tabular} \vspace{4mm}
\noindent This statement\index{once} is identical to the once option of the
id\index{id} statement (see \ref{substaidentify}). Hence
\begin{verbatim}
once ....
\end{verbatim}
is just a shorthand notation for
\begin{verbatim}
id once ....
\end{verbatim}
\vspace{10mm}
%--#] once :
%--#[ only :
\section{only}
\label{substaonly}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & only {\tt<}pattern{\tt>} = {\tt<}expression{\tt>};
\\ See also & identify (\ref{substaidentify})
\end{tabular} \vspace{4mm}
\noindent This statement\index{only} is identical to the only option of the
id\index{id} statement (see \ref{substaidentify}). Hence
\begin{verbatim}
only ....
\end{verbatim}
is just a shorthand notation for
\begin{verbatim}
id only ....
\end{verbatim}
\vspace{10mm}
%--#] only :
%--#[ polyfun :
\section{polyfun}
\label{substapolyfun}
\noindent \begin{tabular}{ll}
Type & Declaration statement\\
Syntax & polyfun {\tt<}name of function{\tt>}; \\
& polyfun;
\\ See also & moduleoption (\ref{substamoduleoption})
\end{tabular}\vspace{4mm}
\noindent Declares the specified\index{polyfun} function to be the
`polyfun'. The polyfun is a function of which the single
argument\index{argument} is considered to be the
coefficient\index{coefficient} of the term. If two terms are otherwise
identical the arguments of their polyfun will be added during the sorting,
even if these arguments are little expressions. Hence
% THIS EXAMPLE IS PART OF THE TESTSUITE. CHANGES HERE SHOULD BE APPLIED THERE AS
% WELL!
\begin{verbatim}
PolyFun acc;
Local F = 3*x^2*acc(1+y+y^2)+2*x^2*acc(1-y+y^2);
\end{verbatim}
will result in
\begin{verbatim}
F = x^2*acc(5+y+5*y^2);
\end{verbatim}
Note that the external numerical coefficient\index{coefficient} is also
pulled inside the polyfun.
\noindent If the polyfun statement has no argument, \FORM\ reverts to its
default mode in which no polyfun exists. This does not change any terms. If
one would like to remove the polyfun from the terms one has to do that
`manually' as in
\begin{verbatim}
PolyFun;
id acc(x?) = x;
\end{verbatim}
in which we assume that previously the function acc had been declared to be
the `polyfun'. \vspace{10mm}
%--#] polyfun :
%--#[ polyratfun :
\section{polyratfun}
\label{substapolyratfun}
\noindent \begin{tabular}{ll}
Type & Declaration statement\\
Syntax & polyratfun {\tt<}name of function{\tt>}; \\
& polyratfun {\tt<}name of function{\tt>},{\tt<}name of function{\tt>}; \\
& polyratfun;
\\ See also & polyfun (\ref{substapolyfun}),
moduleoption (\ref{substamoduleoption})
\end{tabular}\vspace{4mm}
\noindent Declares the specified\index{polyratfun} function to be the
`polyratfun'. The polyratfun is a function with two
arguments\index{argument} which together form a rational polynomial that
acts as the
coefficient\index{coefficient} of the term. If two terms are otherwise
identical the arguments of their polyratfun will be added during the sorting,
even if these arguments are little nontrivial. Hence
\begin{verbatim}
PolyRatFun acc;
Local F = 3*x^2*acc(1+y+y^2,1-y)+2*x^2*acc(1-y+y^2,1+y);
\end{verbatim}
will result in
\begin{verbatim}
F = x^2*acc(-y^3-10*y^2-2*y-5,y^2-1);
\end{verbatim}
Note that the external numerical coefficient\index{coefficient} is also
pulled inside the polyratfun.
\noindent If the polyratfun statement has no argument, \FORM\ reverts to its
default mode in which no polyratfun exists. This does not change any terms.
\noindent The polyratfun has many similarities with the polyfun (see
\ref{substapolyfun}). At any moment there can only be at most either one
polyfun or one polyratfun. Occurrences of the polyfun or the polyratfun
with the wrong number or the wrong type of arguments are treated as regular
functions.
\noindent There is a fundamental difference between the polyfun and the
polyratfun. The last one is far more restrictive. It can have only numbers
and symbols for its arguments. Also the ordering of the terms in the
arguments can be different. In the polyratfun the terms are always sorted
with the highest power first. In the polyfun the ordering is as with the
regular terms. By default the lowest powers come first as one usually likes
for power series expansions.
\noindent When two functions are specified, the first will be the
PolyRatFun, and the second will be its inverse as in
\begin{verbatim}
PolyRatFun rat,RAT;
\end{verbatim}
in which case
\begin{verbatim}
RAT(x1,x2) = rat(x2,x1)
\end{verbatim}
This can be handy when one needs to solve systems of equations by manual
interference. In that case exchanging numerators and denominators can be
rather messy, while just changing a name is far less error-prone.
\noindent In many cases it may be very wasteful to keep full track of the
complete rational polynomial. An example is the reduction of a complicated
4-loop massless propagator diagram for which the rational polynomials can
easily have hundreds of powers of the dimension parameter $D=4-2\epsilon$.
In the end one has to expand in terms of $\epsilon$ although it is not
known in advance to how many powers. For this there are two extra options
in the polyratfun statement. The first is
\begin{verbatim}
PolyRatFun rat(divergence,x);
\end{verbatim}
in which x is the name of the symbol of interest. In this case the
polyratfun keeps only its most divergent term in this variable x and gives
it the coefficient one. The result is that terms will never cancel and at
the end of the calcuation one can see how many poles in x were maximally
present, and hence how far one has to expand in x. Because the contents of
the polyratfun are extremely simple, the expensive rational arithmetic is
completely absent and things should go rather fast.
\noindent In the second option one can specify how far one should expand:
\begin{verbatim}
PolyRatFun rat(expand,x,power);
\end{verbatim}
In this case the denomnator can only be a polynomial in the variable x. It
will be expanded and multiplied by the numerator and eventually all terms
with powers of x that are greater than 'power' will be discarded. The
remaining incidence of the function rat will then have only one argument,
like the polyfun (see \ref{substapolyfun}). The advantage is that now the
addition of two coefficients is a simple and straightforward operation that
does not need the expensive polynomial GCD computations.
\noindent Of course one can program such expansions externally and maybe
better suited for the problem at hand, but using this option of the
polyratfun is much faster and gives fewer chances of mistakes.
\vspace{10mm}
%--#] polyratfun :
%--#[ pophide :
\section{pophide}
\label{substapophide}
\noindent \begin{tabular}{ll}
Type & Specification statement\\
Syntax & pophide;
\\ See also & hide (\ref{substahide}),
nhide (\ref{substanhide}),
unhide (\ref{substaunhide}),
nunhide (\ref{substanunhide}),
pushhide (\ref{substapushhide})
\end{tabular} \vspace{4mm}
\noindent Undoes\index{pophide} the action of the most recent
pushhide\index{pushhide} statement (see \ref{substapushhide}). If there is
no matching pushhide statement an error will result. \vspace{10mm}
%--#] pophide :
%--#[ print :
\section{print}
\label{substaprint}
\noindent \begin{tabular}{ll}
Type & Print statement\\
Syntax & Print [{\tt<}options{\tt>}]; \\
& Print \verb:{:[{\tt<}options{\tt>}] {\tt<}expression{\tt>}\verb:}:; \\
& Print [{\tt<}options{\tt>}] "{\tt<}format string{\tt>}" [{\tt<}objects{\tt>}];
\\ See also & print[\,] (\ref{substaprintc}),
nprint (\ref{substanprint}),
printtable (\ref{substaprinttable})
\end{tabular}\vspace{4mm}
\noindent General purpose print\index{print} statement. It has three modes. In
the first two modes flags are set for the printing of expressions after the
current module has been finished. The third mode concerns printing during
execution. This allows the printing of individual terms or
\$-variables\index{\$-variable} on a term by term basis. It should be
considered as a useful debugging\index{debugging} device.
\noindent In the first mode all active\index{active} expressions are
scheduled for printing. The options are
\leftvitem{1cm}{+f}
\rightvitem{15cm}{Printing will be only to the log\index{log}
file\index{file!log}.}
\leftvitem{1cm}{-f}
\rightvitem{15cm}{Printing will be both to the screen\index{screen} and to
the log\index{log} file\index{file!log}. This is the default.}
\leftvitem{1cm}{+s}
\rightvitem{15cm}{Each term will start a new line. This is called the
single\index{single term mode} term mode\index{mode!single term}.}
\leftvitem{1cm}{+ss}
\rightvitem{15cm}{Each term will start a new line. In addition each
internal group will start a new line. A group is either a single function
or all symbols together, or all dotproducts together, or all vectors
together, or all Kronecker delta's together.}
\leftvitem{1cm}{+sss}
\rightvitem{15cm}{Like the +ss option but now each symbol and its power
will start a new line. The same for individual dotproducts (and their
power), vectors and Kronecker delta's.}
\leftvitem{1cm}{-s}
\rightvitem{15cm}{Regular term mode. There can be more terms in a line.
Linebreaks\index{linebreaks} are placed when the line is full. The line
size is set in the format\index{format} statement (see \ref{substaformat}).
This is the default.}
\leftvitem{1cm}{-ss}
\rightvitem{15cm}{Lowers the single term mode to -s. If one would like to
switch off the single term mode altogether, -s suffices.}
\leftvitem{1cm}{-sss}
\rightvitem{15cm}{Lowers the single term mode to -ss. If one would like to
switch off the single term mode altogether, -s suffices.}
\noindent In the second mode one can specify
individual\index{individual expressions} expressions to be printed. The
options hold for all the expressions that follow them until new options are
specified. The options are the same as for the first mode.
\noindent In the third mode there is a format\index{format string} string
as for the printf\index{printf} command in the C\index{C} programming
language. Of course the control characters are not exactly the same as for
the C language because the objects are different. The special characters
are:
\leftvitem{1cm}{\%t\index{print!\%t}}
\rightvitem{15cm}{The current term will be printed at this position
including its sign, even if this is a plus sign.}
\leftvitem{1cm}{\%T\index{print!\%T}}
\rightvitem{15cm}{The current term will be printed at this position. If its
coeficient is positive no leading plus sign is printed.}
\leftvitem{1cm}{\%w\index{print!\%w}}
\rightvitem{15cm}{The number of the current thread will be printed. This is
for \TFORM\ only. In the sequential version this combination is skipped. The
number zero refers to the master thread.}
\leftvitem{1cm}{\%W\index{print!\%W}}
\rightvitem{15cm}{The number of the current thread and its CPU-time at the
moment of printing. This is for \TFORM\ only. In the sequential version
this combination is skipped. The number zero refers to the master thread.}
\leftvitem{1cm}{\%\$\index{print!\%\$}}
\rightvitem{15cm}{A dollar expression will be printed at this position. The
name(s) of the dollar expression(s) should follow the format string in the
order in which they are used in the format string.}
\leftvitem{1cm}{\%\%\index{print!\%\%}}
\rightvitem{15cm}{The character \%.}
\leftvitem{1cm}{\%}
\rightvitem{15cm}{If this is the last character of the string no linefeed
will be printed at the end of the print command.}
\leftvitem{1cm}{$\backslash$n}
\rightvitem{15cm}{A linefeed\index{linefeed}.}
\noindent Each call is terminated with a linefeed\index{linefeed}. Example:
% THIS EXAMPLE IS PART OF THE TESTSUITE. CHANGES HERE SHOULD BE APPLIED THERE AS
% WELL!
\begin{verbatim}
Symbols a,b,c;
Local F = 3*a+2*b;
Print "> %T";
id a = b+c;
Print ">> %t";
Print;
.end
> 3*a
>> + 3*b
>> + 3*c
> 2*b
>> + 2*b
F =
5*b + 3*c;
\end{verbatim}
\noindent In the third mode one can also use the +/--\,f options of the
first mode. This should be placed before the format string as in
\begin{verbatim}
Print +f "(%$) %t",$var;
\end{verbatim}
\noindent Because of the mixed nature of this statement it can occur in
more than one location in the module. \vspace{10mm}
%--#] print :
%--#[ print[] :
\section{\texorpdfstring{print[\,]}{print[ ]}}
\label{substaprintc}
\noindent \begin{tabular}{ll}
Type & Output control statement\\
Syntax & print[\,] \verb:{:[{\tt<}options{\tt>}] {\tt<}name{\tt>}\verb:}:;
\\ See also & print (\ref{substaprint})
\end{tabular}\vspace{4mm}
\noindent Print\index{print} statement\index{print[]} to cause the printing
of expressions at the end of the current module. Is like the first two
modes of the regular print statement (see \ref{substaprint}), but when
printing \FORM\ does not print the contents of each bracket\index{bracket},
only the number of terms inside the bracket. Is to be used in combination
with a bracket or an antibracket\index{antibracket} statement (see
\ref{substabracket} and \ref{substaabrackets}). Apart from this the options
are identical to those of the first two modes of the print statement.
\vspace{10mm}
%--#] print[] :
%--#[ printtable :
\section{printtable}
\label{substaprinttable}
\noindent \begin{tabular}{ll}
Type & Print statement\\
Syntax & printtable [{\tt<}options{\tt>}] {\tt<}tablename{\tt>}; \\
& printtable [{\tt<}options{\tt>}] {\tt<}tablename{\tt>} $>$ {\tt<}filename{\tt>}; \\
& printtable [{\tt<}options{\tt>}] {\tt<}tablename{\tt>} $>\!\!>$ {\tt<}filename{\tt>};
\\ See also & print (\ref{substaprint}),
table (\ref{substatable}),
fill (\ref{substafill}),
fillexpression (\ref{substafillexpression}), \\ &
and the table\_ function (\ref{funtable})
\end{tabular}\vspace{4mm}
\noindent Almost\index{printtable} the opposite of a
FillExpression\index{fillexpression} statement (see
\ref{substafillexpression}). Prints\index{print} the contents of a
table\index{table} according to the current format (see
\ref{substaformat}). The output can go to standard output, the
log\index{log} file\index{file!log} or a specified file. The elements of
the table that have been defined and filled are written in the form of
fill\index{fill} statements (see \ref{substafill}) in such a way that they
can be read in a future program to fill the table with the current
contents. This is especially useful when the fillexpression statement has
been used to dynamically extend tables based on what \FORM\ has encountered
during running. This way those elements will not have to be computed again
in future programs.
\noindent The options are
\leftvitem{1.3cm}{+f}
\rightvitem{14.7cm}{Output is to the logfile and not to the screen.}
\leftvitem{1.3cm}{-f}
\rightvitem{14.7cm}{Output is both to the logfile and to the screen. This is
the default.}
\leftvitem{1.3cm}{+s}
\rightvitem{14.7cm}{Output will be in a mode in which each new term starts a
new line.}
\leftvitem{1.3cm}{-s}
\rightvitem{14.7cm}{Output will be in the regular mode in which new terms
continue to be written on the same line within the limits of the number of
characters per line as set in the format statement. Default is 72
characters per line. This can be changed with the format\index{format}
statement (see \ref{substaformat}).}
\noindent If redirection to a file is specified output will be only to this
file. The +f option will be ignored. There are two possibilities:
\leftvitem{2.8cm}{$>$ filename}
\rightvitem{13.2cm}{The old contents of the file with name `filename' will be
overwritten\index{overwrite}.}
\leftvitem{2.8cm}{$>\!\!>$ filename}
\rightvitem{13.2cm}{The table will be appended\index{append} to the file
with the name `filename'. This allows the writing of more than one table to
a file.}
\vspace{10mm}
%--#] printtable :
%--#[ processbucketsize :
\section{processbucketsize}
\label{substaprocessbucketsize}
\noindent \begin{tabular}{ll}
Type & Declaration statement\\
Syntax & processbucketsize {\tt<}value{\tt>};
\\ See also & moduleoption (\ref{substamoduleoption}), setup
(\ref{setupprocessbucketsize})
\end{tabular}\vspace{4mm}
\noindent Sets the number of terms\index{processbucketsize} in the buckets that are sent
to the secondary processors in \ParFORM\index{ParFORM}, one of the
parallel\index{parallel} versions of \FORM\ (see chapter \ref{parallel}). In
all other versions this statement is ignored. See also the moduleoption
(\ref{substamoduleoption}) statement and the corresponding parameter for
the setup (\ref{setupprocessbucketsize}). \vspace{10mm}
%--#] processbucketsize :
%--#[ propercount :
\section{propercount}
\label{substapropercount}
\noindent \begin{tabular}{ll}
Type & Declaration statement\\
Syntax & propercount {\tt<}on/off{\tt>};
\\ See also & on (\ref{substaon}), off (\ref{substaoff})
\end{tabular} \vspace{4mm}
\noindent This statement\index{propercount} is obsolete\index{obsolete}.
The user should try to use the propercount option of the on\index{on} (see
\ref{substaon}) or the off\index{off} (see \ref{substaoff}) statements.
\vspace{10mm}
%--#] propercount :
%--#[ pushhide :
\section{pushhide}
\label{substapushhide}
\noindent \begin{tabular}{ll}
Type & Specification statement\\
Syntax & pushhide;
\\ See also & hide (\ref{substahide}),
nhide (\ref{substanhide}),
unhide (\ref{substaunhide}),
nunhide (\ref{substanunhide}),
pophide (\ref{substapophide})
\end{tabular} \vspace{4mm}
\noindent Hides\index{hide} all currently\index{pushhide} active
expressions (see \ref{substahide}). The pophide\index{pophide} statement
(see \ref{substapophide}) can bring them back to active status again.
\vspace{10mm}
%--#] pushhide :
%--#[ putinside :
\section{putinside}
\label{substaputinside}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & putinside {\tt<}name of function{\tt>} [,$<$bracket information$>$];
\\ See also & AntiPutInside (\ref{substaantiputinside})
\end{tabular}\vspace{4mm}
\noindent This statement\index{putinside} puts the complete term inside a
function argument. The function must be a regular function (hence no tensor
or table which are special types of functions). If there is
bracket\index{bracket} information, this information should adhere to the
syntax of the bracket statement (\ref{substaantiputinside}) and only
occurrences of the bracket variables will be put inside the function. The
coefficient will also be put inside the function.
\vspace{10mm}
%--#] putinside :
%--#[ ratio :
\section{ratio}
\label{substaratio}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & ratio {\tt<}symbol1{\tt>} {\tt<}symbol2{\tt>} {\tt<}symbol3{\tt>};
\end{tabular} \vspace{4mm}
\noindent This statement\index{ratio} can be used for limited but fast
partial\index{partial fractioning} fractioning. In the statement
\begin{verbatim}
ratio a,b,c;
\end{verbatim}
in which \verb:a:, \verb:b: and \verb:c: should be three symbols {\FORM}
will assume that $c = b-a$ and then make the substitutions
\begin{eqnarray}
\frac{1}{a^m}\frac{1}{b^n} & = & \sum_{i=0}^{m-1}\sign(i)
\binom(n-1+i,n-1)\frac{1}{a^{m-i}}\frac{1}{c^{n+i}}
+\sum_{i=0}^{n-1}\sign(m)
\binom(m-1+i,m-1)\frac{1}{b^{n-i}}\frac{1}{c^{m+i}}
\nonumber \\
\frac{b^n}{a^m} & = & \sum_{i=0}^n\binom(n,i)\frac{c^i}{a^{m-n+i}}
\ \ \ \ \ \ \ \hfill m\ge n \nonumber \\
\frac{b^n}{a^m} & = & \sum_{i=0}^{m-1}\binom(n,i)\frac{c^{n-i}}{a^{m-i}}
+ \sum_{i=0}^{n-m}\binom(m-1+i,m-1)
c^ib^{n-m-i}
\ \ \ \ \ \ \ \hfill m}
[{\tt<}argument specifications{\tt>}];\verb:}: \\
See also & symmetrize (\ref{substasymmetrize}), cyclesymmetrize
(\ref{substacyclesymmetrize}), antisymmetrize (\ref{substaantisymmetrize})
\end{tabular} \vspace{4mm}
\noindent The argument\index{rcyclesymmetrize} specifications are explained
in the section on the symmetrize\index{symmetrize} statement (see
\ref{substasymmetrize}). \medskip
\noindent The action of this statement is to
reverse\index{reverse cycle symmetrize}-cycle-symmetrize
\index{symmetrize!reverse cycle} the (specified) arguments of the functions
that are mentioned. This means that the arguments are brought to `natural
order' in the notation of \FORM\ by trying cyclic and reverse cyclic
permutations\index{permutations} of the arguments or groups of arguments.
The `natural order' may depend on the order of declaration of the
variables. \vspace{10mm}
%--#] rcyclesymmetrize :
%--#[ redefine :
\section{redefine}
\label{substaredefine}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & r[edefine] {\tt<}preprocessor variable{\tt>} "{\tt<}string{\tt>}";
\\ See also & preprocessor variables in the chapter on the preprocessor
(\ref{preprocessor})
\end{tabular} \vspace{4mm}
\noindent This statement\index{redefine} can be used to change the contents
of preprocessor\index{preprocessor variables}
variables\index{variables!preprocessor}. The new contents can be used after
the current module has finished execution and the preprocessor becomes
active again for further translation and compilation\index{compilation}.
This termwise adaptation of the value of a preprocessor variable can be
very useful in setting up multi module loops until a certain condition is
not met any longer. Example:
\begin{verbatim}
#do i = 1,1
statements;
if ( condition ) redefine i "0";
.sort
#enddo
\end{verbatim}
As long as there is a term that fulfils the condition the loop\index{loop}
will continue. This defines effectively a while loop\index{loop!while} (see
\ref{substawhile}) over various modules. Note that the .sort\index{.sort}
instruction is essential. Note also that a construction like
\begin{verbatim}
if ( count(x,1) > 3 ) redefine i "`i'+1";
\end{verbatim}
is probably not going to do what the user intends. It is not going to count
terms with more than three powers of x. The preprocessor will insert the
compile time value of the preprocessor variable i. If this is 0, then each
time a term has more than three powers of x, i will get the string value
\verb:0+1:. If one would like to do such counting, one should use a
dollar variable\index{\$-variable} (see \ref{dollars}). \vspace{10mm}
%--#] redefine :
%--#[ removespectator :
\section{removespectator}
\label{substaremovespectator}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & removespectator {\tt<}spectator;{\tt>};
\end{tabular} \vspace{4mm}
\noindent See chapter\ref{spectators} on spectators.
\vspace{10mm}
%--#] removespectator :
%--#[ renumber :
\section{renumber}
\label{substarenumber}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & renumber {\tt<}number{\tt>};
\\ See also & sum (\ref{substasum})
\end{tabular}\vspace{4mm}
\noindent Renumbers\index{renumber} the dummy\index{dummy}
indices\index{indices!dummy}. Dummy indices are indices of the type
\verb:N1_?:. Normally \FORM\ tries to renumber these indices to make the
internal representation of a term `minimal'. It does not try exhaustively
though. Especially interference with symmetric or antisymmetric functions
is far from perfect. This is due to considerations of economy. With the
renumber statement the user can force \FORM\ to do better. The allowable
options are:
\leftvitem{1cm}{0}
\rightvitem{15cm}{All exchanges of one pair of dummy indices are tried
until all pair exchanges yield no improvements. This is the default if no
option is specified.}
\leftvitem{1cm}{1}
\rightvitem{15cm}{If there are N sets of dummy indices all N!
permutations\index{permutations} are tried. This can be very costly when a
large number of indices is involved. Use with care!}\vspace{10mm}
%--#] renumber :
%--#[ repeat :
\section{repeat}
\label{substarepeat}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & repeat; \\
& repeat {\tt<}executable statement{\tt>}
\\ See also & endrepeat (\ref{substaendrepeat}), while (\ref{substawhile})
\end{tabular} \vspace{4mm}
\noindent The repeat\index{repeat} statement starts a
repeat\index{repeat environment} environment. It is terminated with an
endrepeat\index{endrepeat} statement (see \ref{substaendrepeat}). The
repeat statement and its matching endrepeat statement should be inside the
same module. \vspace{4mm}
\noindent The statements inside the repeat environment should all be
executable statements (or print statements) and if any of the executable
statements inside the environment has changed the current term, the action
of the endrepeat statement will be to bring control back to the beginning
of the environment. In that sense the repeat/endrepeat combination acts as
\begin{verbatim}
do
executable statements
while any action due to any of the statements
\end{verbatim}
The second form of the statement is a shorthand\index{shorthand} notation:
\begin{verbatim}
repeat;
single statement;
endrepeat;
\end{verbatim}
is equivalent to
\begin{verbatim}
repeat single statement;
\end{verbatim}
Particular attention should be given to avoid infinite\index{infinite loop}
loops\index{loop!infinite} as in
\begin{verbatim}
repeat id a = a+1;
\end{verbatim}
A more complicated infinite loop is
\begin{verbatim}
repeat;
id S(x1?)*R(x2?) = T(x1,x2,x2-x1);
id T(x1?,x2?,x3?pos_) = T(x1,x2-2,x3-1)*X(x2);
id T(x1?,x2?,x3?) = S(x1)*R(x2);
endrepeat;
\end{verbatim}
If the current term is S(2)*R(2), the statements in the loop do not change
it in the end. Yet the program goes into an infinite loop, because the
first id statement will change the term (action) and the third statement
will change it back. {\FORM} does not check that the term is the same
again. Hence there is action inside the repeat environment and hence the
statements will be executed again. This kind of hidden action is a major
source of premature\index{premature}
terminations\index{termination!premature} of {\FORM} programs. \vspace{4mm}
\noindent Repeat environments can be nested\index{nested} with all other
environments (and of course also with other repeat/endrepeat combinations).
\vspace{10mm}
%--#] repeat :
%--#[ replaceloop :
\section{replaceloop}
\label{substareplaceloop}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & replaceloop {\tt<}parameters{\tt>};
\\ See also & the findloop option of the if statement (\ref{substaif})
\end{tabular}\vspace{4mm}
\noindent This statement\index{replaceloop} causes the substitution of
index\index{index loop} loops\index{loop!index}. An index loop is a
sequence of contracted indices in which the indices are arguments of
various instances of the same function and each contracted\index{contracted
indices} index\index{index!contracted} occurs once in one instance of the
function and once in another instance of the function. Such a contraction
defines a connection and if a number of such connections between
occurrences of the function form a loop this structure is a candidate for
replacement. Examples of such loops are:
\begin{verbatim}
f(i1,i2,j1)*f(i2,i1,j2)
f(i1,i2,j1)*f(i2,i3,j2)*f(i1,i3,j3)
f(i1,k1,i2,j1)*f(k2,i2,i3,j2)*f(i1,k3,i3,j3)
\end{verbatim}
The first term has a loop of two functions or vertices\index{vertices} and
the other two terms each define a loop of three vertices. The parameters
are:
\leftvitem{4cm}{$<$name$>$}
\rightvitem{12cm}{The name of the function that defines the `vertices'.
This must always be the first parameter.}
\leftvitem{4cm}{arguments=number}
\rightvitem{12cm}{Only occurrences of the vertex function with the
specified number of arguments will be considered. The specification of this
parameter is mandatory.}
\leftvitem{4cm}{loopsize=number}
\rightvitem{12cm}{Only a loop with this number of vertices will be
considered.}
\leftvitem{4cm}{loopsize=all}
\rightvitem{12cm}{All loop\index{loopsize} sizes will be considered and the
smallest loop is substituted.}
\leftvitem{4cm}{loopsize$<$number}
\rightvitem{12cm}{Only loops with fewer vertices than `number' will be
considered and the smallest looop will be substituted.}
\leftvitem{4cm}{outfun=$<$name$>$}
\rightvitem{12cm}{Name of an output function in which the remaining
arguments of all the vertex functions will be given. This parameter is
mandatory.}
\leftvitem{4cm}{include-$<$name$>$}
\rightvitem{12cm}{Name of a summable index that must be one of the links in
the loop. This parameter is optional.}
\noindent The loopsize\index{loopsize} parameter is mandatory. Hence one of
its options must be specified. The order of the parameters is not
important. The only important thing is that the name of the vertex function
must be first. The names of the keywords may be abbreviated as in
\begin{verbatim}
ReplaceLoop f,a=3,l=all,o=ff,i=i2;
\end{verbatim}
although this does not improve the readability of the program. Hence a more
readable abbreviated version might be
\begin{verbatim}
ReplaceLoop f,arg=3,loop=all,out=ff,inc=i2;
\end{verbatim}
\noindent The action of the statement is to remove the vertex functions
that constitute the loop and replace them by the output function. This
outfun will have the arguments of all the vertex functions minus the
contracted indices that define the loop. The order of the arguments is the
order in which they are encountered when following the loop. The order of
the arguments in the outfun depends however on the order in which \FORM\
encounters the vertices. Hence the outfun will often be
cyclesymmetric\index{symmetric!cycle}\index{cyclesymmetric} (see
\ref{substafunctions} and \ref{substacyclesymmetrize}). If \FORM\ has to
exchange indices to make a `proper loop' (i.e. giving relevance to the
first index as if it is something incoming and the second index as if it is
something outgoing) and if the vertex function is
antisymmetric\index{antisymmetric}\index{symmetric!anti}, each exchange will
result in a minus sign. Examples:
% THIS EXAMPLE IS PART OF THE TESTSUITE. CHANGES HERE SHOULD BE APPLIED THERE AS
% WELL!
\begin{verbatim}
Functions f(antisymmetric),ff(cyclesymmetric);
Indices i1,...,i8;
Local F = f(i1,i4,i2)*f(i5,i2,i3)*f(i3,i1,i6)*f(i4,i7,i8);
ReplaceLoop f,arg=3,loop=3,out=ff;
\end{verbatim}
would result in
\begin{verbatim}
-f(i4,i7,i8)*ff(i4,i5,i6)
\end{verbatim}
and
\begin{verbatim}
Functions f(antisymmetric),ff(cyclesymmetric);
Indices i1,...,i9;
Local F = f(i1,i4,i2)*f(i5,i2,i3)*f(i3,i1,i6)*f(i4,i7,i8)
*f(i6,i7,i8);
ReplaceLoop f,arg=3,loop=all,out=ff;
\end{verbatim}
would give
\begin{verbatim}
-f(i1,i4,i2)*f(i5,i2,i3)*f(i3,i1,i6)*ff(i4,i6)
\end{verbatim}
because the smallest loop will be taken. A number of examples can be found
in the package\index{package!color} `color'\index{color package} for group
theory\index{group theory} invariants that is part of the \FORM\
distribution.
\noindent A related object is the findloop\index{findloop} option of the
if\index{if} statement (see \ref{substaif}). This option just probes
whether a loop is present but makes no replacements.\vspace{10mm}
%--#] replaceloop :
%--#[ save :
\section{save}
\label{substasave}
\noindent \begin{tabular}{ll}
Type & Declaration statement\\
Syntax & sa[ve] {\tt<}filename{\tt>} [{\tt<}names of global expressions{\tt>}];
\\ See also & load (\ref{substaload})
\end{tabular}\vspace{4mm}
\noindent Saves\index{save} the contents of the store\index{store file}
file\index{file!store} (all global expressions that were stored in
.store\index{.store}
instructions) to a file with the indicated name. If a list of expressions
is provided only those expressions are saved and the others are ignored.
\noindent Together with the load\index{load} statement (see
\ref{substaload}) the save statement provides a mechanism to transfer data
in internal notation from one program to another. It is the preferred method
to keep results of a lengthy job for further analysis without the need for
the long initial running time.
\noindent In order to avoid confusion .sav\label{ex:sav}\index{.sav} is the
preferred extension\index{extension!.sav} of saved files.\vspace{10mm}
%--#] save :
%--#[ select :
\section{select}
\label{substaselect}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & select {\tt<}list of sets{\tt>} {\tt<}pattern{\tt>} = {\tt<}expression{\tt>};
\\ See also & identify (\ref{substaidentify})
\end{tabular} \vspace{4mm}
\noindent This statement\index{select} is identical to the select option of
the id\index{id} statement (see \ref{substaidentify}). Hence
\begin{verbatim}
select ....
\end{verbatim}
is just a shorthand notation for
\begin{verbatim}
id select ....
\end{verbatim}
\vspace{10mm}
%--#] select :
%--#[ set :
\section{set}
\label{substaset}
\noindent \begin{tabular}{ll}
Type & Declaration statement\\
Syntax & set {\tt<}set to be declared{\tt>}[(option)]:{\tt<}element{\tt>} [{\tt<}more elements{\tt>}];
\end{tabular} \vspace{4mm}
\noindent Declares a single set\index{set} and specifies its
elements\index{elements}. Sets have a type of variables connected to them.
There can be sets of symbols, sets of functions, sets of vectors, sets of
indices and sets of numbers. For the purpose of sets tensors\index{tensor}
and tables\index{table} count as functions.
\noindent There can also be mixed sets\index{set!mixed} of indices and
numbers. When a number could be either a fixed index or just a number \FORM\
will keep the type of the set unfixed. This can change either when the next
element is a symbolic index or a number that cannot be a fixed index (like
a negative number). If the status does not get resolved the set can be used
in the wildcarding of both symbols and indices. Normally sets of numbers
can be used only in the wildcarding of symbols.
Currently the only option is the ordered
set\index{set!ordered}\index{ordered set}, indicated by
\begin{verbatim}
Set name(ordered):x4,x3,x1,x6,x2;
\end{verbatim}
which would be stored as x1,x2,x3,x4,x6 if that would be the order of
declaration.
\vspace{10mm}
%--#] set :
%--#[ setexitflag :
\section{setexitflag}
\label{substasetexitflag}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & setexitflag;
\\ See also & exit (\ref{substaexit})
\end{tabular} \vspace{4mm}
\noindent Causes\index{setexitflag} termination\index{termination} of the
program after execution\index{execution} of the current module has
finished. \vspace{10mm}
%--#] setexitflag :
%--#[ shuffle :
%
\section{shuffle}
\label{substashuffle}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & shuffle,functionname; \\
& shuffle,once,functionname;
\\ See also & stuffle (\ref{substastuffle}) \\
& merge (\ref{substamerge})
\end{tabular} \vspace{4mm}
\noindent This statement is exactly the same as the merge\index{merge}
statement. It takes two occurrences of the mentioned function and outputs
terms, each with one function in which the two argument lists have been
merged in all different ways, keeping the relative ordering of the two
lists preserved. It is the opposite of the
distrib\_\index{distrib\_}\index{function!distrib\_} function (see
\ref{fundistrib}). Hence
% THIS EXAMPLE IS PART OF THE TESTSUITE. CHANGES HERE SHOULD BE APPLIED THERE AS
% WELL!
\begin{verbatim}
Local F = f(a,b)*f(c,d);
shuffle,f;
\end{verbatim}
will result in
\begin{verbatim}
+f(a,b,c,d)+f(a,c,b,d)+f(a,c,d,b)+f(c,a,b,d)+f(c,a,d,b)+f(c,d,a,b)
\end{verbatim}
One can also obtain the same result with the statements
\begin{verbatim}
Multiply,ff;
repeat id f(x1?,?a)*f(x2?,?b)*ff(?c) =
+f(?a)*f(x2,?b)*ff(?c,x1)
+f(x1,?a)*f(?b)*ff(?c,x2);
id f(?a)*f(?b)*ff(?c) = f(?c,?a,?b);
\end{verbatim}
but the advantage of the shuffle statement is that is also does a certain
amount of combinatorics when there are identical arguments. Unfortunately
the combinatorics doesn't extend over groups of arguments that are
identical as in
\begin{verbatim}
CF f;
L F = f(0,1,0,1,0,1)*f(0,1,0,1,0,1);
Shuffle,f;
.end
Time = 0.00 sec Generated terms = 141
F Terms in output = 32
Bytes used = 892
\end{verbatim}
It does get the combinatorics between two zeroes or two ones, but it cannot
handle the groups. The explicit method above however doesn't do any
combinatorics and generates 924 terms.
One of the applications of this statement is in the field of harmonic
sums\index{harmonic sum},
harmonic polylogarithms\index{harmonic polylogarithm} and multiple zeta
values\index{multiple zeta value}\index{MZV}. Its twin brother is the
stuffle statement\index{stuffle} (see \ref{substastuffle}).
When the option once is mentioned, only one pair will be contracted this
way. Without this option all occurrences of the function inside a term will
be treated till there are only terms with a single occurrence of the
function.
\vspace{10mm}
%
%--#] shuffle :
%--#[ skip :
\section{skip}
\label{substaskip}
\noindent \begin{tabular}{ll}
Type & Specification statement\\
Syntax & skip; \\
& skip {\tt<}list of expressions{\tt>};
\\ See also & nskip (\ref{substanskip})
\end{tabular} \vspace{4mm}
\noindent In the first\index{skip} variety this statement marks all
active\index{active} expressions that are in existence at the moment this
statement is compiled, to be skipped. In the second variety this is done
only to the active expressions that are specified. If an expression is
skipped in a given module, the statements in the module have no effect on
it. Also it will not be sorted\index{sort} again at the end of the module.
This means that any bracket\index{bracket} information (see
\ref{substabracket}) in the expression remains the way it was. Consult also
the nskip\index{nskip} statement in \ref{substanskip}. \vspace{4mm}
\noindent Skipped expressions can be used in the expressions in the r.h.s.\
of id\index{id} statements (see \ref{substaidentify}) or
multiply\index{multiply} statements (see \ref{substamultiply}), etc.
\vspace{10mm}
%--#] skip :
%--#[ sort :
\section{sort}
\label{substasort}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & sort;
\\ See also & term (\ref{substaterm}), endterm (\ref{substaendterm})
\end{tabular} \vspace{4mm}
\noindent Statement\index{sort} to be used inside the term\index{term}
environment\index{environment!term} (see \ref{substaterm} and
\ref{substaendterm}). It forces a sort in the same way as a
.sort\index{.sort} instruction forces a sort for entire expressions.
\vspace{10mm}
%--#] sort :
%--#[ splitarg :
\section{splitarg}
\label{substasplitarg}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & splitarg options \verb:{:{\tt<}name of function/set{\tt>}
[{\tt<}argument specifications{\tt>}]\verb:}:;
\\ See also & splitfirstarg (\ref{substasplitfirstarg}),
splitlastarg (\ref{substasplitlastarg}),
factarg (\ref{substafactarg})
\end{tabular}\vspace{4mm}
\noindent Takes\index{splitarg} the indicated argument\index{argument} of a
function and if such an argument is a subexpression that consists on more
than one term, all terms become single arguments of the function as in
\begin{verbatim}
f(a+b-5*c*d) --> f(a,b,-5*c*d)
\end{verbatim}
The way arguments are indicated is rather similar to the way this is done
in the argument\index{argument statement} statement (see
\ref{substaargument}). One can however indicate only a single group of
functions in one statement. Additionally there are other options. All
options are in the order that they should be specified:
\leftvitem{5cm}{(term)}
\rightvitem{11cm}{Only terms that are a numerical multiple of the given
term are split off. The terms that are split off will trail the remainder.}
\leftvitem{5cm}{((term))}
\rightvitem{11cm}{Only terms that contain the given term will be split off.
The terms that are split off will trail the remainder.}
\noindent The statement is terminated with a sequence of functions or
sets\index{set} of functions. The splitting action will apply only to the
specified functions or to members of the set(s). If no functions or sets of
functions are specified all functions will be treated, including the built
in functions.
\noindent The argument specifications consist of a list of numbers,
indicating the arguments that should be treated. If no arguments are
specified, all arguments will be treated. \vspace{10mm}
%--#] splitarg :
%--#[ splitfirstarg :
\section{splitfirstarg}
\label{substasplitfirstarg}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & splitfirstarg \verb:{:{\tt<}name of function/set{\tt>}
[{\tt<}argument specifications{\tt>}]\verb:}:;
\\ See also & splitarg (\ref{substasplitarg}),
splitlastarg (\ref{substasplitlastarg})
\end{tabular}\vspace{4mm}
\noindent A little\index{splitfirstarg} bit like the
SplitArg\index{splitarg} statement (see \ref{substasplitarg}). Splits the
given argument(s) into its first term and a remainder. Then replaces the
argument by the remainder\index{remainder}, followed by the first term.
\noindent The statement is terminated with a sequence of functions or sets
of functions. The splitting action will apply only to the specified
functions or to members of the set(s). If no functions or sets\index{set}
of functions are specified all functions will be treated, including the
built in functions.
\noindent The argument specifications consist of a list of numbers,
indicating the arguments that should be treated. If no arguments are
specified all arguments will be treated. \vspace{10mm}
%--#] splitfirstarg :
%--#[ splitlastarg :
\section{splitlastarg}
\label{substasplitlastarg}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & splitlastarg \verb:{:{\tt<}name of function/set{\tt>}
[{\tt<}argument specifications{\tt>}]\verb:}:;
\\ See also & splitarg (\ref{substasplitarg}),
splitfirstarg (\ref{substasplitfirstarg})
\end{tabular}\vspace{4mm}
\noindent A little\index{splitlastarg} bit like the
SplitArg\index{splitarg} statement (see \ref{substasplitarg}). Splits the
given argument(s) into its last term and a remainder. Then replaces the
argument by the remainder, followed by the last term.
\noindent The statement is terminated with a sequence of functions or sets
of functions. The splitting action will apply only to the specified
functions or to members of the set(s). If no functions or sets\index{set}
of functions are specified all functions will be treated, including the
built in functions.
\noindent The argument specifications consist of a list of numbers,
indicating the arguments that should be treated. If no arguments are
specified all arguments will be treated. \vspace{10mm}
%--#] splitlastarg :
%--#[ stuffle :
%
\section{stuffle}
\label{substastuffle}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & stuffle,functionname+; \\
& stuffle,functionname-; \\
& stuffle,once,functionname+; \\
& stuffle,once,functionname-; \\
\\ See also & shuffle (\ref{substashuffle})
\end{tabular} \vspace{4mm}
\noindent This statement
takes two occurrences of the mentioned function and outputs
terms, each with one function in which the two argument lists have been
merged according to the rules for nested sums. The plus and minus signs
refer to ones favorite definition for nested sums. In the case of the plus
sign, the definition is
\begin{eqnarray}
\sum_{i=1}^N \sum_{i=1}^N & = & \sum_{i=1}^N \sum_{j=1}^{i-1}
+ \sum_{j=1}^N \sum_{i=1}^{j-1}
+ \sum_{i=j=1}^N
\end{eqnarray}
\setcounter{equation}{4}
while in the case of the minus the definition is
\begin{eqnarray}
\sum_{i=1}^N \sum_{i=1}^N & = & \sum_{i=1}^N \sum_{j=1}^{i}
+ \sum_{j=1}^N \sum_{i=1}^{j}
- \sum_{i=j=1}^N
\end{eqnarray}
\setcounter{equation}{5}
It is assumed that we have harmonic sums\index{harmonic sum} (see the
summer library in the \FORM\ distribution). For such sums we expect
functions with lists of nonzero integer arguments. Example:
% THIS EXAMPLE IS PART OF THE TESTSUITE. CHANGES HERE SHOULD BE APPLIED THERE AS
% WELL!
\begin{verbatim}
CF S,R;
Symbols N,n;
L F = S(R(1,-3),N)*S(R(-5,1),N);
id S(R(?a),n?)*S(R(?b),n?) = S(?a)*S(?b)*R(n);
Stuffle,S-;
id S(?a)*R(n?) = S(R(?a),n);
Print +s;
.end
Time = 0.00 sec Generated terms = 12
F Terms in output = 12
Bytes used = 462
F =
+ S(R(-6,-4),N)
- S(R(-6,-3,1),N)
- S(R(-6,1,-3),N)
- S(R(-5,1,-4),N)
+ S(R(-5,1,-3,1),N)
+ 2*S(R(-5,1,1,-3),N)
- S(R(-5,2,-3),N)
- S(R(1,-5,-4),N)
+ S(R(1,-5,-3,1),N)
+ S(R(1,-5,1,-3),N)
+ S(R(1,-3,-5,1),N)
- S(R(1,8,1),N)
;
\end{verbatim}
The above program is equivalent to the basis procedure in the summer
library. As with the shuffle\index{shuffle} statement (see
\ref{substashuffle}) a certain amount of combinatorics has been built in.
When the option once is mentioned, only one pair will be contracted this
way. Without this option all occurrences of the function inside a term will
be treated till there are only terms with a single occurrence of the
function.
The stuffle command takes also the effect of roots of
unity~\ref{rootofunity}\index{root of unity} into account in the same way
that the signs of alternating sums are taken into account. This means that
the sum indices don't have to be integers, but could be multiples of a
single symbol that has been declared to be a root of
unity~\ref{substasymbols}.
\vspace{10mm}
%
%--#] stuffle :
%--#[ sum :
\section{sum}
\label{substasum}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & sum {\tt<}list of indices{\tt>};
\\ See also & renumber (\ref{substarenumber})
\end{tabular}\vspace{4mm}
\noindent The given indices will be summed\index{sum} over. There are two
varieties. In the first the index is followed by a sequence of nonnegative
short integers. In that case the summation means that for each of the
integers a new instance of the term is created in which the index is
replaced by that integer. In the second variety the index is either the
last object in the statement or followed by another index. In that case the
index is replaced by an internal dummy\index{dummy}
index\index{index!dummy} of the type \verb:N1_?: (or with another number
instead of the 1). Such indices have the current
default\index{default dimension} dimension\index{dimension!default} and can
be renamed at will by \FORM\ to bring terms into standard notation. For
example:
\begin{verbatim}
f(N2_?,N1_?)*g(N2_?,N1_?)
\end{verbatim}
will be changed into
\begin{verbatim}
f(N1_?,N2_?)*g(N1_?,N2_?).
\end{verbatim}
The user can use these dummy indices in the left hand side of
id\index{id} statements.
\vspace{10mm}
%--#] sum :
%--#[ switch :
%
\section{switch}
\label{substaswitch}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & switch,\$-variable; \\
\\ See also & case (\ref{substacase}), break (\ref{substabreak}),
default(\ref{substadefault}), endswitch (\ref{substaendswitch}).
\end{tabular} \vspace{4mm}
\noindent The argument of the switch statement should be a dollar variable
which evaluates into an integer that first inside a {\FORM} word.
On a 64-bit processor this would be an integer in the range $-2^{31}$ to
$2^{31}-1$. The switch statement should be paired with an endswitch
statement. Between the two there will be a number of cases, each marked by
an integer. If the value of the dollar variable corresponds to the value of
one of these cases, execution will continue with the first statement after
the corresponding case statement. Example:
\begin{verbatim}
id f(x?$x) = f(x);
switch $x;
case -1;
some statements
break;
case 3;
more statements
break;
case 4;
case 5;
and a few more
break;
default;
and the default action
break;
endswitch;
\end{verbatim}
In principle the action is the same as in any computer language that has a
switch construction, including the fall-through between case 4 and case 5.
Whether the selection of the cases goes by binary search in a sorted list
or by jumptable is determined by the endswitch statement.
\vspace{10mm}
%
%--#] switch :
%--#[ symbols :
\section{symbols}
\label{substasymbols}
\noindent \begin{tabular}{ll}
Type & Declaration statement\\
Syntax & s[ymbols] {\tt<}list of symbols to be declared{\tt>};
\end{tabular}\vspace{4mm}
\noindent Declares one or more symbols\index{symbol}. Each symbol can be
followed by a number of options. These are (assuming that x is the symbol
to be declared):
\leftvitem{2.4cm}{x\hash{}r}
\rightvitem{13.8cm}{The symbol is real\index{real}. This is the default.}
\leftvitem{2.4cm}{x\hash{}c}
\rightvitem{13.8cm}{The symbol is complex\index{complex}. This means that two
spaces are reserved for this symbol, one for x and one for x\hash (the
complex conjugate).}
\leftvitem{2.4cm}{x\hash{}i}
\rightvitem{13.8cm}{The symbol is imaginary\index{imaginary}.}
\leftvitem{2.4cm}{x\hash{}=number}
\rightvitem{13.8cm}{The symbol is a number-th root of
unity\index{root of unity}\label{rootofunity} This means that the number-th
power of the symbol will be replaced by one and half this power (if even)
by -1. Negative powers will be replaced by corresponding positive powers.}
\leftvitem{2.4cm}{x(:5)}
\rightvitem{13.8cm}{The symbol has the maximum power 5. This means that $x^6$
and higher powers are automatically eliminated during the
normalization\index{normalization} of a term. Of course any other number,
positive or negative, is allowed.}
\leftvitem{2.4cm}{x(-3:)}
\rightvitem{13.8cm}{The symbol has the minimum power -3. This means that
$x^{-4}$ and lower powers are automatically eliminated during the
normalization of a term. Of course any other number, positive or negative,
is allowed. Note that when the minimum power is positive, terms that have
no power of x should technically be eliminated, but \FORM\ will not do so.
Such an action can be achieved at any moment with a combination of the
count\index{if!count}\index{count} option of an if\index{if} statement (see
\ref{substaif}) and a discard\index{discard} statement (see
\ref{substadiscard}).}
\leftvitem{2.4cm}{x(-3:5)}
\rightvitem{13.8cm}{The combination of a maximum and a minimum power
restriction (see above).}\vspace{4mm}
\noindent Complexity properties and power restrictions can be combined. In
that case the complexity properties come first and then the power
restrictions.\vspace{10mm}
%--#] symbols :
%--#[ symmetrize :
\section{symmetrize}
\label{substasymmetrize}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & symm[etrize] \verb:{:{\tt<}name of function/tensor{\tt>}
[{\tt<}argument specifications{\tt>}];\verb:}: \\
See also & antisymmetrize (\ref{substaantisymmetrize}), cyclesymmetrize
(\ref{substacyclesymmetrize}), rcyclesymmetrize (\ref{substarcyclesymmetrize})
\end{tabular} \vspace{4mm}
\noindent The arguments\index{symmetrize} consist of the name of a function
(or a tensor), possibly followed by some specifications. Hence we have the
following varieties: \vspace{1mm}
\leftvitem{5cm}{{\tt<}name{\tt>}}
\rightvitem{11cm}{The function is symmetrized in all its arguments.}
\leftvitem{5cm}{{\tt<}name{\tt><}numbers{\tt>}}
\rightvitem{11cm}{The function is symmetrized in the arguments that are
mentioned. If there are fewer arguments than the highest number mentioned
in the list or arguments, no symmetrization will take place.}
\leftvitem{5cm}{{\tt<}name{\tt>:<}number{\tt>}}
\rightvitem{11cm}{Only functions with the specified number of arguments
will be considered. Note: the number should follow the colon directly
without intermediate space or comma.}
\leftvitem{5cm}{{\tt<}name{\tt>:<}number{\tt><}numbers{\tt>}}
\rightvitem{11cm}{If there is a number immediately following the colon,
only functions with exactly that number of arguments will be considered. If
the list of arguments contains numbers greater than this number, they will
be ignored. If no number follows the colon directly, this indicates that
symmetrization will take place, no matter the number of arguments of the
function. If the list of arguments has numbers greater than the number of
arguments of the function, these numbers will be ignored.}
\leftvitem{5cm}{{\tt<}name{\tt>}
{\tt<}(groups of numbers){\tt>}}
\rightvitem{11cm}{The groups are specified as lists of numbers of arguments
between parenthesis. All groups must have the same number of arguments or
there will be a compile error. The groups are symmetrized as groups. The
arguments do not have to be adjacent. Neither do they have to be ordered.
The symmetrization\index{symmetrization} takes place in a way that the first elements of the
groups are most significant, etc. If any argument number is greater than
the number of arguments of the function, no symmetrization will take place.}
\leftvitem{5cm}{{\tt<}name{\tt>:<}number{\tt>}
{\tt<}(groups of numbers){\tt>}}
\rightvitem{11cm}{The groups are specified as lists of numbers of arguments
between parenthesis. All groups must have the same number of arguments or
there will be a compile error. The groups are symmetrized as groups. The
arguments do not have to be adjacent. Neither do they have to be ordered.
The symmetrization takes place in a way that the first elements of the
groups are most significant, etc. If no number follows the colon directly
symmetrization takes place no matter the number of arguments of the
function. Groups that contain a number that is greater than the number of
arguments of the function will be ignored. If a number follows the colon
directly, only functions with that number of arguments will be symmetrized.
Again, groups that contain a number that is greater than the number of
arguments of the function will be ignored.}
\vspace{3mm}
\noindent The action of this statement is to symmetrize the
(specified) arguments of the functions that are mentioned. This means that
the arguments are brought to `natural order' in the notation of \FORM\ by
trying permutations\index{permutation} of the arguments or groups of
arguments. The `natural order' may depend on the order of declaration of
the variables. \vspace{4mm}
\noindent Examples:
\begin{verbatim}
Symmetrize Fun;
Symmetrize Fun 1,2,4;
Symmetrize Fun:5;
Symmetrize Fun: 1,2,4;
Symmetrize Fun:5 1,2,4;
Symmetrize Fun (1,6),(7,3),(5,2);
Symmetrize Fun:8 (1,6),(7,3),(5,2);
Symmetrize Fun: (1,6),(7,3),(5,2);
\end{verbatim}
\vspace{10mm}
%--#] symmetrize :
%--#[ table :
\section{table}
\label{substatable}
\noindent \begin{tabular}{ll}
Type & Declaration statement\\
Syntax & table {\tt<}options{\tt>} {\tt<}table to be
declared{\tt>}; \\
See also & functions (\ref{substafunctions}), ctable (\ref{substactable}),
ntable (\ref{substantable}), fill (\ref{substafill})
\end{tabular}\vspace{4mm}
\noindent The statement declares a single table\index{table}. A table is a
very special instance of a function. Hence it can be either
commuting\index{commuting} or noncommuting\index{noncommuting}. The table
statement declares its function to be commuting. A noncommuting table is
declared with the ntable\index{ntable} statement (see \ref{substantable}).
A table has a number of table\index{table indices} indices (in the case of
zero indices the table has to be sparse) and after that it can have a
number of regular function arguments with or without wildcarding. The table
indices can come in two varieties: matrix\index{matrix like} like or
sparse\index{sparse}. In the case of a matrix like table\index{table!matrix
like}, for each of the indices a range has to be specified. \FORM\ then
reserves a location for each of the potential elements. For a sparse
table\index{table!sparse} one only specifies the number of indices. Sparse
tables take less space, but they require more time searching whether an
element has been defined. For a matrix like table \FORM\ can look directly
whether an element has been defined. Hence one has a tradeoff between space
and speed. A zero-dimensional (sparse) table has of course only a single
element.\vspace{4mm}
\noindent Table elements are defined with the fill\index{fill} statement (see
\ref{substafill}). Fill statements for table elements cannot be used before
the table has been declared with a table or ntable statement.\vspace{4mm}
\noindent When \FORM\ encounters an unsubstituted table it will look for its
indices. Then it can check whether the table element has been defined. If
not, it can either complain (when the `strict'\index{strict} option is
used) or continue without substitution. Note that an unsubstituted table
element is a rather expensive object as \FORM\ will frequently check whether
it can be substituted (new elements can be defined in a variety of
ways....). If the indices match a defined table element, \FORM\ will check
whether the remaining arguments of the table will match the function-type
arguments given in the table declaration in the same way regular function
arguments are matched. Hence these arguments can contain
wildcards\index{wildcards} and even argument\index{argument field} field
wildcards. If a match occurs, the table is replaced immediately.
\noindent The options are
\lefttabitem{check\index{table!check}}
\tabitem{A check is executed on table boundaries. An element that is
outside the table boundaries (regular matrix type tables only) will cause
an error message and execution will be halted.}
\lefttabitem{relax\index{table!relax}}
\tabitem{Normally all elements of a table should be defined during
execution and an undefined element will give an error message. The relax
option switches this off and undefined elements will remain as if they are
regular functions.}
\lefttabitem{sparse\index{table!sparse}}
\tabitem{The table is considered to be sparse. In the case of a sparse
table only the number of indices should be specified. Ranges are not
relevant. Each table element is stored separately. Searching for table
elements is done via a balanced tree\index{tree!balanced}. This takes of
course more time than the matrix type search with is just by indexing. A
matrix like table\index{table!matrix like} is the default.}
\lefttabitem{strict\index{table!strict}}
\tabitem{If this option is specified all table elements that are
encountered during execution should be defined. An undefined table element
will result in an error and execution is halted. Additionally all table
elements should be properly defined at the end of the module in which the
table has been defined.}
\lefttabitem{zerofill\index{table!zerofill}}
\tabitem{Any undefined table element is considered to be
zero.}
\lefttabitem{onefill\index{table!onefill}}
\tabitem{Any undefined table element is considered to be
one.}\vspace{10mm}
\noindent The defaults are that the table is matrix like and table elements
that cannot be substituted will result in an error.\vspace{4mm}
\noindent Ranges for indices in matrix like tables are indicated with a
colon as in
\begin{verbatim}
Symbol x;
Table t1(1:3,-2:4);
Table t2(0:3,0:3,x?);
Table sparse,t3(4);
\end{verbatim}
The table \verb:t1: is two dimensional and has 21 elements. The table
\verb:t2: is also two dimensional and has 16 elements. In addition there is
an extra argument which can be anything that a wildcard symbol will match.
The table \verb:t3: is a sparse table with 4 indices.\vspace{4mm}
\noindent If the computer on which \FORM\ runs is a 32\index{32 bits} bit
computer no table can have more than $2^{15} = 32768$ elements. On a
64\index{64 bits} bit computer the limit is $2^{31}$, but one should take
into account that each element declared causes some overhead. \vspace{4mm}
\noindent If the wildcarding in the declaration of a table involves the
definition of a dollar variable\index{\$-variable} (this is allowed! See
\ref{dollars}) parallel execution of the entire remainder of the \FORM\
program is switched off. This is of course only relevant for parallel
versions of \FORM. But if at all possible one should try to find better
solutions than this use of dollar variables, allowing future parallel
processing of the program.
\noindent In some cases tables are built up slowly during the execution of
a program and used incrementally. This means that more and more CPU memory
is needed. Eventually this can cause a crash by lack of memory. In the case
that the earlier elements of the table aren't needed anymore, one could use
the ClearTable~\ref{substacleartable} statement.
\vspace{10mm}
%--#] table :
%--#[ tablebase :
\section{tablebase}
\label{substatablebase}
\noindent This statement is explained in the chapter on
tablebases\index{tablebase} (\ref{tablebase}).
\vspace{10mm}
%--#] tablebase :
%--#[ tensors :
\section{tensors}
\label{substatensors}
\noindent \begin{tabular}{ll}
Type & Declaration statement\\
Syntax & t[ensors] {\tt<}list of tensors to be declared{\tt>}; \\
See also & functions (\ref{substafunctions}), ctensors
(\ref{substactensors}), ntensors (\ref{substantensors})
\end{tabular}\vspace{4mm}
\noindent A tensor\index{tensor} is a special function that can have only
indices for its arguments. If an index a contracted with the index of a
vector Schoonschip\index{Schoonschip} notation is used. This means that the
vector is written as a pseudo argument of the tensor. It should always be
realized that in that case in principle the actual argument is a dummy
index. Tensors come in two varieties: commuting\index{commuting} and
noncommuting\index{noncommuting}. The tensor statement declares a tensor to
be commuting. In order to declare a tensor to be noncommuting one should
use the ntensor\index{ntensor} statement (see \ref{substantensors}).
\noindent The options that exist for properties of tensors are the same as
those for functions (see \ref{substafunctions}). \vspace{10mm}
%--#] tensors :
%--#[ term :
\section{term}
\label{substaterm}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & term;
\\ See also & endterm (\ref{substaendterm}), sort (\ref{substasort})
\end{tabular} \vspace{4mm}
\noindent Begins the term\index{term} environment\index{environment!term}.
This environment is terminated with the endterm\index{endterm} statement
(see \ref{substaendterm}). The action is that temporarily the current term
is seen as a little expression by itself. The statements inside the
environment are applied to it and one can even sort the results with the
sort\index{sort} statement (see \ref{substasort}) which should not be
confused with the .sort\index{.sort} instruction that terminates a module.
Inside the term environment one can have only executable statements and
possibly term-wise print statements (see \ref{substaprint}). When the end
of the term environment is reached, the results are sorted (as would be
done with an expression at the end of a module) and execution continues
with the resulting terms. This environment can be nested\index{nested}.
\vspace{10mm}
%--#] term :
%--#[ testuse :
\section{testuse}
\label{substatestuse}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & testuse ["{\tt<}tablename(s){\tt>}"];
\\ See also & tablebases (\ref{tablebase}), testuse (\ref{tbltestuse})
\end{tabular} \vspace{4mm}
\noindent This statement\index{testuse} is explained in the chapter on
tablebases\index{tablebase}.\vspace{10mm}
%--#] testuse :
%--#[ threadbucketsize :
\section{threadbucketsize}
\label{substathreadbucketsize}
\noindent \begin{tabular}{ll}
Type & Declaration\\
Syntax & ThreadBucketSize,number;
\\ See also & the section on \TFORM (\ref{tform})
\end{tabular} \vspace{4mm}
\noindent This statement\index{threadbucketsize} is only active in
\TFORM\index{TFORM}. In all other versions of \FORM\ it is ignored. It sets
the size of the buckets\index{bucket} that the master\index{master} thread
prepares for treatment by the workers. Bigger buckets means less overhead
in signals, but when the buckets are too big the workers may have to wait
too long before getting tasks. The best bucket size is usually between 100
and 1000, although this depends very much on the problem. The default value
is currently 500. For more ways to set this variable one should consult the
section on \TFORM\ (\ref{tform}). To find out what its value is, use the
`ON,setup;' statement (\ref{substaon} and \ref{setup}). \vspace{10mm}
%--#] threadbucketsize :
%--#[ topolynomial :
\section{topolynomial}
\label{substatopolynomial}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & topolynomial[,OnlyFunctions[,{\tt<}list of functions{\tt>}]];
\\ See also & factarg (\ref{substafactarg}), FromPolynomial
(\ref{substafrompolynomial}), ArgToExtraSymbol (\ref{substaargtoextrasymbol})
\\& and ExtraSymbols (\ref{substaextrasymbols},
\ref{sect-extrasymbols}).
\end{tabular} \vspace{4mm}
\noindent Starting with version 4.0 of \FORM{} some built in operations or
statements can only deal with symbols and numbers. Examples of this are
factorization~(\ref{substafactarg}) and output simplification (still to be
implemented). The ToPolynomial statement takes each term, looks for objects
that are not symbols to positive powers and replaces them by symbols. If
the object has been encountered before, the same symbol will be used,
otherwise a new symbol will be defined. The object represented by the
`extra symbol' is stored internally and can be printed if needed with the
\%X option in the \#write instruction (\ref{prewrite}). Note that negative
powers of symbols will also be replaced.
In some cases one would like to do this only for a subset of objects. It is
possible to do this only for functions, using the OnlyFunctions option. If
no functions are specified, all functions will be replaced by extra
symbols. If a list of functions is specified, only those functions will be
replaced.
\vspace{10mm}
%--#] topolynomial :
%--#[ tospectator :
\section{tospectator}
\label{substatospectator}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & tospectator {\tt<}spectator;{\tt>};
\end{tabular} \vspace{4mm}
\noindent See chapter\ref{spectators} on spectators.
\vspace{10mm}
%--#] tospectator :
%--#[ totensor :
\section{totensor}
\label{substatotensor}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & totensor [nosquare] [functions] [!{\tt<}vector or set{\tt>}] {\tt<}vector{\tt>} {\tt<}tensor{\tt>}; \\
& totensor [nosquare] [functions] [!{\tt<}vector or set{\tt>}] {\tt<}tensor{\tt>} {\tt<}vector{\tt>};
\\ See also & tovector (\ref{substatovector})
\end{tabular} \vspace{4mm}
\noindent Looks for multiple\index{totensor} occurrences of the given
vector, either inside dotproducts, contracted with a tensor, as argument of
a function or as a loose vector with an index. In all occurrences in
which the vector has been contracted a dummy index is introduced to make
the contraction apparent. Then all these vectors with their indices are
replaced by the specified tensor with all the indices of these vectors. To
make this clearer:
\begin{eqnarray}
p^{\mu_1}p^{\mu_2}p^{\mu_3} \rightarrow t^{\mu_1\mu_2\mu_3} \nonumber
\end{eqnarray}
\setcounter{equation}{6}
and hence
\begin{verbatim}
p.p1^2*f(p,p1)*p(mu)*tt(p1,p,p2,p)
\end{verbatim}
gives after \verb:totensor p,t;:
\begin{verbatim}
f(N1_?,p1)*tt(p1,N2_?,p2,N3_?)*t(p1,p1,mu,N1_?,N2_?,N3_?)
\end{verbatim}\vspace{4mm}
\noindent The options are
\leftvitem{3.5cm}{nosquare\index{totensor!nosquare}}
\rightvitem{13cm}{Dotproducts with twice the specified vector (square of
the vector) are not taken into account.}
\leftvitem{3.5cm}{functions\index{totensor!functions}}
\rightvitem{13cm}{Vectors that are arguments of regular functions will also
be considered. By default this is not done.}
\leftvitem{3.5cm}{!vector\index{totensor!"!vector}}
\rightvitem{13cm}{Dotproducts involving the specified vector are not
treated.}
\leftvitem{3.5cm}{!set\index{totensor!"!set}}
\rightvitem{13cm}{The set should be a set of vectors. All dotproducts
involving a vector of the set are not treated.}\vspace{10mm}
%--#] totensor :
%--#[ tovector :
\section{tovector}
\label{substatovector}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & tovector {\tt<}tensor{\tt>} {\tt<}vector{\tt>}; \\
& tovector {\tt<}vector{\tt>} {\tt<}tensor{\tt>};
\\ See also & totensor (\ref{substatotensor})
\end{tabular} \vspace{4mm}
\noindent The opposite\index{tovector} of the totensor\index{totensor}
statement. The tensor is replaced by a product of the given vectors, each
with one of the indices of the tensor as in:
\begin{eqnarray}
t^{\mu_1\mu_2\mu_3} \rightarrow p^{\mu_1}p^{\mu_2}p^{\mu_3} \nonumber
\end{eqnarray}\vspace{10mm}
\setcounter{equation}{7}
%--#] tovector :
%--#[ trace4 :
\section{trace4}
\label{substatrace}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & trace4 [{\tt<}options{\tt>}] {\tt<}index{\tt>}; \\
See also & tracen (\ref{substatracen}), chisholm (\ref{substachisholm}),
unittrace (\ref{substaunittrace}) \\ &
and the chapter on gamma algebra (\ref{gammaalgebra})
\end{tabular} \vspace{4mm}
\noindent Takes the trace\index{trace4} of the gamma\index{gamma matrices}
matrices with the given trace\index{trace line} line
index\index{index!trace line}. It assumes that the matrices are
defined in four dimensions, hence it uses some relations that are only
valid in four dimensions. For details about these relations and other
methods used, consult chapter~\ref{gammaalgebra} on gamma matrices. The
options are: \vspace{4mm}
\lefttabitem{contract\index{trace4!contract}}
\tabitem{Try to use the Chisholm\index{Chisholm} identity to eliminate this
trace and contract it with other gamma matrices. See also
\ref{substachisholm}. This is the default.}
\lefttabitem{nocontract\index{trace4!nocontract}}
\tabitem{Do not use the Chisholm\index{Chisholm} identity to eliminate this
trace and contract it with other gamma matrices. See also
\ref{substachisholm}.}
\lefttabitem{nosymmetrize\index{trace4!nosymmetrize}}
\tabitem{When using the Chisholm\index{Chisholm} identity to eliminate this
trace and contract it with other gamma matrices, do not do it in the
symmetric fashion, but use the first contraction encountered. See also
\ref{substachisholm}.}
\lefttabitem{notrick\index{trace4!notrick}}
\tabitem{The final stage of trace taking, when all indices are different
and there are no contractions with identical vectors, as well as no
$\gamma_5$ matrices present, is done with n-dimensional methods, rather
than with 4-dimensional tricks.}
\lefttabitem{symmetrize}
\tabitem{When using the Chisholm identity to eliminate this trace and
contract it with other gamma matrices, try to do it in the symmetric
fashion. See also \ref{substachisholm}.}
\lefttabitem{trick}
\tabitem{The final stage of trace taking, when all indices are different
and there are no contractions with identical vectors is done using the
4-dimensional relation
$\gamma^a\gamma^b\gamma^c = \epsilon^{abcd}\gamma_5\gamma^d
+\gamma^a\delta^{bc}-\gamma^b\delta^{ac}+\gamma^c\delta^{ab}$
This gives a shorter result for long traces. It is the default.
} \vspace{10mm}
%--#] trace4 :
%--#[ tracen :
\section{tracen}
\label{substatracen}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & tracen {\tt<}index{\tt>}; \\
See also & trace4 (\ref{substatrace}), chisholm (\ref{substachisholm}),
unittrace (\ref{substaunittrace}) \\ &
and the chapter on gamma algebra (\ref{gammaalgebra})
\end{tabular} \vspace{4mm}
\noindent Takes\index{tracen} the trace of the gamma\index{gamma matrices}
matrices with the spin\index{spin line} line indicated by the index. It is
assumed that the trace is over a symbolic number of dimensions. Hence no
special 4-dimensional tricks are used. The presence of $\gamma_5$,
$\gamma_6$ or $\gamma_7$ is not tolerated. When indices are contracted
{\FORM} will try to use the special symbol for the dimension$-4$ if it has
been defined in the declaration of the index (see \ref{substaindex}. This
results in relatively compact expressions. For more details on the
algorithm used, see chapter~\ref{gammaalgebra} on gamma matrices.
\vspace{10mm}
%--#] tracen :
%--#[ transform :
\section{transform}
\label{substatransform}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & transform,function(s),{\tt<}one or more transformations{\tt>};
\end{tabular} \vspace{4mm}
\noindent Statement\index{Transform} to manipulation function arguments and
fields of arguments. Allows speedy transformations without the need of
multiple statements or repeat loops.
The function(s) is/are indicated as individual, comma or blank space
separated, functions or sets of functions.
If there is more than one transformation, the transformations are separated
by comma's (or blanks, unless the blank space would not induce a comma).
Each transformation consists of its keyword, indicating its type, followed
by a range of arguments that is enclosed by parentheses. After that
specific information may follow. The range\index{last}\index{range} is as
in
\begin{verbatim}
(1,4)
(3,last)
(last-6,last-2)
\end{verbatim}
hence two indicators, separated by a comma. If the first number is bigger
than the second the arguments will be processed in reverse order whenever
this is relevant. In the descriptions below we will indicate the range by
(r1,r2). The numbers in the above examples may be also dollar variables,
provided they evaluate into numbers at the time of execution. Hence
\begin{verbatim}
($x,$y)
($x,last)
(last-$x,last-2)
\end{verbatim}
are potentially legal ranges. One may not use \verb:$x+2: or other
expressions that still need evaluation.
The transformations that are allowed currently are:
\leftvitem{3.2cm}{replace\index{transform!replace}\index{replace}}
\rightvitem{13cm}{replace(r1,r2)=(from1,to1,from2,to2,...,fromn,ton) in
which the from-to pairs are as in the replace\_ function. Here however
there are more options than in the replace\_ function as we can specify
(small) numbers as well as in \\
replace(1,last)=(0,1,1,0) which would replace arguments that are zero by
one and arguments that are one by zero. Generic arguments are indicated by
the new variables xarg\_, iarg\_, parg\_ and farg\_ as in \\
replace(1,last)=(xarg\_,2\*xarg\_+1,p) which would replace f(2,a) by f(5,
2\*a+1,p) if a is a symbol and p a vector. To catch p one would need to use
parg\_.}
\leftvitem{3.2cm}{encode\index{transform!encode}\index{encode}}
\rightvitem{13cm}{encode(r1,r2):base=number will interprete the arguments as
the digits in a base 2 number system, compute the complete number and
replace the arguments by a single argument that is that number. The number
must fit inside a single FORM word and so must each of the original
arguments. They should actually be smaller than the number of the base.}
\leftvitem{3.2cm}{decode\index{transform!decode}\index{decode}}
\rightvitem{13cm}{decode(r1,r2):base=number will do the opposite of encode.
It will take a single argument (the smallest of the two given) and expand
it into digits in a number system given by the base. It will create the
specified number of digits and replace the original number by the given
number of arguments representing these digits. If r2 is less than r1 the
digits will be in reverse order.}
\leftvitem{3.2cm}{tosumnotation\index{transform!tosumnotation}\index{tosumnotation}
\index{transform!implode}\index{implode}}
\rightvitem{13cm}{tosumnotation(r1,r2) or implode(r1,r2) realizes an
encoding in which zeroes are absorbed as extra values in the first nonzero
argument that is following. This is used when dealing with harmonic sums
and harmonic polylogarithms. An example is that (0,0,1,0,a,0,0,0,-1) (which
is in integral notation) goes into (3,2*a,-4) (which is in sum notation).
Currently only a single symbol is allowed and the numbers should be (small)
integers because otherwise the reverse operation (explode) would generate
too many arguments. Instead of ``tosumnotation'' one may also use the word
``implode'' in accordance with the argimplode statement.}
\leftvitem{3.2cm}{tointegralnotation\index{transform!tointegralnotation}
\index{tointegralnotation}\index{transform!explode}\index{explode}}
\rightvitem{13cm}{tointegralnotation(r1,r2) or explode(r1,r2) undoes what
implode might have done. Hence each integer with an absolute value $n$
generates $n-1$ zeroes and leaves something with absolute value one.
Instead of ``tointegralnotation'' one may also use the word
``explode'' in accordance with the argexplode statement.}
\leftvitem{3.2cm}{permute\index{transform!permute}\index{permute}}
\rightvitem{13cm}{permute(1,3,5)(2,6) will permute the arguments
according to the cycles indicated. The cycles are executed in order and may
overlap. Their number is not restricted. In the above example
f(a1,a2,a3,a4,a5,a6,a7) $\rightarrow$ f(a3,a6,a5,a4,a1,a2,a7).
It is allowed to use \$-variables in the cycles, including \$-variables
that are obtained by matching argument field wildcards.}
\leftvitem{3.2cm}{reverse\index{transform!reverse}\index{reverse}}
\rightvitem{13cm}{reverse(r1,r2) reverses the order of the arguments in
specified range.}
\leftvitem{3.2cm}{dedup\index{transform!dedup}\index{dedup}}
\rightvitem{13cm}{dedup(r1,r2) removes duplicates from the arguments in the range, keeping the first.}
\leftvitem{3.2cm}{cycle\index{transform!cycle}\index{cycle}}
\rightvitem{13cm}{cycle(r1,r2)=+/-number will perform a cyclic permutation
of the indicated range of arguments. If the number is preceeded by a - the
cycling is to the left. If there is a plus sign the cycling is to the
right. Note that either the plus or the minus sign is mandatory. The number
following the +/- sign is also allowed to be a dollar variable provided it
evaluates to a legal number during execution.}
\leftvitem{3.2cm}{islyndon\index{transform!islyndon}\index{islyndon}}
\rightvitem{13cm}{islyndon(r1,r2)=(yes,no) will test whether the indicated
range of arguments forms a Lyndon word\index{Lyndon word} according to the
ordering of arguments in FORM. The yes and no arguments are what the main
term will be multiplied by when the range forms a Lyndon word or does not
respectively. Because the definition of a Lyndon word is the unique minimal
cyclic permutation of the arguments, and because often we may need the
unique maximal cyclic permutation there are varieties: for the minimum one
may also use islyndon$<$(r1,r2)=(yes,no) or islyndon-(r1,r2)=(yes,no),
while for the maximum one may use islyndon$>$(r1,r2)=(yes,no) or
islyndon+(r1,r2)=(yes,no).}
\leftvitem{3.2cm}{tolyndon\index{transform!tolyndon}\index{tolyndon}}
\rightvitem{13cm}{tolyndon(r1,r2)=(yes,no) will permute the given range in
a cyclic manner till it is (if possible) a Lyndon word\index{Lyndon word}
according to the ordering of arguments in FORM. The yes and no arguments
are what the main term will be multiplied by when afterwards the range
forms a Lyndon word or does not respectively. Because the definition of a
Lyndon word is the unique minimal cyclic permutation of the arguments, and
because often we may need the unique maximal cyclic permutation there are
varieties: for the minimum one may also use tolyndon$<$(r1,r2)=(yes,no) or
tolyndon-(r1,r2)=(yes,no), while for the maximum one may use
tolyndon$>$(r1,r2)=(yes,no) or tolyndon+(r1,r2)=(yes,no). If the output is
not a Lyndon word, this will be due to that it is a minimum or maximum that
is not unique.}
\leftvitem{3.2cm}{addargs\index{transform!addargs}\index{addargs}}
\rightvitem{13cm}{addargs(r1,r2) replaces the indicated range of arguments
by their sum. This is effectively the inverse of the SplitArg statement.}
\leftvitem{3.2cm}{mulargs\index{transform!mulargs}\index{mulargs}}
\rightvitem{13cm}{mulargs(r1,r2) replaces the indicated range of arguments
by their product. This is effectively the inverse of the FactArg statement.}
\leftvitem{3.2cm}{dropargs\index{transform!dropargs}\index{dropargs}}
\rightvitem{13cm}{dropargs(r1,r2) removes the indicated range of arguments.}
\leftvitem{3.2cm}{selectargs\index{transform!selectargs}\index{selectargs}}
\rightvitem{13cm}{selectargs(r1,r2) removes all arguments with the exception
of the indicated range of arguments.}
Some Examples. Assume that we have some Multiple Zeta Values\index{Multiple
Zeta Value}\index{MZV} (see the papers on harmonic sums\index{harmonic
sums}, harmonic polylogarithms\index{harmonic polylogarithm} and the MZV
data mine\index{MZV data mine}) in the sum notation, but for calculational
reason we want to use a binary encoding (as used in the MZV programs). We
could have
\begin{verbatim}
Symbol x,x1,x2;
CF H,H1;
Off Statistics;
L F = H(3,4,2,6,1,1,1,2);
repeat id H(?a,x?!{0,1},?b) = H(?a,0,x-1,?b);
Print;
.sort
F =
H(0,0,1,0,0,0,1,0,1,0,0,0,0,0,1,1,1,1,0,1);
Multiply H1;
repeat id H(x?,?a)*H1(?b) = H(?a)*H1(?b,1-x);
id H1(?a)*H = H(?a);
Print;
.sort
F =
H(1,1,0,1,1,1,0,1,0,1,1,1,1,1,0,0,0,0,1,0);
repeat id H(x1?,x2?,?a) = H(2*x1+x2,?a);
Print;
.end
F =
H(907202);
\end{verbatim}
The new version of the same program would be
\begin{verbatim}
Symbol x,x1,x2;
CF H,H1;
Off Statistics;
L F = H(3,4,2,6,1,1,1,2);
Transform,H,explode(1,last),
replace(1,last)=(0,1,1,0),
encode(1,last):base=2;
Print;
.end
F =
H(907202);
\end{verbatim}
It should be clear that this is simpler and faster. On a 64-bits computer
it is faster by more than a factor 100.
\vspace{10mm}
%--#] transform :
%--#[ tryreplace :
\section{tryreplace}
\label{substatryreplace}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & tryreplace \verb:{:{\tt<}name{\tt>} {\tt<}replacement{\tt>}\verb:}:;
\\ See also & the replace\_ function (\ref{funreplace})
\end{tabular} \vspace{4mm}
\noindent The list\index{tryreplace} of potential replacements should be
similar to the arguments of the replace\_\index{replace\_}
function\index{function!replace\_} (see \ref{funreplace}). {\FORM} will
make a copy of the current term, try the replacement and if the replacement
results in a term which, by the internal ordering of {\FORM}, comes before
the current term, the current term is replaced by the new variety.
\vspace{10mm}
%--#] tryreplace :
%--#[ unfactorize :
\section{unfactorize}
\label{substaunfactorize}
\noindent \begin{tabular}{ll}
Type & Output control statement\\
Syntax & unfactorize \verb:{:{\tt<}name of expression(s){\tt>}\verb:}:;
\\ See also & the chapter on polynomials~\ref{polynomials} and the
factorize statement~\ref{substafactorize}.
\end{tabular} \vspace{4mm}
\noindent Without arguments the statement causes all expressions that were
factorized to be 'unfactorized'. This means that all factors are multiplied
and the expression is replaced by this new version. Like the factorize
statement this statement is an output control statement, which means that
it takes effect after an expression has been processed in the current
module (see also the factorize~\ref{substafactorize} statement).
\noindent Because an immediate multiplication of all factors is sometimes
far from optimal, FORM uses a binary scheme to combine factors. After each
step there will be a sort operation. This means that when statistics are
printed, there may be several statistics for this step.
\noindent When the statement has arguments, these arguments should be names
of expressions. In that case the unfactorization is applied only to the
expressions that are specified.
\noindent If one likes to unfactorized all expressions except for a few
ones, one can use the unfactorize statement without arguments and then
exclude the few expressions that should not be treated with the
nunfactorize statement (see \ref{substanunfactorize}).
\vspace{10mm}
%--#] unfactorize :
%--#[ unhide :
\section{unhide}
\label{substaunhide}
\noindent \begin{tabular}{ll}
Type & Specification statement\\
Syntax & unhide; \\
& unhide {\tt<}list of expressions{\tt>};
\\ See also & hide (\ref{substahide}),
nhide (\ref{substanhide}),
nunhide (\ref{substanunhide}),
pushhide (\ref{substapushhide}),
pophide (\ref{substapophide})
\end{tabular} \vspace{4mm}
\noindent In its\index{unhide} first variety this statement causes all
statements in the hide\index{hide} file\index{file!hide} to become
active\index{active} expressions again. In its second variety only the
specified expressions are taken from the hide system and become active
again. An expression that is made active again can be manipulated again in
the module in which the unhide statement occurs. For more information one
should look at the hide statement in \ref{substahide}. \vspace{4mm}
\noindent Note that if only a number of expressions is taken from the hide
system, the hide file may be left with `holes', i.e. space between the
remaining expressions that contain no relevant information any longer.
{\FORM} contains no mechanism to use the space in these holes. Hence if
space is at a premium and many holes develop one should unhide all
expressions (this causes the hide system to be started from zero size
again) and then send the relevant expressions back to the hide system.
\vspace{10mm}
%--#] unhide :
%--#[ unittrace :
\section{unittrace}
\label{substaunittrace}
\noindent \begin{tabular}{ll}
Type & Declaration statement\\
Syntax & u[nittrace] {\tt<}value{\tt>}; \\
See also & trace4 (\ref{substatrace}), tracen (\ref{substatracen}),
chisholm (\ref{substachisholm}) \\ &
and the chapter on gamma algebra (\ref{gammaalgebra}).
\end{tabular} \vspace{4mm}
\noindent Sets\index{unittrace} the value of the trace of the
unit\index{unit matrix} matrix\index{matrix!unit} in the Dirac\index{Dirac}
algebra\index{algebra!Dirac} (i.e. the object \verb:g1_(n): for trace line
\verb:n:)). The parameter \verb:value: can be either a short positive
number or any symbol with the exception of \verb:i_:. See also
chapter~\ref{gammaalgebra}. \vspace{10mm}
%--#] unittrace :
%--#[ vectors :
\section{vectors}
\label{substavectors}
\noindent \begin{tabular}{ll}
Type & Declaration statement\\
Syntax & v[ectors] {\tt<}list of vectors to be declared{\tt>};
\end{tabular} \vspace{4mm}
\noindent Used for the declaration of vectors\index{vector}. Example:
\begin{verbatim}
Vectors p,q,q1,q2,q3;
\end{verbatim}
\vspace{10mm}
%--#] vectors :
%--#[ while :
\section{while}
\label{substawhile}
\noindent \begin{tabular}{ll}
Type & Executable statement\\
Syntax & while ( condition );
\\ See also & endwhile (\ref{substaendwhile}), repeat (\ref{substarepeat}),
if (\ref{substaif})
\end{tabular} \vspace{4mm}
\noindent This statement\index{while} starts the while
environment\index{environment!while}. It should be paired with an
endwhile\index{endwhile} statement (see \ref{substaendwhile}) which
terminates the while environment. The statements between the while and the
endwhile statements will be executed as long as the condition is met. For
the description of the condition one should consult the if\index{if}
statement (see \ref{substaif}). The while/endwhile combination is
equivalent to the construction
\begin{verbatim}
repeat;
if ( condition );
endif;
endrepeat;
\end{verbatim}
If only a single statement is inside the environment one can also use
\begin{verbatim}
while ( condition ) statement;
\end{verbatim}
Of course one should try to avoid infinite\index{infinite loop}
loops\index{loops!infinite}. In order to maximize the speed of {\FORM} not
all internal stacks are protected and hence the result may be that {\FORM}
may crash. It is also possible that {\FORM} may detect a shortage of buffer
space and quit with an error message. \vspace{4mm}
\noindent For each term for which execution reaches the endwhile statement,
control is brought back to the while statement. For each term that reaches
the while statement the condition is checked and if it is met, the
statements inside the environment are executed again on this term. If the
condition is not met, execution continues after the endwhile statement.
\vspace{10mm}
%--#] while :
%--#[ write :
\section{write}
\label{substawrite}
\noindent \begin{tabular}{ll}
Type & Declaration statement\\
Syntax & w[rite] {\tt<}keyword{\tt>};
\\ See also & on (\ref{substaon}), off (\ref{substaoff})
\end{tabular} \vspace{4mm}
\noindent This statement\index{write} is considered
obsolete\index{obsolete}. All its varieties have been taken over by the
on\index{on} statement (see \ref{substaon}) and the off\index{off}
statement (see \ref{substaoff}). The current version of {\FORM} will still
recognize it, but the user is advised to avoid its usage. In future
versions of {\FORM} it is scheduled to be used for a different kind of
writing and hence its syntax may change considerably. The conversion
program conv2to3 should help in the conversion of programs written for
version 2. For completeness we still give the syntax and how it should be
converted. The keywords are: \vspace{4mm}
\leftvitem{3.5cm}{allnames\index{write!allnames}}
\rightvitem{13cm}{Same as: On allnames;}
\leftvitem{3.5cm}{allwarnings\index{write!allwarnings}}
\rightvitem{13cm}{Same as: On allwarnings;}
\leftvitem{3.5cm}{highfirst\index{write!highfirst}}
\rightvitem{13cm}{Same as: On highfirst;}
\leftvitem{3.5cm}{lowfirst\index{write!lowfirst}}
\rightvitem{13cm}{Same as: On lowfirst;}
\leftvitem{3.5cm}{names\index{write!names}}
\rightvitem{13cm}{Same as: On names;}
\leftvitem{3.5cm}{powerfirst\index{write!powerfirst}}
\rightvitem{13cm}{Same as: On powerfirst;}
\leftvitem{3.5cm}{setup\index{write!setup}}
\rightvitem{13cm}{Same as: On setup;}
\leftvitem{3.5cm}{shortstatistics\index{write!shortstatistics}}
\rightvitem{13cm}{Same as: On shortstatistics;}
\leftvitem{3.5cm}{shortstats\index{write!shortstats}}
\rightvitem{13cm}{Same as: On shortstats;}
\leftvitem{3.5cm}{statistics\index{write!statistics}}
\rightvitem{13cm}{Same as: On statistics;}
\leftvitem{3.5cm}{stats\index{write!stats}}
\rightvitem{13cm}{Same as: On stats;}
\leftvitem{3.5cm}{warnings\index{write!warnings}}
\rightvitem{13cm}{Same as: On warnings;}
\vspace{10mm}
%--#] write :
form-master/doc/manual/tablebas.tex 0000664 0000000 0000000 00000035315 13565763364 0017664 0 ustar 00root root 0000000 0000000
\chapter{The TableBase}
\label{tablebase}
The tablebase\index{tablebase} statement controls a
database\index{database}-like structure that allows \FORM\ to control massive
amounts of data in the form of tables and
table\index{table elements}\index{table} elements.
The contents of a tablebase are formed by one or more table declarations
and a number of fill\index{fill} statements. These fill statements however
are not immediately compiled. For each fill statement a special fill
statement is generated and compiled that is of the form
\begin{verbatim}
Fill tablename(indices) = tbl_(tablename,indices,arguments);
\end{verbatim}
The function tbl\_\index{tbl\_} is a special function to make a temporary
table substitution. It indicates that the corresponding element can be
found in a tablebase that has been opened. At a later stage one can tell
\FORM\ to see which table elements are actually needed and then only those
will be loaded from the tablebase and compiled.
Tablebases have a special internal structure and the right hand sides of
the fill statements are actually stored in a compressed\index{compressed}
state. These tablebases can be created with special statements and uploaded
with any previously compiled table. Hence one can prepare a tablebase in a
previous job, to be used at a later stage, without the time penalty of
loading the whole table at that later stage.
Assume we have a file named no11fill.h that loooks like
\begin{verbatim}
Symbols ...;
Table,sparse,no11fill(11,N?);
Fill no11fill(-3,1,1,1,1,1,1,1,0,0,0) = ....
Fill no11fill(-2,1,1,1,1,1,1,1,0,0,0) = ....
etc.
\end{verbatim}
It should be noted that only sparse\index{sparse} tables can be stored
inside a tablebase. The right hand sides could be typically a few kilobytes
of formulas and there could be a few thousand of these fill statements. To
make this into a tablebase one would use the program
\begin{verbatim}
#-
#include no11fill.h
#+
TableBase "no11.tbl" create;
TableBase "no11.tbl" addto no11fill;
.end
\end{verbatim}
The include\index{\#include} instruction makes that \FORM\ reads and compiles
the table. Then the first tablebase statement creates a new tablebase file
by the name no11.tbl. If such a file existed already, the old version will
be lost. If one would like to add to an existing tablebase, one should use
the `open'\index{open} keyword. The second tablebase statement adds the
table no11fill to the tablebase file no11.tbl. This takes care of declaring
the table, making an index of all elements that have been filled and
putting their right hand sides, in compressed form, into the tablebase. The
compression is based on the zlib\index{zlib} library, provided by Jean-loup
Gailly\index{Gailly!Jean-loup} and Mark Adler\index{Adler!Mark} (version
1.2.3, July 18, 2005) and it strikes a nice balance between speed and
compression ratio.
The tablebase can be loaded in a different program as in
\begin{verbatim}
TableBase "no11.tbl" open;
\end{verbatim}
This loads the main index\index{index!main} of the file into memory.
If one would like to compile the short version of the fill statements (the
normal action at this point) one needs to use the load\index{load} option.
Without any names of tables it will read the index of all tables. If tables
are specied, only the index of those tables is taken and the proper
tbl\_ fill statements are generated:
\begin{verbatim}
TableBase "no11.tbl" open;
TableBase "no11.tbl" load no11fill;
\end{verbatim}
If one would like to compile\index{compile} the complete tables, rather
than just the shortened versions, one can use the enter option as in:
\begin{verbatim}
TableBase "no11.tbl" open;
TableBase "no11.tbl" enter no11fill;
\end{verbatim}
Let us assume we used the load option. Hence now an occurrence of a table
element will be replaced by the stub\index{stub function}-function
tbl\_\index{tbl\_}. In order to have this replaced by the actual right hand
side of the original fill statement we have to do some more work. At a
given moment we have to make \FORM\ look which elements are actually needed.
This is done with the TestUse\index{testuse} statement as in
\begin{verbatim}
TestUse no11fill;
\end{verbatim}
This does nothing visible. It just marks internally which elements will be
needed and have not been entered yet.
The actual entering of the needed elements is done with the use\index{use}
option:\begin{verbatim}
TableBase "no11.tbl" use;
\end{verbatim}
If many elements are needed, this statement may need some compilation time.
Note however that this is time at a moment that it is clear that the
elements are needed, which is entirely different from a fixed time at the
startup of a program when the whole table is loaded as would have to be
done before the tablebase statement existed. Usually however only a
part of the table is needed, and in the extreme case only one or two
elements. In that case the profit is obvious.
At this point the proper elements are available inside the system, but
because we have two versions of the table (one the short version with
tbl\_, the other the complete elements) we have to tell \FORM\ to apply
the proper definitions with the `apply'\index{apply} statement.
\begin{verbatim}
Apply;
\end{verbatim}
Now the actual rhs will be inserted.
One may wonder why this has to be done in such a `slow' way with this much
control over the process. The point is that at the moment the table
elements are recognized, one may not want the rhs yet, because it may be
many lines. Yet one may want to take the elements away from the main stream
of action. Similarly, having a table element recognized at a certain stage,
may not mean automatically that it will be needed. The coefficient may
still become zero during additional manipulations. Hence the user is left
with full control over the process, even though that may lead to slightly
more programming. It will allow for the fastest program.
For the name of a tablebase we advise the use of the extension
.tbl\index{.tbl} to avoid confusion.
Note that the above scheme may need several applications, if table elements
refer in their definition to other table elements. This can be done with a
construction like:
\begin{verbatim}
#do i = 1,1
TestUse;
.sort
TableBase "basename.tbl" use;
Apply;
if ( count(tbl_,1) ) Redefine i "0";
.sort
#enddo
\end{verbatim}
It will stay in the loop until there are no more tbl\_ functions to be
resolved.
\medskip\noindent The complete syntax (more is planned):
%--#[ addto :
\section{addto}
\label{tbladdto}
\noindent Syntax:
TableBase "file.tbl" addto tablename;
TableBase "file.tbl" addto tablename(tableelement);
\noindent See also open (\ref{tblopen}) and create (\ref{tblcreate}).
\noindent Adds\index{addto} the contents of a (sparse\index{sparse}) table
to a tablebase. The base must be either an existing tablebase (made
accessible with an open statement) or a new tablebase (made available with
a create statement). In the first version what is added is the collection
of all fill statements that have been used to define elements of the
indicated table, in addition to a definition of the table (if that had not
been done yet). In the second version only individual elements of the
indicated table are added. These elements are indicated as it should be in
the left hand side of a fill\index{fill} statement.
\noindent One is allowed to specify more than one table, or more than one
element. If one likes to specify anything after an element, it should be
realized that one needs to use a comma for a separator, because blank
spaces after a parenthesis are seen as irrelevant.
\noindent Examples:
\begin{verbatim}
TableBase "no11.tbl" open;
TableBase "no11.tbl" load;
TableBase "no11.tbl" addto no11filb;
TableBase "no11.tbl" addto no11fill(-3,1,1,1,1,2,1,1,0,0,0),
no11fill(-2,1,1,2,1,1,1,1,0,0,0);
\end{verbatim}
%--#] addto :
%--#[ apply :
\section{apply}
\label{tblapply}
\noindent Syntax:
Apply [number] [tablename(s)];
\noindent See also testuse (\ref{tbltestuse}) and use (\ref{tbluse}).
\noindent The actual application\index{apply} of fill\index{fill}
statements that were taken from the tablebases. If no tables are specified,
this is done for all tables, otherwise only for the tables whose names are
mentioned. The elements must have been registered as used before with the
application of a testuse\index{testuse} statement, and they must have been
compiled from the tablebase with the use\index{use} option of the tablebase
statement. The number refers to the maximum number of table elements that
can be substituted in each term. This way one can choose to replace only
one element at a time. If no number is present all occurrences will be
replaced. This refers also to occurrences inside function arguments. If only
a limited number is specified in the apply statement, the occurrences
inside function arguments have priority.
%--#] apply :
%--#[ audit :
\section{audit}
\label{tblaudit}
\noindent Syntax:
TableBase "file.tbl" audit;
\noindent See also open (\ref{tblopen})
\noindent Prints\index{audit} a list of all tables and table elements that
are defined in the specified tablebase. This tablebase needs to be opened
first. As of the moment there are no options for the audit. Future options
might include formatting of the output.
%--#] audit :
%--#[ create :
\section{create}
\label{tblcreate}
\noindent Syntax:
TableBase "file.tbl" create;
\noindent See also open (\ref{tblopen})
\noindent This creates\index{create} a new file\index{file!new} with the
indicated name. This file will be initialized as a tablebase. If there was
already a file with the given name, its old contents will be lost. If one
would like to add to an existing tablebase, one should use the
`open'\index{open} option.
%--#] create :
%--#[ enter :
\section{enter}
\label{tblenter}
\noindent Syntax:
TableBase "file.tbl" enter;
TableBase "file.tbl" enter tablename(s);
\noindent See also open (\ref{tblenter}) and load (\ref{tblload}).
\noindent Scans\index{enter} the specified tablebase and (in the first
variety) creates for all elements of all tables in the tablebase a
fill\index{fill} statement with its full contents. This is at times faster
than reading the fill statements from a regular input
file\index{file!input}, because the tablebase has its contents
compressed\index{compress}.
Hence this costs less file access time. When table names are specified,
only the tables that are mentioned have their elements treated this way.
\noindent The tablebase must of course be open for its contents to be
available.
\noindent If one would like \FORM\ to only see what elements are available
and load that information one should use the load\index{load} option.
%--#] enter :
%--#[ load :
\section{load}
\label{tblload}
\noindent Syntax:
TableBase "file.tbl" load;
TableBase "file.tbl" load tablename(s);
\noindent See also open (\ref{tblopen}) and enter (\ref{tblenter}).
\noindent Scans\index{load} the index of the specified tablebase and (in
the first variety) creates for all elements of all tables in the tablebase
a fill\index{fill} statement of the type
\begin{verbatim}
Fill tablename(indices) = tbl_(tablename,indices,arguments);
\end{verbatim}
This is the fill statement that will be used when elements of one of these
tables are encountered. The function tbl\_ is called the (table)stub
function. When table names are specified, only the tables that are
mentioned have their elements treated this way.
\noindent The tablebase must of course be open for its contents to be
available.
\noindent If one would like to actually load the complete fill statements,
one should use the enter option.
%--#] load :
%--#[ off :
\section{off}
\label{tbloff}
\noindent Syntax:
TableBase "file.tbl" off subkey;
\noindent See also addto (\ref{tbladdto}) and off (\ref{tblon}).
\noindent Currently\index{off} only the subkey `compress'\index{compress}
is recognized. It makes sure that no compression is used when elements are
being stored in a tablebase with the addto\index{addto} option. This could
be interesting when the right hand sides of the fill statements are
relatively short.
%--#] off :
%--#[ on :
\section{on}
\label{tblon}
\noindent Syntax:
TableBase "file.tbl" on subkey;
\noindent See also addto (\ref{tbladdto}) and off (\ref{tbloff}).
\noindent Currently\index{on} only the subkey `compress'\index{compress} is
recognized. It makes sure that compression with the gzip\index{gzip}
algorithms is used when elements are being stored in a tablebase with the
addto\index{addto} option. This is the default.
%--#] on :
%--#[ open :
\section{open}
\label{tblopen}
\noindent Syntax:
TableBase "file.tbl" open;
\noindent See also create (\ref{tblcreate})
\noindent This opens\index{open} an existing file with the indicated name.
It is assumed that the file has been created\index{create} with the
`create' option in a previous \FORM\ program. It gives the user access to the
contents of the tablebase. In addition it allows the user to add to its
contents.
\noindent Just like with other files, \FORM\ will look for the file in in
current directory and in all other directories mentioned in the environment
variable `FORMPATH'\index{FORMPATH} (see for instance the
\#call\index{\#call} (\ref{precall}) and the \#include\index{\#include}
(\ref{preinclude}) instructions).
%--#] open :
%--#[ testuse :
\section{testuse}
\label{tbltestuse}
\noindent Syntax:
TestUse;
TestUse tablename(s);
\noindent See also use (\ref{tbluse}).
\noindent Tests\index{testuse} for all elements of the specified tables (if
no tables are mentioned, this is done for all tables) whether they are used
in a stub\index{stub} function tbl\_\index{tbl\_}. If so, this indicates
that these elements must be compiled from a tablebase, provided this has
not been done already. The compilation will have to be done at a time,
specified by the user. This can be done with the use\index{use} option. All
this statement does is set some flags in the internals of \FORM\ for the
table elements that are encountered in the currently active expressions.
%--#] testuse :
%--#[ use :
\section{use}
\label{tbluse}
\noindent Syntax:
TableBase "file.tbl" use;
TableBase "file.tbl" use tablename(s);
\noindent See also testuse (\ref{tbltestuse}) and apply (\ref{tblapply}).
\noindent Causes\index{use} those elements of the specified tables to be
compiled, that a previous testuse\index{testuse} statement has encountered
and that have not yet been compiled before. If no tables are mentioned this
is done for all tables. The right hand sides of the definition of the table
elements will not yet be substituted. That is done with an
apply\index{apply} statement.
%--#] use :
form-master/doc/manual/variable.tex 0000664 0000000 0000000 00000127755 13565763364 0017706 0 ustar 00root root 0000000 0000000
\chapter{Variables}
\label{ch-variables}
The objects of symbolic manipulations are expressions\index{expression}.
Expressions are built up from terms\index{terms} and terms are composed of
variables. {\FORM} knows several types of variables, each
of which has special rules assigned to it. The types of variables are
symbols, vectors, indices, functions, sets, and expressions. In addition
there are tensors and tables which are special functions, preprocessor
variables\index{variables!preprocessor} (see chapter~\ref{preprocessor}),
and there are dollar variables\index{variables!dollar} (see
chapter~\ref{dollars}). The expressions are used either in the definition
of an expression or in the right hand side of an expression or a
substitution. When an expression is used in the right hand side of another
expression or a substitution, it will be replaced by its contents at the
first opportunity. Therefore an expression will never occur as a variable
in the output of other expressions and we will ignore their potential
presence in the remainder of this chapter. Similarly preprocessor variables
and dollar variables will be replaced immediately when they are
encountered.
The right hand side of an expression can consist of symbols, vectors,
indices, functions and elements of a set. All these objects have to be
declared before they can be used. The rules
connected to each of these types of variables are described in the
sections below.
\section{Names}
There are two types of names\index{names}. Regular
names\index{names!definition} consist of alphabetic and numeric characters
with the condition that the first character must be alphabetic. {\FORM} is
case sensitive with respect to names. In addition there are {\bf formal
names}. These names start with the character \verb:[: and end with a
matching character \verb:]:. In between there can be any characters that
are not intercepted by the preprocessor. This allows the use of variables
like \verb:[x+a]:. Using formal names can improve the readability of
programs very much, while at the same time giving the user the benefits of
the greater speed. The use of denominators\index{denominators} that are
composite (like \verb:1/(x+a):) is usually rather costly in time. Often
\verb:1/[x+a]: is equally readable, while leading to the same results. Note
however that the variable \verb:[x+a]: will have to be declared properly.
On the other hand: {\FORM} may not have to know about x and a. These formal
names can also be used for the names of expressions, but they are not valid
for the names of dollar variables and the names of
preprocessor variables\index{variables!preprocessor}.
Some names may contain special characters. All built in objects have for
their last character an underscore\index{underscore} (\_).
Dotproducts\index{dotproducts} (the scalar product of two vectors) consist
of two vectors separated either by a period or by a dollar sign. The dollar
sign is used by {\FORM}, when the output of the program has to be
Fortran\index{fortran} compatible. The user can replace the dollar sign in
the output by an arbitrary character by defining the variable
"DotChar"\index{dotchar} in the setup\index{setup file} file. How this is
done is explained in chapter~\ref{setup}. In the input the user may apply
either the notation with the period or the notation with the dollar. It is
however recommended to use the period\index{period} because in future
versions the notation with the dollar may be dropped. The above
conventions avoid the possibility of conflicts with reserved names,
allowing the user full freedom when choosing names.
The dollar sign is also used as the first character in the name of dollar
variables\index{variables!dollar}. The rest of the name should consist of
alphanumeric characters of which the first should be alphabetic. The names
of preprocessor variables\index{variables!preprocessor} should also consist
of alphanumeric characters of which the first should be alphabetic. Also
here the ones that are defined by the system have a trailing
underscore\index{underscore} (\_) character.
With respect to the user defined names {\FORM} is case sensitive. This
means that the variables a and A are different objects. With respect to
system defined objects {\FORM} is case insensitive. Hence both d\_ and D\_
indicate the same Kronecker delta.
In many languages the use of the underscore\index{underscore} (\_)
character is also permitted in the definition of user defined names. In
{\FORM} this is NOT the case. Even though the earlier manuals `forbade'
this specifically there was a bug in earlier versions that permitted it to
some degree. And because people don't read manuals, there were those who
used this character and even made it into a vital part of their naming
conventions. This then broke when version 3 was introduced. It should be
clear though that the underscore character is reserved for a completely
different type of future use and hence nothing can be done about this. Just
remember: it is never a good idea to use undocumented features without
consulting with the development team first.
The complex conjugate\index{conjugate!complex} of a complex
quantity is indicated by the character \verb:#: appended to the name of the
variable. In the current version of {\FORM} not much is done with it. The
latest approach is that it is seen as obsolete. If possible, please avoid
using it.
The length of names\index{names!length} is not restricted in {\FORM}. There
is one exception to this rule: names of expressions cannot be longer than
16 characters. Of course in practise there are physical limits on the size
of names, posed by the size of the memory of the computer being used.
\section{Symbols}
\label{sect-symbols}
Symbols\index{symbols} are plain objects that behave most like normal
variables in hand manipulations. Many hand manipulations concern
polynomial formulae of simple algebraic variables. {\FORM} assumes that
symbols commute with all other objects and have a power connected to
them. This power is limited to an installation dependent maximum and
minimum. A power outside this range will lead to an error message. The
user may override this built in restriction by one of private design that
is more restrictive. Any power that falls outside the user defined range
leads to the removal of the term that contains the variable with this
power. Such a power restriction can be defined for
each symbol separately.
Symbols can also have complex conjugation\index{conjugation!complex}
properties. A symbol can be declared to be real, imaginary or complex. This
property is only relevant, when the complex conjugation operator is used.
This operator has not been implemented and currently there are no plans to
do so.
The syntax of the statement that defines symbols is given by (see also
\ref{substasymbols}):
\begin{verbatim}
S[ymbols] name[#{R|I|C}][(min:max)];
\end{verbatim}
Each variable is declared by the presence of its name in a
symbol-statement. If the \# symbol is appended, it should be followed by
either the character C, I or R to indicate whether the variable is
complex\index{complex}, imaginary\index{imaginary} or real\index{real}. The
\#R is not really necessary, as the type `real' is the default. It is not
relevant whether the C, I, R are in upper or in lower case. A power
restriction\index{restriction!power} is indicated with a range between
regular parentheses. If one of the two numbers is not present, the default
value is taken. This default value is installation dependent, but it is at
least -10000 and 10000 respectively. Each symbol-statement can define more
than one variable. In that case the variables have to be separated either
by comma's or by blanks. Example:
\begin{verbatim}
S x,y,z,a#c,b#c,c#c,r(-5:5),s(:20),t#i(6:9);
\end{verbatim}
In this statement x, y and z are normal real algebraic variables. The
variables a, b and c are complex. This means that for each of these
variables two entries are reserved in the property lists: one for the
variable and one for its complex conjugate. The variable r has a power
restriction: Any power outside the specified range will cause the term
containing this power to be eliminated. This is particularly useful in
power series expansions. The restrictions on s are such that there is
no limitation on the minimum power of s --with the exception of the
built in restrictions-- but a term with a power of s that is larger
than 20 is eliminated. The variable t is imaginary. This means that
under complex conjugation it changes sign. Its power restrictions are
somewhat uncommon. Any power outside the range 6 to 9 is eliminated.
There is however one exception: a term that does not contain t to any
power ($t^0$) is not affected.
% THIS EXAMPLE IS PART OF THE TESTSUITE. CHANGES HERE SHOULD BE APPLIED THERE AS
% WELL!
\begin{verbatim}
s x(:10),y;
L F=y^7;
id y=x+x^2;
print;
.end
Time = 0.01 sec Generated terms = 4
F Terms in output = 4
Bytes used = 54
F =
x^7 + 7*x^8 + 21*x^9 + 35*x^10;
\end{verbatim}
Note that all terms with a power greater than 10 do not even count
as generated terms. They are intercepted immediately after the
replacement, before any possible additional statements can be
carried out.
There are several built in symbols\index{symbols!built in}. They are:
\noindent i\_\index{i\_}: it is defined by \verb:i_^2 = -1: and this
property is used by {\FORM} to simplify terms. It is the only symbol that
cannot be used as a dimension or a wildcard.
\noindent pi\_\index{pi\_}: a reserved variable which will eventually be
used to indicate the variable $\pi$.
\noindent coeff\_\index{coeff\_}: this variable is automatically replaced
by the coefficient of the current term.
\noindent num\_\index{num\_}: this variable is automatically replaced by
the numerator of the coefficient of the current term.
\noindent den\_\index{den\_}: this variable is automatically replaced by
the denominator of the coefficient of the current term.
\noindent extrasymbols\_\index{extrasymbols\_}: this symbol represents the
number of extra symbols (see \ref{sect-extrasymbols}).
\section{Vectors}
\label{sect-vectors}
A vector\index{vectors} is an object with a single index\index{index}. This
index represents a number that indicates which component of the vector is
meant. Vectors have a dimension\index{dimension} connected to them which is
the dimension of the vector space in which they are defined. In {\FORM}
this dimension is by default set to 4. If the user likes to change this
default, this can be done with the `Dimension'-statement. The use of this
command affects the dimension of all vectors and the default dimension of
indices. Its syntax is (see also \ref{substadimension}):
\begin{verbatim}
Dimension number;
\end{verbatim}
or
\begin{verbatim}
Dimension symbol;
\end{verbatim}
The number must be a number that fits inside a {\FORM} word which is
an installation dependent size, but it will be at least 32767.
The number must be positive or zero. Negative values are illegal.
If a symbol is specified, it must have been declared before. Any symbol
may be used with the exception of i\_\index{i\_}.
The declaration of vectors (see \ref{substavectors}) is rather
straightforward:
\begin{verbatim}
V[ector] name [,MoreNames];
\end{verbatim}
The names of the vectors may be separated either by comma's or
by blanks. Example:
\begin{verbatim}
V p,q;
I mu,nu;
L F=p(mu)*q(nu);
\end{verbatim}
\section{Indices}
\label{sect-indices}
Indices\index{indices}\index{index} are objects that represent a number
that is used as an integer argument for counting purposes. They are used
mostly as the arguments of vectors or multidimensional arrays (or tensors).
Their main property is that they have a dimension\index{dimension}. This
dimension indicates what values the index can take. A four-dimensional
index can usually take the values 1 to 4. A very important property of an
index is found in the convention that it is assumed that an index that is
used twice in the same term is summed over. This is called the
Einstein\index{Einstein} summation\index{summation!Einstein} convention.
Hence the term p(mu)$*$q(mu) is equivalent to the scalar product of the
vectors p and q (which can also be written as p.q).
There are of course also indices that should not be summed over.
Such indices we call zero-dimensional. This is just a convention.
To declare indices we use the statement (see also \ref{substaindex}):
\begin{verbatim}
Index name[={number|symbol}]
[,othername[={number|symbol}]];
\end{verbatim}
When the equals sign is used, this indicates the specification of a
dimension. Indices that are not followed by an equals sign get the
dimension that is currently the default dimension (see also
\ref{substadimension})). The dimension can be either a number that is
zero or positive (zero indicates that the summation convention does not
apply for this index) or it can be any symbol with the exception of the
symbol i\_. The symbol must have been declared before.
The most important use of the dimension of an index is the built in rule
that a Kronecker\index{Kronecker} delta\index{delta!Kronecker} with twice
the same index is replaced by the dimension of this index, provided this
index has a non-zero dimension. Therefore when mu is 4-dimensional, d\_(mu,
mu) will be replaced by 4 and when nu is n-dimensional, d\_(nu,nu) will be
replaced by n. If rho is zero dimensional, the expression d\_(rho,rho) is
left untouched.
In addition to the symbolic indices there is a number of fixed
indices\index{indices!fixed} with a numeric\index{indices!numeric} value.
The values of these indices runs from zero to an installation dependent
number (usually 127). Users who like a different maximum value should
consult chapter~\ref{setup} about the setup parameters. The numeric indices
are all assumed to have dimension zero, hence no summation is applied to
them. This means that they can be used for vector components. It is
therefore perfectly legal to use:
\begin{verbatim}
V p,q,r;
L F=p(1)*q(1)*r(1)+p(2)*q(2)*r(2);
\end{verbatim}
When two numeric indices occur inside the same Kronecker delta, a value
is substituted for this delta. Normally this value is one, when the two
indices are identical and zero, when they are different. The value for
the diagonal elements can be changed with the
`FixIndex'-statement (see also \ref{substafixindex}):
\begin{verbatim}
Fi[xIndex] number:value [,number:value];
\end{verbatim}
This command assigns to d\_(number,number) the given value.
This value must fit inside a single {\FORM} word. This means that this
value can at least be in the range -32768 to +32767. For more
details on the size of a {\FORM} word one should consult the
installation manual.
In the case of summable indices\index{indices!summable} the use of three
times the same index in the same term would cause problems. {\FORM} will
execute the contraction for the first pair it encounters, after which the
third index is left. In the case of four or more indices the pairing for
the contractions depends on the order in which the parts of the term are
processed. Hence to the user the result may seem to be quasi random.
Nothing can be done about this and the user should guard against such
ambiguous notation\index{notation!ambiguous}.
There is a special version of the index declarations that is used for
traces\index{traces} of gamma\index{gamma matrices}
matrices\index{matrices!gamma} in n dimensions. If an index is declared with
\begin{verbatim}
Symbols n,epsilon;
Index m=n:epsilon;
\end{verbatim}
its dimension will be n and it is assumed that epsilon can be used for
$(n-4)$ during the taking of the trace of a string of gamma matrices. It
is also possible to use this notation in the dimension-statement. See
also chapter~\ref{gammaalgebra} on the gamma matrices.
\section{Functions}
\label{sect-functions}
There are two classes of functions\index{functions}: {\bf commuting
functions} which commute automatically with all other objects, and {\bf
non-commuting functions} which do not necessarily commute with other
non-commuting functions. An object is declared to be a
commuting\index{commuting} function\index{function!commuting} with the
`cfunction' command. Of this command the first two characters are
mandatory, the others optional. An object is declared to be a
non-commuting\index{non-commuting} function\index{function!non-commuting}
with the `function' command. Here only the f is mandatory. The declaration
of a function knows one option. This option concerns the complexity
properties of the function. It is indicated by a \# following the name,
after which one of the characters R, I, C specifies whether the function is
real\index{real}, imaginary\index{imaginary} or complex\index{complex}. The
declaration that a function is real is unnecessary as `real' is the default
property. Example:
\begin{verbatim}
CF fa,fb,fc;
F ga,gb,gc#c;
\end{verbatim}
In this example the functions fa, fb, fc are commuting and the
functions ga, gb and gc are not necessarily commuting. In addition the
function gc is complex. More about functions and their conventions
is explained in chapter~\ref{functions}.
Within the commutation classes there are several types of special
functions. Currently these are tensors\index{tensors} and
tables\index{tables}. The tables are described in section~\ref{substatable}
and in chapter~\ref{tablebase}.
Tensors\index{tensors} are special functions. Their arguments can be
indices and vectors only. When an argument is a vector, it is assumed that
this vector has been put in this position as the result of an
Einstein\index{Einstein} summation\index{summation!Einstein}, i.e., there
used to be an index in this position, but the index was contracted with the
index of the vector. Hence {\FORM} assumes that there is a linearity
property with respect to such vectors. Tensors are declared with one of the
following statements (see also pages~\ref{substatensors},
\ref{substantensors}, \ref{substactensors}):
\begin{verbatim}
T[ensors] t1;
CT[ensors] t2;
NT[ensors] t3;
\end{verbatim}
The type `ntensor' indicates a non-commuting tensor, while the other two
types indicate commuting tensors. Note that the 'T' is a
commuting tensor, while the 'F' indicates a non-commuting function. In
addition to the above declarations one may add the same complexity
properties that can be added for functions. This is currently not very
useful though as there exists no complex conjugation
operator yet. Internally a tensor is a function with special properties.
Hence when function properties are discussed, usually these properties
refer also to tensors, unless the type of the arguments would not allow
the operations or arguments specified.
\section{Sets}
\label{sect-sets}
A set\index{sets} is a (non-empty) collection of variables that should
all be of the same type. This type can be symbols, vectors, indices or
functions. A set has a name which can be used to refer to
it, and this name may not coincide with any of the other names in the
program. A set is declared by giving its name, followed by a
colon\index{colon}, after which the elements of the set are listed. The first
element determines the type of all the elements of the set. All
elements must have been declared as variables before the set-statement.
There can be only one set per statement. Example (see also
\ref{substaset}):
\begin{verbatim}
s xa, xb, xc, xd, ya, x, y;
i mu, nu, rho;
set exxes: xa, xb, xc, xd;
set yyy: xc, xd, xb, ya;
set indi: mu, nu, rho, 1, 2, 3;
set xandy: xa, ya;
\end{verbatim}
We see here that a single symbol (xa) can belong to more than one set.
Also the fixed indices (1, 2 and 3) can be elements
of a set of indices and the numbers that can be powers can also be
members of a set of symbols (usually -9999 to + 9999). If this can cause
confusion, {\FORM} will give a warning and interpret the set as a
set of symbols.
In addition to the user defined sets there are some built in sets with a
special meaning. These are:
\begin{description}
\item[int\_]\index{int\_} This is a set of symbols. It refers to all integer numbers
that fit inside a {\FORM} word.
\item[pos\_]\index{pos\_} This is a set of symbols. They are the positive integers that
fit inside a {\FORM} word.
\item[pos0\_]\index{pos0\_} A set of symbols. They are all non-negative integers that
fit inside a {\FORM} word.
\item[neg\_]\index{neg\_} A set of symbols. They are all negative integers that
fit inside a {\FORM} word.
\item[neg0\_]\index{neg0\_} A set of symbols. They are all non-positive integers that
fit inside a {\FORM} word.
\item[symbol\_]\index{symbol\_} The set of all formal symbols. It excludes integers,
numbers and whole function arguments.
\item[fixed\_]\index{fixed\_} The set of all fixed indices.
\item[index\_]\index{index\_} The set of all indices.
\item[vector\_]\index{index\_} The set of all (auto)declared vectors.
\item[number\_]\index{number\_} The set of all rational numbers.
\item[even\_]\index{even\_} This is a set of symbols. It refers to all even integer numbers
that fit inside a {\FORM} word.
\item[odd\_]\index{odd\_} This is a set of symbols. It refers to all odd integer numbers
that fit inside a {\FORM} word.
\item[dummyindices\_]\index{dummyindices\_} This is a set of indices. It refers to all
indices of the type Nm\_? (m a positive integer) that were obtained by
summing over indices with a sum statement\index{sum} \ref{substasum}.
\end{description}
Sets can be used during wildcarding\index{wildcarding}. When x is a symbol,
the notation x? indicates `any symbol'. This is sometimes more than we
want. In the case that we would like `any symbol that belongs to the set
exxes' we would write x?exxes which is an unique notation as usually
the question mark cannot be followed by a name. There should be no blank
between the question mark and the name of the set. The object x?indi
would result in a type mismatch error, if x is a symbol and indi a set of
indices.
This use of wildcards belonging to sets can be extended even more:
The notation x?exxes?yyy means that x should belong to the set exxes, and
its replacement should be the corresponding element of set yyy. At first
this notation looks unnecessarily complicated. The statement
\begin{verbatim}
id x?exxes?yyy = x;
\end{verbatim}
should have the much simpler syntax
\begin{verbatim}
id exxes = yyy;
\end{verbatim}
This last notation cannot be maintained, when the patterns are more
complicated, hence it has been omitted altogether.
When things become really complicated\index{complicated}, the sets can be
used as kind of an array. They can be used with a fixed array index
(running from 1 for the first element). When they have a symbolic argument
(must be a symbol), they are either in the right hand side of an
id-statement and the symbol must be replaced by a number by means of a
wildcard substitution or in the left hand side and the symbol is
automatically seen as a wildcard. The set must still follow the question
mark of a wildcard. An example will clarify the above:
\begin{verbatim}
s a1,a2,a3,b1,b2,b3,x,n;
f g1,g2,g3,g;
set aa:a1,a2,a3;
set bb:b1,b2,b3;
set gg:g1,g2,g3;
id g(x?aa[n]) = gg[n](bb[n]) + bb[2]*n;
\end{verbatim}
The n in the left hand side is automatically a symbol wildcard. x must
match an element in aa and n takes its number. In the right hand side
\verb:gg[n]: becomes an array element, when the n is substituted. The
same holds for \verb:bb[n]:. The element \verb:bb[2]: is immediately
replaced by b2, so there is rarely profit by using this, unless the
preprocessor had something to do with the construction of this
quantity. As should be clear from the above: the array elements are
indicated with straight braces\index{braces}.
Another use of sets is in the select
option\index{option!select} of the id-statement. This is discussed in
chapter~\ref{pattern} on pattern\index{pattern matching} matching.
Neither the array properties of the sets nor the select option of the
id-statement can be used in conjunction with the built in sets. These
sets are not supposed to have a finite number of indices.
Apart from the above sets that were formally declared and used by name
there is a second way to use sets. These sets are called {\bf implicitly
declared sets\index{sets!implicitly declared}}. They are declared at the
position that they are used and their use defines their contents. The
elements of the set should be enclosed by a pair of curly
brackets\index{brackets!curly} and the set is placed at the position where
otherwise the name of the set would be used:
% THIS EXAMPLE IS PART OF THE TESTSUITE. CHANGES HERE SHOULD BE APPLIED THERE AS
% WELL!
\begin{verbatim}
Symbols a1,a2,a3,b1,b2,b3,x,n;
CFunctions g1,g2,g3,g;
Local expr =
g(a1)+g(a2)+g(a3)+g(x);
id,g(x?{a1,a2,a3}[n]) = {g1,g2,g3}[n]({b1,b2,b3}[n]);
print;
.end
expr =
g1(b1) + g2(b2) + g3(b3) + g(x);
\end{verbatim}
Such a set exists internally only till the end of the module in which it
is used. It can be used at all positions where named sets can be used.
Hence they can also be used, when the array properties of sets are
considered.
The preprocessor has to be able to distinguish these sets from strings for
its calculator\index{calculator!preprocessor} (see
chapter~\ref{preprocessor}). Usually this is no problem, because any
regular name contains at least one character that is not accepted by this
calculator. If the only elements in the set are numeric the
comma\index{comma} will tell the preprocessor that it is a set and the
calculator should not be used. This leaves the case of a set with a single
numeric element. By placing a comma either before or after it the use of
the calculator is vetoed. For the interpretation of the set this makes no
difference.
When it is possible to demand an object to be inside a
set\index{set!inside}, it should also be possible to demand that an object
be outside a set\index{set!outside}. This is done with the `?!' operator
instead of the `?' operator. The extra exclamation\index{exclamation} mark
is like a `not' operator. It can be used only, when its use makes sense.
Hence it cannot be used in conjunction with the array properties of sets
and together with the select option of the id-statement. So its only use is
in patterns of the type
\begin{verbatim}
x?!setname
x?!{a,b,c}
\end{verbatim}
as is done in
\begin{verbatim}
id x^n?!{,-1} = x^(n+1)/(n+1);
\end{verbatim}
There is a variation of the second type that is not possible with named
sets\index{sets!named}:
\begin{verbatim}
Symbols a,b,x,y,z;
CFunction f;
id f(x?!{a,y?,z?})*f(y?!{b,x?,z?})*f(z?!{x?,y?})
= .........
\end{verbatim}
In this complicated pattern the z is easiest: It is not allowed to be equal
to the objects that will be substituted for the wildcards x and y. The
symbol x cannot be equal to the wildcards y and z, but in addition it
should not be equal to a. A similar condition holds for y. One could argue
that at least one of these conditions is superfluous from the strictly
logical viewpoint. It depends however on the order of the declarations in
how {\FORM} runs through the pattern, so it would require some trying to
see which `not' specifications are superfluous. If for instance the first
function is matched first, there is still no assignment for z. This means
that the z? in the set cannot be used yet and hence it places no
restrictions on x. Therefore it is the x? in the last function that causes
x and z to be different. If on the other hand the last function would be
matched first, we need the z? in the set of the first function. From the
strict logical viewpoint, {\FORM} could go back over the pattern and still
make the appropriate rejections, but this would cost too much extra time.
As one can see, it is safer to specify both.
\section{The autodeclare conventions}
As we have seen above, all variables that are introduced by the user have
to be declared. As such {\FORM} is a strong\index{strong typing} typing
language. This isn't always handy. Hence it is possible to introduce some
rules about the automatic declaration of classes of variables. This is done
with the AutoDeclare\index{autodeclare} statement (see also
\ref{substaautodeclare}). If we use the statements
\begin{verbatim}
AutoDeclare Symbol x,tt;
AutoDeclare CFunction f,t;
\end{verbatim}
any object encountered by the compiler of which the name starts with the
character x will automatically be declared as a symbol. Also objects of
which the name starts with the characters tt will be declared as symbols.
Objects of which the name starts with the characters f or t, but not with
the string tt, and that have not yet been declared will be declared
automatically as commuting functions. As one can see, in the case of
potential conflicts\index{conflicts} (like with t and tt) the more
restrictive one takes precedence. This is independent of the order of the
AutoDeclare statements. One disadvantage of the use of the AutoDeclare
statement is that one looses a certain amount of control over the order of
declaration of the variables, as now they will be declared in the order in
which they occur in the statements. The order of the declaration determines
the ordering of the objects in the output.
\section{Name lists}
\label{sect-namelists}
Sometimes it is necessary to see how {\FORM} has interpreted a set
of declarations. It can also be that declarations were made in an unlisted
include file and that the user wants to know what variables have been
defined. The lists\index{lists} of active variables\index{variables!lists}
can be printed with the statement
\begin{verbatim}
On names;
\end{verbatim}
This statement sets a flag that causes the listing of all name tables and
default properties that are active at the moment that the compiler has
finished compiling the current module and all modules after. The printing
is just before the algebra processor takes over for the execution of the
module -- assuming that no error condition exists. If the `On names' is
specified in a module that ends with a .global-instruction, the name lists
will be printed at the end of each module, as printing the name lists will
then be the default option. If one likes to switch this flag off, this can
be done with the statement
\begin{verbatim}
Off names;
\end{verbatim}
which prohibits the printing of the name lists in the current module and
all modules following.
\section{Dummy indices}
\label{sect-dummies}
Sometimes indices\index{indices!dummy} are to be summed over but due to the
evaluation procedures some terms contain the index mu and other terms
contain the index nu. There is a command to sum over indices in such a way
that {\FORM} recognizes that the exact name of the index is irrelevant.
This is the `sum'-statement (see also \ref{substasum}):%
% THIS EXAMPLE IS PART OF THE TESTSUITE. CHANGES HERE SHOULD BE APPLIED THERE AS
% WELL!
\begin{verbatim}
i mu,nu;
f f1,f2;
L F=f1(mu)*f2(mu)+f1(nu)*f2(nu);
sum mu;
sum nu;
print;
.end
\end{verbatim}
At first the expression contains two terms. After the summations {\FORM}
recognizes the terms as identical. In the output we see the term:
\begin{verbatim}
2*f1(N1_?)*f2(N1_?)
\end{verbatim}
The \verb:N1_?: are dummy indices.
The dimension of these dummy indices is the current
default dimension\index{dimension!default} as set with the last
dimension-statement. This may look like it is a restriction, but in
practice it is possible to declare the default dimension to have one
value in one module, take some sums, and do some more operations, and
then give the default dimension another value in the next module. It should
be realized however that then the dimension of the already existing dummy
indices may change with it.
The scheme that is used to renumber\index{renumber} the
indices\index{indices!renumber} in a term is quite
involved. It will catch nearly all possibilities, but in order to avoid
to try all $n!$ permutations, when there are n pairs of dummy indices,
{\FORM} does not try everything. It is possible to come up with examples
in which the scheme is not perfect. It is left as a
challenge for the reader to find such an example. In the case that the
scheme isn't sufficient one can use the Renumber statement (see
\ref{substarenumber}) to force a complete renumbering. As this involves
n! attempts in which n is the number of different dummy indices, this can
become time consuming.
These dummy indices can be used to solve a well known problem in the
automatic summation of indices. This problem occurs, when summed indices
are found inside a subexpression that is raised to a power:
% THIS EXAMPLE IS PART OF THE TESTSUITE. CHANGES HERE SHOULD BE APPLIED THERE AS
% WELL!
\begin{verbatim}
Index mu,nu;
CFunctions f,g;
Vectors p,q;
Local F = (f(mu)*g(mu))^2;
sum mu;
id f(nu?) = p(nu);
id g(nu?) = q(nu);
print;
.end
F =
p.p*q.q;
\end{verbatim}
Clearly the answer is not what we had in mind, when we made the program.
There is an easy way out:
% THIS EXAMPLE IS PART OF THE TESTSUITE. CHANGES HERE SHOULD BE APPLIED THERE AS
% WELL!
\begin{verbatim}
Index mu,nu;
Symbol x;
CFunctions f,g;
Vectors p,q;
Local F = x^2;
repeat;
id,once,x = f(mu)*g(mu);
sum mu;
endrepeat;
id f(nu?) = p(nu);
id g(nu?) = q(nu);
print;
.end
F =
p.q^2;
\end{verbatim}
This time things went better, because each sum-statement moves an index
mu to a new dummy index.
There are some extra problems connected to dummy indices. Assume that we
have the expression F which contains
% THIS EXAMPLE IS PART OF THE TESTSUITE. CHANGES HERE SHOULD BE APPLIED THERE AS
% WELL!
\begin{verbatim}
F = f(N1_?,N2_?)*f(N2_?,N1_?);
\end{verbatim}
and next we have the module
% THIS EXAMPLE IS PART OF THE TESTSUITE. CHANGES HERE SHOULD BE APPLIED THERE AS
% WELL!
\begin{verbatim}
Indices mu,nu,rho,si;
Vectors p1,p2,p3,v;
Tensor g;
Local G = e_(mu,nu,rho,si)*g(mu,nu,p1,v)*g(rho,si,p2,v);
sum mu,nu,rho,si;
Multiply F^3;
id v = e_(p1,p2,p3,?);
print;
.end
G =
f(N1_?,N2_?)*f(N2_?,N1_?)*f(N3_?,N4_?)*f(N4_?,N3_?)*
f(N5_?,N6_?)*f(N6_?,N5_?)*g(N7_?,N8_?,p1,N9_?)*
g(N10_?,N11_?,p2,N12_?)*e_(p1,p2,p3,N9_?)*
e_(p1,p2,p3,N12_?)*e_(N7_?,N8_?,N10_?,N11_?);
\end{verbatim}
Here the situation with the dummy indices becomes rather messy, and all
earlier versions of {\FORM} were not prepared for this. Their answer could be:
\begin{verbatim}
G =
f(N1_?,N2_?)*f(N1_?,N2_?)*f(N1_?,N2_?)*f(N2_?,N1_?)*
f(N2_?,N1_?)*f(N2_?,N1_?)*g(N1_?,N2_?,p2,N3_?)*
g(N4_?,N5_?,p1,N6_?)*e_(p1,p2,p3,N3_?)*
e_(p1,p2,p3,N6_?)*e_(N1_?,N2_?,N4_?,N5_?);
\end{verbatim}
which is clearly not what the program is supposed to give. In the current
version we have made the tracing of the dummy indices and the renumbering
of them at the proper moment a lot better. It is however not complete as a
complete implementation might severely influence the speed of execution at
some points. The scheme is complete for the inclusion of local and global
expressions. On the other hand it doesn't work for the contents of dollar
variables\index{variables!dollar}. Neither does it work for dummy indices
introduced in user defined code as in
\begin{verbatim}
id x^n? = (f(N1_?)*g(N1_?))^n;
\end{verbatim}
For the latter case we showed a workaround above. Anyway there is a certain
ambiguity here. Just imagine we write
\begin{verbatim}
id x^n? = f(N1_?)^n*g(N1_?)^n;
\end{verbatim}
Formally it is exactly the same, but what we mean is far from clear. For
the dollar variables we considered the contracted dummy indices rare enough
that it doesn't merit sacrificing speed. And then there is one more little
caveat\index{caveat}. Global expressions that were stored with older
versions of {\FORM} than version 3.2, but are read with version 3.2 or later
would have a problem if the expression were to contain dummy indices. The
newer version of the .sav files\index{files!.sav} will contain information
about the dummy indices. {\FORM} can still read the old versions but will
have to `invent' information by assuming that there are no dummy indices.
If there are expressions with such dummy indices the best is to copy the
expressions to a new expression and let the copying be followed by a .sort.
That should set things straight. A final remark: if an elegant solution is
found with which the above cases could be made to work without the penalty
in execution time, it will be built in in the future.
\section{Kronecker delta's}
\label{sect-kroneckerdelta}
The built in object d\_ represents the Kronecker\index{Kronecker}
delta\index{delta!Kronecker}. Even though this
object looks a little bit like a tensor, internally it isn't treated as
such. Actually it has its own data type. It must have exactly two arguments
and these arguments should be either indices or vectors. A d\_ with at
least one vector is immediately replaced, either by a vector with an index
(if there is one vector and one index) or by a dotproduct (when there are
two vectors). If a Kronecker delta contains an index that occurs also at
another position in the same term, and if that index is summable, and if
the index occurs as the index of a vector, inside a tensor, inside another
d\_ or as the argument of a function, and the object inside which it occurs
is not inside the argument of a function itself (unless the d\_ is inside
the same argument) then the Einstein\index{Einstein}
summation\index{summation!Einstein} convention is used and the
d\_ is eliminated, while the second occurrence of the index is replaced by
the other index in the d\_ (Are you still with us?). When a
Kronecker delta has two identical indices and these indices are summable,
the d\_ is replaced by the dimension of the index. If they are fixed
indices, the d\_ is replaced by one, unless this value has been altered
with the fixindex-statement. Some examples of Kronecker delta's are given
in section~\ref{fund}.
\section{Extra Symbols}
\label{sect-extrasymbols}
\label{extrasymbols}
Starting with version 4.0 \FORM{} is equipped with a mechanism to replace
non-symbol objects by internally generated symbols. These are called the
extra symbols. Their numbering starts at maximum number allowed for
internal objects and then counts down. Hence their ordering will be
opposite to what might otherwise be expected. It is possible to control
their representation when they are to be printed in the output. For this
there is the ExtraSymbols (\ref{substaextrasymbols}) statement. The
definitions of the extra symbols can be made visible with the \%X option in
the \#write preprocessor instruction.
Extra symbols can be introduced by the user with the ToPolynomial statement
(\ref{substatopolynomial}). This statement replaces all objects that are
not numbers or symbols to positive powers by extra symbols. This may be
needed for some new manipulations and can also be very handy for output
that is to be treated by for instance a FORTRAN or C compiler. The
FromPolynomial statement replaces the extra symbols again by their original
meaning.
% THIS EXAMPLE IS PART OF THE TESTSUITE. CHANGES HERE SHOULD BE APPLIED THERE AS
% WELL! Not yet
\begin{verbatim}
Vector p,q,p1,p2;
CFunction f;
CFunction Dot,InvDot;
Symbol x,x1,x2;
Set pdot:p,q;
Off Statistics;
Local F = x+x^2+1/x+1/x^2+f(x1)+f(x2)*p.q*x+f(x2)/p.q^2;
id p1?pdot.p2?pdot = Dot(p1,p2);
id 1/p1?pdot.p2?pdot = InvDot(p1,p2);
Print;
.sort
F =
x^-2 + x^-1 + x + x^2 + f(x1) + f(x2)*Dot(p,q)*x + f(x2)*InvDot(p,q)^2;
ExtraSymbols,array,Y;
Format DOUBLEFORTRAN;
ToPolynomial;
Print;
.sort
F =
& Y(1) + Y(1)**2 + Y(2) + Y(5)**2*Y(3) + x + x*Y(4)*Y(3) + x**2
#write " SUBROUTINE sub(Y)"
#write "*"
#write "* Compute the extra symbols. Generated on `DATE_'"
#write "*"
#write " REAL*8 Y(`EXTRASYMBOLS_')"
#write " REAL*8 Dot,InvDot"
#write " Dot(p1,p2)=p1(1)*p2(1)-p1(2)*p2(2)-p1(3)*p2(3)\
-p1(4)*p2(4)"
#write " InvDot(p1,p2)=1.D0/(Dot(p1,p2))"
#write "*"
#write "* We still have to add definitions here."
#write "* And we have to import all the variables."
#write "*"
#write "%X"
#write "*"
#write " RETURN"
#write " END"
ExtraSymbols,underscore,Z;
Format Normal;
Format 80;
Print;
.end
F =
Z1_ + Z1_^2 + Z2_ + Z5_^2*Z3_ + x + x*Z4_*Z3_ + x^2;
FromPolynomial;
Print;
.end
F =
x^-2 + x^-1 + x + x^2 + f(x1) + f(x2)*Dot(p,q)*x + f(x2)*InvDot(p,q)^2;
\end{verbatim}
In the ExtraSymbols statement we say that we want the extra symbols to be
presented as an array with the name Y. The alternative is a set of symbols
with names ending in an underscore, but that would not make the FORTRAN
compiler very happy. Then we convert the expression to symbols. As one can
see, everything got converted to elements of an array Y which are treated
as symbols. After we have written the file sub.f (notice that
EXTRASYMBOLS\_ is a built in symbol indicating the number of extra symbols)
we change the representation to the (default) notation with an underscore
and the character Z. The contents of the file sub.f are:
\begin{verbatim}
SUBROUTINE sub(Y)
*
* Compute the extra symbols. Generated on Sat Apr 2 20:40:33 2011
*
REAL*8 Y(5)
REAL*8 Dot,InvDot
Dot(p1,p2)=p1(1)*p2(1)-p1(2)*p2(2)-p1(3)*p2(3)-p1(4)*p2(4)
InvDot(p1,p2)=1.D0/(Dot(p1,p2))
*
* We still have to add definitions here.
* And we have to import all the variables.
*
Y(1)=x**(-1)
Y(2)=f(x1)
Y(3)=f(x2)
Y(4)=Dot(p,q)
Y(5)=InvDot(p,q)
*
RETURN
END
\end{verbatim}
As one can see, with very little effort this routine can be made into a
proper subroutine that computes all elements of the array Y which can then
be used for computing the expression F.
\section{Restrictions}
There is a restriction\index{restrictions} on the total number of
variables\index{variables!total number of} that {\FORM} can handle. For the
number of symbols, vectors, indices, functions and sets together the exact
number depends on the type of computer. For a computer with a 32-bits
processor this number is 32768. This includes the built in objects.
Individual types of variables (like symbols) are usually restricted to
about 8000. For a
computer with a 64-bits processor the maximum has been set arbitrarily at
2000000000. In addition there are restrictions on the total amount of
memory\index{memory!total amount of} needed by {\FORM} to maintain an
administration of all these variables. These restrictions are set by the
memory allocator of the computer on which {\FORM} is running.
\section{Some common bugs}
There is a type of error\index{error}\index{bug} by the user (including at
times the author) that is so common that it deserves mentioning here.
Consider the code:
\begin{verbatim}
Symbol x1,x2
Index m1,m2;
\end{verbatim}
As a statement it is perfectly legal\index{legal}, but it may produce
rather funny errors at a later stage when we try to use m1 or m2.
Inspection with the `On names;' statement shows that we have the symbols
x1,x2,Index,m1,m2. This is most likely not what the user wanted. Closer
inspection shows that we forgot the semicolon at the end of the symbol
statement. We should have had:
\begin{verbatim}
Symbol x1,x2;
Index m1,m2;
\end{verbatim}
This is the most common error for which {\FORM} cannot give a direct error
message (it is after all a legal statement). Hence when faced with
mysterious errors or error messages, one could have a good look by using
the `On names' statement. Maybe it shows something, and if not, one has to
look for other causes.
form-master/scripts/ 0000775 0000000 0000000 00000000000 13565763364 0015023 5 ustar 00root root 0000000 0000000 form-master/scripts/cleanup.sh 0000775 0000000 0000000 00000001133 13565763364 0017007 0 ustar 00root root 0000000 0000000 #!/bin/sh
# This shell script deletes files that are created by autoreconf,
# e.g. aclocal.m4.
# It is NO replacement for the cleanup done by "make clean",
# "make distclean", or "make maintainer-clean".
FILES="\
Makefile.in \
aclocal.m4 \
build-aux/ \
config.h.in \
config.h.in~ \
configure \
check/Makefile.in \
doc/Makefile.in \
doc/devref/Makefile.in \
doc/doxygen/Makefile.in \
doc/manual/Makefile.in \
sources/Makefile.in \
"
echo "Deleting $FILES"
echo -n "Okay (y/n) : "
read answer
if [ x"$answer" == "xy" -o x"$answer" == "xY" ]
then
rm -fr $FILES
else
echo "Exit. No deletions."
fi
form-master/scripts/git-version-gen.sh 0000775 0000000 0000000 00000011745 13565763364 0020407 0 ustar 00root root 0000000 0000000 #!/bin/sh
set -eu
rootdir=`dirname "$0"`/..
prog=`basename "$0"`
print_usage() {
cat <, --dir use as the reference directory
-r, --raw raw output (default)
-c, --c C output
-t, --tex TeX output
-v, --only-version only-version output
-o , --output output to
--date-format date format (default: '%b %e %Y')
END
}
# Format the date given in the form of '%Y-%m-%d %H:%M:%S %z'.
# fmt_isodate
fmt_isodate() {
# dash (0.5.5.1) needs the following exports.
export LANG
export TZ
# BSD date
date -j -f '%Y-%m-%d %H:%M:%S %z' "$1" +"$2" 2>/dev/null ||
# GNU date
date -d "$1" +"$2" 2>/dev/null ||
# perl Time::Piece
# XXX: It has problems on the time zone.
perl -MTime::Piece </dev/null ||
print Time::Piece->strptime('$1', '%Y-%m-%d %H:%M:%S %z')->strftime('$2')
END
# Failed.
{
echo "$prog: error: failed to format datetime ($1)" >&2
echo "$prog: info: GNU/BSD date not available?" >&2
false
}
}
refdir=$rootdir
mode=raw
output_file=
date_format='%b %e %Y'
next=
for a in "$@"; do
if [ -n "$next" ]; then
eval "$next=\$a"
next=
continue
fi
case $a in
-h|--help)
print_usage
exit
;;
-C|--dir)
next=refdir
;;
-r|--raw)
mode=raw
;;
-c|--c)
mode=c
;;
-t|--tex)
mode=tex
;;
-v|--only-version)
mode=only-version
;;
-o|--output)
next=output_file
;;
--date-format)
next=date_format
;;
*)
echo "$prog: error: unknown option $a" >&2
exit 1
;;
esac
done
if [ -n "$next" ]; then
echo "$prog: error: missing argument for $a" >&2
exit 1
fi
git_C() {
(cd "$refdir" && git "$@")
}
# Extract the version number from the latest tag, e.g.,
# v1.0.0-xxx-yyy-zzz -> 1.0.0
version_tag=`git_C describe --match 'v[0-9]*' --tags HEAD`
version_tmp=`echo "$version_tag" | sed 's/^v//'`
version_num=`echo "$version_tmp" | sed 's/-.*//'`
version=$version_num
# Support typical pre-release versions (e.g., v1.0.0-alpha-xxx-yyy-zzz) for
# -alpha, -alpha.1, -beta, -beta.1, -rc, -rc.1
case $version_tmp in
*-alpha*|*-beta*|*-rc*)
version_tmp=`echo "$version_tmp" | sed 's/^[^-]*-//' | sed 's/-.*//'`
case $version_tmp in
alpha*|beta*|rc*)
version="$version-$version_tmp"
;;
esac
;;
esac
if [ "$mode" != "only-version" ]; then
# Get the revision identifier by git-describe.
revision=`git_C describe --tags --always --abbrev=7 HEAD`
# Check if the working tree is dirty.
git_C update-index -q --refresh
if git_C diff-index --quiet HEAD .; then
# If the working tree is not dirty, use the latest commit date.
isodate=`git_C log -1 --pretty=%ci .`
date=`LANG=C TZ=UTC fmt_isodate "$isodate" "$date_format"`
else
# If the working tree is dirty, suffix "-dirty" to the revision identifier
# and use the current date time.
revision="$revision-dirty"
date=`LANG=C TZ=UTC date +"$date_format"`
fi
# Extract MAJOR.MINOR.PATCH from the version number.
major_version=`expr "$version_num" : '\([0-9]\+\)' || :`
version_num=`expr "$version_num" : '[0-9]\+\.\?\(.*\)' || :`
minor_version=`expr "$version_num" : '\([0-9]\+\)' || :`
version_num=`expr "$version_num" : '[0-9]\+\.\?\(.*\)' || :`
patch_version=`expr "$version_num" : '\([0-9]\+\)' || :`
[ -z "$major_version" ] && major_version=0
[ -z "$minor_version" ] && minor_version=0
[ -z "$patch_version" ] && patch_version=0
fi
print_versions() {
case $mode in
raw)
cat <&2
exit 1
;;
esac
}
say () {
cat <"$output_file"
else
print_versions >"$output_file"
fi
fi
form-master/scripts/travis-after_success.sh 0000775 0000000 0000000 00000000377 13565763364 0021530 0 ustar 00root root 0000000 0000000 #!/bin/bash
set -eu
set -o pipefail
# Print all executed commands to the log.
set -x
case $CI_TARGET in
*coverage*)
if type pyenv >/dev/null 2>&1; then
eval "$(pyenv init -)"
fi
coveralls -i sources --gcov-options '\-lp'
;;
esac
form-master/scripts/travis-install.sh 0000775 0000000 0000000 00000011631 13565763364 0020340 0 ustar 00root root 0000000 0000000 #!/bin/bash
set -eu
set -o pipefail
# travis_retry() is taken from
# travis-ci/travis-build/lib/travis/build/templates/header.sh (e3400b7),
# which is covered by the MIT licence. Two lines for "set +-x" are added.
ANSI_RED="\033[31;1m"
ANSI_RESET="\033[0m"
travis_retry() {
set +x
local result=0
local count=1
while [ $count -le 3 ]; do
[ $result -ne 0 ] && {
echo -e "\n${ANSI_RED}The command \"$@\" failed. Retrying, $count of 3.${ANSI_RESET}\n" >&2
}
"$@" && { result=0 && break; } || result=$?
count=$(($count + 1))
sleep 1
done
[ $count -gt 3 ] && {
echo -e "\n${ANSI_RED}The command \"$@\" failed 3 times.${ANSI_RESET}\n" >&2
}
set -x
return $result
}
# Print all executed commands to the log.
set -x
if [ "x$TRAVIS_OS_NAME" = xlinux ]; then
case $CI_TARGET in
*parform*|*parvorm*)
# When MPI is not installed from APT, manually install MPICH.
if type mpicc >/dev/null 2>&1; then :; else
if [ ! -e ./mpich/bin/mpicc ]; then
# Install MPICH to "./mpich".
travis_retry wget http://www.mpich.org/static/downloads/3.2.1/mpich-3.2.1.tar.gz
tar xfz mpich-3.2.1.tar.gz
(
cd mpich-3.2.1
./configure --prefix=$TRAVIS_BUILD_DIR/mpich --disable-dependency-tracking --disable-fortran
make
make check
make install
)
fi
export PATH=`pwd`/mpich/bin:$PATH
fi
;;
esac
case $CI_TARGET in
*coverage*)
travis_retry pip install --user cpp-coveralls
;;
esac
case $CI_TARGET in
*doc*)
# Install TeX Live to "./texlive".
if [ ! -e ./texlive/bin/`uname -m`-linux/tlmgr ]; then
travis_retry wget http://mirror.ctan.org/systems/texlive/tlnet/install-tl-unx.tar.gz -O - | tar -x --gzip
echo "
selected_scheme scheme-minimal
TEXDIR ./texlive
TEXMFCONFIG ~/.texlive2016/texmf-config
TEXMFHOME ~/texmf
TEXMFLOCAL ./texlive/texmf-local
TEXMFSYSCONFIG ./texlive/texmf-config
TEXMFSYSVAR ./texlive/texmf-var
TEXMFVAR ~/.texlive2016/texmf-var
collection-fontsrecommended 1
collection-latex 1
option_doc 0
option_src 0
" | sed -e 's/^ *//' >texlive.profile
./install-tl-20*/install-tl --profile texlive.profile
fi
export PATH=`pwd`/texlive/bin/`uname -m`-linux:$PATH
;;
esac
case $CI_TARGET in
*doc-html*)
# Install LaTeX2HTML to the TeX Live directory.
if [ ! -e ./texlive/bin/`uname -m`-linux/latex2html ]; then
travis_retry wget https://github.com/latex2html/latex2html/archive/v2019.tar.gz -O - | tar -x --gzip
(
cd latex2html-*
./configure --prefix=$TRAVIS_BUILD_DIR/texlive/texmf-local/latex2html
make install
)
(
cd texlive/bin/`uname -m`-linux
ln -s ../../texmf-local/latex2html/bin/latex2html
ln -s ../../texmf-local/latex2html/bin/pstoimg
ln -s ../../texmf-local/latex2html/bin/texexpand
)
fi
;;
esac
fi
if [ "x$TRAVIS_OS_NAME" = xosx ]; then
case $CI_TARGET in
*parform*|*parvorm*)
# See travis-ci/travis-ci#8826
travis_retry brew update
brew cask uninstall oclint
travis_retry brew install mpich
;;
esac
case $CI_TARGET in
*valgrind*)
travis_retry brew update
# valgrind 3.11.0
travis_retry brew install https://raw.githubusercontent.com/Homebrew/homebrew-core/7a4dabfc1a2acd9f01a1670fde4f0094c4fb6ffa/Formula/valgrind.rb
;;
esac
case $CI_TARGET in
*coverage*)
# NOTE: Python needs a manual setup on osx: travis-ci/travis-ci#2312.
if type pyenv >/dev/null 2>&1; then :;else
travis_retry brew update
travis_retry brew install pyenv
fi
eval "$(pyenv init -)"
travis_retry pyenv install 2.7.12
pyenv global 2.7.12
pyenv rehash
travis_retry brew install openssl
LDFLAGS="-L$(brew --prefix openssl)/lib" CFLAGS="-I$(brew --prefix openssl)/include" travis_retry pip install cryptography # pyca/cryptography#3367
travis_retry pip install cpp-coveralls
pyenv rehash
;;
esac
fi
case $CI_TARGET in
form|tform|form-i386|tform-i386)
# Install Forcer to "./formlib".
mkdir -p formlib
travis_retry wget https://github.com/benruijl/forcer/archive/v1.0.0.tar.gz -O - | tar -x --gzip
mv forcer-1.0.0/forcer.h formlib
mv forcer-1.0.0/forcer formlib
rm -rf forcer-1.0.0
;;
esac
case $CI_TARGET in
form-i386|tform-i386)
# Use Docker (travis-ci/travis-ci#5770).
travis_retry docker run -d --name build_test -v "$(pwd):$(pwd)" toopher/centos-i386:centos6 /sbin/init
docker exec -i -t build_test /bin/sh -c 'linux32 --32bit i386 sudo rpm --rebuilddb'
docker exec -i -t build_test /bin/sh -c 'linux32 --32bit i386 sudo yum install -y automake gcc-c++ git gmp-devel ruby zlib-devel'
;;
esac
form-master/scripts/travis-script.sh 0000775 0000000 0000000 00000014542 13565763364 0020202 0 ustar 00root root 0000000 0000000 #!/bin/bash
set -eu
set -o pipefail
if [ -d `pwd`/mpich/bin ]; then
export PATH=`pwd`/mpich/bin:$PATH
fi
if [ -d `pwd`/texlive/bin ]; then
export PATH=`pwd`/texlive/bin/`uname -m`-linux:$PATH
fi
if [ -d `pwd`/formlib ]; then
export FORMPATH=`pwd`/formlib
fi
# Print all executed commands to the log.
set -x
case $CI_TARGET in
form)
autoreconf -iv
./configure --disable-dependency-tracking --enable-scalar --disable-threaded --disable-parform --with-gmp --with-zlib
make
./check/check.rb ./sources/form --stat
./check/check.rb ./sources/form --stat -C forcer --timeout 60
;;
tform)
autoreconf -iv
./configure --disable-dependency-tracking --disable-scalar --enable-threaded --disable-parform --with-gmp --with-zlib
make
./check/check.rb ./sources/tform --stat
./check/check.rb ./sources/tform --stat -C forcer --timeout 60
;;
parform)
autoreconf -iv
./configure --disable-dependency-tracking --disable-scalar --disable-threaded --enable-parform --with-gmp --with-zlib
make
./check/check.rb ./sources/parform --stat
;;
form-i386)
# Use Docker (travis-ci/travis-ci#5770).
docker exec -i -t build_test /bin/sh -c "export CI_TARGET=form && cd $(pwd) && linux32 --32bit i386 ./scripts/travis-script.sh"
;;
tform-i386)
# Use Docker (travis-ci/travis-ci#5770).
docker exec -i -t build_test /bin/sh -c "export CI_TARGET=tform && cd $(pwd) && linux32 --32bit i386 ./scripts/travis-script.sh"
;;
sanitize-vorm)
autoreconf -iv
./configure --disable-dependency-tracking --enable-scalar --disable-threaded --disable-parform --enable-debug --enable-sanitize --with-gmp --with-zlib
make -C sources vorm
./check/check.rb ./sources/vorm --stat --timeout 60
;;
sanitize-tvorm)
autoreconf -iv
./configure --disable-dependency-tracking --disable-scalar --enable-threaded --disable-parform --enable-debug --enable-sanitize --with-gmp --with-zlib
make -C sources tvorm
./check/check.rb ./sources/tvorm --stat --timeout 60
;;
sanitize-parvorm)
autoreconf -iv
./configure --disable-dependency-tracking --disable-scalar --disable-threaded --enable-parform --enable-debug --enable-sanitize --with-gmp --with-zlib
make -C sources parvorm
./check/check.rb ./sources/parvorm --stat --timeout 60
;;
coverage-vorm)
autoreconf -iv
./configure --disable-dependency-tracking --enable-scalar --disable-threaded --disable-parform --enable-debug --enable-coverage --with-gmp --with-zlib
make -C sources vorm
./check/check.rb ./sources/vorm --stat --timeout 30
;;
coverage-tvorm)
autoreconf -iv
./configure --disable-dependency-tracking --disable-scalar --enable-threaded --disable-parform --enable-debug --enable-coverage --with-gmp --with-zlib
make -C sources tvorm
./check/check.rb ./sources/tvorm --stat --timeout 30
;;
coverage-parvorm)
autoreconf -iv
./configure --disable-dependency-tracking --disable-scalar --disable-threaded --enable-parform --enable-debug --enable-coverage --with-gmp --with-zlib
make -C sources parvorm
./check/check.rb ./sources/parvorm --stat --timeout 30
;;
valgrind-vorm)
autoreconf -iv
./configure --disable-dependency-tracking --enable-scalar --disable-threaded --disable-parform --enable-debug --with-gmp --with-zlib
make -C sources vorm
./check/check.rb valgrind ./sources/vorm --stat $TEST
;;
valgrind-tvorm)
autoreconf -iv
./configure --disable-dependency-tracking --disable-scalar --enable-threaded --disable-parform --enable-debug --with-gmp --with-zlib
make -C sources tvorm
./check/check.rb valgrind ./sources/tvorm --stat $TEST
;;
valgrind-parvorm)
autoreconf -iv
./configure --disable-dependency-tracking --disable-scalar --disable-threaded --enable-parform --enable-debug --with-gmp --with-zlib
make -C sources parvorm
./check/check.rb valgrind ./sources/parvorm --stat $TEST
;;
src-release)
distname=form-`./scripts/git-version-gen.sh -r | sed '2q;d' | sed 's/^v//'`
distdir=$distname
autoreconf -iv
./configure --disable-dependency-tracking
make distdir=$distdir distcheck
ls -l $distdir.tar.gz && file $distdir.tar.gz
;;
doc-pdf-release)
distname=form-`./scripts/git-version-gen.sh -r | sed '2q;d' | sed 's/^v//'`
distname=$distname-manual
autoreconf -iv
./configure --disable-dependency-tracking
make pdf
cp doc/manual/manual.pdf $distname.pdf
ls -l $distname.pdf && file $distname.pdf
;;
doc-html-release)
distname=form-`./scripts/git-version-gen.sh -r | sed '2q;d' | sed 's/^v//'`
distdir=$distname-manual-html
autoreconf -iv
./configure --disable-dependency-tracking
make -C doc/manual latex2html
(
cd doc/manual/manual
rm -f images.aux images.idx images.log images.pl images.tex internals.pl labels.pl WARNINGS
)
cp -r doc/manual/manual $distdir
tar c $distdir/* | gzip -c -9 > $distdir.tar.gz
ls -l $distdir.tar.gz && file $distdir.tar.gz
;;
bin-release)
distname=form-`./scripts/git-version-gen.sh -r | sed '2q;d' | sed 's/^v//'`
distdir=$distname-`uname -m`-$TRAVIS_OS_NAME
autoreconf -iv
if [ "x$TRAVIS_OS_NAME" = xosx ]; then
# --static fails on macOS but we want to statically link to brewed gmp.
# The linker supports neither -Wl,-static nor -l:libgmp.a.
# Make a library directory with libgmp.a but without libgmp.dylib.
mkdir static-lib
ln -s /usr/local/opt/gmp/lib/libgmp.a static-lib/libgmp.a
export LIBRARY_PATH="`pwd`/static-lib:${LIBRARY_PATH:-}"
./configure --disable-dependency-tracking --disable-native --enable-scalar --enable-threaded
else
./configure --disable-dependency-tracking --enable-static-link --disable-native --enable-scalar --enable-threaded
fi
make
make check TEST_OPTS=--stat
mkdir $distdir
cp sources/form sources/tform $distdir
tar c $distdir/* | gzip -c -9 > $distdir.tar.gz
ls -l $distdir.tar.gz && file $distdir.tar.gz sources/form sources/tform
if [ "x$TRAVIS_OS_NAME" = xosx ]; then
otool -L sources/form sources/tform
# Check if gmp is statically linked.
if otool -L sources/form sources/tform | grep -q gmp; then
echo 'Error: failed to statically link to gmp' >&2
exit 1
fi
fi
;;
*)
echo "Error: unknown CI_TARGET=$CI_TARGET" >&2
exit 1
;;
esac
form-master/sources/ 0000775 0000000 0000000 00000000000 13565763364 0015017 5 ustar 00root root 0000000 0000000 form-master/sources/Makefile.am 0000664 0000000 0000000 00000011655 13565763364 0017063 0 ustar 00root root 0000000 0000000 SRCBASE = \
argument.c \
checkpoint.c \
comexpr.c \
compcomm.c \
compiler.c \
compress.c \
comtool.c \
comtool.h \
declare.h \
diagrams.c \
dict.c \
dollar.c \
execute.c \
extcmd.c \
factor.c \
findpat.c \
form3.h \
fsizes.h \
ftypes.h \
function.c \
gentopo.cc \
gentopo.h \
if.c \
index.c \
inivar.h \
lus.c \
mallocprotect.h \
message.c \
minos.c \
minos.h \
module.c \
names.c \
normal.c \
notation.c \
opera.c \
optimize.cc \
pattern.c \
poly.cc \
poly.h \
polyfact.cc \
polyfact.h \
polygcd.cc \
polygcd.h \
polywrap.cc \
portsignals.h \
pre.c \
proces.c \
ratio.c \
reken.c \
reshuf.c \
sch.c \
setfile.c \
smart.c \
sort.c \
spectator.c \
startup.c \
store.c \
structs.h \
symmetr.c \
tables.c \
token.c \
tools.c \
topowrap.cc \
transform.c \
variable.h \
wildcard.c \
mytime.h \
mytime.cc \
vector.h
if ONUNIX
SRCBASE += \
unixfile.c \
unix.h
endif
if ONWINDOWS
SRCBASE += \
fwin.h
endif
SRCPTHREAD = \
threads.c
SRCPARALLEL = \
mpi.c \
parallel.c \
parallel.h \
mpidbg.h
# Automatic versioning.
CLEANFILES = version.h *.gcno *.gcda *.gcov gmon.out
form-startup.$(OBJEXT): version.h
tform-startup.$(OBJEXT): version.h
parform-startup.$(OBJEXT): version.h
vorm-startup.$(OBJEXT): version.h
tvorm-startup.$(OBJEXT): version.h
parvorm-startup.$(OBJEXT): version.h
.PHONY: update_version_h
version.h: update_version_h
$(UPDATE_VERSION_H)
dist-hook:
$(DISTHOOK_VERSION_H)
if FIXED_VERSION
UPDATE_VERSION_H = \
[ -f version.h ] || $(LN_S) "$(srcdir)/version.h.in" version.h
DISTHOOK_VERSION_H = \
cp "$(srcdir)/version.h.in" "$(distdir)/version.h.in"
else
UPDATE_VERSION_H = \
$(SHELL) "$(top_srcdir)/scripts/git-version-gen.sh" -C "$(srcdir)" -c -o version.h
DISTHOOK_VERSION_H = \
$(SHELL) "$(top_srcdir)/scripts/git-version-gen.sh" -C "$(srcdir)" -c -o "$(distdir)/version.h.in"
endif
# NOTE: maude_CXXFLAGS is not used while linking maude by default
# in automake < 1.10. A workaround is to define maude_LINK
# explicitly in all cases. (TU 22 Sep 2011)
bin_PROGRAMS =
if BUILD_FORM
bin_PROGRAMS += form
form_SOURCES = $(SRCBASE)
form_CPPFLAGS =
form_CFLAGS = $(COMPILEFLAGS)
form_CXXFLAGS = $(COMPILEFLAGS)
form_LDFLAGS = $(LINKFLAGS) $(STATIC_LDFLAGS)
form_LDADD = $(TOOL_LIBS)
if AUTOMAKE_GE_110
form_LINK = $(CXXLD) $(form_CXXFLAGS) $(CXXFLAGS) $(form_LDFLAGS) $(LDFLAGS) -o $@
else
form_LINK = $(CXXLD) $(form_CXXFLAGS) $(CXXFLAGS) $(LDFLAGS) -o $@
endif
endif
if BUILD_VORM
bin_PROGRAMS += vorm
vorm_SOURCES = $(SRCBASE)
vorm_CPPFLAGS = -DDEBUGGING
vorm_CFLAGS = $(DEBUGCOMPILEFLAGS)
vorm_CXXFLAGS = $(DEBUGCOMPILEFLAGS)
vorm_LDFLAGS = $(DEBUGLINKFLAGS)
vorm_LDADD = $(DEBUGTOOL_LIBS)
if AUTOMAKE_GE_110
vorm_LINK = $(CXXLD) $(vorm_CXXFLAGS) $(CXXFLAGS) $(vorm_LDFLAGS) $(LDFLAGS) -o $@
else
vorm_LINK = $(CXXLD) $(vorm_CXXFLAGS) $(CXXFLAGS) $(LDFLAGS) -o $@
endif
endif
if BUILD_TFORM
bin_PROGRAMS += tform
tform_SOURCES = $(SRCBASE) $(SRCPTHREAD)
tform_CPPFLAGS = -DWITHPTHREADS $(PTHREAD_CPPFLAGS)
tform_CFLAGS = $(COMPILEFLAGS) $(PTHREAD_CFLAGS)
tform_CXXFLAGS = $(COMPILEFLAGS) $(PTHREAD_CFLAGS)
tform_LDFLAGS = $(LINKFLAGS) $(STATIC_LDFLAGS)
tform_LDADD = $(PTHREAD_LIBS) $(TOOL_LIBS)
if AUTOMAKE_GE_110
tform_LINK = $(CXXLD) $(tform_CXXFLAGS) $(CXXFLAGS) $(tform_LDFLAGS) $(LDFLAGS) -o $@
else
tform_LINK = $(CXXLD) $(tform_CXXFLAGS) $(CXXFLAGS) $(LDFLAGS) -o $@
endif
endif
if BUILD_TVORM
bin_PROGRAMS += tvorm
tvorm_SOURCES = $(SRCBASE) $(SRCPTHREAD)
tvorm_CPPFLAGS = -DWITHPTHREADS -DDEBUGGING $(PTHREAD_CPPFLAGS)
tvorm_CFLAGS = $(DEBUGCOMPILEFLAGS) $(PTHREAD_CFLAGS)
tvorm_CXXFLAGS = $(DEBUGCOMPILEFLAGS) $(PTHREAD_CFLAGS)
tvorm_LDFLAGS = $(DEBUGLINKFLAGS)
tvorm_LDADD = $(PTHREAD_LIBS) $(DEBUGTOOL_LIBS)
if AUTOMAKE_GE_110
tvorm_LINK = $(CXXLD) $(tvorm_CXXFLAGS) $(CXXFLAGS) $(tvorm_LDFLAGS) $(LDFLAGS) -o $@
else
tvorm_LINK = $(CXXLD) $(tvorm_CXXFLAGS) $(CXXFLAGS) $(LDFLAGS) -o $@
endif
endif
if BUILD_PARFORM
bin_PROGRAMS += parform
parform_SOURCES = $(SRCBASE) $(SRCPARALLEL)
parform_CPPFLAGS = -DWITHMPI -DPF_WITHGETENV -DPF_WITHLOG $(MPI_CPPFLAGS)
parform_CFLAGS = $(COMPILEFLAGS) $(MPI_CFLAGS)
parform_CXXFLAGS = $(COMPILEFLAGS) $(MPI_CXXFLAGS)
parform_LDFLAGS = $(LINKFLAGS) $(MPI_STATIC_LDFLAGS)
parform_LDADD = $(TOOL_LIBS)
if AUTOMAKE_GE_110
parform_LINK = $(MPICXX) $(parform_CXXFLAGS) $(CXXFLAGS) $(parform_LDFLAGS) $(LDFLAGS) -o $@
else
parform_LINK = $(MPICXX) $(parform_CXXFLAGS) $(CXXFLAGS) $(LDFLAGS) -o $@
endif
endif
if BUILD_PARVORM
bin_PROGRAMS += parvorm
parvorm_SOURCES = $(SRCBASE) $(SRCPARALLEL)
parvorm_CPPFLAGS = -DWITHMPI -DPF_WITHGETENV -DPF_WITHLOG -DDEBUGGING $(MPI_CPPFLAGS)
parvorm_CFLAGS = $(DEBUGCOMPILEFLAGS) $(MPI_CFLAGS)
parvorm_CXXFLAGS = $(DEBUGCOMPILEFLAGS) $(MPI_CXXFLAGS)
parvorm_LDFLAGS = $(DEBUGLINKFLAGS)
parvorm_LDADD = $(DEBUGTOOL_LIBS)
if AUTOMAKE_GE_110
parvorm_LINK = $(MPICXX) $(parvorm_CXXFLAGS) $(CXXFLAGS) $(parvorm_LDFLAGS) $(LDFLAGS) -o $@
else
parvorm_LINK = $(MPICXX) $(parvorm_CXXFLAGS) $(CXXFLAGS) $(LDFLAGS) -o $@
endif
endif
form-master/sources/argument.c 0000664 0000000 0000000 00000261652 13565763364 0017021 0 ustar 00root root 0000000 0000000 /** @file argument.c
*
* Contains the routines that deal with the execution phase of the argument
* and related statements (like term)
*/
/* #[ License : */
/*
* Copyright (C) 1984-2017 J.A.M. Vermaseren
* When using this file you are requested to refer to the publication
* J.A.M.Vermaseren "New features of FORM" math-ph/0010025
* This is considered a matter of courtesy as the development was paid
* for by FOM the Dutch physics granting agency and we would like to
* be able to track its scientific use to convince FOM of its value
* for the community.
*
* This file is part of FORM.
*
* FORM 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.
*
* FORM 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 FORM. If not, see .
*/
/* #] License : */
/*
#[ include : argument.c
*/
#include "form3.h"
/*
#] include :
#[ execarg :
Executes the subset of statements in an argument environment.
The calling routine should be of the type
if ( C->lhs[level][0] == TYPEARG ) {
if ( execarg(term,level) ) goto GenCall;
level = C->lhs[level][2];
goto SkipCount;
}
Note that there will be cases in which extra space is needed.
In addition the compare with C->numlhs isn't very fine, because we
need to insert a different value (C->lhs[level][2]).
*/
WORD execarg(PHEAD WORD *term, WORD level)
{
GETBIDENTITY
WORD *t, *r, *m, *v;
WORD *start, *stop, *rstop, *r1, *r2 = 0, *r3 = 0, *r4, *r5, *r6, *r7, *r8, *r9;
WORD *mm, *mstop, *rnext, *rr, *factor, type, ngcd, nq;
CBUF *C = cbuf+AM.rbufnum, *CC = cbuf+AT.ebufnum;
WORD i, j, k, oldnumlhs = AR.Cnumlhs, count, action = 0, olddefer = AR.DeferFlag;
WORD oldnumrhs = CC->numrhs, size, pow, jj;
LONG oldcpointer = CC->Pointer - CC->Buffer, oldppointer = AT.pWorkPointer, lp;
WORD *oldwork = AT.WorkPointer, *oldwork2, scale, renorm;
WORD kLCM = 0, kGCD = 0, kGCD2, kkLCM = 0, jLCM = 0, jGCD, sign = 1;
int ii;
UWORD *EAscrat, *GCDbuffer = 0, *GCDbuffer2, *LCMbuffer, *LCMb, *LCMc;
AT.WorkPointer += *term;
start = C->lhs[level];
AR.Cnumlhs = start[2];
stop = start + start[1];
type = *start;
scale = start[4];
renorm = start[5];
start += TYPEARGHEADSIZE;
/*
#[ Dollars :
*/
if ( renorm && start[1] != 0 ) {/* We have to evaluate $ symbols inside () */
t = start+1; factor = oldwork2 = v = AT.WorkPointer;
i = *t; t++;
*v++ = i+3; i--; NCOPY(v,t,i);
*v++ = 1; *v++ = 1; *v++ = 3;
AT.WorkPointer = v;
start = t; AR.Eside = LHSIDEX;
NewSort(BHEAD0);
if ( Generator(BHEAD factor,AR.Cnumlhs) ) {
LowerSortLevel();
AT.WorkPointer = oldwork;
return(-1);
}
AT.WorkPointer = v;
if ( EndSort(BHEAD factor,0) < 0 ) {}
if ( *factor && *(factor+*factor) != 0 ) {
MLOCK(ErrorMessageLock);
MesPrint("&$ in () does not evaluate into a single term");
MUNLOCK(ErrorMessageLock);
return(-1);
}
AR.Eside = RHSIDE;
if ( *factor > 0 ) {
v = factor+*factor;
v -= ABS(v[-1]);
*factor = v-factor;
}
AT.WorkPointer = v;
}
else {
if ( *start < 0 ) {
factor = start + 1;
start += -*start;
}
else factor = 0;
}
/*
#] Dollars :
*/
t = term;
r = t + *t;
rstop = r - ABS(r[-1]);
t++;
/*
#[ Argument detection : + argument statement
*/
while ( t < rstop ) {
if ( *t >= FUNCTION && functions[*t-FUNCTION].spec == 0 ) {
/*
We have a function. First count the number of arguments.
Tensors are excluded.
*/
count = 0;
v = t;
m = t + FUNHEAD;
r = t + t[1];
while ( m < r ) {
count++;
NEXTARG(m)
}
if ( count <= 0 ) { t += t[1]; continue; }
/*
Now we take the arguments one by one and test for a match
*/
for ( i = 1; i <= count; i++ ) {
m = start;
while ( m < stop ) {
r = m + m[1];
j = *r++;
if ( j > 1 ) {
while ( --j > 0 ) {
if ( *r == i ) goto RightNum;
r++;
}
m = r;
continue;
}
RightNum:
if ( m[1] == 2 ) {
m += 2;
m += *m;
goto HaveTodo;
}
else {
r = m + m[1];
m += 2;
while ( m < r ) {
if ( *m == CSET ) {
r1 = SetElements + Sets[m[1]].first;
r2 = SetElements + Sets[m[1]].last;
while ( r1 < r2 ) {
if ( *r1++ == *t ) goto HaveTodo;
}
}
else if ( m[1] == *t ) goto HaveTodo;
m += 2;
}
}
m += *m;
}
continue;
HaveTodo:
/*
If we come here we have to do the argument i (first is 1).
*/
sign = 1;
action = 1;
v[2] |= DIRTYFLAG;
r = t + FUNHEAD;
j = i;
while ( --j > 0 ) { NEXTARG(r) }
if ( ( type == TYPESPLITARG ) || ( type == TYPESPLITFIRSTARG )
|| ( type == TYPESPLITLASTARG ) ) {
if ( *t > FUNCTION && *r > 0 ) {
WantAddPointers(2);
AT.pWorkSpace[AT.pWorkPointer++] = t;
AT.pWorkSpace[AT.pWorkPointer++] = r;
}
continue;
}
else if ( type == TYPESPLITARG2 ) {
if ( *t > FUNCTION && *r > 0 ) {
WantAddPointers(2);
AT.pWorkSpace[AT.pWorkPointer++] = t;
AT.pWorkSpace[AT.pWorkPointer++] = r;
}
continue;
}
else if ( type == TYPEFACTARG || type == TYPEFACTARG2 ) {
if ( *t > FUNCTION || *t == DENOMINATOR ) {
if ( *r > 0 ) {
mm = r + ARGHEAD; mstop = r + *r;
if ( mm + *mm < mstop ) {
WantAddPointers(2);
AT.pWorkSpace[AT.pWorkPointer++] = t;
AT.pWorkSpace[AT.pWorkPointer++] = r;
continue;
}
if ( *mm == 1+ABS(mstop[-1]) ) continue;
if ( mstop[-3] != 1 || mstop[-2] != 1
|| mstop[-1] != 3 ) {
WantAddPointers(2);
AT.pWorkSpace[AT.pWorkPointer++] = t;
AT.pWorkSpace[AT.pWorkPointer++] = r;
continue;
}
GETSTOP(mm,mstop); mm++;
if ( mm + mm[1] < mstop ) {
WantAddPointers(2);
AT.pWorkSpace[AT.pWorkPointer++] = t;
AT.pWorkSpace[AT.pWorkPointer++] = r;
continue;
}
if ( *mm == SYMBOL && ( mm[1] > 4 ||
( mm[3] != 1 && mm[3] != -1 ) ) ) {
WantAddPointers(2);
AT.pWorkSpace[AT.pWorkPointer++] = t;
AT.pWorkSpace[AT.pWorkPointer++] = r;
continue;
}
else if ( *mm == DOTPRODUCT && ( mm[1] > 5 ||
( mm[4] != 1 && mm[4] != -1 ) ) ) {
WantAddPointers(2);
AT.pWorkSpace[AT.pWorkPointer++] = t;
AT.pWorkSpace[AT.pWorkPointer++] = r;
continue;
}
else if ( ( *mm == DELTA || *mm == VECTOR )
&& mm[1] > 4 ) {
WantAddPointers(2);
AT.pWorkSpace[AT.pWorkPointer++] = t;
AT.pWorkSpace[AT.pWorkPointer++] = r;
continue;
}
}
else if ( factor && *factor == 4 && factor[2] == 1 ) {
WantAddPointers(2);
AT.pWorkSpace[AT.pWorkPointer++] = t;
AT.pWorkSpace[AT.pWorkPointer++] = r;
continue;
}
else if ( factor && *factor == 0
&& ( *r == -SNUMBER && r[1] != 1 ) ) {
WantAddPointers(2);
AT.pWorkSpace[AT.pWorkPointer++] = t;
AT.pWorkSpace[AT.pWorkPointer++] = r;
continue;
}
else if ( *r == -MINVECTOR ) {
WantAddPointers(2);
AT.pWorkSpace[AT.pWorkPointer++] = t;
AT.pWorkSpace[AT.pWorkPointer++] = r;
continue;
}
}
continue;
}
else if ( type == TYPENORM || type == TYPENORM2 || type == TYPENORM3 || type == TYPENORM4 ) {
if ( *r < 0 ) {
WORD rone;
if ( *r == -MINVECTOR ) { rone = -1; *r = -INDEX; }
else if ( *r != -SNUMBER || r[1] == 1 || r[1] == 0 ) continue;
else { rone = r[1]; r[1] = 1; }
/*
Now we must multiply the general coefficient by r[1]
*/
if ( scale && ( factor == 0 || *factor ) ) {
action = 1;
v[2] |= DIRTYFLAG;
if ( rone < 0 ) {
if ( type == TYPENORM3 ) k = 1;
else k = -1;
rone = -rone;
}
else k = 1;
r1 = term + *term;
size = r1[-1];
size = REDLENG(size);
if ( scale > 0 ) {
for ( jj = 0; jj < scale; jj++ ) {
if ( Mully(BHEAD (UWORD *)rstop,&size,(UWORD *)(&rone),k) )
goto execargerr;
}
}
else {
for ( jj = 0; jj > scale; jj-- ) {
if ( Divvy(BHEAD (UWORD *)rstop,&size,(UWORD *)(&rone),k) )
goto execargerr;
}
}
size = INCLENG(size);
k = size < 0 ? -size: size;
rstop[k-1] = size;
*term = (WORD)(rstop - term) + k;
}
continue;
}
/*
Now we have to find a reference term.
If factor is defined and *factor != 0 we have to
look for the first term that matches the pattern exactly
Otherwise the first term plays this role
If its coefficient is not one,
we must set up a division of the whole argument by
this coefficient, and a multiplication of the term
when the type is not equal to TYPENORM2.
We first multiply the coefficient of the term.
Then we set up the division.
First find the magic term
*/
if ( type == TYPENORM4 ) {
/*
For normalizing everything to integers we have to
determine for all elements of this argument the LCM of
the denominators and the GCD of the numerators.
*/
GCDbuffer = NumberMalloc("execarg");
GCDbuffer2 = NumberMalloc("execarg");
LCMbuffer = NumberMalloc("execarg");
LCMb = NumberMalloc("execarg"); LCMc = NumberMalloc("execarg");
r4 = r + *r;
r1 = r + ARGHEAD;
/*
First take the first term to load up the LCM and the GCD
*/
r2 = r1 + *r1;
j = r2[-1];
if ( j < 0 ) sign = -1;
r3 = r2 - ABS(j);
k = REDLENG(j);
if ( k < 0 ) k = -k;
while ( ( k > 1 ) && ( r3[k-1] == 0 ) ) k--;
for ( kGCD = 0; kGCD < k; kGCD++ ) GCDbuffer[kGCD] = r3[kGCD];
k = REDLENG(j);
if ( k < 0 ) k = -k;
r3 += k;
while ( ( k > 1 ) && ( r3[k-1] == 0 ) ) k--;
for ( kLCM = 0; kLCM < k; kLCM++ ) LCMbuffer[kLCM] = r3[kLCM];
r1 = r2;
/*
Now go through the rest of the terms in this argument.
*/
while ( r1 < r4 ) {
r2 = r1 + *r1;
j = r2[-1];
r3 = r2 - ABS(j);
k = REDLENG(j);
if ( k < 0 ) k = -k;
while ( ( k > 1 ) && ( r3[k-1] == 0 ) ) k--;
if ( ( ( GCDbuffer[0] == 1 ) && ( kGCD == 1 ) ) ) {
/*
GCD is already 1
*/
}
else if ( ( ( k != 1 ) || ( r3[0] != 1 ) ) ) {
if ( GcdLong(BHEAD GCDbuffer,kGCD,(UWORD *)r3,k,GCDbuffer2,&kGCD2) ) {
NumberFree(GCDbuffer,"execarg");
NumberFree(GCDbuffer2,"execarg");
NumberFree(LCMbuffer,"execarg");
NumberFree(LCMb,"execarg"); NumberFree(LCMc,"execarg");
goto execargerr;
}
kGCD = kGCD2;
for ( ii = 0; ii < kGCD; ii++ ) GCDbuffer[ii] = GCDbuffer2[ii];
}
else {
kGCD = 1; GCDbuffer[0] = 1;
}
k = REDLENG(j);
if ( k < 0 ) k = -k;
r3 += k;
while ( ( k > 1 ) && ( r3[k-1] == 0 ) ) k--;
if ( ( ( LCMbuffer[0] == 1 ) && ( kLCM == 1 ) ) ) {
for ( kLCM = 0; kLCM < k; kLCM++ )
LCMbuffer[kLCM] = r3[kLCM];
}
else if ( ( k != 1 ) || ( r3[0] != 1 ) ) {
if ( GcdLong(BHEAD LCMbuffer,kLCM,(UWORD *)r3,k,LCMb,&kkLCM) ) {
NumberFree(GCDbuffer,"execarg"); NumberFree(GCDbuffer2,"execarg");
NumberFree(LCMbuffer,"execarg"); NumberFree(LCMb,"execarg"); NumberFree(LCMc,"execarg");
goto execargerr;
}
DivLong((UWORD *)r3,k,LCMb,kkLCM,LCMb,&kkLCM,LCMc,&jLCM);
MulLong(LCMbuffer,kLCM,LCMb,kkLCM,LCMc,&jLCM);
for ( kLCM = 0; kLCM < jLCM; kLCM++ )
LCMbuffer[kLCM] = LCMc[kLCM];
}
else {} /* LCM doesn't change */
r1 = r2;
}
/*
Now put the factor together: GCD/LCM
*/
r3 = (WORD *)(GCDbuffer);
if ( kGCD == kLCM ) {
for ( jGCD = 0; jGCD < kGCD; jGCD++ )
r3[jGCD+kGCD] = LCMbuffer[jGCD];
k = kGCD;
}
else if ( kGCD > kLCM ) {
for ( jGCD = 0; jGCD < kLCM; jGCD++ )
r3[jGCD+kGCD] = LCMbuffer[jGCD];
for ( jGCD = kLCM; jGCD < kGCD; jGCD++ )
r3[jGCD+kGCD] = 0;
k = kGCD;
}
else {
for ( jGCD = kGCD; jGCD < kLCM; jGCD++ )
r3[jGCD] = 0;
for ( jGCD = 0; jGCD < kLCM; jGCD++ )
r3[jGCD+kLCM] = LCMbuffer[jGCD];
k = kLCM;
}
/* NumberFree(GCDbuffer,"execarg"); GCDbuffer = 0; */
NumberFree(GCDbuffer2,"execarg");
NumberFree(LCMbuffer,"execarg");
NumberFree(LCMb,"execarg"); NumberFree(LCMc,"execarg");
j = 2*k+1;
/*
Now we have to correct the overal factor
We have a little problem here.
r3 is in GCDbuffer and we returned that.
At the same time we still use it.
This works as long as each worker has its own TermMalloc
*/
if ( scale && ( factor == 0 || *factor > 0 ) )
goto ScaledVariety;
/*
The if was added 28-nov-2012 to give MakeInteger also
the (0) option.
*/
if ( scale && ( factor == 0 || *factor ) ) {
size = term[*term-1];
size = REDLENG(size);
if ( MulRat(BHEAD (UWORD *)rstop,size,(UWORD *)r3,k,
(UWORD *)rstop,&size) ) goto execargerr;
size = INCLENG(size);
k = size < 0 ? -size: size;
rstop[k-1] = size*sign;
*term = (WORD)(rstop - term) + k;
}
}
else {
if ( factor && *factor >= 1 ) {
r4 = r + *r;
r1 = r + ARGHEAD;
while ( r1 < r4 ) {
r2 = r1 + *r1;
r3 = r2 - ABS(r2[-1]);
j = r3 - r1;
r5 = factor;
if ( j != *r5 ) { r1 = r2; continue; }
r5++; r6 = r1+1;
while ( --j > 0 ) {
if ( *r5 != *r6 ) break;
r5++; r6++;
}
if ( j > 0 ) { r1 = r2; continue; }
break;
}
if ( r1 >= r4 ) continue;
}
else {
r1 = r + ARGHEAD;
r2 = r1 + *r1;
r3 = r2 - ABS(r2[-1]);
}
if ( *r3 == 1 && r3[1] == 1 ) {
if ( r2[-1] == 3 ) continue;
if ( r2[-1] == -3 && type == TYPENORM3 ) continue;
}
action = 1;
v[2] |= DIRTYFLAG;
j = r2[-1];
k = REDLENG(j);
if ( j < 0 ) j = -j;
if ( type == TYPENORM && scale && ( factor == 0 || *factor ) ) {
/*
Now we correct the overal factor
*/
ScaledVariety:;
size = term[*term-1];
size = REDLENG(size);
if ( scale > 0 ) {
for ( jj = 0; jj < scale; jj++ ) {
if ( MulRat(BHEAD (UWORD *)rstop,size,(UWORD *)r3,k,
(UWORD *)rstop,&size) ) goto execargerr;
}
}
else {
for ( jj = 0; jj > scale; jj-- ) {
if ( DivRat(BHEAD (UWORD *)rstop,size,(UWORD *)r3,k,
(UWORD *)rstop,&size) ) goto execargerr;
}
}
size = INCLENG(size);
k = size < 0 ? -size: size;
rstop[k-1] = size*sign;
*term = (WORD)(rstop - term) + k;
}
}
/*
We generate a statement for adapting all terms in the
argument sucessively
*/
r4 = AddRHS(AT.ebufnum,1);
while ( (r4+j+12) > CC->Top ) r4 = DoubleCbuffer(AT.ebufnum,r4,3);
*r4++ = j+1;
i = (j-1)/2; /* was (j-1)*2 ????? 17-oct-2017 */
for ( k = 0; k < i; k++ ) *r4++ = r3[i+k];
for ( k = 0; k < i; k++ ) *r4++ = r3[k];
if ( ( type == TYPENORM3 ) || ( type == TYPENORM4 ) ) *r4++ = j*sign;
else *r4++ = r3[j-1];
*r4++ = 0;
CC->rhs[CC->numrhs+1] = r4;
CC->Pointer = r4;
AT.mulpat[5] = CC->numrhs;
AT.mulpat[7] = AT.ebufnum;
}
else if ( type == TYPEARGTOEXTRASYMBOL ) {
WORD n;
if ( r[0] < 0 ) {
/* The argument is in the fast notation. */
WORD tmp[MaX(9,FUNHEAD+5)];
switch ( r[0] ) {
case -SNUMBER:
if ( r[1] == 0 ) {
tmp[0] = 0;
}
else {
tmp[0] = 4;
tmp[1] = ABS(r[1]);
tmp[2] = 1;
tmp[3] = r[1] > 0 ? 3 : -3;
tmp[4] = 0;
}
break;
case -SYMBOL:
tmp[0] = 8;
tmp[1] = SYMBOL;
tmp[2] = 4;
tmp[3] = r[1];
tmp[4] = 1;
tmp[5] = 1;
tmp[6] = 1;
tmp[7] = 3;
tmp[8] = 0;
break;
case -INDEX:
case -VECTOR:
case -MINVECTOR:
tmp[0] = 7;
tmp[1] = INDEX;
tmp[2] = 3;
tmp[3] = r[1];
tmp[4] = 1;
tmp[5] = 1;
tmp[6] = r[0] != -MINVECTOR ? 3 : -3;
tmp[7] = 0;
break;
default:
if ( r[0] <= -FUNCTION ) {
tmp[0] = FUNHEAD+4;
tmp[1] = -r[0];
tmp[2] = FUNHEAD;
ZeroFillRange(tmp,3,1+FUNHEAD);
tmp[FUNHEAD+1] = 1;
tmp[FUNHEAD+2] = 1;
tmp[FUNHEAD+3] = 3;
tmp[FUNHEAD+4] = 0;
break;
}
else {
MLOCK(ErrorMessageLock);
MesPrint("Unknown fast notation found (TYPEARGTOEXTRASYMBOL)");
MUNLOCK(ErrorMessageLock);
return(-1);
}
}
n = FindSubexpression(tmp);
}
else {
/*
* NOTE: writing to r[r[0]] is legal. As long as we work
* in a part of the term, at least the coefficient of
* the term must follow.
*/
WORD old_rr0 = r[r[0]];
r[r[0]] = 0; /* zero-terminated */
n = FindSubexpression(r+ARGHEAD);
r[r[0]] = old_rr0;
}
/* Put the new argument in the work space. */
if ( AT.WorkPointer+2 > AT.WorkTop ) {
MLOCK(ErrorMessageLock);
MesWork();
MUNLOCK(ErrorMessageLock);
return(-1);
}
r1 = AT.WorkPointer;
if ( scale ) { /* means "tonumber" */
r1[0] = -SNUMBER;
r1[1] = n;
}
else {
r1[0] = -SYMBOL;
r1[1] = MAXVARIABLES-n;
}
/* We need r2, r3, m and k to shift the data. */
r2 = r + (r[0] > 0 ? r[0] : r[0] <= -FUNCTION ? 1 : 2);
r3 = r;
m = r1+ARGHEAD+2;
k = 2;
goto do_shift;
}
r3 = r;
AR.DeferFlag = 0;
if ( *r > 0 ) {
NewSort(BHEAD0);
action = 1;
r2 = r + *r;
r += ARGHEAD;
while ( r < r2 ) { /* Sum over the terms */
m = AT.WorkPointer;
j = *r;
while ( --j >= 0 ) *m++ = *r++;
r1 = AT.WorkPointer;
AT.WorkPointer = m;
/*
What to do with dummy indices?
*/
if ( type == TYPENORM || type == TYPENORM2 || type == TYPENORM3 || type == TYPENORM4 ) {
if ( MultDo(BHEAD r1,AT.mulpat) ) goto execargerr;
AT.WorkPointer = r1 + *r1;
}
if ( Generator(BHEAD r1,level) ) goto execargerr;
AT.WorkPointer = r1;
}
}
else {
r2 = r + (( *r <= -FUNCTION ) ? 1:2);
r1 = AT.WorkPointer;
ToGeneral(r,r1,0);
m = r1 + ARGHEAD;
AT.WorkPointer = r1 + *r1;
NewSort(BHEAD0);
action = 1;
/*
What to do with dummy indices?
*/
if ( type == TYPENORM || type == TYPENORM2 || type == TYPENORM3 || type == TYPENORM4 ) {
if ( MultDo(BHEAD m,AT.mulpat) ) goto execargerr;
AT.WorkPointer = m + *m;
}
if ( (*m != 0 ) && Generator(BHEAD m,level) ) goto execargerr;
AT.WorkPointer = r1;
}
if ( EndSort(BHEAD AT.WorkPointer+ARGHEAD,1) < 0 ) goto execargerr;
AR.DeferFlag = olddefer;
/*
Now shift the sorted entity over the old argument.
*/
m = AT.WorkPointer+ARGHEAD;
while ( *m ) m += *m;
k = WORDDIF(m,AT.WorkPointer);
*AT.WorkPointer = k;
AT.WorkPointer[1] = 0;
if ( ToFast(AT.WorkPointer,AT.WorkPointer) ) {
if ( *AT.WorkPointer <= -FUNCTION ) k = 1;
else k = 2;
}
do_shift:
if ( *r3 > 0 ) j = k - *r3;
else if ( *r3 <= -FUNCTION ) j = k - 1;
else j = k - 2;
t[1] += j;
action = 1;
v[2] |= DIRTYFLAG;
if ( j > 0 ) {
r = m + j;
while ( m > AT.WorkPointer ) *--r = *--m;
AT.WorkPointer = r;
m = term + *term;
r = m + j;
while ( m > r2 ) *--r = *--m;
}
else if ( j < 0 ) {
r = r2 + j;
r1 = term + *term;
while ( r2 < r1 ) *r++ = *r2++;
}
r = r3;
m = AT.WorkPointer;
NCOPY(r,m,k);
*term += j;
rstop += j;
CC->numrhs = oldnumrhs;
CC->Pointer = CC->Buffer + oldcpointer;
}
}
t += t[1];
}
/*
#] Argument detection :
#[ SplitArg : + varieties
*/
if ( ( type == TYPESPLITARG || type == TYPESPLITARG2
|| type == TYPESPLITFIRSTARG || type == TYPESPLITLASTARG ) &&
AT.pWorkPointer > oldppointer ) {
t = term+1;
r1 = AT.WorkPointer + 1;
lp = oldppointer;
while ( t < rstop ) {
if ( lp < AT.pWorkPointer && t == AT.pWorkSpace[lp] ) {
v = t;
m = t + FUNHEAD;
r = t + t[1];
r2 = r1; while ( t < m ) *r1++ = *t++;
while ( m < r ) {
t = m;
NEXTARG(m)
if ( lp >= AT.pWorkPointer || t != AT.pWorkSpace[lp+1] ) {
if ( *t > 0 ) t[1] = 0;
while ( t < m ) *r1++ = *t++;
continue;
}
/*
Now we have a nontrivial argument that should be done.
*/
lp += 2;
action = 1;
v[2] |= DIRTYFLAG;
r3 = t + *t;
t += ARGHEAD;
if ( type == TYPESPLITFIRSTARG ) {
r4 = r1; r5 = t; r7 = oldwork;
*r1++ = *t + ARGHEAD;
for ( i = 1; i < ARGHEAD; i++ ) *r1++ = 0;
j = 0;
while ( t < r3 ) {
i = *t;
if ( j == 0 ) {
NCOPY(r7,t,i)
j++;
}
else {
NCOPY(r1,t,i)
}
}
*r4 = r1 - r4;
if ( j ) {
if ( ToFast(r4,r4) ) {
r1 = r4;
if ( *r1 > -FUNCTION ) r1++;
r1++;
}
r7 = oldwork;
while ( --j >= 0 ) {
r4 = r1; i = *r7;
*r1++ = i+ARGHEAD; *r1++ = 0;
FILLARG(r1);
NCOPY(r1,r7,i)
if ( ToFast(r4,r4) ) {
r1 = r4;
if ( *r1 > -FUNCTION ) r1++;
r1++;
}
}
}
t = r3;
}
else if ( type == TYPESPLITLASTARG ) {
r4 = r1; r5 = t; r7 = oldwork;
*r1++ = *t + ARGHEAD;
for ( i = 1; i < ARGHEAD; i++ ) *r1++ = 0;
j = 0;
while ( t < r3 ) {
i = *t;
if ( t+i >= r3 ) {
NCOPY(r7,t,i)
j++;
}
else {
NCOPY(r1,t,i)
}
}
*r4 = r1 - r4;
if ( j ) {
if ( ToFast(r4,r4) ) {
r1 = r4;
if ( *r1 > -FUNCTION ) r1++;
r1++;
}
r7 = oldwork;
while ( --j >= 0 ) {
r4 = r1; i = *r7;
*r1++ = i+ARGHEAD; *r1++ = 0;
FILLARG(r1);
NCOPY(r1,r7,i)
if ( ToFast(r4,r4) ) {
r1 = r4;
if ( *r1 > -FUNCTION ) r1++;
r1++;
}
}
}
t = r3;
}
else if ( factor == 0 || ( type == TYPESPLITARG2 && *factor == 0 ) ) {
while ( t < r3 ) {
r4 = r1;
*r1++ = *t + ARGHEAD;
for ( i = 1; i < ARGHEAD; i++ ) *r1++ = 0;
i = *t;
while ( --i >= 0 ) *r1++ = *t++;
if ( ToFast(r4,r4) ) {
r1 = r4;
if ( *r1 > -FUNCTION ) r1++;
r1++;
}
}
}
else if ( type == TYPESPLITARG2 ) {
/*
Here we better put the pattern matcher at work?
Remember: there are no wildcards.
*/
WORD *oRepFunList = AN.RepFunList;
WORD *oWildMask = AT.WildMask, *oWildValue = AN.WildValue;
AN.WildValue = AT.locwildvalue; AT.WildMask = AT.locwildvalue+2;
AN.NumWild = 0;
r4 = r1; r5 = t; r7 = oldwork;
*r1++ = *t + ARGHEAD;
for ( i = 1; i < ARGHEAD; i++ ) *r1++ = 0;
j = 0;
while ( t < r3 ) {
AN.UseFindOnly = 0; oldwork2 = AT.WorkPointer;
AN.RepFunList = r1;
AT.WorkPointer = r1+AN.RepFunNum+2;
i = *t;
if ( FindRest(BHEAD t,factor) &&
( AN.UsedOtherFind || FindOnce(BHEAD t,factor) ) ) {
NCOPY(r7,t,i)
j++;
}
else if ( factor[0] == FUNHEAD+1 && factor[1] >= FUNCTION ) {
WORD *rr1 = t+1, *rr2 = t+i;
rr2 -= ABS(rr2[-1]);
while ( rr1 < rr2 ) {
if ( *rr1 == factor[1] ) break;
rr1 += rr1[1];
}
if ( rr1 < rr2 ) {
NCOPY(r7,t,i)
j++;
}
else {
NCOPY(r1,t,i)
}
}
else {
NCOPY(r1,t,i)
}
AT.WorkPointer = oldwork2;
}
AN.RepFunList = oRepFunList;
*r4 = r1 - r4;
if ( j ) {
if ( ToFast(r4,r4) ) {
r1 = r4;
if ( *r1 > -FUNCTION ) r1++;
r1++;
}
r7 = oldwork;
while ( --j >= 0 ) {
r4 = r1; i = *r7;
*r1++ = i+ARGHEAD; *r1++ = 0;
FILLARG(r1);
NCOPY(r1,r7,i)
if ( ToFast(r4,r4) ) {
r1 = r4;
if ( *r1 > -FUNCTION ) r1++;
r1++;
}
}
}
t = r3;
AT.WildMask = oWildMask; AN.WildValue = oWildValue;
}
else {
/*
This code deals with splitting off a single term
*/
r4 = r1; r5 = t;
*r1++ = *t + ARGHEAD;
for ( i = 1; i < ARGHEAD; i++ ) *r1++ = 0;
j = 0;
while ( t < r3 ) {
r6 = t + *t; r6 -= ABS(r6[-1]);
if ( (r6 - t) == *factor ) {
k = *factor - 1;
for ( ; k > 0; k-- ) {
if ( t[k] != factor[k] ) break;
}
if ( k <= 0 ) {
j = r3 - t; t += *t; continue;
}
}
else if ( (r6 - t) == 1 && *factor == 0 ) {
j = r3 - t; t += *t; continue;
}
i = *t;
NCOPY(r1,t,i)
}
*r4 = r1 - r4;
if ( j ) {
if ( ToFast(r4,r4) ) {
r1 = r4;
if ( *r1 > -FUNCTION ) r1++;
r1++;
}
t = r3 - j;
r4 = r1;
*r1++ = *t + ARGHEAD;
for ( i = 1; i < ARGHEAD; i++ ) *r1++ = 0;
i = *t;
while ( --i >= 0 ) *r1++ = *t++;
if ( ToFast(r4,r4) ) {
r1 = r4;
if ( *r1 > -FUNCTION ) r1++;
r1++;
}
}
t = r3;
}
}
r2[1] = r1 - r2;
}
else {
r = t + t[1];
while ( t < r ) *r1++ = *t++;
}
}
r = term + *term;
while ( t < r ) *r1++ = *t++;
m = AT.WorkPointer;
i = m[0] = r1 - m;
t = term;
while ( --i >= 0 ) *t++ = *m++;
if ( AT.WorkPointer < m ) AT.WorkPointer = m;
}
/*
#] SplitArg :
#[ FACTARG :
*/
if ( ( type == TYPEFACTARG || type == TYPEFACTARG2 ) &&
AT.pWorkPointer > oldppointer ) {
t = term+1;
r1 = AT.WorkPointer + 1;
lp = oldppointer;
while ( t < rstop ) {
if ( lp < AT.pWorkPointer && AT.pWorkSpace[lp] == t ) {
v = t;
m = t + FUNHEAD;
r = t + t[1];
r2 = r1; while ( t < m ) *r1++ = *t++;
while ( m < r ) {
rr = t = m;
NEXTARG(m)
if ( lp >= AT.pWorkPointer || AT.pWorkSpace[lp+1] != t ) {
if ( *t > 0 ) t[1] = 0;
while ( t < m ) *r1++ = *t++;
continue;
}
/*
Now we have a nontrivial argument that should be studied.
Try to find common factors.
*/
lp += 2;
if ( *t < 0 ) {
if ( factor && ( *factor == 0 && *t == -SNUMBER ) ) {
*r1++ = *t++;
if ( *t == 0 ) *r1++ = *t++;
else { *r1++ = 1; t++; }
continue;
}
else if ( factor && *factor == 4 && factor[2] == 1 ) {
if ( *t == -SNUMBER ) {
if ( factor[3] < 0 || t[1] >= 0 ) {
while ( t < m ) *r1++ = *t++;
}
else {
*r1++ = -SNUMBER; *r1++ = -1;
*r1++ = *t++; *r1++ = -*t++;
}
}
else {
while ( t < m ) *r1++ = *t++;
*r1++ = -SNUMBER; *r1++ = 1;
}
continue;
}
else if ( *t == -MINVECTOR ) {
*r1++ = -VECTOR; t++; *r1++ = *t++;
*r1++ = -SNUMBER; *r1++ = -1;
*r1++ = -SNUMBER; *r1++ = 1;
continue;
}
}
/*
Now we have a nontrivial argument
*/
r3 = t + *t;
t += ARGHEAD; r5 = t; /* Store starting point */
/* We have terms from r5 to r3 */
if ( r5+*r5 == r3 && factor ) { /* One term only */
if ( *factor == 0 ) {
GETSTOP(t,r6);
r9 = r1; *r1++ = 0; *r1++ = 1;
FILLARG(r1);
*r1++ = (r6-t)+3; t++;
while ( t < r6 ) *r1++ = *t++;
*r1++ = 1; *r1++ = 1; *r1++ = 3;
*r9 = r1-r9;
if ( ToFast(r9,r9) ) {
if ( *r9 <= -FUNCTION ) r1 = r9+1;
else r1 = r9+2;
}
t = r3; continue;
}
if ( factor[0] == 4 && factor[2] == 1 ) {
GETSTOP(t,r6);
r7 = r1; *r1++ = (r6-t)+3+ARGHEAD; *r1++ = 0;
FILLARG(r1);
*r1++ = (r6-t)+3; t++;
while ( t < r6 ) *r1++ = *t++;
*r1++ = 1; *r1++ = 1; *r1++ = 3;
if ( ToFast(r7,r7) ) {
if ( *r7 <= -FUNCTION ) r1 = r7+1;
else r1 = r7+2;
}
if ( r3[-1] < 0 && factor[3] > 0 ) {
*r1++ = -SNUMBER; *r1++ = -1;
if ( r3[-1] == -3 && r3[-2] == 1
&& ( r3[-3] & MAXPOSITIVE ) == r3[-3] ) {
*r1++ = -SNUMBER; *r1++ = r3[-3];
}
else {
*r1++ = (r3-r6)+1+ARGHEAD;
*r1++ = 0;
FILLARG(r1);
*r1++ = (r3-r6+1);
while ( t < r3 ) *r1++ = *t++;
r1[-1] = -r1[-1];
}
}
else {
if ( ( r3[-1] == -3 || r3[-1] == 3 )
&& r3[-2] == 1
&& ( r3[-3] & MAXPOSITIVE ) == r3[-3] ) {
*r1++ = -SNUMBER; *r1++ = r3[-3];
if ( r3[-1] < 0 ) r1[-1] = - r1[-1];
}
else {
*r1++ = (r3-r6)+1+ARGHEAD;
*r1++ = 0;
FILLARG(r1);
*r1++ = (r3-r6+1);
while ( t < r3 ) *r1++ = *t++;
}
}
t = r3; continue;
}
}
/*
Now we take the first term and look for its pieces
inside the other terms.
It is at this point that a more general factorization
routine could take over (allowing for writing the output
properly of course).
*/
if ( AC.OldFactArgFlag == NEWFACTARG ) {
if ( factor == 0 ) {
WORD *oldworkpointer2 = AT.WorkPointer;
AT.WorkPointer = r1 + AM.MaxTer+FUNHEAD;
if ( ArgFactorize(BHEAD t-ARGHEAD,r1) < 0 ) {
MesCall("ExecArg");
return(-1);
}
AT.WorkPointer = oldworkpointer2;
t = r3;
while ( *r1 ) { NEXTARG(r1) }
}
else {
rnext = t + *t;
GETSTOP(t,r6);
t++;
t = r5; pow = 1;
while ( t < r3 ) {
t += *t; if ( t[-1] > 0 ) { pow = 0; break; }
}
/*
We have to add here the code for computing the GCD
and to divide it out.
#[ Numerical factor :
*/
t = r5;
EAscrat = (UWORD *)(TermMalloc("execarg"));
if ( t + *t == r3 ) goto onetermnew;
GETSTOP(t,r6);
ngcd = t[t[0]-1];
i = abs(ngcd)-1;
while ( --i >= 0 ) EAscrat[i] = r6[i];
t += *t;
while ( t < r3 ) {
GETSTOP(t,r6);
i = t[t[0]-1];
if ( AccumGCD(BHEAD EAscrat,&ngcd,(UWORD *)r6,i) ) goto execargerr;
if ( ngcd == 3 && EAscrat[0] == 1 && EAscrat[1] == 1 ) break;
t += *t;
}
if ( ngcd != 3 || EAscrat[0] != 1 || EAscrat[1] != 1 ) {
if ( pow ) ngcd = -ngcd;
t = r5; r9 = r1; *r1++ = t[-ARGHEAD]; *r1++ = 1;
FILLARG(r1); ngcd = REDLENG(ngcd);
while ( t < r3 ) {
GETSTOP(t,r6);
r7 = t; r8 = r1;
while ( r7 < r6) *r1++ = *r7++;
t += *t;
i = REDLENG(t[-1]);
if ( DivRat(BHEAD (UWORD *)r6,i,EAscrat,ngcd,(UWORD *)r1,&nq) ) goto execargerr;
nq = INCLENG(nq);
i = ABS(nq)-1;
r1 += i; *r1++ = nq; *r8 = r1-r8;
}
*r9 = r1-r9;
ngcd = INCLENG(ngcd);
i = ABS(ngcd)-1;
if ( factor && *factor == 0 ) {}
else if ( ( factor && factor[0] == 4 && factor[2] == 1
&& factor[3] == -3 ) || pow == 0 ) {
r9 = r1; *r1++ = ARGHEAD+2+i; *r1++ = 0;
FILLARG(r1); *r1++ = i+2;
for ( j = 0; j < i; j++ ) *r1++ = EAscrat[j];
*r1++ = ngcd;
if ( ToFast(r9,r9) ) r1 = r9+2;
}
else if ( factor && factor[0] == 4 && factor[2] == 1
&& factor[3] > 0 && pow ) {
if ( ngcd < 0 ) ngcd = -ngcd;
*r1++ = -SNUMBER; *r1++ = -1;
r9 = r1; *r1++ = ARGHEAD+2+i; *r1++ = 0;
FILLARG(r1); *r1++ = i+2;
for ( j = 0; j < i; j++ ) *r1++ = EAscrat[j];
*r1++ = ngcd;
if ( ToFast(r9,r9) ) r1 = r9+2;
}
else {
if ( ngcd < 0 ) ngcd = -ngcd;
if ( pow ) { *r1++ = -SNUMBER; *r1++ = -1; }
if ( ngcd != 3 || EAscrat[0] != 1 || EAscrat[1] != 1 ) {
r9 = r1; *r1++ = ARGHEAD+2+i; *r1++ = 0;
FILLARG(r1); *r1++ = i+2;
for ( j = 0; j < i; j++ ) *r1++ = EAscrat[j];
*r1++ = ngcd;
if ( ToFast(r9,r9) ) r1 = r9+2;
}
}
}
/*
#] Numerical factor :
*/
else {
onetermnew:;
if ( factor == 0 || *factor > 2 ) {
if ( pow > 0 ) {
*r1++ = -SNUMBER; *r1++ = -1;
t = r5;
while ( t < r3 ) {
t += *t; t[-1] = -t[-1];
}
}
t = rr; *r1++ = *t++; *r1++ = 1; t++;
COPYARG(r1,t);
while ( t < m ) *r1++ = *t++;
}
}
TermFree(EAscrat,"execarg");
}
}
else { /* AC.OldFactArgFlag is ON */
{
WORD *mnext, ncom;
rnext = t + *t;
GETSTOP(t,r6);
t++;
if ( factor == 0 ) {
while ( t < r6 ) {
/*
#[ SYMBOL :
*/
if ( *t == SYMBOL ) {
r7 = t; r8 = t + t[1]; t += 2;
while ( t < r8 ) {
pow = t[1];
mm = rnext;
while ( mm < r3 ) {
mnext = mm + *mm;
GETSTOP(mm,mstop); mm++;
while ( mm < mstop ) {
if ( *mm != SYMBOL ) mm += mm[1];
else break;
}
if ( *mm == SYMBOL ) {
mstop = mm + mm[1]; mm += 2;
while ( *mm != *t && mm < mstop ) mm += 2;
if ( mm >= mstop ) pow = 0;
else if ( pow > 0 && mm[1] > 0 ) {
if ( mm[1] < pow ) pow = mm[1];
}
else if ( pow < 0 && mm[1] < 0 ) {
if ( mm[1] > pow ) pow = mm[1];
}
else pow = 0;
}
else pow = 0;
if ( pow == 0 ) break;
mm = mnext;
}
if ( pow == 0 ) { t += 2; continue; }
/*
We have a factor
*/
action = 1; i = pow;
if ( i > 0 ) {
while ( --i >= 0 ) {
*r1++ = -SYMBOL;
*r1++ = *t;
}
}
else {
while ( i++ < 0 ) {
*r1++ = 8 + ARGHEAD;
for ( j = 1; j < ARGHEAD; j++ ) *r1++ = 0;
*r1++ = 8; *r1++ = SYMBOL;
*r1++ = 4; *r1++ = *t; *r1++ = -1;
*r1++ = 1; *r1++ = 1; *r1++ = 3;
}
}
/*
Now we have to remove the symbols
*/
t[1] -= pow;
mm = rnext;
while ( mm < r3 ) {
mnext = mm + *mm;
GETSTOP(mm,mstop); mm++;
while ( mm < mstop ) {
if ( *mm != SYMBOL ) mm += mm[1];
else break;
}
mstop = mm + mm[1]; mm += 2;
while ( mm < mstop && *mm != *t ) mm += 2;
mm[1] -= pow;
mm = mnext;
}
t += 2;
}
}
/*
#] SYMBOL :
#[ DOTPRODUCT :
*/
else if ( *t == DOTPRODUCT ) {
r7 = t; r8 = t + t[1]; t += 2;
while ( t < r8 ) {
pow = t[2];
mm = rnext;
while ( mm < r3 ) {
mnext = mm + *mm;
GETSTOP(mm,mstop); mm++;
while ( mm < mstop ) {
if ( *mm != DOTPRODUCT ) mm += mm[1];
else break;
}
if ( *mm == DOTPRODUCT ) {
mstop = mm + mm[1]; mm += 2;
while ( ( *mm != *t || mm[1] != t[1] )
&& mm < mstop ) mm += 3;
if ( mm >= mstop ) pow = 0;
else if ( pow > 0 && mm[2] > 0 ) {
if ( mm[2] < pow ) pow = mm[2];
}
else if ( pow < 0 && mm[2] < 0 ) {
if ( mm[2] > pow ) pow = mm[2];
}
else pow = 0;
}
else pow = 0;
if ( pow == 0 ) break;
mm = mnext;
}
if ( pow == 0 ) { t += 3; continue; }
/*
We have a factor
*/
action = 1; i = pow;
if ( i > 0 ) {
while ( --i >= 0 ) {
*r1++ = 9 + ARGHEAD;
for ( j = 1; j < ARGHEAD; j++ ) *r1++ = 0;
*r1++ = 9; *r1++ = DOTPRODUCT;
*r1++ = 5; *r1++ = *t; *r1++ = t[1]; *r1++ = 1;
*r1++ = 1; *r1++ = 1; *r1++ = 3;
}
}
else {
while ( i++ < 0 ) {
*r1++ = 9 + ARGHEAD;
for ( j = 1; j < ARGHEAD; j++ ) *r1++ = 0;
*r1++ = 9; *r1++ = DOTPRODUCT;
*r1++ = 5; *r1++ = *t; *r1++ = t[1]; *r1++ = -1;
*r1++ = 1; *r1++ = 1; *r1++ = 3;
}
}
/*
Now we have to remove the dotproducts
*/
t[2] -= pow;
mm = rnext;
while ( mm < r3 ) {
mnext = mm + *mm;
GETSTOP(mm,mstop); mm++;
while ( mm < mstop ) {
if ( *mm != DOTPRODUCT ) mm += mm[1];
else break;
}
mstop = mm + mm[1]; mm += 2;
while ( mm < mstop && ( *mm != *t
|| mm[1] != t[1] ) ) mm += 3;
mm[2] -= pow;
mm = mnext;
}
t += 3;
}
}
/*
#] DOTPRODUCT :
#[ DELTA/VECTOR :
*/
else if ( *t == DELTA || *t == VECTOR ) {
r7 = t; r8 = t + t[1]; t += 2;
while ( t < r8 ) {
mm = rnext;
pow = 1;
while ( mm < r3 ) {
mnext = mm + *mm;
GETSTOP(mm,mstop); mm++;
while ( mm < mstop ) {
if ( *mm != *r7 ) mm += mm[1];
else break;
}
if ( *mm == *r7 ) {
mstop = mm + mm[1]; mm += 2;
while ( ( *mm != *t || mm[1] != t[1] )
&& mm < mstop ) mm += 2;
if ( mm >= mstop ) pow = 0;
}
else pow = 0;
if ( pow == 0 ) break;
mm = mnext;
}
if ( pow == 0 ) { t += 2; continue; }
/*
We have a factor
*/
action = 1;
*r1++ = 8 + ARGHEAD;
for ( j = 1; j < ARGHEAD; j++ ) *r1++ = 0;
*r1++ = 8; *r1++ = *r7;
*r1++ = 4; *r1++ = *t; *r1++ = t[1];
*r1++ = 1; *r1++ = 1; *r1++ = 3;
/*
Now we have to remove the delta's/vectors
*/
mm = rnext;
while ( mm < r3 ) {
mnext = mm + *mm;
GETSTOP(mm,mstop); mm++;
while ( mm < mstop ) {
if ( *mm != *r7 ) mm += mm[1];
else break;
}
mstop = mm + mm[1]; mm += 2;
while ( mm < mstop && (
*mm != *t || mm[1] != t[1] ) ) mm += 2;
*mm = mm[1] = NOINDEX;
mm = mnext;
}
*t = t[1] = NOINDEX;
t += 2;
}
}
/*
#] DELTA/VECTOR :
#[ INDEX :
*/
else if ( *t == INDEX ) {
r7 = t; r8 = t + t[1]; t += 2;
while ( t < r8 ) {
mm = rnext;
pow = 1;
while ( mm < r3 ) {
mnext = mm + *mm;
GETSTOP(mm,mstop); mm++;
while ( mm < mstop ) {
if ( *mm != *r7 ) mm += mm[1];
else break;
}
if ( *mm == *r7 ) {
mstop = mm + mm[1]; mm += 2;
while ( *mm != *t
&& mm < mstop ) mm++;
if ( mm >= mstop ) pow = 0;
}
else pow = 0;
if ( pow == 0 ) break;
mm = mnext;
}
if ( pow == 0 ) { t++; continue; }
/*
We have a factor
*/
action = 1;
/*
The next looks like an error.
We should have here a VECTOR or INDEX like object
*r1++ = 7 + ARGHEAD;
for ( j = 1; j < ARGHEAD; j++ ) *r1++ = 0;
*r1++ = 7; *r1++ = *r7;
*r1++ = 3; *r1++ = *t;
*r1++ = 1; *r1++ = 1; *r1++ = 3;
Replace this by: (11-apr-2007)
*/
if ( *t < 0 ) { *r1++ = -VECTOR; }
else { *r1++ = -INDEX; }
*r1++ = *t;
/*
Now we have to remove the index
*/
*t = NOINDEX;
mm = rnext;
while ( mm < r3 ) {
mnext = mm + *mm;
GETSTOP(mm,mstop); mm++;
while ( mm < mstop ) {
if ( *mm != *r7 ) mm += mm[1];
else break;
}
mstop = mm + mm[1]; mm += 2;
while ( mm < mstop &&
*mm != *t ) mm += 1;
*mm = NOINDEX;
mm = mnext;
}
t += 1;
}
}
/*
#] INDEX :
#[ FUNCTION :
*/
else if ( *t >= FUNCTION ) {
/*
In the next code we should actually look inside
the DENOMINATOR or EXPONENT for noncommuting objects
*/
if ( *t >= FUNCTION &&
functions[*t-FUNCTION].commute == 0 ) ncom = 0;
else ncom = 1;
if ( ncom ) {
mm = r5 + 1;
while ( mm < t && ( *mm == DUMMYFUN
|| *mm == DUMMYTEN ) ) mm += mm[1];
if ( mm < t ) { t += t[1]; continue; }
}
mm = rnext; pow = 1;
while ( mm < r3 ) {
mnext = mm + *mm;
GETSTOP(mm,mstop); mm++;
while ( mm < mstop ) {
if ( *mm == *t && mm[1] == t[1] ) {
for ( i = 2; i < t[1]; i++ ) {
if ( mm[i] != t[i] ) break;
}
if ( i >= t[1] )
{ mm += mm[1]; goto nextmterm; }
}
if ( ncom && *mm != DUMMYFUN && *mm != DUMMYTEN )
{ pow = 0; break; }
mm += mm[1];
}
if ( mm >= mstop ) pow = 0;
if ( pow == 0 ) break;
nextmterm: mm = mnext;
}
if ( pow == 0 ) { t += t[1]; continue; }
/*
Copy the function
*/
action = 1;
*r1++ = t[1] + 4 + ARGHEAD;
for ( i = 1; i < ARGHEAD; i++ ) *r1++ = 0;
*r1++ = t[1] + 4;
for ( i = 0; i < t[1]; i++ ) *r1++ = t[i];
*r1++ = 1; *r1++ = 1; *r1++ = 3;
/*
Now we have to take out the functions
*/
mm = rnext;
while ( mm < r3 ) {
mnext = mm + *mm;
GETSTOP(mm,mstop); mm++;
while ( mm < mstop ) {
if ( *mm == *t && mm[1] == t[1] ) {
for ( i = 2; i < t[1]; i++ ) {
if ( mm[i] != t[i] ) break;
}
if ( i >= t[1] ) {
if ( functions[*t-FUNCTION].spec > 0 )
*mm = DUMMYTEN;
else
*mm = DUMMYFUN;
mm += mm[1];
goto nextterm;
}
}
mm += mm[1];
}
nextterm: mm = mnext;
}
if ( functions[*t-FUNCTION].spec > 0 )
*t = DUMMYTEN;
else
*t = DUMMYFUN;
action = 1;
v[2] = DIRTYFLAG;
t += t[1];
}
/*
#] FUNCTION :
*/
else {
t += t[1];
}
}
}
t = r5; pow = 1;
while ( t < r3 ) {
t += *t; if ( t[-1] > 0 ) { pow = 0; break; }
}
/*
We have to add here the code for computing the GCD
and to divide it out.
*/
/*
#[ Numerical factor :
*/
t = r5;
EAscrat = (UWORD *)(TermMalloc("execarg"));
if ( t + *t == r3 ) goto oneterm;
GETSTOP(t,r6);
ngcd = t[t[0]-1];
i = abs(ngcd)-1;
while ( --i >= 0 ) EAscrat[i] = r6[i];
t += *t;
while ( t < r3 ) {
GETSTOP(t,r6);
i = t[t[0]-1];
if ( AccumGCD(BHEAD EAscrat,&ngcd,(UWORD *)r6,i) ) goto execargerr;
if ( ngcd == 3 && EAscrat[0] == 1 && EAscrat[1] == 1 ) break;
t += *t;
}
if ( ngcd != 3 || EAscrat[0] != 1 || EAscrat[1] != 1 ) {
if ( pow ) ngcd = -ngcd;
t = r5; r9 = r1; *r1++ = t[-ARGHEAD]; *r1++ = 1;
FILLARG(r1); ngcd = REDLENG(ngcd);
while ( t < r3 ) {
GETSTOP(t,r6);
r7 = t; r8 = r1;
while ( r7 < r6) *r1++ = *r7++;
t += *t;
i = REDLENG(t[-1]);
if ( DivRat(BHEAD (UWORD *)r6,i,EAscrat,ngcd,(UWORD *)r1,&nq) ) goto execargerr;
nq = INCLENG(nq);
i = ABS(nq)-1;
r1 += i; *r1++ = nq; *r8 = r1-r8;
}
*r9 = r1-r9;
ngcd = INCLENG(ngcd);
i = ABS(ngcd)-1;
if ( factor && *factor == 0 ) {}
else if ( ( factor && factor[0] == 4 && factor[2] == 1
&& factor[3] == -3 ) || pow == 0 ) {
r9 = r1; *r1++ = ARGHEAD+2+i; *r1++ = 0;
FILLARG(r1); *r1++ = i+2;
for ( j = 0; j < i; j++ ) *r1++ = EAscrat[j];
*r1++ = ngcd;
if ( ToFast(r9,r9) ) r1 = r9+2;
}
else if ( factor && factor[0] == 4 && factor[2] == 1
&& factor[3] > 0 && pow ) {
if ( ngcd < 0 ) ngcd = -ngcd;
*r1++ = -SNUMBER; *r1++ = -1;
r9 = r1; *r1++ = ARGHEAD+2+i; *r1++ = 0;
FILLARG(r1); *r1++ = i+2;
for ( j = 0; j < i; j++ ) *r1++ = EAscrat[j];
*r1++ = ngcd;
if ( ToFast(r9,r9) ) r1 = r9+2;
}
else {
if ( ngcd < 0 ) ngcd = -ngcd;
if ( pow ) { *r1++ = -SNUMBER; *r1++ = -1; }
if ( ngcd != 3 || EAscrat[0] != 1 || EAscrat[1] != 1 ) {
r9 = r1; *r1++ = ARGHEAD+2+i; *r1++ = 0;
FILLARG(r1); *r1++ = i+2;
for ( j = 0; j < i; j++ ) *r1++ = EAscrat[j];
*r1++ = ngcd;
if ( ToFast(r9,r9) ) r1 = r9+2;
}
}
}
/*
#] Numerical factor :
*/
else {
oneterm:;
if ( factor == 0 || *factor > 2 ) {
if ( pow > 0 ) {
*r1++ = -SNUMBER; *r1++ = -1;
t = r5;
while ( t < r3 ) {
t += *t; t[-1] = -t[-1];
}
}
t = rr; *r1++ = *t++; *r1++ = 1; t++;
COPYARG(r1,t);
while ( t < m ) *r1++ = *t++;
}
}
TermFree(EAscrat,"execarg");
}
} /* AC.OldFactArgFlag */
}
r2[1] = r1 - r2;
action = 1;
v[2] = DIRTYFLAG;
}
else {
r = t + t[1];
while ( t < r ) *r1++ = *t++;
}
}
r = term + *term;
while ( t < r ) *r1++ = *t++;
m = AT.WorkPointer;
i = m[0] = r1 - m;
t = term;
while ( --i >= 0 ) *t++ = *m++;
if ( AT.WorkPointer < t ) AT.WorkPointer = t;
}
/*
#] FACTARG :
*/
AR.Cnumlhs = oldnumlhs;
if ( action && Normalize(BHEAD term) ) goto execargerr;
AT.WorkPointer = oldwork;
if ( AT.WorkPointer < term + *term ) AT.WorkPointer = term + *term;
AT.pWorkPointer = oldppointer;
if ( GCDbuffer ) NumberFree(GCDbuffer,"execarg");
return(action);
execargerr:
AT.WorkPointer = oldwork;
AT.pWorkPointer = oldppointer;
MLOCK(ErrorMessageLock);
MesCall("execarg");
MUNLOCK(ErrorMessageLock);
return(-1);
}
/*
#] execarg :
#[ execterm :
*/
WORD execterm(PHEAD WORD *term, WORD level)
{
GETBIDENTITY
CBUF *C = cbuf+AM.rbufnum;
WORD oldnumlhs = AR.Cnumlhs;
WORD maxisat = C->lhs[level][2];
WORD *buffer1 = 0;
WORD *oldworkpointer = AT.WorkPointer;
WORD *t1, i;
WORD olddeferflag = AR.DeferFlag, tryterm = 0;
AR.DeferFlag = 0;
do {
AR.Cnumlhs = C->lhs[level][3];
NewSort(BHEAD0);
/*
Normally for function arguments we do not use PolyFun/PolyRatFun.
Hence NewSort sets the corresponding variables to zero.
Here we overwrite that.
*/
AN.FunSorts[AR.sLevel]->PolyFlag = ( AR.PolyFun != 0 ) ? AR.PolyFunType: 0;
if ( AR.PolyFun == 0 ) { AN.FunSorts[AR.sLevel]->PolyFlag = 0; }
else if ( AR.PolyFunType == 1 ) { AN.FunSorts[AR.sLevel]->PolyFlag = 1; }
else if ( AR.PolyFunType == 2 ) {
if ( AR.PolyFunExp == 2 ) AN.FunSorts[AR.sLevel]->PolyFlag = 1;
else AN.FunSorts[AR.sLevel]->PolyFlag = 2;
}
if ( buffer1 ) {
term = buffer1;
while ( *term ) {
t1 = oldworkpointer;
i = *term; while ( --i >= 0 ) *t1++ = *term++;
AT.WorkPointer = t1;
if ( Generator(BHEAD oldworkpointer,level) ) goto exectermerr;
}
}
else {
if ( Generator(BHEAD term,level) ) goto exectermerr;
}
if ( buffer1 ) {
if ( tryterm ) { TermFree(buffer1,"buffer in sort statement"); tryterm = 0; }
else { M_free((void *)buffer1,"buffer in sort statement"); }
buffer1 = 0;
}
AN.tryterm = 1;
if ( EndSort(BHEAD (WORD *)((VOID *)(&buffer1)),2) < 0 ) goto exectermerr;
tryterm = AN.tryterm; AN.tryterm = 0;
level = AR.Cnumlhs;
} while ( AR.Cnumlhs < maxisat );
AR.Cnumlhs = oldnumlhs;
AR.DeferFlag = olddeferflag;
term = buffer1;
while ( *term ) {
t1 = oldworkpointer;
i = *term; while ( --i >= 0 ) *t1++ = *term++;
AT.WorkPointer = t1;
if ( Generator(BHEAD oldworkpointer,level) ) goto exectermerr;
}
if ( tryterm ) { TermFree(buffer1,"buffer in term statement"); tryterm = 0; }
else { M_free(buffer1,"buffer in term statement"); }
buffer1 = 0;
AT.WorkPointer = oldworkpointer;
return(0);
exectermerr:
AT.WorkPointer = oldworkpointer;
AR.DeferFlag = olddeferflag;
MLOCK(ErrorMessageLock);
MesCall("execterm");
MUNLOCK(ErrorMessageLock);
return(-1);
}
/*
#] execterm :
#[ ArgumentImplode :
*/
int ArgumentImplode(PHEAD WORD *term, WORD *thelist)
{
GETBIDENTITY
WORD *liststart, *liststop, *inlist;
WORD *w, *t, *tend, *tstop, *tt, *ttstop, *ttt, ncount, i;
int action = 0;
liststop = thelist + thelist[1];
liststart = thelist + 2;
t = term;
tend = t + *t;
tstop = tend - ABS(tend[-1]);
t++;
while ( t < tstop ) {
if ( *t >= FUNCTION ) {
inlist = liststart;
while ( inlist < liststop && *inlist != *t ) inlist += inlist[1];
if ( inlist < liststop ) {
tt = t; ttstop = t + t[1]; w = AT.WorkPointer;
for ( i = 0; i < FUNHEAD; i++ ) *w++ = *tt++;
while ( tt < ttstop ) {
ncount = 0;
if ( *tt == -SNUMBER && tt[1] == 0 ) {
ncount = 1; ttt = tt; tt += 2;
while ( tt < ttstop && *tt == -SNUMBER && tt[1] == 0 ) {
ncount++; tt += 2;
}
}
if ( ncount > 0 ) {
if ( tt < ttstop && *tt == -SNUMBER && ( tt[1] == 1 || tt[1] == -1 ) ) {
*w++ = -SNUMBER;
*w++ = (ncount+1) * tt[1];
tt += 2;
action = 1;
}
else if ( ( tt[0] == tt[ARGHEAD] + ARGHEAD )
&& ( ABS(tt[tt[0]-1]) == 3 )
&& ( tt[tt[0]-2] == 1 )
&& ( tt[tt[0]-3] == 1 ) ) { /* Single term with coef +/- 1 */
i = *tt; NCOPY(w,tt,i)
w[-3] = ncount+1;
action = 1;
}
else if ( *tt == -SYMBOL ) {
*w++ = ARGHEAD+8;
*w++ = 0;
FILLARG(w)
*w++ = 8;
*w++ = SYMBOL;
*w++ = tt[1];
*w++ = 1;
*w++ = ncount+1; *w++ = 1; *w++ = 3;
tt += 2;
action = 1;
}
else if ( *tt <= -FUNCTION ) {
*w++ = ARGHEAD+FUNHEAD+4;
*w++ = 0;
FILLARG(w)
*w++ = -*tt++;
*w++ = FUNHEAD+4;
FILLFUN(w)
*w++ = ncount+1; *w++ = 1; *w++ = 3;
action = 1;
}
else {
while ( ttt < tt ) *w++ = *ttt++;
if ( tt < ttstop && *tt == -SNUMBER ) {
*w++ = *tt++; *w++ = *tt++;
}
}
}
else if ( *tt <= -FUNCTION ) {
*w++ = *tt++;
}
else if ( *tt < 0 ) {
*w++ = *tt++;
*w++ = *tt++;
}
else {
i = *tt; NCOPY(w,tt,i)
}
}
AT.WorkPointer[1] = w - AT.WorkPointer;
while ( tt < tend ) *w++ = *tt++;
ttt = AT.WorkPointer; tt = t;
while ( ttt < w ) *tt++ = *ttt++;
term[0] = tt - term;
AT.WorkPointer = tt;
tend = tt; tstop = tt - ABS(tt[-1]);
}
}
t += t[1];
}
if ( action ) {
if ( Normalize(BHEAD term) ) return(-1);
}
return(0);
}
/*
#] ArgumentImplode :
#[ ArgumentExplode :
*/
int ArgumentExplode(PHEAD WORD *term, WORD *thelist)
{
GETBIDENTITY
WORD *liststart, *liststop, *inlist, *old;
WORD *w, *t, *tend, *tstop, *tt, *ttstop, *ttt, ncount, i;
int action = 0;
LONG x;
liststop = thelist + thelist[1];
liststart = thelist + 2;
t = term;
tend = t + *t;
tstop = tend - ABS(tend[-1]);
t++;
while ( t < tstop ) {
if ( *t >= FUNCTION ) {
inlist = liststart;
while ( inlist < liststop && *inlist != *t ) inlist += inlist[1];
if ( inlist < liststop ) {
tt = t; ttstop = t + t[1]; w = AT.WorkPointer;
for ( i = 0; i < FUNHEAD; i++ ) *w++ = *tt++;
while ( tt < ttstop ) {
if ( *tt == -SNUMBER && tt[1] != 0 ) {
if ( tt[1] < AM.MaxTer/((WORD)sizeof(WORD)*4)
&& tt[1] > -(AM.MaxTer/((WORD)sizeof(WORD)*4))
&& ( tt[1] > 1 || tt[1] < -1 ) ) {
ncount = ABS(tt[1]);
while ( ncount > 1 ) {
*w++ = -SNUMBER; *w++ = 0; ncount--;
}
*w++ = -SNUMBER;
if ( tt[1] < 0 ) *w++ = -1;
else *w++ = 1;
tt += 2;
action = 1;
}
else {
*w++ = *tt++; *w++ = *tt++;
}
}
else if ( *tt <= -FUNCTION ) {
*w++ = *tt++;
}
else if ( *tt < 0 ) {
*w++ = *tt++;
*w++ = *tt++;
}
else if ( tt[0] == tt[ARGHEAD]+ARGHEAD ) {
ttt = tt + tt[0] - 1;
i = (ABS(ttt[0])-1)/2;
if ( i > 1 ) {
TooMany: old = AN.currentTerm;
AN.currentTerm = term;
MesPrint("Too many arguments in output of ArgExplode");
MesPrint("Term = %t");
AN.currentTerm = old;
return(-1);
}
if ( ttt[-1] != 1 ) goto NoExplode;
x = ttt[-2];
if ( 2*x > (AT.WorkTop-w)-*term ) goto TooMany;
ncount = x - 1;
while ( ncount > 0 ) {
*w++ = -SNUMBER; *w++ = 0; ncount--;
}
ttt[-2] = 1;
i = *tt; NCOPY(w,tt,i)
action = 1;
}
else {
NoExplode:
i = *tt; NCOPY(w,tt,i)
}
}
AT.WorkPointer[1] = w - AT.WorkPointer;
while ( tt < tend ) *w++ = *tt++;
ttt = AT.WorkPointer; tt = t;
while ( ttt < w ) *tt++ = *ttt++;
term[0] = tt - term;
AT.WorkPointer = tt;
tend = tt; tstop = tt - ABS(tt[-1]);
}
}
t += t[1];
}
if ( action ) {
if ( Normalize(BHEAD term) ) return(-1);
}
return(0);
}
/*
#] ArgumentExplode :
#[ ArgFactorize :
*/
/**
* Factorizes an argument in general notation (meaning that the first
* word of the argument is a positive size indicator)
* Input (argin): pointer to the complete argument
* Output (argout): Pointer to where the output should be written.
* This is in the WorkSpace
* Return value should be negative if anything goes wrong.
*
* The notation of the output should be a string of arguments terminated
* by the number zero.
*
* Originally we sorted in a way that the constants came last. This gave
* conflicts with the dollar and expression factorizations (in the expressions
* we wanted the zero first and then followed by the constants).
*/
#define NEWORDER
int ArgFactorize(PHEAD WORD *argin, WORD *argout)
{
/*
#[ step 0 : Declarations and initializations
*/
WORD *argfree, *argextra, *argcopy, *t, *tstop, *a, *a1, *a2;
#ifdef NEWORDER
WORD *tt;
#endif
WORD startebuf = cbuf[AT.ebufnum].numrhs,oldword;
WORD oldsorttype = AR.SortType, numargs;
int error = 0, action = 0, i, ii, number, sign = 1;
*argout = 0;
/*
#] step 0 :
#[ step 1 : Take care of ordering
*/
AR.SortType = SORTHIGHFIRST;
if ( oldsorttype != AR.SortType ) {
NewSort(BHEAD0);
oldword = argin[*argin]; argin[*argin] = 0;
t = argin+ARGHEAD;
while ( *t ) {
tstop = t + *t;
if ( AN.ncmod != 0 ) {
if ( AN.ncmod != 1 || ( (WORD)AN.cmod[0] < 0 ) ) {
MLOCK(ErrorMessageLock);
MesPrint("Factorization modulus a number, greater than a WORD not implemented.");
MUNLOCK(ErrorMessageLock);
Terminate(-1);
}
if ( Modulus(t) ) {
MLOCK(ErrorMessageLock);
MesCall("ArgFactorize");
MUNLOCK(ErrorMessageLock);
Terminate(-1);
}
if ( !*t) { t = tstop; continue; }
}
StoreTerm(BHEAD t);
t = tstop;
}
EndSort(BHEAD argin+ARGHEAD,0);
argin[*argin] = oldword;
}
/*
#] step 1 :
#[ step 2 : take out the 'content'.
*/
argfree = TakeArgContent(BHEAD argin,argout);
{
a1 = argout;
while ( *a1 ) {
if ( a1[0] == -SNUMBER && ( a1[1] == 1 || a1[1] == -1 ) ) {
if ( a1[1] == -1 ) { sign = -sign; a1[1] = 1; }
if ( a1[2] ) {
a = t = a1+2; while ( *t ) NEXTARG(t);
i = t - a1-2;
t = a1; NCOPY(t,a,i);
*t = 0;
continue;
}
else {
a1[0] = 0;
}
break;
}
else if ( a1[0] == FUNHEAD+ARGHEAD+4 && a1[ARGHEAD] == FUNHEAD+4
&& a1[*a1-1] == 3 && a1[*a1-2] == 1 && a1[*a1-3] == 1
&& a1[ARGHEAD+1] >= FUNCTION ) {
a = t = a1+*a1; while ( *t ) NEXTARG(t);
i = t - a;
*a1 = -a1[ARGHEAD+1]; t = a1+1; NCOPY(t,a,i);
*t = 0;
}
NEXTARG(a1);
}
}
if ( argfree == 0 ) {
argfree = argin;
}
else if ( argfree[0] == ( argfree[ARGHEAD]+ARGHEAD ) ) {
Normalize(BHEAD argfree+ARGHEAD);
argfree[0] = argfree[ARGHEAD]+ARGHEAD;
argfree[1] = 0;
if ( ( argfree[0] == ARGHEAD+4 ) && ( argfree[ARGHEAD+3] == 3 )
&& ( argfree[ARGHEAD+1] == 1 ) && ( argfree[ARGHEAD+2] == 1 ) ) {
goto return0;
}
}
else {
/*
The way we took out objects is rather brutish. We have to
normalize
*/
NewSort(BHEAD0);
t = argfree+ARGHEAD;
while ( *t ) {
tstop = t + *t;
Normalize(BHEAD t);
StoreTerm(BHEAD t);
t = tstop;
}
EndSort(BHEAD argfree+ARGHEAD,0);
t = argfree+ARGHEAD;
while ( *t ) t += *t;
*argfree = t - argfree;
}
/*
#] step 2 :
#[ step 3 : look whether we have done this one already.
*/
if ( ( number = FindArg(BHEAD argfree) ) != 0 ) {
if ( number > 0 ) t = cbuf[AT.fbufnum].rhs[number-1];
else t = cbuf[AC.ffbufnum].rhs[-number-1];
/*
Now position on the result. Remember we have in the cache:
inputarg,0,outputargs,0
t is currently at inputarg. *inputarg is always positive.
in principle this holds also for the arguments in the output
but we take no risks here (in case of future developments).
*/
t += *t; t++;
tstop = t;
ii = 0;
while ( *tstop ) {
if ( *tstop == -SNUMBER && tstop[1] == -1 ) {
sign = -sign; ii += 2;
}
NEXTARG(tstop);
}
a = argout; while ( *a ) NEXTARG(a);
#ifndef NEWORDER
if ( sign == -1 ) { *a++ = -SNUMBER; *a++ = -1; *a = 0; sign = 1; }
#endif
i = tstop - t - ii;
ii = a - argout;
a2 = a; a1 = a + i;
*a1 = 0;
while ( ii > 0 ) { *--a1 = *--a2; ii--; }
a = argout;
while ( *t ) {
if ( *t == -SNUMBER && t[1] == -1 ) { t += 2; }
else { COPY1ARG(a,t) }
}
goto return0;
}
/*
#] step 3 :
#[ step 4 : invoke ConvertToPoly
We make a copy first in case there are no factors
*/
argcopy = TermMalloc("argcopy");
for ( i = 0; i <= *argfree; i++ ) argcopy[i] = argfree[i];
tstop = argfree + *argfree;
{
WORD sumcommu = 0;
t = argfree + ARGHEAD;
while ( t < tstop ) {
sumcommu += DoesCommu(t);
t += *t;
}
if ( sumcommu > 1 ) {
MesPrint("ERROR: Cannot factorize an argument with more than one noncommuting object");
Terminate(-1);
}
}
t = argfree + ARGHEAD;
while ( t < tstop ) {
if ( ( t[1] != SYMBOL ) && ( *t != (ABS(t[*t-1])+1) ) ) {
action = 1; break;
}
t += *t;
}
if ( action ) {
t = argfree + ARGHEAD;
argextra = AT.WorkPointer;
NewSort(BHEAD0);
while ( t < tstop ) {
if ( LocalConvertToPoly(BHEAD t,argextra,startebuf,0) < 0 ) {
error = -1;
getout:
AR.SortType = oldsorttype;
TermFree(argcopy,"argcopy");
if ( argfree != argin ) TermFree(argfree,"argfree");
MesCall("ArgFactorize");
Terminate(-1);
return(-1);
}
StoreTerm(BHEAD argextra);
t += *t; argextra += *argextra;
}
if ( EndSort(BHEAD argfree+ARGHEAD,0) ) { error = -2; goto getout; }
t = argfree + ARGHEAD;
while ( *t > 0 ) t += *t;
*argfree = t - argfree;
}
/*
#] step 4 :
#[ step 5 : If not in the tables, we have to do this by hard work.
*/
a = argout;
while ( *a ) NEXTARG(a);
if ( poly_factorize_argument(BHEAD argfree,a) < 0 ) {
MesCall("ArgFactorize");
error = -1;
}
/*
#] step 5 :
#[ step 6 : use now ConvertFromPoly
Be careful: there should be more than one argument now.
*/
if ( error == 0 && action ) {
a1 = a; NEXTARG(a1);
if ( *a1 != 0 ) {
CBUF *C = cbuf+AC.cbufnum;
CBUF *CC = cbuf+AT.ebufnum;
WORD *oldworkpointer = AT.WorkPointer;
WORD *argcopy2 = TermMalloc("argcopy2"), *a1, *a2;
a1 = a; a2 = argcopy2;
while ( *a1 ) {
if ( *a1 < 0 ) {
if ( *a1 > -FUNCTION ) *a2++ = *a1++;
*a2++ = *a1++; *a2 = 0;
continue;
}
t = a1 + ARGHEAD;
tstop = a1 + *a1;
argextra = AT.WorkPointer;
NewSort(BHEAD0);
while ( t < tstop ) {
if ( ConvertFromPoly(BHEAD t,argextra,numxsymbol,CC->numrhs-startebuf+numxsymbol
,startebuf-numxsymbol,1) <= 0 ) {
TermFree(argcopy2,"argcopy2");
LowerSortLevel();
error = -3;
goto getout;
}
t += *t;
AT.WorkPointer = argextra + *argextra;
/*
ConvertFromPoly leaves terms with subexpressions. Hence:
*/
if ( Generator(BHEAD argextra,C->numlhs) ) {
TermFree(argcopy2,"argcopy2");
LowerSortLevel();
error = -4;
goto getout;
}
}
AT.WorkPointer = oldworkpointer;
if ( EndSort(BHEAD a2+ARGHEAD,0) ) { error = -5; goto getout; }
t = a2+ARGHEAD; while ( *t ) t += *t;
*a2 = t - a2; a2[1] = 0; ZEROARG(a2);
ToFast(a2,a2); NEXTARG(a2);
a1 = tstop;
}
i = a2 - argcopy2;
a2 = argcopy2; a1 = a;
NCOPY(a1,a2,i);
*a1 = 0;
TermFree(argcopy2,"argcopy2");
/*
Erase the entries we made temporarily in cbuf[AT.ebufnum]
*/
CC->numrhs = startebuf;
}
else { /* no factorization. recover the argument from before step 3. */
for ( i = 0; i <= *argcopy; i++ ) a[i] = argcopy[i];
}
}
/*
#] step 6 :
#[ step 7 : Add this one to the tables.
Possibly drop some elements in the tables
when they become too full.
*/
if ( error == 0 && AN.ncmod == 0 ) {
if ( InsertArg(BHEAD argcopy,a,0) < 0 ) { error = -1; }
}
/*
#] step 7 :
#[ step 8 : Clean up and return.
Change the order of the arguments in argout and a.
Use argcopy as spare space.
*/
ii = a - argout;
for ( i = 0; i < ii; i++ ) argcopy[i] = argout[i];
a1 = a;
while ( *a1 ) {
if ( *a1 == -SNUMBER && a1[1] < 0 ) {
sign = -sign; a1[1] = -a1[1];
if ( a1[1] == 1 ) {
a2 = a1+2; while ( *a2 ) NEXTARG(a2);
i = a2-a1-2; a2 = a1+2;
NCOPY(a1,a2,i);
*a1 = 0;
}
while ( *a1 ) NEXTARG(a1);
break;
}
else {
if ( *a1 > 0 && *a1 == a1[ARGHEAD]+ARGHEAD && a1[*a1-1] < 0 ) {
a1[*a1-1] = -a1[*a1-1]; sign = -sign;
}
if ( *a1 == ARGHEAD+4 && a1[ARGHEAD+1] == 1 && a1[ARGHEAD+2] == 1 ) {
a2 = a1+ARGHEAD+4; while ( *a2 ) NEXTARG(a2);
i = a2-a1-ARGHEAD-4; a2 = a1+ARGHEAD+4;
NCOPY(a1,a2,i);
*a1 = 0;
break;
}
while ( *a1 ) NEXTARG(a1);
break;
}
NEXTARG(a1);
}
i = a1 - a;
a2 = argout;
NCOPY(a2,a,i);
for ( i = 0; i < ii; i++ ) *a2++ = argcopy[i];
#ifndef NEWORDER
if ( sign == -1 ) { *a2++ = -SNUMBER; *a2++ = -1; sign = 1; }
#endif
*a2 = 0;
TermFree(argcopy,"argcopy");
return0:
if ( argfree != argin ) TermFree(argfree,"argfree");
if ( oldsorttype != AR.SortType ) {
AR.SortType = oldsorttype;
a = argout;
while ( *a ) {
if ( *a > 0 ) {
NewSort(BHEAD0);
oldword = a[*a]; a[*a] = 0;
t = a+ARGHEAD;
while ( *t ) {
tstop = t + *t;
StoreTerm(BHEAD t);
t = tstop;
}
EndSort(BHEAD a+ARGHEAD,0);
a[*a] = oldword;
a += *a;
}
else { NEXTARG(a); }
}
}
#ifdef NEWORDER
t = argout; numargs = 0;
while ( *t ) {
tt = t;
NEXTARG(t);
if ( *tt == ABS(t[-1])+1+ARGHEAD && sign == -1 ) { t[-1] = -t[-1]; sign = 1; }
else if ( *tt == -SNUMBER && sign == -1 ) { tt[1] = -tt[1]; sign = 1; }
numargs++;
}
if ( sign == -1 ) {
*t++ = -SNUMBER; *t++ = -1; *t = 0; sign = 1; numargs++;
}
#else
/*
Now we have to sort the arguments
First have the number of 'nontrivial/nonnumerical' arguments
Then make a piece of code like in FullSymmetrize with that number
of arguments to be symmetrized.
Put a function in front
Call the Symmetrize routine
*/
t = argout; numargs = 0;
while ( *t && *t != -SNUMBER && ( *t < 0 || ( ABS(t[*t-1]) != *t-1 ) ) ) {
NEXTARG(t);
numargs++;
}
#endif
if ( numargs > 1 ) {
WORD *Lijst;
WORD x[3];
x[0] = argout[-FUNHEAD];
x[1] = argout[-FUNHEAD+1];
x[2] = argout[-FUNHEAD+2];
while ( *t ) { NEXTARG(t); }
argout[-FUNHEAD] = SQRTFUNCTION;
argout[-FUNHEAD+1] = t-argout+FUNHEAD;
argout[-FUNHEAD+2] = 0;
AT.WorkPointer = t+1;
Lijst = AT.WorkPointer;
for ( i = 0; i < numargs; i++ ) Lijst[i] = i;
AT.WorkPointer += numargs;
error = Symmetrize(BHEAD argout-FUNHEAD,Lijst,numargs,1,SYMMETRIC);
AT.WorkPointer = Lijst;
argout[-FUNHEAD] = x[0];
argout[-FUNHEAD+1] = x[1];
argout[-FUNHEAD+2] = x[2];
#ifdef NEWORDER
/*
Now we have to get a potential numerical argument to the first position
*/
tstop = argout; while ( *tstop ) { NEXTARG(tstop); }
t = argout; number = 0;
while ( *t ) {
tt = t; NEXTARG(t);
if ( *tt == -SNUMBER ) {
if ( number == 0 ) break;
x[0] = tt[1];
while ( tt > argout ) { *--t = *--tt; }
argout[0] = -SNUMBER; argout[1] = x[0];
break;
}
else if ( *tt == ABS(t[-1])+1+ARGHEAD ) {
if ( number == 0 ) break;
ii = t - tt;
for ( i = 0; i < ii; i++ ) tstop[i] = tt[i];
while ( tt > argout ) { *--t = *--tt; }
for ( i = 0; i < ii; i++ ) argout[i] = tstop[i];
*tstop = 0;
break;
}
number++;
}
#endif
}
/*
#] step 8 :
*/
return(error);
}
/*
#] ArgFactorize :
#[ FindArg :
*/
/**
* Looks the argument up in the (workers) table.
* If it is found the number in the table is returned (plus one to make it positive).
* If it is not found we look in the compiler provided table.
* If it is found - the number in the table is returned (minus one to make it negative).
* If in neither table we return zero.
*/
WORD FindArg(PHEAD WORD *a)
{
int number;
if ( AN.ncmod != 0 ) return(0); /* no room for mod stuff */
number = FindTree(AT.fbufnum,a);
if ( number >= 0 ) return(number+1);
number = FindTree(AC.ffbufnum,a);
if ( number >= 0 ) return(-number-1);
return(0);
}
/*
#] FindArg :
#[ InsertArg :
*/
/**
* Inserts the argument into the (workers) table.
* If the table is too full we eliminate half of it.
* The eliminated elements are the ones that have not been used
* most recently, weighted by their total use and age(?).
* If par == 0 it inserts in the regular factorization cache
* If par == 1 it inserts in the cache defined with the FactorCache statement
*/
WORD InsertArg(PHEAD WORD *argin, WORD *argout,int par)
{
CBUF *C;
WORD *a, i, bufnum;
if ( par == 0 ) {
bufnum = AT.fbufnum;
C = cbuf+bufnum;
if ( C->numrhs >= (C->maxrhs-2) ) CleanupArgCache(BHEAD AT.fbufnum);
}
else if ( par == 1 ) {
bufnum = AC.ffbufnum;
C = cbuf+bufnum;
}
else { return(-1); }
AddRHS(bufnum,1);
AddNtoC(bufnum,*argin,argin,1);
AddToCB(C,0)
a = argout; while ( *a ) NEXTARG(a);
i = a - argout;
AddNtoC(bufnum,i,argout,2);
AddToCB(C,0)
return(InsTree(bufnum,C->numrhs));
}
/*
#] InsertArg :
#[ CleanupArgCache :
*/
/**
* Cleans up the argument factorization cache.
* We throw half the elements.
* For a weight of what we want to keep we use the product of
* usage and the number in the buffer.
*/
int CleanupArgCache(PHEAD WORD bufnum)
{
CBUF *C = cbuf + bufnum;
COMPTREE *boomlijst = C->boomlijst;
LONG *weights = (LONG *)Malloc1(2*(C->numrhs+1)*sizeof(LONG),"CleanupArgCache");
LONG w, whalf, *extraweights;
WORD *a, *to, *from;
int i,j,k;
for ( i = 1; i <= C->numrhs; i++ ) {
weights[i] = ((LONG)i) * (boomlijst[i].usage);
}
/*
Now sort the weights and determine the halfway weight
*/
extraweights = weights+C->numrhs+1;
SortWeights(weights+1,extraweights,C->numrhs);
whalf = weights[C->numrhs/2+1];
/*
We should drop everybody with a weight < whalf.
*/
to = C->Buffer;
k = 1;
for ( i = 1; i <= C->numrhs; i++ ) {
from = C->rhs[i]; w = ((LONG)i) * (boomlijst[i].usage);
if ( w >= whalf ) {
if ( i < C->numrhs-1 ) {
if ( to == from ) {
to = C->rhs[i+1];
}
else {
j = C->rhs[i+1] - from;
C->rhs[k] = to;
NCOPY(to,from,j)
}
}
else if ( to == from ) {
to += *to + 1; while ( *to ) NEXTARG(to); to++;
}
else {
a = from; a += *a+1; while ( *a ) NEXTARG(a); a++;
j = a - from;
C->rhs[k] = to;
NCOPY(to,from,j)
}
weights[k++] = boomlijst[i].usage;
}
}
C->numrhs = --k;
C->Pointer = to;
/*
Next we need to rebuild the tree.
Note that this can probably be done much faster by using the
remains of the old tree !!!!!!!!!!!!!!!!
*/
ClearTree(AT.fbufnum);
for ( i = 1; i <= k; i++ ) {
InsTree(AT.fbufnum,i);
boomlijst[i].usage = weights[i];
}
/*
And cleanup
*/
M_free(weights,"CleanupArgCache");
return(0);
}
/*
#] CleanupArgCache :
#[ ArgSymbolMerge :
*/
int ArgSymbolMerge(WORD *t1, WORD *t2)
{
WORD *t1e = t1+t1[1];
WORD *t2e = t2+t2[1];
WORD *t1a = t1+2;
WORD *t2a = t2+2;
WORD *t3;
while ( t1a < t1e && t2a < t2e ) {
if ( *t1a < *t2a ) {
if ( t1a[1] >= 0 ) {
t3 = t1a+2;
while ( t3 < t1e ) { t3[-2] = *t3; t3[-1] = t3[1]; t3 += 2; }
t1e -= 2;
}
else t1a += 2;
}
else if ( *t1a > *t2a ) {
if ( t2a[1] >= 0 ) t2a += 2;
else {
t3 = t1e;
while ( t3 > t1a ) { *t3 = t3[-2]; t3[1] = t3[-1]; t3 -= 2; }
*t1a++ = *t2a++;
*t1a++ = *t2a++;
t1e += 2;
}
}
else {
if ( t2a[1] < t1a[1] ) t1a[1] = t2a[1];
t1a += 2; t2a += 2;
}
}
while ( t2a < t2e ) {
if ( t2a[1] < 0 ) {
*t1a++ = *t2a++;
*t1a++ = *t2a++;
}
else t2a += 2;
}
while ( t1a < t1e ) {
if ( t1a[1] >= 0 ) {
t3 = t1a+2;
while ( t3 < t1e ) { t3[-2] = *t3; t3[-1] = t3[1]; t3 += 2; }
t1e -= 2;
}
else t1a += 2;
}
t1[1] = t1a - t1;
return(0);
}
/*
#] ArgSymbolMerge :
#[ ArgDotproductMerge :
*/
int ArgDotproductMerge(WORD *t1, WORD *t2)
{
WORD *t1e = t1+t1[1];
WORD *t2e = t2+t2[1];
WORD *t1a = t1+2;
WORD *t2a = t2+2;
WORD *t3;
while ( t1a < t1e && t2a < t2e ) {
if ( *t1a < *t2a || ( *t1a == *t2a && t1a[1] < t2a[1] ) ) {
if ( t1a[2] >= 0 ) {
t3 = t1a+3;
while ( t3 < t1e ) { t3[-3] = *t3; t3[-2] = t3[1]; t3[-1] = t3[2]; t3 += 3; }
t1e -= 3;
}
else t1a += 3;
}
else if ( *t1a > *t2a || ( *t1a == *t2a && t1a[1] > t2a[1] ) ) {
if ( t2a[2] >= 0 ) t2a += 3;
else {
t3 = t1e;
while ( t3 > t1a ) { *t3 = t3[-3]; t3[1] = t3[-2]; t3[2] = t3[-1]; t3 -= 3; }
*t1a++ = *t2a++;
*t1a++ = *t2a++;
*t1a++ = *t2a++;
t1e += 3;
}
}
else {
if ( t2a[2] < t1a[2] ) t1a[2] = t2a[2];
t1a += 3; t2a += 3;
}
}
while ( t2a < t2e ) {
if ( t2a[2] < 0 ) {
*t1a++ = *t2a++;
*t1a++ = *t2a++;
*t1a++ = *t2a++;
}
else t2a += 3;
}
while ( t1a < t1e ) {
if ( t1a[2] >= 0 ) {
t3 = t1a+3;
while ( t3 < t1e ) { t3[-3] = *t3; t3[-2] = t3[1]; t3[-1] = t3[2]; t3 += 3; }
t1e -= 3;
}
else t1a += 2;
}
t1[1] = t1a - t1;
return(0);
}
/*
#] ArgDotproductMerge :
#[ TakeArgContent :
*/
/**
* Implements part of the old ExecArg in which we take common factors
* from arguments with more than one term.
* The common pieces are put in argout as a sequence of arguments.
* The part with the multiple terms that are now relative prime is
* put in argfree which is allocated via TermMalloc and is given as the
* return value.
* The difference with the old code is that negative powers are always
* removed. Hence it is as in MakeInteger in which only numerators will
* be left: now only zero or positive powers will be remaining.
*/
WORD *TakeArgContent(PHEAD WORD *argin, WORD *argout)
{
GETBIDENTITY
WORD *t, *rnext, *r1, *r2, *r3, *r5, *r6, *r7, *r8, *r9;
WORD pow, *mm, *mnext, *mstop, *argin2 = argin, *argin3 = argin, *argfree;
WORD ncom;
int j, i, act;
r5 = t = argin + ARGHEAD;
r3 = argin + *argin;
rnext = t + *t;
GETSTOP(t,r6);
r1 = argout;
t++;
/*
First pass: arrange everything but the symbols and dotproducts.
They need separate treatment because we have to take out negative
powers.
*/
while ( t < r6 ) {
/*
#[ DELTA/VECTOR :
*/
if ( *t == DELTA || *t == VECTOR ) {
r7 = t; r8 = t + t[1]; t += 2;
while ( t < r8 ) {
mm = rnext;
pow = 1;
while ( mm < r3 ) {
mnext = mm + *mm;
GETSTOP(mm,mstop); mm++;
while ( mm < mstop ) {
if ( *mm != *r7 ) mm += mm[1];
else break;
}
if ( *mm == *r7 ) {
mstop = mm + mm[1]; mm += 2;
while ( ( *mm != *t || mm[1] != t[1] )
&& mm < mstop ) mm += 2;
if ( mm >= mstop ) pow = 0;
}
else pow = 0;
if ( pow == 0 ) break;
mm = mnext;
}
if ( pow == 0 ) { t += 2; continue; }
/*
We have a factor
*/
*r1++ = 8 + ARGHEAD;
for ( j = 1; j < ARGHEAD; j++ ) *r1++ = 0;
*r1++ = 8; *r1++ = *r7;
*r1++ = 4; *r1++ = *t; *r1++ = t[1];
*r1++ = 1; *r1++ = 1; *r1++ = 3;
argout = r1;
/*
Now we have to remove the delta's/vectors
*/
mm = rnext;
while ( mm < r3 ) {
mnext = mm + *mm;
GETSTOP(mm,mstop); mm++;
while ( mm < mstop ) {
if ( *mm != *r7 ) mm += mm[1];
else break;
}
mstop = mm + mm[1]; mm += 2;
while ( mm < mstop && (
*mm != *t || mm[1] != t[1] ) ) mm += 2;
*mm = mm[1] = NOINDEX;
mm = mnext;
}
*t = t[1] = NOINDEX;
t += 2;
}
}
/*
#] DELTA/VECTOR :
#[ INDEX :
*/
else if ( *t == INDEX ) {
r7 = t; r8 = t + t[1]; t += 2;
while ( t < r8 ) {
mm = rnext;
pow = 1;
while ( mm < r3 ) {
mnext = mm + *mm;
GETSTOP(mm,mstop); mm++;
while ( mm < mstop ) {
if ( *mm != *r7 ) mm += mm[1];
else break;
}
if ( *mm == *r7 ) {
mstop = mm + mm[1]; mm += 2;
while ( *mm != *t
&& mm < mstop ) mm++;
if ( mm >= mstop ) pow = 0;
}
else pow = 0;
if ( pow == 0 ) break;
mm = mnext;
}
if ( pow == 0 ) { t++; continue; }
/*
We have a factor
*/
if ( *t < 0 ) { *r1++ = -VECTOR; }
else { *r1++ = -INDEX; }
*r1++ = *t;
argout = r1;
/*
Now we have to remove the index
*/
*t = NOINDEX;
mm = rnext;
while ( mm < r3 ) {
mnext = mm + *mm;
GETSTOP(mm,mstop); mm++;
while ( mm < mstop ) {
if ( *mm != *r7 ) mm += mm[1];
else break;
}
mstop = mm + mm[1]; mm += 2;
while ( mm < mstop &&
*mm != *t ) mm += 1;
*mm = NOINDEX;
mm = mnext;
}
t += 1;
}
}
/*
#] INDEX :
#[ FUNCTION :
*/
else if ( *t >= FUNCTION ) {
/*
In the next code we should actually look inside
the DENOMINATOR or EXPONENT for noncommuting objects
*/
if ( *t >= FUNCTION &&
functions[*t-FUNCTION].commute == 0 ) ncom = 0;
else ncom = 1;
if ( ncom ) {
mm = r5 + 1;
while ( mm < t && ( *mm == DUMMYFUN
|| *mm == DUMMYTEN ) ) mm += mm[1];
if ( mm < t ) { t += t[1]; continue; }
}
mm = rnext; pow = 1;
while ( mm < r3 ) {
mnext = mm + *mm;
GETSTOP(mm,mstop); mm++;
while ( mm < mstop ) {
if ( *mm == *t && mm[1] == t[1] ) {
for ( i = 2; i < t[1]; i++ ) {
if ( mm[i] != t[i] ) break;
}
if ( i >= t[1] )
{ mm += mm[1]; goto nextmterm; }
}
if ( ncom && *mm != DUMMYFUN && *mm != DUMMYTEN )
{ pow = 0; break; }
mm += mm[1];
}
if ( mm >= mstop ) pow = 0;
if ( pow == 0 ) break;
nextmterm: mm = mnext;
}
if ( pow == 0 ) { t += t[1]; continue; }
/*
Copy the function
*/
*r1++ = t[1] + 4 + ARGHEAD;
for ( i = 1; i < ARGHEAD; i++ ) *r1++ = 0;
*r1++ = t[1] + 4;
for ( i = 0; i < t[1]; i++ ) *r1++ = t[i];
*r1++ = 1; *r1++ = 1; *r1++ = 3;
argout = r1;
/*
Now we have to take out the functions
*/
mm = rnext;
while ( mm < r3 ) {
mnext = mm + *mm;
GETSTOP(mm,mstop); mm++;
while ( mm < mstop ) {
if ( *mm == *t && mm[1] == t[1] ) {
for ( i = 2; i < t[1]; i++ ) {
if ( mm[i] != t[i] ) break;
}
if ( i >= t[1] ) {
if ( functions[*t-FUNCTION].spec > 0 )
*mm = DUMMYTEN;
else
*mm = DUMMYFUN;
mm += mm[1];
goto nextterm;
}
}
mm += mm[1];
}
nextterm: mm = mnext;
}
if ( functions[*t-FUNCTION].spec > 0 )
*t = DUMMYTEN;
else
*t = DUMMYFUN;
t += t[1];
}
/*
#] FUNCTION :
*/
else {
t += t[1];
}
}
/*
#[ SYMBOL :
Now collect all symbols. We can use the space after r1 as storage
*/
t = argin+ARGHEAD;
rnext = t + *t;
r2 = r1;
while ( t < r3 ) {
GETSTOP(t,r6);
t++;
act = 0;
while ( t < r6 ) {
if ( *t == SYMBOL ) {
act = 1;
i = t[1];
NCOPY(r2,t,i)
}
else { t += t[1]; }
}
if ( act == 0 ) {
*r2++ = SYMBOL; *r2++ = 2;
}
t = rnext; rnext = rnext + *rnext;
}
*r2 = 0;
argin2 = argin;
/*
Now we have a list of all symbols as a sequence of SYMBOL subterms.
Any symbol that is absent in a subterm has power zero.
We now need a list of all minimum powers.
This can be done by subsequent merges.
*/
r7 = r1; /* The first object into which we merge. */
r8 = r7 + r7[1]; /* The object that gets merged into r7. */
while ( *r8 ) {
r2 = r8 + r8[1]; /* Next object */
ArgSymbolMerge(r7,r8);
r8 = r2;
}
/*
Now we have to divide by the object in r7 and take it apart as factors.
The division can be simple if there are no negative powers.
*/
if ( r7[1] > 2 ) {
r8 = r7+2;
r2 = r7 + r7[1];
act = 0;
pow = 0;
while ( r8 < r2 ) {
if ( r8[1] < 0 ) { act = 1; pow += -r8[1]*(ARGHEAD+8); }
else { pow += 2*r8[1]; }
r8 += 2;
}
/*
The amount of space we need to move r7 is given in pow
*/
if ( act == 0 ) { /* this can be done 'in situ' */
t = argin + ARGHEAD;
while ( t < r3 ) {
rnext = t + *t;
GETSTOP(t,r6);
t++;
while ( t < r6 ) {
if ( *t != SYMBOL ) { t += t[1]; continue; }
r8 = r7+2; r9 = t + t[1]; t += 2;
while ( ( t < r9 ) && ( r8 < r2 ) ) {
if ( *t == *r8 ) {
t[1] -= r8[1]; t += 2; r8 += 2;
}
else { /* *t must be < than *r8 !!! */
t += 2;
}
}
t = r9;
}
t = rnext;
}
/*
And now the factors that go to argout.
First we have to move r7 out of the way.
*/
r8 = r7+pow; i = r7[1];
while ( --i >= 0 ) r8[i] = r7[i];
r2 += pow;
r8 += 2;
while ( r8 < r2 ) {
for ( i = 0; i < r8[1]; i++ ) { *r1++ = -SYMBOL; *r1++ = *r8; }
r8 += 2;
}
}
else { /* this needs a new location */
argin2 = TermMalloc("TakeArgContent2");
/*
We have to multiply the inverse of r7 into argin
The answer should go to argin2.
*/
r5 = argin2; *r5++ = 0; *r5++ = 0; FILLARG(r5);
t = argin+ARGHEAD;
while ( t < r3 ) {
rnext = t + *t;
GETSTOP(t,r6);
r9 = r5;
*r5++ = *t++ + r7[1];
while ( t < r6 ) *r5++ = *t++;
i = r7[1] - 2; r8 = r7+2;
*r5++ = r7[0]; *r5++ = r7[1];
while ( i > 0 ) { *r5++ = *r8++; *r5++ = -*r8++; i -= 2; }
while ( t < rnext ) *r5++ = *t++;
Normalize(BHEAD r9);
r5 = r9 + *r9;
}
*r5 = 0;
*argin2 = r5-argin2;
/*
We may have to sort the terms in argin2.
*/
NewSort(BHEAD0);
t = argin2+ARGHEAD;
while ( *t ) {
StoreTerm(BHEAD t);
t += *t;
}
t = argin2+ARGHEAD;
if ( EndSort(BHEAD t,0) < 0 ) goto Irreg;
while ( *t ) t += *t;
*argin2 = t - argin2;
r3 = t;
/*
And now the factors that go to argout.
First we have to move r7 out of the way.
*/
r8 = r7+pow; i = r7[1];
while ( --i >= 0 ) r8[i] = r7[i];
r2 += pow;
r8 += 2;
while ( r8 < r2 ) {
if ( r8[1] >= 0 ) {
for ( i = 0; i < r8[1]; i++ ) { *r1++ = -SYMBOL; *r1++ = *r8; }
}
else {
for ( i = 0; i < -r8[1]; i++ ) {
*r1++ = ARGHEAD+8; *r1++ = 0;
FILLARG(r1);
*r1++ = 8; *r1++ = SYMBOL; *r1++ = 4; *r1++ = *r8;
*r1++ = -1; *r1++ = 1; *r1++ = 1; *r1++ = 3;
}
}
r8 += 2;
}
argout = r1;
}
}
/*
#] SYMBOL :
#[ DOTPRODUCT :
Now collect all dotproducts. We can use the space after r1 as storage
*/
t = argin2+ARGHEAD;
rnext = t + *t;
r2 = r1;
while ( t < r3 ) {
GETSTOP(t,r6);
t++;
act = 0;
while ( t < r6 ) {
if ( *t == DOTPRODUCT ) {
act = 1;
i = t[1];
NCOPY(r2,t,i)
}
else { t += t[1]; }
}
if ( act == 0 ) {
*r2++ = DOTPRODUCT; *r2++ = 2;
}
t = rnext; rnext = rnext + *rnext;
}
*r2 = 0;
argin3 = argin2;
/*
Now we have a list of all dotproducts as a sequence of DOTPRODUCT
subterms. Any dotproduct that is absent in a subterm has power zero.
We now need a list of all minimum powers.
This can be done by subsequent merges.
*/
r7 = r1; /* The first object into which we merge. */
r8 = r7 + r7[1]; /* The object that gets merged into r7. */
while ( *r8 ) {
r2 = r8 + r8[1]; /* Next object */
ArgDotproductMerge(r7,r8);
r8 = r2;
}
/*
Now we have to divide by the object in r7 and take it apart as factors.
The division can be simple if there are no negative powers.
*/
if ( r7[1] > 2 ) {
r8 = r7+2;
r2 = r7 + r7[1];
act = 0;
pow = 0;
while ( r8 < r2 ) {
if ( r8[2] < 0 ) { pow += -r8[2]*(ARGHEAD+9); }
else { pow += r8[2]*(ARGHEAD+9); }
r8 += 3;
}
/*
The amount of space we need to move r7 is given in pow
For dotproducts we always need a new location
*/
{
argin3 = TermMalloc("TakeArgContent3");
/*
We have to multiply the inverse of r7 into argin
The answer should go to argin2.
*/
r5 = argin3; *r5++ = 0; *r5++ = 0; FILLARG(r5);
t = argin2+ARGHEAD;
while ( t < r3 ) {
rnext = t + *t;
GETSTOP(t,r6);
r9 = r5;
*r5++ = *t++ + r7[1];
while ( t < r6 ) *r5++ = *t++;
i = r7[1] - 2; r8 = r7+2;
*r5++ = r7[0]; *r5++ = r7[1];
while ( i > 0 ) { *r5++ = *r8++; *r5++ = *r8++; *r5++ = -*r8++; i -= 3; }
while ( t < rnext ) *r5++ = *t++;
Normalize(BHEAD r9);
r5 = r9 + *r9;
}
*r5 = 0;
*argin3 = r5-argin3;
/*
We may have to sort the terms in argin3.
*/
NewSort(BHEAD0);
t = argin3+ARGHEAD;
while ( *t ) {
StoreTerm(BHEAD t);
t += *t;
}
t = argin3+ARGHEAD;
if ( EndSort(BHEAD t,0) < 0 ) goto Irreg;
while ( *t ) t += *t;
*argin3 = t - argin3;
r3 = t;
/*
And now the factors that go to argout.
First we have to move r7 out of the way.
*/
r8 = r7+pow; i = r7[1];
while ( --i >= 0 ) r8[i] = r7[i];
r2 += pow;
r8 += 2;
while ( r8 < r2 ) {
for ( i = ABS(r8[2]); i > 0; i-- ) {
*r1++ = ARGHEAD+9; *r1++ = 0; FILLARG(r1);
*r1++ = 9; *r1++ = DOTPRODUCT; *r1++ = 5; *r1++ = *r8;
*r1++ = r8[1]; *r1++ = r8[2] < 0 ? -1: 1;
*r1++ = 1; *r1++ = 1; *r1++ = 3;
}
r8 += 3;
}
argout = r1;
}
}
/*
#] DOTPRODUCT :
We have now in argin3 the argument stripped of negative powers and
common factors. The only thing left to deal with is to make the
coefficients integer. For that we have to find the LCM of the denominators
and the GCD of the numerators. And to start with, the sign.
We force the sign of the first term to be positive.
*/
t = argin3 + ARGHEAD; pow = 1;
t += *t;
if ( t[-1] < 0 ) {
pow = 0;
t[-1] = -t[-1];
while ( t < r3 ) {
t += *t; t[-1] = -t[-1];
}
}
/*
Now the GCD of the numerators and the LCM of the denominators:
*/
argfree = TermMalloc("TakeArgContent1");
if ( AN.cmod != 0 ) {
r1 = MakeMod(BHEAD argin3,r1,argfree);
}
else {
r1 = MakeInteger(BHEAD argin3,r1,argfree);
}
if ( pow == 0 ) {
*r1++ = -SNUMBER;
*r1++ = -1;
}
*r1 = 0;
/*
Cleanup
*/
if ( argin3 != argin2 ) TermFree(argin3,"TakeArgContent3");
if ( argin2 != argin ) TermFree(argin2,"TakeArgContent2");
return(argfree);
Irreg:
MesPrint("Irregularity while sorting argument in TakeArgContent");
if ( argin3 != argin2 ) TermFree(argin3,"TakeArgContent3");
if ( argin2 != argin ) TermFree(argin2,"TakeArgContent2");
Terminate(-1);
return(0);
}
/*
#] TakeArgContent :
#[ MakeInteger :
*/
/**
* For normalizing everything to integers we have to
* determine for all elements of this argument the LCM of
* the denominators and the GCD of the numerators.
* The input argument is in argin.
* The number that comes out should go to argout.
* The new pointer in the argout buffer is the return value.
* The normalized argument is in argfree.
*/
WORD *MakeInteger(PHEAD WORD *argin,WORD *argout,WORD *argfree)
{
UWORD *GCDbuffer, *GCDbuffer2, *LCMbuffer, *LCMb, *LCMc;
WORD *r, *r1, *r2, *r3, *r4, *r5, *rnext, i, k, j;
WORD kGCD, kLCM, kGCD2, kkLCM, jLCM, jGCD;
GCDbuffer = NumberMalloc("MakeInteger");
GCDbuffer2 = NumberMalloc("MakeInteger");
LCMbuffer = NumberMalloc("MakeInteger");
LCMb = NumberMalloc("MakeInteger");
LCMc = NumberMalloc("MakeInteger");
r4 = argin + *argin;
r = argin + ARGHEAD;
/*
First take the first term to load up the LCM and the GCD
*/
r2 = r + *r;
j = r2[-1];
r3 = r2 - ABS(j);
k = REDLENG(j);
if ( k < 0 ) k = -k;
while ( ( k > 1 ) && ( r3[k-1] == 0 ) ) k--;
for ( kGCD = 0; kGCD < k; kGCD++ ) GCDbuffer[kGCD] = r3[kGCD];
k = REDLENG(j);
if ( k < 0 ) k = -k;
r3 += k;
while ( ( k > 1 ) && ( r3[k-1] == 0 ) ) k--;
for ( kLCM = 0; kLCM < k; kLCM++ ) LCMbuffer[kLCM] = r3[kLCM];
r1 = r2;
/*
Now go through the rest of the terms in this argument.
*/
while ( r1 < r4 ) {
r2 = r1 + *r1;
j = r2[-1];
r3 = r2 - ABS(j);
k = REDLENG(j);
if ( k < 0 ) k = -k;
while ( ( k > 1 ) && ( r3[k-1] == 0 ) ) k--;
if ( ( ( GCDbuffer[0] == 1 ) && ( kGCD == 1 ) ) ) {
/*
GCD is already 1
*/
}
else if ( ( ( k != 1 ) || ( r3[0] != 1 ) ) ) {
if ( GcdLong(BHEAD GCDbuffer,kGCD,(UWORD *)r3,k,GCDbuffer2,&kGCD2) ) {
NumberFree(GCDbuffer,"MakeInteger");
NumberFree(GCDbuffer2,"MakeInteger");
NumberFree(LCMbuffer,"MakeInteger");
NumberFree(LCMb,"MakeInteger"); NumberFree(LCMc,"MakeInteger");
goto MakeIntegerErr;
}
kGCD = kGCD2;
for ( i = 0; i < kGCD; i++ ) GCDbuffer[i] = GCDbuffer2[i];
}
else {
kGCD = 1; GCDbuffer[0] = 1;
}
k = REDLENG(j);
if ( k < 0 ) k = -k;
r3 += k;
while ( ( k > 1 ) && ( r3[k-1] == 0 ) ) k--;
if ( ( ( LCMbuffer[0] == 1 ) && ( kLCM == 1 ) ) ) {
for ( kLCM = 0; kLCM < k; kLCM++ )
LCMbuffer[kLCM] = r3[kLCM];
}
else if ( ( k != 1 ) || ( r3[0] != 1 ) ) {
if ( GcdLong(BHEAD LCMbuffer,kLCM,(UWORD *)r3,k,LCMb,&kkLCM) ) {
NumberFree(GCDbuffer,"MakeInteger"); NumberFree(GCDbuffer2,"MakeInteger");
NumberFree(LCMbuffer,"MakeInteger"); NumberFree(LCMb,"MakeInteger"); NumberFree(LCMc,"MakeInteger");
goto MakeIntegerErr;
}
DivLong((UWORD *)r3,k,LCMb,kkLCM,LCMb,&kkLCM,LCMc,&jLCM);
MulLong(LCMbuffer,kLCM,LCMb,kkLCM,LCMc,&jLCM);
for ( kLCM = 0; kLCM < jLCM; kLCM++ )
LCMbuffer[kLCM] = LCMc[kLCM];
}
else {} /* LCM doesn't change */
r1 = r2;
}
/*
Now put the factor together: GCD/LCM
*/
r3 = (WORD *)(GCDbuffer);
if ( kGCD == kLCM ) {
for ( jGCD = 0; jGCD < kGCD; jGCD++ )
r3[jGCD+kGCD] = LCMbuffer[jGCD];
k = kGCD;
}
else if ( kGCD > kLCM ) {
for ( jGCD = 0; jGCD < kLCM; jGCD++ )
r3[jGCD+kGCD] = LCMbuffer[jGCD];
for ( jGCD = kLCM; jGCD < kGCD; jGCD++ )
r3[jGCD+kGCD] = 0;
k = kGCD;
}
else {
for ( jGCD = kGCD; jGCD < kLCM; jGCD++ )
r3[jGCD] = 0;
for ( jGCD = 0; jGCD < kLCM; jGCD++ )
r3[jGCD+kLCM] = LCMbuffer[jGCD];
k = kLCM;
}
j = 2*k+1;
/*
Now we have to write this to argout
*/
if ( ( j == 3 ) && ( r3[1] == 1 ) && ( (WORD)(r3[0]) > 0 ) ) {
*argout = -SNUMBER;
argout[1] = r3[0];
r1 = argout+2;
}
else {
r1 = argout;
*r1++ = j+1+ARGHEAD; *r1++ = 0; FILLARG(r1);
*r1++ = j+1; r2 = r3;
for ( i = 0; i < k; i++ ) { *r1++ = *r2++; *r1++ = *r2++; }
*r1++ = j;
}
/*
Next we have to take the factor out from the argument.
This cannot be done in location, because the denominator stuff can make
coefficients longer.
*/
r2 = argfree + 2; FILLARG(r2)
while ( r < r4 ) {
rnext = r + *r;
j = ABS(rnext[-1]);
r5 = rnext - j;
r3 = r2;
while ( r < r5 ) *r2++ = *r++;
j = (j-1)/2; /* reduced length. Remember, k is the other red length */
if ( DivRat(BHEAD (UWORD *)r5,j,GCDbuffer,k,(UWORD *)r2,&i) ) {
goto MakeIntegerErr;
}
i = 2*i+1;
r2 = r2 + i;
if ( rnext[-1] < 0 ) r2[-1] = -i;
else r2[-1] = i;
*r3 = r2-r3;
r = rnext;
}
*r2 = 0;
argfree[0] = r2-argfree;
argfree[1] = 0;
/*
Cleanup
*/
NumberFree(LCMc,"MakeInteger");
NumberFree(LCMb,"MakeInteger");
NumberFree(LCMbuffer,"MakeInteger");
NumberFree(GCDbuffer2,"MakeInteger");
NumberFree(GCDbuffer,"MakeInteger");
return(r1);
MakeIntegerErr:
MesCall("MakeInteger");
Terminate(-1);
return(0);
}
/*
#] MakeInteger :
#[ MakeMod :
*/
/**
* Similar to MakeInteger but now with modulus arithmetic using only
* a one WORD 'prime'. We make the coefficient of the first term in the
* argument equal to one.
* Already the coefficients are taken modulus AN.cmod and AN.ncmod == 1
*/
WORD *MakeMod(PHEAD WORD *argin,WORD *argout,WORD *argfree)
{
WORD *r, *instop, *r1, *m, x, xx, ix, ip;
int i;
r = argin; instop = r + *r; r += ARGHEAD;
x = r[*r-3];
if ( r[*r-1] < 0 ) x += AN.cmod[0];
if ( GetModInverses(x,(WORD)(AN.cmod[0]),&ix,&ip) ) {
Terminate(-1);
}
argout[0] = -SNUMBER;
argout[1] = x;
argout[2] = 0;
r1 = argout+2;
/*
Now we have to multiply all coefficients by ix.
This does not make things longer, but we should keep to the conventions
of MakeInteger.
*/
m = argfree + ARGHEAD;
while ( r < instop ) {
xx = r[*r-3]; if ( r[*r-1] < 0 ) xx += AN.cmod[0];
xx = (WORD)((((LONG)xx)*ix) % AN.cmod[0]);
if ( xx != 0 ) {
i = *r; NCOPY(m,r,i);
m[-3] = xx; m[-1] = 3;
}
else { r += *r; }
}
*m = 0;
*argfree = m - argfree;
argfree[1] = 0;
argfree += 2; FILLARG(argfree);
return(r1);
}
/*
#] MakeMod :
#[ SortWeights :
*/
/**
* Sorts an array of LONGS in the same way SplitMerge (in sort.c) works
* We use gradual division in two.
*/
void SortWeights(LONG *weights,LONG *extraspace,WORD number)
{
LONG w, *fill, *from1, *from2;
int n1,n2,i;
if ( number >= 4 ) {
n1 = number/2; n2 = number - n1;
SortWeights(weights,extraspace,n1);
SortWeights(weights+n1,extraspace,n2);
/*
We copy the first patch to the extra space. Then we merge
Note that a potential remaining n2 objects are already in place.
*/
for ( i = 0; i < n1; i++ ) extraspace[i] = weights[i];
fill = weights; from1 = extraspace; from2 = weights+n1;
while ( n1 > 0 && n2 > 0 ) {
if ( *from1 <= *from2 ) { *fill++ = *from1++; n1--; }
else { *fill++ = *from2++; n2--; }
}
while ( n1 > 0 ) { *fill++ = *from1++; n1--; }
}
/*
Special cases
*/
else if ( number == 3 ) { /* 6 permutations of which one is trivial */
if ( weights[0] > weights[1] ) {
if ( weights[1] > weights[2] ) {
w = weights[0]; weights[0] = weights[2]; weights[2] = w;
}
else if ( weights[0] > weights[2] ) {
w = weights[0]; weights[0] = weights[1];
weights[1] = weights[2]; weights[2] = w;
}
else {
w = weights[0]; weights[0] = weights[1]; weights[1] = w;
}
}
else if ( weights[0] > weights[2] ) {
w = weights[0]; weights[0] = weights[2];
weights[2] = weights[1]; weights[1] = w;
}
else if ( weights[1] > weights[2] ) {
w = weights[1]; weights[1] = weights[2]; weights[2] = w;
}
}
else if ( number == 2 ) {
if ( weights[0] > weights[1] ) {
w = weights[0]; weights[0] = weights[1]; weights[1] = w;
}
}
return;
}
/*
#] SortWeights :
*/
form-master/sources/bugtool.c 0000664 0000000 0000000 00000004204 13565763364 0016636 0 ustar 00root root 0000000 0000000 /** @file bugtool.c
*
* Low level routines for debugging
*/
/* #[ License : */
/*
* Copyright (C) 1984-2017 J.A.M. Vermaseren
* When using this file you are requested to refer to the publication
* J.A.M.Vermaseren "New features of FORM" math-ph/0010025
* This is considered a matter of courtesy as the development was paid
* for by FOM the Dutch physics granting agency and we would like to
* be able to track its scientific use to convince FOM of its value
* for the community.
*
* This file is part of FORM.
*
* FORM 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.
*
* FORM 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 FORM. If not, see .
*/
/* #] License : */
/*
#[ Includes :
*/
#include "form3.h"
/*
#] Includes :
#[ ExprStatus :
*/
static UBYTE *statusexpr[] = {
(UBYTE *)"LOCALEXPRESSION"
,(UBYTE *)"SKIPLEXPRESSION"
,(UBYTE *)"DROPLEXPRESSION"
,(UBYTE *)"DROPPEDEXPRESSION"
,(UBYTE *)"GLOBALEXPRESSION"
,(UBYTE *)"SKIPGEXPRESSION"
,(UBYTE *)"DROPGEXPRESSION"
,(UBYTE *)"UNKNOWN"
,(UBYTE *)"STOREDEXPRESSION"
,(UBYTE *)"HIDDENLEXPRESSION"
,(UBYTE *)"HIDELEXPRESSION"
,(UBYTE *)"DROPHLEXPRESSION"
,(UBYTE *)"UNHIDELEXPRESSION"
,(UBYTE *)"HIDDENGEXPRESSION"
,(UBYTE *)"HIDEGEXPRESSION"
,(UBYTE *)"DROPHGEXPRESSION"
,(UBYTE *)"UNHIDEGEXPRESSION"
,(UBYTE *)"INTOHIDELEXPRESSION"
,(UBYTE *)"INTOHIDEGEXPRESSION"
};
void ExprStatus(EXPRESSIONS e)
{
MesPrint("Expression %s(%d) has status %s(%d,%d). Buffer: %d, Position: %15p",
AC.exprnames->namebuffer+e->name,(WORD)(e-Expressions),
statusexpr[e->status],e->status,e->hidelevel,
e->whichbuffer,&(e->onfile));
}
/*
#] ExprStatus :
*/
form-master/sources/checkpoint.c 0000664 0000000 0000000 00000267040 13565763364 0017323 0 ustar 00root root 0000000 0000000 /*
#[ Explanations :
*/
/** @file checkpoint.c
*
* Contains all functions that deal with the recovery mechanism controlled and
* activated by the On Checkpoint switch.
*
* The main function are DoCheckpoint, DoRecovery, and DoSnapshot. If the
* checkpoints are activated DoCheckpoint is called every time a module is
* finished executing. If the conditions for the creation of a recovery
* snapshot are met DoCheckpoint calls DoSnapshot. DoRecovery is called once
* when FORM starts up with the command line argument -R. Most of the other
* code contains debugging facilities that are only compiled if the macro
* PRINTDEBUG is defined.
*
* The recovery mechanism is atomic, i.e. only if everything went well, the
* final recovery file is created (and the older one overwritten) in a single
* step (copying). If some errors occur, a warning is issued and the program
* continues without having created a new recovery file. The only situation in
* which the creation of the recovery data leads to a termination of the
* running program is if not enough disk or memory space is left.
*
* For ParFORM each slave creates its own recovery file, sends it to the
* master and then it deletes the recovery file. The master stores all the
* recovery files and on recovery it feeds these files to the slaves. It is
* nearly impossible to recover after some MPI fault so ParFORM terminates
* on any recovery failure.
*
* DoRecovery and DoSnapshot do the loading and saving of the recovery data,
* respectively. Every change in one functions needs to be accompanied by the
* appropriate change in the other function. The structure of both functions is
* quite similar. They handle the relevant global structs one after the other
* and then care about the copying of the hide and scratch files.
*
* The names of the recovery, scratch and hide files are hard-coded in the
* variables in fold "filenames and system commands".
*
* If the global structs AM,AP,AC,AR are changed, DoRecovery and DoSnapshot
* usually also have to be changed. Some structs are read/written as a whole
* (AP,AC), some are read/written only partly as a selection of their
* individual elements (AM,AR). If AM or AR have been changed by adding or
* removing an element that is important for the runtime status, then the
* reading/writing statements have to be added to or removed from DoRecovery
* and DoSnapshot. If AP or AC are changed, then for non-pointer variables (in
* the case of a struct it also means that none of its elements is a pointer)
* nothing has to be changed in the functions here. If pointers are involved,
* extra code has to be added (or removed). See the comments of DoRecovery and
* DoSnapshot.
*
*/
/*
#] Explanations :
#[ License :
*
* Copyright (C) 1984-2017 J.A.M. Vermaseren
* When using this file you are requested to refer to the publication
* J.A.M.Vermaseren "New features of FORM" math-ph/0010025
* This is considered a matter of courtesy as the development was paid
* for by FOM the Dutch physics granting agency and we would like to
* be able to track its scientific use to convince FOM of its value
* for the community.
*
* This file is part of FORM.
*
* FORM 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.
*
* FORM 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 FORM. If not, see .
*/
/*
#] License :
#[ Includes :
*/
#include "form3.h"
#include
/*
#define PRINTDEBUG
*/
/*
#define PRINTTIMEMARKS
*/
/*
#] Includes :
#[ filenames and system commands :
*/
/**
* BaseName of recovery files
*/
#ifdef WITHMPI
#define BASENAME_FMT "%c%04dFORMrecv"
/**
* The basenames for ParFORM will be created from BASENAME_FMT by means of
* sprintf(BaseName,BASENAME_FMT,(PF.me == MASTER)?'m':'s',PF.me);
* in InitRecovery(). Here just reserve the space:
*/
static char BaseName[] = BASENAME_FMT;
#else
static char *BaseName = "FORMrecv";
#endif
/**
* filename for the recovery file
*/
static char *recoveryfile = 0;
/**
* filename for the intermediate recovery file. only if the write is
* completely successful, this file will be moved/renamed to the one
* named by recoveryfile. this offers atomicity for the snapshot generation.
*/
static char *intermedfile = 0;
/**
* filename of sort file copy
*/
static char *sortfile = 0;
/**
* filename of hide file copy
*/
static char *hidefile = 0;
/**
* filename of store file copy
*/
static char *storefile = 0;
/**
* >0 if at least once the respective file has been created.
* Checked by DeleteRecoveryFile().
*/
static int done_snapshot = 0;
#ifdef WITHMPI
/**
* The position at which BASENAME_FMT should be applied.
* Initialized in InitRecovery().
*/
static int PF_fmt_pos;
/**
* Returns the contents of recoveryfile or intermedfile but with the renaming
* specified by the arguments.
*/
static const char *PF_recoveryfile(char prefix, int id, int intermed)
{
/*
* Assume that InitRecovery() has been already called, namely
* recoveryfile, intermedfile and PF_fmt_pos are already initialized.
*/
static char *tmp_recovery = NULL;
static char *tmp_intermed = NULL;
char *tmp, c;
if ( tmp_recovery == NULL ) {
if ( PF.numtasks > 9999 ) { /* see BASENAME_FMT */
MesPrint("Checkpoint: too many number of processors.");
Terminate(-1);
}
tmp_recovery = (char *)Malloc1(strlen(recoveryfile) + strlen(intermedfile) + 2, "PF_recoveryfile");
tmp_intermed = tmp_recovery + strlen(recoveryfile) + 1;
strcpy(tmp_recovery, recoveryfile);
strcpy(tmp_intermed, intermedfile);
}
tmp = intermed ? tmp_intermed : tmp_recovery;
c = tmp[PF_fmt_pos + 13]; /* The magic number 13 comes from BASENAME_FMT. */
sprintf(tmp + PF_fmt_pos, BASENAME_FMT, prefix, id);
tmp[PF_fmt_pos + 13] = c;
return tmp;
}
#endif
/*
#] filenames and system commands :
#[ CheckRecoveryFile :
*/
/**
* Checks whether a snapshot/recovery file exists.
* Returns 1 if it exists, 0 otherwise.
*/
#ifdef WITHMPI
/**
* The master has all the recovery files. It checks whether these files
* exist and sends proper files to slaves. On any error PF_CheckRecoveryFile()
* returns -1 which leads to the program termination.
*/
static int PF_CheckRecoveryFile()
{
int i,ret=0;
FILE *fd;
/* Check if the recovery file for the master exists. */
if ( PF.me == MASTER ) {
if ( (fd = fopen(recoveryfile, "r")) ) {
fclose(fd);
PF_BroadcastNumber(1);
}
else {
PF_BroadcastNumber(0);
return 0;
}
}
else {
if ( !PF_BroadcastNumber(0) )
return 0;
}
/* Now the main part. */
if (PF.me == MASTER){
/*We have to have recovery files for the master and all the slaves:*/
for(i=1; i 0 ) {
if ( AC.CheckpointFlag != -1 ) {
/* recovery file exists but recovery option is not given */
#ifdef WITHMPI
if ( PF.me == MASTER ) {
#endif
MesPrint("The recovery file %s exists, but the recovery option -R has not been given!", RecoveryFilename());
MesPrint("FORM will be terminated to avoid unintentional loss of data.");
MesPrint("Delete the recovery file manually, if you want to start FORM without recovery.");
#ifdef WITHMPI
}
if(PF.me != MASTER)
remove(RecoveryFilename());
#endif
Terminate(-1);
}
}
else {
if ( AC.CheckpointFlag == -1 ) {
/* recovery option given but recovery file does not exist */
#ifdef WITHMPI
if ( PF.me == MASTER )
#endif
MesPrint("Option -R for recovery has been given, but the recovery file %s does not exist!", RecoveryFilename());
Terminate(-1);
}
}
return(ret);
}
/*
#] CheckRecoveryFile :
#[ DeleteRecoveryFile :
*/
/**
* Deletes the recovery files. It is called by CleanUp() in the case of a
* successful completion.
*/
void DeleteRecoveryFile()
{
if ( done_snapshot ) {
remove(recoveryfile);
#ifdef WITHMPI
if( PF.me == MASTER){
int i;
for(i=1; i> 4);
UBYTE l = (UBYTE)(*((UBYTE*)p) & 0x0F);
if ( h > 9 ) h += 55; else h += 48;
if ( l > 9 ) l += 55; else l += 48;
printf("%c%c ", h, l);
}
static void print_STR(UBYTE *p)
{
if ( p ) {
MesPrint("%s", (char*)p);
}
else {
MesPrint("NULL");
}
}
static void print_WORDB(WORD *buf, WORD *top)
{
LONG size = top-buf;
int i;
while ( size > 0 ) {
if ( size > MAXPOSITIVE ) i = MAXPOSITIVE;
else i = size;
size -= i;
MesPrint("%a",i,buf);
buf += i;
}
}
static void print_VOIDP(void *p, size_t size)
{
int i;
if ( p ) {
while ( size > 0 ) {
if ( size > MAXPOSITIVE ) i = MAXPOSITIVE;
else i = size;
size -= i;
MesPrint("%b",i,(UBYTE *)p);
p = ((UBYTE *)p)+i;
}
}
else {
MesPrint("NULL");
}
}
static void print_CHARS(UBYTE *p, size_t size)
{
int i;
while ( size > 0 ) {
if ( size > MAXPOSITIVE ) i = MAXPOSITIVE;
else i = size;
size -= i;
MesPrint("%C",i,(char *)p);
p += i;
}
}
static void print_WORDV(WORD *p, size_t size)
{
int i;
if ( p ) {
while ( size > 0 ) {
if ( size > MAXPOSITIVE ) i = MAXPOSITIVE;
else i = size;
size -= i;
MesPrint("%a",i,p);
p += i;
}
}
else {
MesPrint("NULL");
}
}
static void print_INTV(int *p, size_t size)
{
int iarray[8];
WORD i = 0;
if ( p ) {
while ( size > 0 ) {
if ( i >= 8 ) {
MesPrint("%I",i,iarray);
i = 0;
}
iarray[i++] = *p++;
size--;
}
if ( i > 0 ) MesPrint("%I",i,iarray);
}
else {
MesPrint("NULL");
}
}
static void print_LONGV(LONG *p, size_t size)
{
LONG larray[8];
WORD i = 0;
if ( p ) {
while ( size > 0 ) {
if ( i >= 8 ) {
MesPrint("%I",i,larray);
i = 0;
}
larray[i++] = *p++;
size--;
}
if ( i > 0 ) MesPrint("%I",i,larray);
}
else {
MesPrint("NULL");
}
}
static void print_PRELOAD(PRELOAD *l)
{
if ( l->size ) {
print_CHARS(l->buffer, l->size);
}
MesPrint("%ld", l->size);
}
static void print_PREVAR(PREVAR *l)
{
MesPrint("%s", l->name);
print_STR(l->value);
if ( l->nargs ) print_STR(l->argnames);
MesPrint("%d", l->nargs);
MesPrint("%d", l->wildarg);
}
static void print_DOLLARS(DOLLARS l)
{
print_VOIDP(l->where, l->size);
MesPrint("%ld", l->size);
MesPrint("%ld", l->name);
MesPrint("%s", AC.dollarnames->namebuffer+l->name);
MesPrint("%d", l->type);
MesPrint("%d", l->node);
MesPrint("%d", l->index);
MesPrint("%d", l->zero);
MesPrint("%d", l->numdummies);
MesPrint("%d", l->nfactors);
}
static void print_LIST(LIST *l)
{
print_VOIDP(l->lijst, l->size);
MesPrint("%s", l->message);
MesPrint("%d", l->num);
MesPrint("%d", l->maxnum);
MesPrint("%d", l->size);
MesPrint("%d", l->numglobal);
MesPrint("%d", l->numtemp);
MesPrint("%d", l->numclear);
}
static void print_DOLOOP(DOLOOP *l)
{
print_PRELOAD(&(l->p));
print_STR(l->name);
if ( l->type != NUMERICALLOOP ) {
print_STR(l->vars);
}
print_STR(l->contents);
if ( l->type != LISTEDLOOP && l->type != NUMERICALLOOP ) {
print_STR(l->dollarname);
}
MesPrint("%l", l->startlinenumber);
MesPrint("%l", l->firstnum);
MesPrint("%l", l->lastnum);
MesPrint("%l", l->incnum);
MesPrint("%d", l->type);
MesPrint("%d", l->NoShowInput);
MesPrint("%d", l->errorsinloop);
MesPrint("%d", l->firstloopcall);
}
static void print_PROCEDURE(PROCEDURE *l)
{
if ( l->loadmode != 1 ) {
print_PRELOAD(&(l->p));
}
print_STR(l->name);
MesPrint("%d", l->loadmode);
}
static void print_NAMETREE(NAMETREE *t)
{
int i;
for ( i=0; inodefill; ++i ) {
MesPrint("%l %d %d %d %d %d %d\n", t->namenode[i].name,
t->namenode[i].parent, t->namenode[i].left, t->namenode[i].right,
t->namenode[i].balance, t->namenode[i].type, t->namenode[i].number );
}
print_CHARS(t->namebuffer, t->namefill);
MesPrint("%l", t->namesize);
MesPrint("%l", t->namefill);
MesPrint("%l", t->nodesize);
MesPrint("%l", t->nodefill);
MesPrint("%l", t->oldnamefill);
MesPrint("%l", t->oldnodefill);
MesPrint("%l", t->globalnamefill);
MesPrint("%l", t->globalnodefill);
MesPrint("%l", t->clearnamefill);
MesPrint("%l", t->clearnodefill);
MesPrint("%d", t->headnode);
}
void print_CBUF(CBUF *c)
{
int i;
print_WORDV(c->Buffer, c->BufferSize);
/*
MesPrint("%x", c->Buffer);
MesPrint("%x", c->lhs);
MesPrint("%x", c->rhs);
*/
for ( i=0; inumlhs; ++i ) {
if ( c->lhs[i]) MesPrint("%d", *(c->lhs[i]));
}
for ( i=0; inumrhs; ++i ) {
if ( c->rhs[i]) MesPrint("%d", *(c->rhs[i]));
}
MesPrint("%l", *c->CanCommu);
MesPrint("%l", *c->NumTerms);
MesPrint("%d", *c->numdum);
for ( i=0; iMaxTreeSize; ++i ) {
MesPrint("%d %d %d %d %d", c->boomlijst[i].parent, c->boomlijst[i].left, c->boomlijst[i].right,
c->boomlijst[i].value, c->boomlijst[i].blnce);
}
}
static void print_STREAM(STREAM *t)
{
print_CHARS(t->buffer, t->inbuffer);
MesPrint("%l", (LONG)(t->pointer-t->buffer));
print_STR(t->FoldName);
print_STR(t->name);
if ( t->type == PREVARSTREAM || t->type == DOLLARSTREAM ) {
print_STR(t->pname);
}
MesPrint("%l", (LONG)t->fileposition);
MesPrint("%l", (LONG)t->linenumber);
MesPrint("%l", (LONG)t->prevline);
MesPrint("%l", t->buffersize);
MesPrint("%l", t->bufferposition);
MesPrint("%l", t->inbuffer);
MesPrint("%d", t->previous);
MesPrint("%d", t->handle);
switch ( t->type ) {
case FILESTREAM: MesPrint("%d == FILESTREAM", t->type); break;
case PREVARSTREAM: MesPrint("%d == PREVARSTREAM", t->type); break;
case PREREADSTREAM: MesPrint("%d == PREREADSTREAM", t->type); break;
case PIPESTREAM: MesPrint("%d == PIPESTREAM", t->type); break;
case PRECALCSTREAM: MesPrint("%d == PRECALCSTREAM", t->type); break;
case DOLLARSTREAM: MesPrint("%d == DOLLARSTREAM", t->type); break;
case PREREADSTREAM2: MesPrint("%d == PREREADSTREAM2", t->type); break;
case EXTERNALCHANNELSTREAM: MesPrint("%d == EXTERNALCHANNELSTREAM", t->type); break;
case PREREADSTREAM3: MesPrint("%d == PREREADSTREAM3", t->type); break;
default: MesPrint("%d == UNKNOWN", t->type);
}
}
static void print_M()
{
MesPrint("%%%% M_const");
MesPrint("%d", *AM.gcmod);
MesPrint("%d", *AM.gpowmod);
print_STR(AM.TempDir);
print_STR(AM.TempSortDir);
print_STR(AM.IncDir);
print_STR(AM.InputFileName);
print_STR(AM.LogFileName);
print_STR(AM.OutBuffer);
print_STR(AM.Path);
print_STR(AM.SetupDir);
print_STR(AM.SetupFile);
MesPrint("--MARK 1");
MesPrint("%l", (LONG)BASEPOSITION(AM.zeropos));
#ifdef WITHPTHREADS
MesPrint("%l", AM.ThreadScratSize);
MesPrint("%l", AM.ThreadScratOutSize);
#endif
MesPrint("%l", AM.MaxTer);
MesPrint("%l", AM.CompressSize);
MesPrint("%l", AM.ScratSize);
MesPrint("%l", AM.SizeStoreCache);
MesPrint("%l", AM.MaxStreamSize);
MesPrint("%l", AM.SIOsize);
MesPrint("%l", AM.SLargeSize);
MesPrint("%l", AM.SSmallEsize);
MesPrint("%l", AM.SSmallSize);
MesPrint("--MARK 2");
MesPrint("%l", AM.STermsInSmall);
MesPrint("%l", AM.MaxBracketBufferSize);
MesPrint("%l", AM.hProcessBucketSize);
MesPrint("%l", AM.gProcessBucketSize);
MesPrint("%l", AM.shmWinSize);
MesPrint("%l", AM.OldChildTime);
MesPrint("%l", AM.OldSecTime);
MesPrint("%l", AM.OldMilliTime);
MesPrint("%l", AM.WorkSize);
MesPrint("%l", AM.gThreadBucketSize);
MesPrint("--MARK 3");
MesPrint("%l", AM.ggThreadBucketSize);
MesPrint("%d", AM.FileOnlyFlag);
MesPrint("%d", AM.Interact);
MesPrint("%d", AM.MaxParLevel);
MesPrint("%d", AM.OutBufSize);
MesPrint("%d", AM.SMaxFpatches);
MesPrint("%d", AM.SMaxPatches);
MesPrint("%d", AM.StdOut);
MesPrint("%d", AM.ginsidefirst);
MesPrint("%d", AM.gDefDim);
MesPrint("%d", AM.gDefDim4);
MesPrint("--MARK 4");
MesPrint("%d", AM.NumFixedSets);
MesPrint("%d", AM.NumFixedFunctions);
MesPrint("%d", AM.rbufnum);
MesPrint("%d", AM.dbufnum);
MesPrint("%d", AM.SkipClears);
MesPrint("%d", AM.gfunpowers);
MesPrint("%d", AM.gStatsFlag);
MesPrint("%d", AM.gNamesFlag);
MesPrint("%d", AM.gCodesFlag);
MesPrint("%d", AM.gTokensWriteFlag);
MesPrint("%d", AM.gSortType);
MesPrint("%d", AM.gproperorderflag);
MesPrint("--MARK 5");
MesPrint("%d", AM.hparallelflag);
MesPrint("%d", AM.gparallelflag);
MesPrint("%d", AM.totalnumberofthreads);
MesPrint("%d", AM.gSizeCommuteInSet);
MesPrint("%d", AM.gThreadStats);
MesPrint("%d", AM.ggThreadStats);
MesPrint("%d", AM.gFinalStats);
MesPrint("%d", AM.ggFinalStats);
MesPrint("%d", AM.gThreadsFlag);
MesPrint("%d", AM.ggThreadsFlag);
MesPrint("%d", AM.gThreadBalancing);
MesPrint("%d", AM.ggThreadBalancing);
MesPrint("%d", AM.gThreadSortFileSynch);
MesPrint("%d", AM.ggThreadSortFileSynch);
MesPrint("%d", AM.gProcessStats);
MesPrint("%d", AM.ggProcessStats);
MesPrint("%d", AM.gOldParallelStats);
MesPrint("%d", AM.ggOldParallelStats);
MesPrint("%d", AM.gWTimeStatsFlag);
MesPrint("%d", AM.ggWTimeStatsFlag);
MesPrint("%d", AM.maxFlevels);
MesPrint("--MARK 6");
MesPrint("%d", AM.resetTimeOnClear);
MesPrint("%d", AM.gcNumDollars);
MesPrint("%d", AM.MultiRun);
MesPrint("%d", AM.gNoSpacesInNumbers);
MesPrint("%d", AM.ggNoSpacesInNumbers);
MesPrint("%d", AM.MaxTal);
MesPrint("%d", AM.IndDum);
MesPrint("%d", AM.DumInd);
MesPrint("%d", AM.WilInd);
MesPrint("%d", AM.gncmod);
MesPrint("%d", AM.gnpowmod);
MesPrint("%d", AM.gmodmode);
MesPrint("--MARK 7");
MesPrint("%d", AM.gUnitTrace);
MesPrint("%d", AM.gOutputMode);
MesPrint("%d", AM.gCnumpows);
MesPrint("%d", AM.gOutputSpaces);
MesPrint("%d", AM.gOutNumberType);
MesPrint("%d %d %d %d", AM.gUniTrace[0], AM.gUniTrace[1], AM.gUniTrace[2], AM.gUniTrace[3]);
MesPrint("%d", AM.MaxWildcards);
MesPrint("%d", AM.mTraceDum);
MesPrint("%d", AM.OffsetIndex);
MesPrint("%d", AM.OffsetVector);
MesPrint("%d", AM.RepMax);
MesPrint("%d", AM.LogType);
MesPrint("%d", AM.ggStatsFlag);
MesPrint("%d", AM.gLineLength);
MesPrint("%d", AM.qError);
MesPrint("--MARK 8");
MesPrint("%d", AM.FortranCont);
MesPrint("%d", AM.HoldFlag);
MesPrint("%d %d %d %d %d", AM.Ordering[0], AM.Ordering[1], AM.Ordering[2], AM.Ordering[3], AM.Ordering[4]);
MesPrint("%d %d %d %d %d", AM.Ordering[5], AM.Ordering[6], AM.Ordering[7], AM.Ordering[8], AM.Ordering[9]);
MesPrint("%d %d %d %d %d", AM.Ordering[10], AM.Ordering[11], AM.Ordering[12], AM.Ordering[13], AM.Ordering[14]);
MesPrint("%d", AM.silent);
MesPrint("%d", AM.tracebackflag);
MesPrint("%d", AM.expnum);
MesPrint("%d", AM.denomnum);
MesPrint("%d", AM.facnum);
MesPrint("%d", AM.invfacnum);
MesPrint("%d", AM.sumnum);
MesPrint("%d", AM.sumpnum);
MesPrint("--MARK 9");
MesPrint("%d", AM.OldOrderFlag);
MesPrint("%d", AM.termfunnum);
MesPrint("%d", AM.matchfunnum);
MesPrint("%d", AM.countfunnum);
MesPrint("%d", AM.gPolyFun);
MesPrint("%d", AM.gPolyFunInv);
MesPrint("%d", AM.gPolyFunType);
MesPrint("%d", AM.gPolyFunExp);
MesPrint("%d", AM.gPolyFunVar);
MesPrint("%d", AM.gPolyFunPow);
MesPrint("--MARK 10");
MesPrint("%d", AM.dollarzero);
MesPrint("%d", AM.atstartup);
MesPrint("%d", AM.exitflag);
MesPrint("%d", AM.NumStoreCaches);
MesPrint("%d", AM.gIndentSpace);
MesPrint("%d", AM.ggIndentSpace);
MesPrint("%d", AM.gShortStatsMax);
MesPrint("%d", AM.ggShortStatsMax);
MesPrint("%%%% END M_const");
/* fflush(0); */
}
static void print_P()
{
int i;
MesPrint("%%%% P_const");
print_LIST(&AP.DollarList);
for ( i=0; iname);
MesPrint("%l", (LONG)(AR.outfile-AR.Fscr));
MesPrint("%s", AR.outfile->name);
MesPrint("%l", AR.hidefile-AR.Fscr);
MesPrint("%s", AR.hidefile->name);
for ( i=0; i<3; ++i ) {
MesPrint("FSCR %d", i);
print_WORDB(AR.Fscr[i].PObuffer, AR.Fscr[i].POfull);
}
/* ... */
MesPrint("%l", AR.OldTime);
MesPrint("%l", AR.InInBuf);
MesPrint("%l", AR.InHiBuf);
MesPrint("%l", AR.pWorkSize);
MesPrint("%l", AR.lWorkSize);
MesPrint("%l", AR.posWorkSize);
MesPrint("%d", AR.NoCompress);
MesPrint("%d", AR.gzipCompress);
MesPrint("%d", AR.Cnumlhs);
#ifdef WITHPTHREADS
MesPrint("%d", AR.exprtodo);
#endif
MesPrint("%d", AR.GetFile);
MesPrint("%d", AR.KeptInHold);
MesPrint("%d", AR.BracketOn);
MesPrint("%d", AR.MaxBracket);
MesPrint("%d", AR.CurDum);
MesPrint("%d", AR.DeferFlag);
MesPrint("%d", AR.TePos);
MesPrint("%d", AR.sLevel);
MesPrint("%d", AR.Stage4Name);
MesPrint("%d", AR.GetOneFile);
MesPrint("%d", AR.PolyFun);
MesPrint("%d", AR.PolyFunInv);
MesPrint("%d", AR.PolyFunType);
MesPrint("%d", AR.PolyFunExp);
MesPrint("%d", AR.PolyFunVar);
MesPrint("%d", AR.PolyFunPow);
MesPrint("%d", AR.Eside);
MesPrint("%d", AR.MaxDum);
MesPrint("%d", AR.level);
MesPrint("%d", AR.expchanged);
MesPrint("%d", AR.expflags);
MesPrint("%d", AR.CurExpr);
MesPrint("%d", AR.SortType);
MesPrint("%d", AR.ShortSortCount);
MesPrint("%%%% END R_const");
/* fflush(0); */
}
#endif /* ifdef PRINTDEBUG */
/*
#] Debugging :
#[ Cached file operation functions :
*/
#define CACHED_SNAPSHOT
#define CACHE_SIZE 4096
#ifdef CACHED_SNAPSHOT
unsigned char cache_buffer[CACHE_SIZE];
size_t cache_fill = 0;
size_t fwrite_cached(const void *ptr, size_t size, size_t nmemb, FILE *fd)
{
size_t fullsize = size*nmemb;
if ( fullsize+cache_fill >= CACHE_SIZE ) {
size_t overlap = CACHE_SIZE-cache_fill;
memcpy(cache_buffer+cache_fill, (unsigned char*)ptr, overlap);
if ( fwrite(cache_buffer, 1, CACHE_SIZE, fd) != CACHE_SIZE ) return 0;
fullsize -= overlap;
if ( fullsize >= CACHE_SIZE ) {
cache_fill = fullsize % CACHE_SIZE;
if ( cache_fill ) memcpy(cache_buffer, (unsigned char*)ptr+overlap+fullsize-cache_fill, cache_fill);
if ( fwrite((unsigned char*)ptr+overlap, 1, fullsize-cache_fill, fd) != fullsize-cache_fill ) return 0;
}
else {
memcpy(cache_buffer, (unsigned char*)ptr+overlap, fullsize);
cache_fill = fullsize;
}
}
else {
memcpy(cache_buffer+cache_fill, (unsigned char*)ptr, fullsize);
cache_fill += fullsize;
}
return nmemb;
}
size_t flush_cache(FILE *fd)
{
if ( cache_fill ) {
size_t retval = fwrite(cache_buffer, 1, cache_fill, fd);
if ( retval != cache_fill ) {
cache_fill = 0;
return 0;
}
cache_fill = 0;
}
return 1;
}
#else
size_t fwrite_cached(const void *ptr, size_t size, size_t nmemb, FILE *fd)
{
return fwrite(ptr, size, nmemb, fd);
}
size_t flush_cache(FILE *fd)
{
DUMMYUSE(fd)
return 1;
}
#endif
/*
#] Cached file operation functions :
#[ Helper Macros :
*/
/* some helper macros to streamline the code in DoSnapshot() and DoRecovery() */
/* freeing memory */
#define R_FREE(ARG) \
if ( ARG ) M_free(ARG, #ARG);
#define R_FREE_NAMETREE(ARG) \
R_FREE(ARG->namenode); \
R_FREE(ARG->namebuffer); \
R_FREE(ARG);
#define R_FREE_STREAM(ARG) \
R_FREE(ARG.buffer); \
R_FREE(ARG.FoldName); \
R_FREE(ARG.name);
/* reading a single variable */
#define R_SET(VAR,TYPE) \
VAR = *((TYPE*)p); p = (unsigned char*)p + sizeof(TYPE);
/* general buffer */
#define R_COPY_B(VAR,SIZE,CAST) \
VAR = (CAST)Malloc1(SIZE,#VAR); \
memcpy(VAR, p, SIZE); p = (unsigned char*)p + SIZE;
#define S_WRITE_B(BUF,LEN) \
if ( fwrite_cached(BUF, 1, LEN, fd) != (size_t)(LEN) ) return(__LINE__);
#define S_FLUSH_B \
if ( flush_cache(fd) != 1 ) return(__LINE__);
/* character strings */
#define R_COPY_S(VAR,CAST) \
if ( VAR ) { \
VAR = (CAST)Malloc1(strlen(p)+1,"R_COPY_S"); \
strcpy((char*)VAR, p); p = (unsigned char*)p + strlen(p) + 1; \
}
#define S_WRITE_S(STR) \
if ( STR ) { \
l = strlen((char*)STR) + 1; \
if ( fwrite_cached(STR, 1, l, fd) != (size_t)l ) return(__LINE__); \
}
/* LIST */
#define R_COPY_LIST(ARG) \
if ( ARG.maxnum ) { \
R_COPY_B(ARG.lijst, ARG.size*ARG.maxnum, void*) \
}
#define S_WRITE_LIST(LST) \
if ( LST.maxnum ) { \
S_WRITE_B((char*)LST.lijst, LST.maxnum*LST.size) \
}
/* NAMETREE */
#define R_COPY_NAMETREE(ARG) \
R_COPY_B(ARG, sizeof(NAMETREE), NAMETREE*); \
if ( ARG->namenode ) { \
R_COPY_B(ARG->namenode, ARG->nodesize*sizeof(NAMENODE), NAMENODE*); \
} \
if ( ARG->namebuffer ) { \
R_COPY_B(ARG->namebuffer, ARG->namesize, UBYTE*); \
}
#define S_WRITE_NAMETREE(ARG) \
S_WRITE_B(ARG, sizeof(NAMETREE)); \
if ( ARG->namenode ) { \
S_WRITE_B(ARG->namenode, ARG->nodesize*sizeof(struct NaMeNode)); \
} \
if ( ARG->namebuffer ) { \
S_WRITE_B(ARG->namebuffer, ARG->namesize); \
}
/* DOLLAR */
#define S_WRITE_DOLLAR(ARG) \
if ( ARG.size && ARG.where && ARG.where != &(AM.dollarzero) ) { \
S_WRITE_B(ARG.where, ARG.size*sizeof(WORD)) \
}
/* Printing time marks with ANNOUNCE macro */
#ifdef PRINTTIMEMARKS
time_t announce_time;
#define ANNOUNCE(str) time(&announce_time); MesPrint("TIMEMARK %s %s", ctime(&announce_time), #str);
#else
#define ANNOUNCE(str)
#endif
/*
#] Helper Macros :
#[ DoRecovery :
*/
/**
* Reads from the recovery file and restores all necessary variables and
* states in FORM, so that the execution can recommence in preprocessor() as
* if no restart of FORM had occurred.
*
* The recovery file is read into memory as a whole. The pointer p then points
* into this memory at the next non-processed data. The macros by which
* variables are restored, like R_SET, automatically increase p appropriately.
*
* If something goes wrong, the function returns with a non-zero value.
*
* Allocated memory that would be lost when overwriting the global structs with
* data from the file is freed first. A major part of the code deals with the
* restoration of pointers. The idiom we use is to memorize the original
* pointer value (org), allocate new memory and copy the data from the file
* into this memory, calculate the offset between the old pointer value
* and the new allocated memory position (ofs), and then correct all affected
* pointers (+=ofs).
*
* We rely on the fact that several variables (especially in AM) are already
* assigned the correct values by the startup functions. That means, in
* principle, that a change in the setup files between snapshot creation and
* recovery will be noticed.
*/
int DoRecovery(int *moduletype)
{
GETIDENTITY
FILE *fd;
POSITION pos;
void *buf, *p;
LONG size, l;
int i, j;
UBYTE *org;
char *namebufout, *namebufhide;
LONG ofs;
void *oldAMdollarzero;
LIST PotModDolListBackup;
LIST ModOptDolListBackup;
WORD oldLogHandle;
MesPrint("Recovering ... %"); fflush(0);
if ( !(fd = fopen(recoveryfile, "r")) ) return(__LINE__);
/* load the complete recovery file into a buffer */
if ( fread(&pos, sizeof(POSITION), 1, fd) != 1 ) return(__LINE__);
size = BASEPOSITION(pos) - sizeof(POSITION);
buf = Malloc1(size, "recovery buffer");
if ( fread(buf, size, 1, fd) != 1 ) return(__LINE__);
/* pointer p will go through the buffer in the following */
p = buf;
/* read moduletype */
R_SET(*moduletype, int);
/*#[ AM : */
/* only certain elements will be restored. the rest of AM should have gotten
* the correct values at startup. */
R_SET(AM.hparallelflag, int);
R_SET(AM.gparallelflag, int);
R_SET(AM.gCodesFlag, int);
R_SET(AM.gNamesFlag, int);
R_SET(AM.gStatsFlag, int);
R_SET(AM.gTokensWriteFlag, int);
R_SET(AM.gNoSpacesInNumbers, int);
R_SET(AM.gIndentSpace, WORD);
R_SET(AM.gUnitTrace, WORD);
R_SET(AM.gDefDim, int);
R_SET(AM.gDefDim4, int);
R_SET(AM.gncmod, WORD);
R_SET(AM.gnpowmod, WORD);
R_SET(AM.gmodmode, WORD);
R_SET(AM.gOutputMode, WORD);
R_SET(AM.gCnumpows, WORD);
R_SET(AM.gOutputSpaces, WORD);
R_SET(AM.gOutNumberType, WORD);
R_SET(AM.gfunpowers, int);
R_SET(AM.gPolyFun, WORD);
R_SET(AM.gPolyFunInv, WORD);
R_SET(AM.gPolyFunType, WORD);
R_SET(AM.gPolyFunExp, WORD);
R_SET(AM.gPolyFunVar, WORD);
R_SET(AM.gPolyFunPow, WORD);
R_SET(AM.gProcessBucketSize, LONG);
R_SET(AM.OldChildTime, LONG);
R_SET(AM.OldSecTime, LONG);
R_SET(AM.OldMilliTime, LONG);
R_SET(AM.gproperorderflag, int);
R_SET(AM.gThreadBucketSize, LONG);
R_SET(AM.gSizeCommuteInSet, int);
R_SET(AM.gThreadStats, int);
R_SET(AM.gFinalStats, int);
R_SET(AM.gThreadsFlag, int);
R_SET(AM.gThreadBalancing, int);
R_SET(AM.gThreadSortFileSynch, int);
R_SET(AM.gProcessStats, int);
R_SET(AM.gOldParallelStats, int);
R_SET(AM.gSortType, int);
R_SET(AM.gShortStatsMax, WORD);
R_SET(AM.gIsFortran90, int);
R_SET(oldAMdollarzero, void*);
R_FREE(AM.gFortran90Kind);
R_SET(AM.gFortran90Kind,UBYTE *);
R_COPY_S(AM.gFortran90Kind,UBYTE *);
R_COPY_S(AM.gextrasym,UBYTE *);
R_COPY_S(AM.ggextrasym,UBYTE *);
R_SET(AM.PrintTotalSize,int);
R_SET(AM.fbuffersize,int);
R_SET(AM.gOldFactArgFlag,int);
R_SET(AM.ggOldFactArgFlag,int);
R_SET(AM.gnumextrasym,int);
R_SET(AM.ggnumextrasym,int);
R_SET(AM.NumSpectatorFiles,int);
R_SET(AM.SizeForSpectatorFiles,int);
R_SET(AM.gOldGCDflag,int);
R_SET(AM.ggOldGCDflag,int);
R_SET(AM.gWTimeStatsFlag, int);
R_FREE(AM.Path);
R_SET(AM.Path,UBYTE *);
R_COPY_S(AM.Path,UBYTE *);
#ifdef PRINTDEBUG
print_M();
#endif
/*#] AM : */
/*#[ AC : */
/* #[ AC free pointers */
/* AC will be overwritten by data from the recovery file, therefore
* dynamically allocated memory must be freed first. */
R_FREE_NAMETREE(AC.dollarnames);
R_FREE_NAMETREE(AC.exprnames);
R_FREE_NAMETREE(AC.varnames);
for ( i=0; ibuffers);
R_FREE(T->mm);
R_FREE(T->flags);
R_FREE(T->prototype);
R_FREE(T->tablepointers);
if ( T->sparse ) {
R_FREE(T->boomlijst);
R_FREE(T->argtail);
}
if ( T->spare ) {
R_FREE(T->spare->buffers);
R_FREE(T->spare->mm);
R_FREE(T->spare->flags);
R_FREE(T->spare->tablepointers);
if ( T->spare->sparse ) {
R_FREE(T->spare->boomlijst);
}
R_FREE(T->spare);
}
R_FREE(T);
}
}
R_FREE(AC.FunctionList.lijst);
for ( i=0; isymb.lo);
R_FREE(Expressions[i].renum);
}
if ( Expressions[i].bracketinfo ) {
R_FREE(Expressions[i].bracketinfo->indexbuffer);
R_FREE(Expressions[i].bracketinfo->bracketbuffer);
R_FREE(Expressions[i].bracketinfo);
}
if ( Expressions[i].newbracketinfo ) {
R_FREE(Expressions[i].newbracketinfo->indexbuffer);
R_FREE(Expressions[i].newbracketinfo->bracketbuffer);
R_FREE(Expressions[i].newbracketinfo);
}
if ( Expressions[i].renumlists != AN.dummyrenumlist ) {
R_FREE(Expressions[i].renumlists);
}
R_FREE(Expressions[i].inmem);
}
R_FREE(AC.ExpressionList.lijst);
R_FREE(AC.IndexList.lijst);
R_FREE(AC.SetElementList.lijst);
R_FREE(AC.SetList.lijst);
R_FREE(AC.SymbolList.lijst);
R_FREE(AC.VectorList.lijst);
for ( i=0; itablepointers ) {
if ( tabl->sparse ) {
R_COPY_B(tabl->tablepointers,
tabl->reserved*sizeof(WORD)*(tabl->numind+TABLEEXTENSION),
WORD*);
}
else {
R_COPY_B(tabl->tablepointers,
TABLEEXTENSION*sizeof(WORD)*(tabl->totind), WORD*);
}
}
org = (UBYTE*)tabl->prototype;
#ifdef WITHPTHREADS
R_COPY_B(tabl->prototype, tabl->prototypeSize, WORD**);
ofs = (UBYTE*)tabl->prototype - org;
for ( j=0; jprototype[j] ) {
tabl->prototype[j] = (WORD*)((UBYTE*)tabl->prototype[j] + ofs);
}
}
if ( tabl->pattern ) {
tabl->pattern = (WORD**)((UBYTE*)tabl->pattern + ofs);
for ( j=0; jpattern[j] ) {
tabl->pattern[j] = (WORD*)((UBYTE*)tabl->pattern[j] + ofs);
}
}
}
#else
ofs = tabl->pattern - tabl->prototype;
R_COPY_B(tabl->prototype, tabl->prototypeSize, WORD*);
if ( tabl->pattern ) {
tabl->pattern = tabl->prototype + ofs;
}
#endif
R_COPY_B(tabl->mm, tabl->numind*(LONG)sizeof(MINMAX), MINMAX*);
R_COPY_B(tabl->flags, tabl->numind*(LONG)sizeof(WORD), WORD*);
if ( tabl->sparse ) {
R_COPY_B(tabl->boomlijst, tabl->MaxTreeSize*(LONG)sizeof(COMPTREE), COMPTREE*);
R_COPY_S(tabl->argtail,UBYTE*);
}
R_COPY_B(tabl->buffers, tabl->bufferssize*(LONG)sizeof(WORD), WORD*);
if ( tabl->spare ) {
TABLES spare;
R_COPY_B(spare, sizeof(struct TaBlEs), TABLES);
tabl->spare = spare;
if ( spare->tablepointers ) {
if ( spare->sparse ) {
R_COPY_B(spare->tablepointers,
spare->reserved*sizeof(WORD)*(spare->numind+TABLEEXTENSION),
WORD*);
}
else {
R_COPY_B(spare->tablepointers,
TABLEEXTENSION*sizeof(WORD)*(spare->totind), WORD*);
}
}
spare->prototype = tabl->prototype;
spare->pattern = tabl->pattern;
R_COPY_B(spare->mm, spare->numind*(LONG)sizeof(MINMAX), MINMAX*);
R_COPY_B(spare->flags, spare->numind*(LONG)sizeof(WORD), WORD*);
if ( tabl->sparse ) {
R_COPY_B(spare->boomlijst, spare->MaxTreeSize*(LONG)sizeof(COMPTREE), COMPTREE*);
spare->argtail = tabl->argtail;
}
spare->spare = tabl;
R_COPY_B(spare->buffers, spare->bufferssize*(LONG)sizeof(WORD), WORD*);
}
}
}
AC.FunctionList.message = "function";
R_COPY_LIST(AC.ExpressionList);
for ( i=0; irenum ) {
R_COPY_B(ex->renum, sizeof(struct ReNuMbEr), RENUMBER);
org = (UBYTE*)ex->renum->symb.lo;
R_SET(size, size_t);
R_COPY_B(ex->renum->symb.lo, size, WORD*);
ofs = (UBYTE*)ex->renum->symb.lo - org;
ex->renum->symb.start = (WORD*)((UBYTE*)ex->renum->symb.start + ofs);
ex->renum->symb.hi = (WORD*)((UBYTE*)ex->renum->symb.hi + ofs);
ex->renum->indi.lo = (WORD*)((UBYTE*)ex->renum->indi.lo + ofs);
ex->renum->indi.start = (WORD*)((UBYTE*)ex->renum->indi.start + ofs);
ex->renum->indi.hi = (WORD*)((UBYTE*)ex->renum->indi.hi + ofs);
ex->renum->vect.lo = (WORD*)((UBYTE*)ex->renum->vect.lo + ofs);
ex->renum->vect.start = (WORD*)((UBYTE*)ex->renum->vect.start + ofs);
ex->renum->vect.hi = (WORD*)((UBYTE*)ex->renum->vect.hi + ofs);
ex->renum->func.lo = (WORD*)((UBYTE*)ex->renum->func.lo + ofs);
ex->renum->func.start = (WORD*)((UBYTE*)ex->renum->func.start + ofs);
ex->renum->func.hi = (WORD*)((UBYTE*)ex->renum->func.hi + ofs);
ex->renum->symnum = (WORD*)((UBYTE*)ex->renum->symnum + ofs);
ex->renum->indnum = (WORD*)((UBYTE*)ex->renum->indnum + ofs);
ex->renum->vecnum = (WORD*)((UBYTE*)ex->renum->vecnum + ofs);
ex->renum->funnum = (WORD*)((UBYTE*)ex->renum->funnum + ofs);
}
if ( ex->bracketinfo ) {
R_COPY_B(ex->bracketinfo, sizeof(BRACKETINFO), BRACKETINFO*);
R_COPY_B(ex->bracketinfo->indexbuffer, ex->bracketinfo->indexbuffersize*sizeof(BRACKETINDEX), BRACKETINDEX*);
R_COPY_B(ex->bracketinfo->bracketbuffer, ex->bracketinfo->bracketbuffersize*sizeof(WORD), WORD*);
}
if ( ex->newbracketinfo ) {
R_COPY_B(ex->newbracketinfo, sizeof(BRACKETINFO), BRACKETINFO*);
R_COPY_B(ex->newbracketinfo->indexbuffer, ex->newbracketinfo->indexbuffersize*sizeof(BRACKETINDEX), BRACKETINDEX*);
R_COPY_B(ex->newbracketinfo->bracketbuffer, ex->newbracketinfo->bracketbuffersize*sizeof(WORD), WORD*);
}
#ifdef WITHPTHREADS
ex->renumlists = 0;
#else
ex->renumlists = AN.dummyrenumlist;
#endif
if ( ex->inmem ) {
R_SET(size, size_t);
R_COPY_B(ex->inmem, size, WORD*);
}
}
AC.ExpressionList.message = "expression";
R_COPY_LIST(AC.IndexList);
AC.IndexList.message = "index";
R_COPY_LIST(AC.SetElementList);
AC.SetElementList.message = "set element";
R_COPY_LIST(AC.SetList);
AC.SetList.message = "set";
R_COPY_LIST(AC.SymbolList);
AC.SymbolList.message = "symbol";
R_COPY_LIST(AC.VectorList);
AC.VectorList.message = "vector";
AC.PotModDolList = PotModDolListBackup;
AC.ModOptDolList = ModOptDolListBackup;
R_COPY_LIST(AC.TableBaseList);
for ( i=0; isize * sizeof(WORD);
if ( size && d->where && d->where != oldAMdollarzero ) {
R_COPY_B(d->where, size, void*);
}
#ifdef WITHPTHREADS
d->pthreadslockread = dummylock;
d->pthreadslockwrite = dummylock;
#endif
if ( d->nfactors > 1 ) {
R_COPY_B(d->factors,sizeof(FACDOLLAR)*d->nfactors,FACDOLLAR*);
for ( j = 0; j < d->nfactors; j++ ) {
if ( d->factors[j].size > 0 ) {
R_COPY_B(d->factors[i].where,sizeof(WORD)*(d->factors[j].size+1),WORD*);
}
}
}
}
AP.DollarList.message = "$-variable";
R_COPY_LIST(AP.PreVarList);
for ( i=0; iname;
namebufhide = AR.hidefile->name;
R_FREE(AR.outfile->PObuffer);
#ifdef WITHZLIB
R_FREE(AR.outfile->zsp);
R_FREE(AR.outfile->ziobuffer);
#endif
namebufhide = AR.hidefile->name;
R_FREE(AR.hidefile->PObuffer);
#ifdef WITHZLIB
R_FREE(AR.hidefile->zsp);
R_FREE(AR.hidefile->ziobuffer);
#endif
/* no files should be opened -> nothing to do with handle */
/* #] AR free pointers */
/* outfile */
R_SET(*AR.outfile, FILEHANDLE);
org = (UBYTE*)AR.outfile->PObuffer;
size = AR.outfile->POfull - AR.outfile->PObuffer;
AR.outfile->PObuffer = (WORD*)Malloc1(AR.outfile->POsize, "PObuffer");
if ( size ) {
memcpy(AR.outfile->PObuffer, p, size*sizeof(WORD));
p = (unsigned char*)p + size*sizeof(WORD);
}
ofs = (UBYTE*)AR.outfile->PObuffer - org;
AR.outfile->POstop = (WORD*)((UBYTE*)AR.outfile->POstop + ofs);
AR.outfile->POfill = (WORD*)((UBYTE*)AR.outfile->POfill + ofs);
AR.outfile->POfull = (WORD*)((UBYTE*)AR.outfile->POfull + ofs);
AR.outfile->name = namebufout;
#ifdef WITHPTHREADS
AR.outfile->wPObuffer = AR.outfile->PObuffer;
AR.outfile->wPOstop = AR.outfile->POstop;
AR.outfile->wPOfill = AR.outfile->POfill;
AR.outfile->wPOfull = AR.outfile->POfull;
#endif
#ifdef WITHZLIB
/* zsp and ziobuffer will be allocated when used */
AR.outfile->zsp = 0;
AR.outfile->ziobuffer = 0;
#endif
/* reopen old outfile */
#ifdef WITHMPI
if(PF.me==MASTER)
#endif
if ( AR.outfile->handle >= 0 ) {
if ( CopyFile(sortfile, AR.outfile->name) ) {
MesPrint("ERROR: Could not copy old output sort file %s!",sortfile);
Terminate(-1);
}
AR.outfile->handle = ReOpenFile(AR.outfile->name);
if ( AR.outfile->handle == -1 ) {
MesPrint("ERROR: Could not reopen output sort file %s!",AR.outfile->name);
Terminate(-1);
}
SeekFile(AR.outfile->handle, &AR.outfile->POposition, SEEK_SET);
}
/* hidefile */
R_SET(*AR.hidefile, FILEHANDLE);
AR.hidefile->name = namebufhide;
if ( AR.hidefile->PObuffer ) {
org = (UBYTE*)AR.hidefile->PObuffer;
size = AR.hidefile->POfull - AR.hidefile->PObuffer;
AR.hidefile->PObuffer = (WORD*)Malloc1(AR.hidefile->POsize, "PObuffer");
if ( size ) {
memcpy(AR.hidefile->PObuffer, p, size*sizeof(WORD));
p = (unsigned char*)p + size*sizeof(WORD);
}
ofs = (UBYTE*)AR.hidefile->PObuffer - org;
AR.hidefile->POstop = (WORD*)((UBYTE*)AR.hidefile->POstop + ofs);
AR.hidefile->POfill = (WORD*)((UBYTE*)AR.hidefile->POfill + ofs);
AR.hidefile->POfull = (WORD*)((UBYTE*)AR.hidefile->POfull + ofs);
#ifdef WITHPTHREADS
AR.hidefile->wPObuffer = AR.hidefile->PObuffer;
AR.hidefile->wPOstop = AR.hidefile->POstop;
AR.hidefile->wPOfill = AR.hidefile->POfill;
AR.hidefile->wPOfull = AR.hidefile->POfull;
#endif
}
#ifdef WITHZLIB
/* zsp and ziobuffer will be allocated when used */
AR.hidefile->zsp = 0;
AR.hidefile->ziobuffer = 0;
#endif
/* reopen old hidefile */
if ( AR.hidefile->handle >= 0 ) {
if ( CopyFile(hidefile, AR.hidefile->name) ) {
MesPrint("ERROR: Could not copy old hide file %s!",hidefile);
Terminate(-1);
}
AR.hidefile->handle = ReOpenFile(AR.hidefile->name);
if ( AR.hidefile->handle == -1 ) {
MesPrint("ERROR: Could not reopen hide file %s!",AR.hidefile->name);
Terminate(-1);
}
SeekFile(AR.hidefile->handle, &AR.hidefile->POposition, SEEK_SET);
}
/* store file */
R_SET(pos, POSITION);
if ( ISNOTZEROPOS(pos) ) {
CloseFile(AR.StoreData.Handle);
R_SET(AR.StoreData, FILEDATA);
if ( CopyFile(storefile, FG.fname) ) {
MesPrint("ERROR: Could not copy old store file %s!",storefile);
Terminate(-1);
}
AR.StoreData.Handle = (WORD)ReOpenFile(FG.fname);
SeekFile(AR.StoreData.Handle, &AR.StoreData.Position, SEEK_SET);
}
R_SET(AR.DefPosition, POSITION);
R_SET(AR.OldTime, LONG);
R_SET(AR.InInBuf, LONG);
R_SET(AR.InHiBuf, LONG);
R_SET(AR.NoCompress, int);
R_SET(AR.gzipCompress, int);
R_SET(AR.outtohide, int);
R_SET(AR.GetFile, WORD);
R_SET(AR.KeptInHold, WORD);
R_SET(AR.BracketOn, WORD);
R_SET(AR.MaxBracket, WORD);
R_SET(AR.CurDum, WORD);
R_SET(AR.DeferFlag, WORD);
R_SET(AR.TePos, WORD);
R_SET(AR.sLevel, WORD);
R_SET(AR.Stage4Name, WORD);
R_SET(AR.GetOneFile, WORD);
R_SET(AR.PolyFun, WORD);
R_SET(AR.PolyFunInv, WORD);
R_SET(AR.PolyFunType, WORD);
R_SET(AR.PolyFunExp, WORD);
R_SET(AR.PolyFunVar, WORD);
R_SET(AR.PolyFunPow, WORD);
R_SET(AR.Eside, WORD);
R_SET(AR.MaxDum, WORD);
R_SET(AR.level, WORD);
R_SET(AR.expchanged, WORD);
R_SET(AR.expflags, WORD);
R_SET(AR.CurExpr, WORD);
R_SET(AR.SortType, WORD);
R_SET(AR.ShortSortCount, WORD);
/* this is usually done in Process(), but sometimes FORM doesn't
end up executing Process() before it uses the AR.CompressPointer,
so we need to explicitely set it here. */
AR.CompressPointer = AR.CompressBuffer;
#ifdef WITHPTHREADS
for ( j = 0; j < AM.totalnumberofthreads; j++ ) {
R_SET(AB[j]->R.wranfnpair1, int);
R_SET(AB[j]->R.wranfnpair2, int);
R_SET(AB[j]->R.wranfcall, int);
R_SET(AB[j]->R.wranfseed, ULONG);
R_SET(AB[j]->R.wranfia,ULONG*);
if ( AB[j]->R.wranfia ) {
R_COPY_B(AB[j]->R.wranfia, sizeof(ULONG)*AB[j]->R.wranfnpair2, ULONG*);
}
}
#else
R_SET(AR.wranfnpair1, int);
R_SET(AR.wranfnpair2, int);
R_SET(AR.wranfcall, int);
R_SET(AR.wranfseed, ULONG);
R_SET(AR.wranfia,ULONG*);
if ( AR.wranfia ) {
R_COPY_B(AR.wranfia, sizeof(ULONG)*AR.wranfnpair2, ULONG*);
}
#endif
#ifdef PRINTDEBUG
print_R();
#endif
/*#] AR : */
/*#[ AO :*/
/*
We copy all non-pointer variables.
*/
l = sizeof(A.O) - ((UBYTE *)(&(A.O.NumInBrack))-(UBYTE *)(&A.O));
memcpy(&(A.O.NumInBrack), p, l); p = (unsigned char*)p + l;
/*
Now the variables in OptimizeResult
*/
memcpy(&(A.O.OptimizeResult),p,sizeof(OPTIMIZERESULT));
p = (unsigned char*)p + sizeof(OPTIMIZERESULT);
if ( A.O.OptimizeResult.codesize > 0 ) {
R_COPY_B(A.O.OptimizeResult.code,A.O.OptimizeResult.codesize*sizeof(WORD),WORD *);
}
R_COPY_S(A.O.OptimizeResult.nameofexpr,UBYTE *);
/*
And now the dictionaries. We know how many there are. We also know
how many elements the array AO.Dictionaries should have.
*/
if ( AO.SizeDictionaries > 0 ) {
AO.Dictionaries = (DICTIONARY **)Malloc1(AO.SizeDictionaries*sizeof(DICTIONARY *),
"Dictionaries");
for ( i = 0; i < AO.NumDictionaries; i++ ) {
R_SET(l,LONG)
AO.Dictionaries[i] = DictFromBytes(p);
p = (char *)p + l;
}
}
/*#] AO :*/
#ifdef WITHMPI
/*#[ PF : */
{/*Block*/
int numtasks;
R_SET(numtasks, int);
if(numtasks!=PF.numtasks){
MesPrint("%d number of tasks expected instead of %d; use mpirun -np %d",
numtasks,PF.numtasks,numtasks);
if(PF.me!=MASTER)
remove(RecoveryFilename());
Terminate(-1);
}
}/*Block*/
R_SET(PF.rhsInParallel, int);
R_SET(PF.exprbufsize, int);
R_SET(PF.log, int);
/*#] PF : */
#endif
#ifdef WITHPTHREADS
/* read timing information of individual threads */
R_SET(i, int);
for ( j=1; jR.OldTime = -(*((LONG*)p+j));
}
WriteTimerInfo((LONG*)p,(LONG *)((unsigned char*)p + i*(LONG)sizeof(LONG)));
p = (unsigned char*)p + 2*i*(LONG)sizeof(LONG);
#endif /* ifdef WITHPTHREADS */
if ( fclose(fd) ) return(__LINE__);
M_free(buf,"recovery buffer");
/* cares about data in S_const */
UpdatePositions();
AT.SS = AT.S0;
/*
Set the checkpoint parameter right for the next checkpoint.
*/
AC.CheckpointStamp = TimeWallClock(1);
done_snapshot = 1;
MesPrint("done."); fflush(0);
return(0);
}
/*
#] DoRecovery :
#[ DoSnapshot :
*/
/**
* Writes all relevant information for a recovery to the recovery file. It
* writes first to an intermediate file and then only if everything went well
* it renames this intermediate file to the final recovery file. Then it copies
* the sort and store files if necessary.
*
* The data is directly written to file from the structs or struct element.
*
* No data is changed in the global structs and this function should never crash.
* Honorably exception might be: not enough memory for the allocation of the
* command strings (usually less than 100 bytes), or not enough disk space for
* the recovery file and the copies of the hide/scratch/store files.
*
* If something goes wrong, the function returns with a non-zero value.
*/
static int DoSnapshot(int moduletype)
{
GETIDENTITY
FILE *fd;
POSITION pos;
int i, j;
LONG l;
WORD *w;
void *adr;
#ifdef WITHPTHREADS
LONG *longp,*longpp;
#endif /* ifdef WITHPTHREADS */
MesPrint("Saving recovery point ... %"); fflush(0);
#ifdef PRINTTIMEMARKS
MesPrint("\n");
#endif
if ( !(fd = fopen(intermedfile, "wb")) ) return(__LINE__);
/* reserve space in the file for a length field */
if ( fwrite(&pos, 1, sizeof(POSITION), fd) != sizeof(POSITION) ) return(__LINE__);
/* write moduletype */
if ( fwrite(&moduletype, 1, sizeof(int), fd) != sizeof(int) ) return(__LINE__);
/*#[ AM :*/
/* since most values don't change during execution, AM doesn't need to be
* written as a whole. all values will be correctly set when starting up
* anyway. only the exceptions need to be taken care of. see MakeGlobal()
* and PopVariables() in execute.c. */
ANNOUNCE(AM)
S_WRITE_B(&AM.hparallelflag, sizeof(int));
S_WRITE_B(&AM.gparallelflag, sizeof(int));
S_WRITE_B(&AM.gCodesFlag, sizeof(int));
S_WRITE_B(&AM.gNamesFlag, sizeof(int));
S_WRITE_B(&AM.gStatsFlag, sizeof(int));
S_WRITE_B(&AM.gTokensWriteFlag, sizeof(int));
S_WRITE_B(&AM.gNoSpacesInNumbers, sizeof(int));
S_WRITE_B(&AM.gIndentSpace, sizeof(WORD));
S_WRITE_B(&AM.gUnitTrace, sizeof(WORD));
S_WRITE_B(&AM.gDefDim, sizeof(int));
S_WRITE_B(&AM.gDefDim4, sizeof(int));
S_WRITE_B(&AM.gncmod, sizeof(WORD));
S_WRITE_B(&AM.gnpowmod, sizeof(WORD));
S_WRITE_B(&AM.gmodmode, sizeof(WORD));
S_WRITE_B(&AM.gOutputMode, sizeof(WORD));
S_WRITE_B(&AM.gCnumpows, sizeof(WORD));
S_WRITE_B(&AM.gOutputSpaces, sizeof(WORD));
S_WRITE_B(&AM.gOutNumberType, sizeof(WORD));
S_WRITE_B(&AM.gfunpowers, sizeof(int));
S_WRITE_B(&AM.gPolyFun, sizeof(WORD));
S_WRITE_B(&AM.gPolyFunInv, sizeof(WORD));
S_WRITE_B(&AM.gPolyFunType, sizeof(WORD));
S_WRITE_B(&AM.gPolyFunExp, sizeof(WORD));
S_WRITE_B(&AM.gPolyFunVar, sizeof(WORD));
S_WRITE_B(&AM.gPolyFunPow, sizeof(WORD));
S_WRITE_B(&AM.gProcessBucketSize, sizeof(LONG));
S_WRITE_B(&AM.OldChildTime, sizeof(LONG));
S_WRITE_B(&AM.OldSecTime, sizeof(LONG));
S_WRITE_B(&AM.OldMilliTime, sizeof(LONG));
S_WRITE_B(&AM.gproperorderflag, sizeof(int));
S_WRITE_B(&AM.gThreadBucketSize, sizeof(LONG));
S_WRITE_B(&AM.gSizeCommuteInSet, sizeof(int));
S_WRITE_B(&AM.gThreadStats, sizeof(int));
S_WRITE_B(&AM.gFinalStats, sizeof(int));
S_WRITE_B(&AM.gThreadsFlag, sizeof(int));
S_WRITE_B(&AM.gThreadBalancing, sizeof(int));
S_WRITE_B(&AM.gThreadSortFileSynch, sizeof(int));
S_WRITE_B(&AM.gProcessStats, sizeof(int));
S_WRITE_B(&AM.gOldParallelStats, sizeof(int));
S_WRITE_B(&AM.gSortType, sizeof(int));
S_WRITE_B(&AM.gShortStatsMax, sizeof(WORD));
S_WRITE_B(&AM.gIsFortran90, sizeof(int));
adr = &AM.dollarzero;
S_WRITE_B(&adr, sizeof(void*));
S_WRITE_B(&AM.gFortran90Kind,sizeof(UBYTE *));
S_WRITE_S(AM.gFortran90Kind);
S_WRITE_S(AM.gextrasym);
S_WRITE_S(AM.ggextrasym);
S_WRITE_B(&AM.PrintTotalSize,sizeof(int));
S_WRITE_B(&AM.fbuffersize,sizeof(int));
S_WRITE_B(&AM.gOldFactArgFlag,sizeof(int));
S_WRITE_B(&AM.ggOldFactArgFlag,sizeof(int));
S_WRITE_B(&AM.gnumextrasym,sizeof(int));
S_WRITE_B(&AM.ggnumextrasym,sizeof(int));
S_WRITE_B(&AM.NumSpectatorFiles,sizeof(int));
S_WRITE_B(&AM.SizeForSpectatorFiles,sizeof(int));
S_WRITE_B(&AM.gOldGCDflag,sizeof(int));
S_WRITE_B(&AM.ggOldGCDflag,sizeof(int));
S_WRITE_B(&AM.gWTimeStatsFlag, sizeof(int));
S_WRITE_B(&AM.Path,sizeof(UBYTE *));
S_WRITE_S(AM.Path);
/*#] AM :*/
/*#[ AC :*/
/* we write AC as a whole and then write all additional data step by step.
* AC.DubiousList doesn't need to be treated, because it should be empty. */
ANNOUNCE(AC)
S_WRITE_B(&AC, sizeof(struct C_const));
S_WRITE_NAMETREE(AC.dollarnames);
S_WRITE_NAMETREE(AC.exprnames);
S_WRITE_NAMETREE(AC.varnames);
S_WRITE_LIST(AC.ChannelList);
for ( i=0; itablepointers ) {
if ( tabl->sparse ) {
/* sparse tables. reserved holds number of allocated
* elements. the size of an element is numind plus
* TABLEEXTENSION times the size of WORD. */
S_WRITE_B(tabl->tablepointers,
tabl->reserved*sizeof(WORD)*(tabl->numind+TABLEEXTENSION));
}
else {
/* matrix like tables. */
S_WRITE_B(tabl->tablepointers,
TABLEEXTENSION*sizeof(WORD)*(tabl->totind));
}
}
S_WRITE_B(tabl->prototype, tabl->prototypeSize);
S_WRITE_B(tabl->mm, tabl->numind*(LONG)sizeof(MINMAX));
S_WRITE_B(tabl->flags, tabl->numind*(LONG)sizeof(WORD));
if ( tabl->sparse ) {
S_WRITE_B(tabl->boomlijst, tabl->MaxTreeSize*(LONG)sizeof(COMPTREE));
S_WRITE_S(tabl->argtail);
}
S_WRITE_B(tabl->buffers, tabl->bufferssize*(LONG)sizeof(WORD));
if ( tabl->spare ) {
TABLES spare = tabl->spare;
S_WRITE_B(spare, sizeof(struct TaBlEs));
if ( spare->tablepointers ) {
if ( spare->sparse ) {
/* sparse tables */
S_WRITE_B(spare->tablepointers,
spare->reserved*sizeof(WORD)*(spare->numind+TABLEEXTENSION));
}
else {
/* matrix like tables */
S_WRITE_B(spare->tablepointers,
TABLEEXTENSION*sizeof(WORD)*(spare->totind));
}
}
S_WRITE_B(spare->mm, spare->numind*(LONG)sizeof(MINMAX));
S_WRITE_B(spare->flags, spare->numind*(LONG)sizeof(WORD));
if ( spare->sparse ) {
S_WRITE_B(spare->boomlijst, spare->MaxTreeSize*(LONG)sizeof(COMPTREE));
}
S_WRITE_B(spare->buffers, spare->bufferssize*(LONG)sizeof(WORD));
}
}
}
ANNOUNCE(AC.ExpressionList)
S_WRITE_LIST(AC.ExpressionList);
for ( i=0; irenum ) {
S_WRITE_B(ex->renum, sizeof(struct ReNuMbEr));
/* there is one dynamically allocated buffer for struct ReNuMbEr and
* symb.lo points to its beginning. the size of the buffer is not
* stored anywhere but we know it is 2*sizeof(WORD)*N, where N is
* the number of all vectors, indices, functions and symbols. since
* funum points into the buffer at a distance 2N-[Number of
* functions] from symb.lo (see GetTable() in store.c), we can
* calculate the buffer size by some pointer arithmetic. the size is
* then written to the file. */
l = ex->renum->funnum - ex->renum->symb.lo;
l += ex->renum->funnum - ex->renum->func.lo;
S_WRITE_B(&l, sizeof(size_t));
S_WRITE_B(ex->renum->symb.lo, l);
}
if ( ex->bracketinfo ) {
S_WRITE_B(ex->bracketinfo, sizeof(BRACKETINFO));
S_WRITE_B(ex->bracketinfo->indexbuffer, ex->bracketinfo->indexbuffersize*sizeof(BRACKETINDEX));
S_WRITE_B(ex->bracketinfo->bracketbuffer, ex->bracketinfo->bracketbuffersize*sizeof(WORD));
}
if ( ex->newbracketinfo ) {
S_WRITE_B(ex->newbracketinfo, sizeof(BRACKETINFO));
S_WRITE_B(ex->newbracketinfo->indexbuffer, ex->newbracketinfo->indexbuffersize*sizeof(BRACKETINDEX));
S_WRITE_B(ex->newbracketinfo->bracketbuffer, ex->newbracketinfo->bracketbuffersize*sizeof(WORD));
}
/* don't need to write ex->renumlists */
if ( ex->inmem ) {
/* size of the inmem buffer has to be determined. we use the fact
* that the end of an expression is marked by a zero. */
w = ex->inmem;
while ( *w++ ) ;
l = w - ex->inmem;
S_WRITE_B(&l, sizeof(size_t));
S_WRITE_B(ex->inmem, l);
}
}
ANNOUNCE(AC.IndexList)
S_WRITE_LIST(AC.IndexList);
S_WRITE_LIST(AC.SetElementList);
S_WRITE_LIST(AC.SetList);
S_WRITE_LIST(AC.SymbolList);
S_WRITE_LIST(AC.VectorList);
ANNOUNCE(AC.TableBaseList)
S_WRITE_LIST(AC.TableBaseList);
for ( i=0; infactors > 1 ) {
S_WRITE_B(&(d->factors),sizeof(FACDOLLAR)*d->nfactors);
for ( j = 0; j < d->nfactors; j++ ) {
if ( d->factors[j].size > 0 ) {
S_WRITE_B(&(d->factors[i].where),sizeof(WORD)*(d->factors[j].size+1));
}
}
}
}
S_WRITE_LIST(AP.PreVarList);
for ( i=0; iPOfull - AR.outfile->PObuffer;
if ( l ) {
S_WRITE_B(AR.outfile->PObuffer, l*sizeof(WORD));
}
S_WRITE_B(AR.hidefile, sizeof(FILEHANDLE));
l = AR.hidefile->POfull - AR.hidefile->PObuffer;
if ( l ) {
S_WRITE_B(AR.hidefile->PObuffer, l*sizeof(WORD));
}
S_WRITE_B(&AR.StoreData.Fill, sizeof(POSITION));
if ( ISNOTZEROPOS(AR.StoreData.Fill) ) {
S_WRITE_B(&AR.StoreData, sizeof(FILEDATA));
}
S_WRITE_B(&AR.DefPosition, sizeof(POSITION));
l = TimeCPU(1); l = -l;
S_WRITE_B(&l, sizeof(LONG));
ANNOUNCE(AR.InInBuf)
S_WRITE_B(&AR.InInBuf, sizeof(LONG));
S_WRITE_B(&AR.InHiBuf, sizeof(LONG));
S_WRITE_B(&AR.NoCompress, sizeof(int));
S_WRITE_B(&AR.gzipCompress, sizeof(int));
S_WRITE_B(&AR.outtohide, sizeof(int));
S_WRITE_B(&AR.GetFile, sizeof(WORD));
S_WRITE_B(&AR.KeptInHold, sizeof(WORD));
S_WRITE_B(&AR.BracketOn, sizeof(WORD));
S_WRITE_B(&AR.MaxBracket, sizeof(WORD));
S_WRITE_B(&AR.CurDum, sizeof(WORD));
S_WRITE_B(&AR.DeferFlag, sizeof(WORD));
S_WRITE_B(&AR.TePos, sizeof(WORD));
S_WRITE_B(&AR.sLevel, sizeof(WORD));
S_WRITE_B(&AR.Stage4Name, sizeof(WORD));
S_WRITE_B(&AR.GetOneFile, sizeof(WORD));
S_WRITE_B(&AR.PolyFun, sizeof(WORD));
S_WRITE_B(&AR.PolyFunInv, sizeof(WORD));
S_WRITE_B(&AR.PolyFunType, sizeof(WORD));
S_WRITE_B(&AR.PolyFunExp, sizeof(WORD));
S_WRITE_B(&AR.PolyFunVar, sizeof(WORD));
S_WRITE_B(&AR.PolyFunPow, sizeof(WORD));
S_WRITE_B(&AR.Eside, sizeof(WORD));
S_WRITE_B(&AR.MaxDum, sizeof(WORD));
S_WRITE_B(&AR.level, sizeof(WORD));
S_WRITE_B(&AR.expchanged, sizeof(WORD));
S_WRITE_B(&AR.expflags, sizeof(WORD));
S_WRITE_B(&AR.CurExpr, sizeof(WORD));
S_WRITE_B(&AR.SortType, sizeof(WORD));
S_WRITE_B(&AR.ShortSortCount, sizeof(WORD));
#ifdef WITHPTHREADS
for ( j = 0; j < AM.totalnumberofthreads; j++ ) {
S_WRITE_B(&(AB[j]->R.wranfnpair1), sizeof(int));
S_WRITE_B(&(AB[j]->R.wranfnpair2), sizeof(int));
S_WRITE_B(&(AB[j]->R.wranfcall), sizeof(int));
S_WRITE_B(&(AB[j]->R.wranfseed), sizeof(ULONG));
S_WRITE_B(&(AB[j]->R.wranfia),sizeof(ULONG *));
if ( AB[j]->R.wranfia ) {
S_WRITE_B(AB[j]->R.wranfia, sizeof(ULONG)*AB[j]->R.wranfnpair2);
}
}
#else
S_WRITE_B(&(AR.wranfnpair1), sizeof(int));
S_WRITE_B(&(AR.wranfnpair2), sizeof(int));
S_WRITE_B(&(AR.wranfcall), sizeof(int));
S_WRITE_B(&(AR.wranfseed), sizeof(ULONG));
S_WRITE_B(&(AR.wranfia),sizeof(ULONG *));
if ( AR.wranfia ) {
S_WRITE_B(AR.wranfia, sizeof(ULONG)*AR.wranfnpair2);
}
#endif
/*#] AR :*/
/*#[ AO :*/
/*
We copy all non-pointer variables.
*/
ANNOUNCE(AO)
l = sizeof(A.O) - ((UBYTE *)(&(A.O.NumInBrack))-(UBYTE *)(&A.O));
S_WRITE_B(&(A.O.NumInBrack),l);
/*
Now the variables in OptimizeResult
*/
S_WRITE_B(&(A.O.OptimizeResult),sizeof(OPTIMIZERESULT));
if ( A.O.OptimizeResult.codesize > 0 ) {
S_WRITE_B(A.O.OptimizeResult.code,A.O.OptimizeResult.codesize*sizeof(WORD));
}
S_WRITE_S(A.O.OptimizeResult.nameofexpr);
/*
And now the dictionaries.
We write each dictionary to a buffer and get the size of that buffer.
Then we write the size and the buffer.
*/
for ( i = 0; i < AO.NumDictionaries; i++ ) {
l = DictToBytes(AO.Dictionaries[i],(UBYTE *)(AT.WorkPointer));
S_WRITE_B(&l,sizeof(LONG));
S_WRITE_B(AT.WorkPointer,l);
}
/*#] AO :*/
/*#[ PF :*/
#ifdef WITHMPI
S_WRITE_B(&PF.numtasks, sizeof(int));
S_WRITE_B(&PF.rhsInParallel, sizeof(int));
S_WRITE_B(&PF.exprbufsize, sizeof(int));
S_WRITE_B(&PF.log, sizeof(int));
#endif
/*#] PF :*/
#ifdef WITHPTHREADS
ANNOUNCE(GetTimerInfo)
/*
write timing information of individual threads
*/
i = GetTimerInfo(&longp,&longpp);
S_WRITE_B(&i, sizeof(int));
S_WRITE_B(longp, i*(LONG)sizeof(LONG));
S_WRITE_B(&i, sizeof(int));
S_WRITE_B(longpp, i*(LONG)sizeof(LONG));
#endif
S_FLUSH_B /* because we will call fwrite() directly in the following code */
/* save length of data at the beginning of the file */
ANNOUNCE(file length)
SETBASEPOSITION(pos, (ftell(fd)));
fseek(fd, 0, SEEK_SET);
if ( fwrite(&pos, 1, sizeof(POSITION), fd) != sizeof(POSITION) ) return(__LINE__);
fseek(fd, BASEPOSITION(pos), SEEK_SET);
ANNOUNCE(file close)
if ( fclose(fd) ) return(__LINE__);
#ifdef WITHMPI
if ( PF.me == MASTER ) {
#endif
/*
copy store file if necessary
*/
ANNOUNCE(copy store file)
if ( ISNOTZEROPOS(AR.StoreData.Fill) ) {
if ( CopyFile(FG.fname, storefile) ) return(__LINE__);
}
/*
copy sort file if necessary
*/
ANNOUNCE(copy sort file)
if ( AR.outfile->handle >= 0 ) {
if ( CopyFile(AR.outfile->name, sortfile) ) return(__LINE__);
}
/*
copy hide file if necessary
*/
ANNOUNCE(copy hide file)
if ( AR.hidefile->handle >= 0 ) {
if ( CopyFile(AR.hidefile->name, hidefile) ) return(__LINE__);
}
#ifdef WITHMPI
}
/*
* For ParFORM, the renaming will be performed after the master got
* all recovery files from the slaves.
*/
#else
/*
make the intermediate file the recovery file
*/
ANNOUNCE(rename intermediate file)
if ( rename(intermedfile, recoveryfile) ) return(__LINE__);
done_snapshot = 1;
MesPrint("done."); fflush(0);
#endif
#ifdef PRINTDEBUG
print_M();
print_C();
print_P();
print_R();
#endif
return(0);
}
/*
#] DoSnapshot :
#[ DoCheckpoint :
*/
/**
* Checks whether a snapshot should be done. Calls DoSnapshot() to create the
* snapshot.
*/
void DoCheckpoint(int moduletype)
{
int error;
LONG timestamp = TimeWallClock(1);
#ifdef WITHMPI
if(PF.me == MASTER){
#endif
if ( timestamp - AC.CheckpointStamp >= AC.CheckpointInterval ) {
char argbuf[20];
int retvalue = 0;
if ( AC.CheckpointRunBefore ) {
size_t l, l2;
char *str;
l = strlen(AC.CheckpointRunBefore);
NumToStr((UBYTE*)argbuf, AC.CModule);
l2 = strlen(argbuf);
str = (char*)Malloc1(l+l2+2, "callbefore");
strcpy(str, AC.CheckpointRunBefore);
*(str+l) = ' ';
strcpy(str+l+1, argbuf);
retvalue = system(str);
M_free(str, "callbefore");
if ( retvalue ) {
MesPrint("Script returned error -> no recovery file will be created.");
}
}
#ifdef WITHMPI
/* Confirm slaves to make snapshots. */
PF_BroadcastNumber(retvalue == 0);
#endif
if ( retvalue == 0 ) {
if ( (error = DoSnapshot(moduletype)) ) {
MesPrint("Error creating recovery files: %d", error);
}
#ifdef WITHMPI
{
int i;
/*get recovery files from slaves:*/
for(i=1; i %s", src, dst);
}
}
done_snapshot = 1;
MesPrint("done."); fflush(0);
}
#endif
}
if ( AC.CheckpointRunAfter ) {
size_t l, l2;
char *str;
l = strlen(AC.CheckpointRunAfter);
NumToStr((UBYTE*)argbuf, AC.CModule);
l2 = strlen(argbuf);
str = (char*)Malloc1(l+l2+2, "callafter");
strcpy(str, AC.CheckpointRunAfter);
*(str+l) = ' ';
strcpy(str+l+1, argbuf);
retvalue = system(str);
M_free(str, "callafter");
if ( retvalue ) {
MesPrint("Error calling script after recovery.");
}
}
AC.CheckpointStamp = TimeWallClock(1);
}
#ifdef WITHMPI
else{/* timestamp - AC.CheckpointStamp < AC.CheckpointInterval*/
/* The slaves don't need to make snapshots. */
PF_BroadcastNumber(0);
}
}/*if(PF.me == MASTER)*/
else{/*Slave*/
int i;
/* Check if the slave needs to make a snapshot. */
if ( PF_BroadcastNumber(0) ) {
error = DoSnapshot(moduletype);
if(error == 0){
FILE *fd;
/*
* Send the recovery file to the master. Note that no renaming
* has been performed and what we have to send is actually sitting
* in the intermediate file.
*/
fd = fopen(intermedfile, "r");
i=PF_SendFile(MASTER, fd);/*if fd==NULL, PF_SendFile seds to a slave the failure tag*/
if(fd == NULL)
Terminate(-1);
fclose(fd);
if(i<=0)
Terminate(-1);
/*Now the slave need not the recovery file so remove it:*/
remove(intermedfile);
}
else{
/*send the error tag to the master:*/
PF_SendFile(MASTER,NULL);/*if fd==NULL, PF_SendFile seds to a slave the failure tag*/
Terminate(-1);
}
done_snapshot = 1;
}/*if(tag=PF_DATA_MSGTAG)*/
}/*if(PF.me != MASTER)*/
#endif
}
/*
#] DoCheckpoint :
*/
form-master/sources/comexpr.c 0000664 0000000 0000000 00000147514 13565763364 0016654 0 ustar 00root root 0000000 0000000 /** @file comexpr.c
*
* Compiler routines for statements that involve algebraic expressions.
* These involve definitions, id-statements, the multiply statement
* and the fill statement.
*/
/* #[ License : */
/*
* Copyright (C) 1984-2017 J.A.M. Vermaseren
* When using this file you are requested to refer to the publication
* J.A.M.Vermaseren "New features of FORM" math-ph/0010025
* This is considered a matter of courtesy as the development was paid
* for by FOM the Dutch physics granting agency and we would like to
* be able to track its scientific use to convince FOM of its value
* for the community.
*
* This file is part of FORM.
*
* FORM 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.
*
* FORM 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 FORM. If not, see .
*/
/* #] License : */
/*
#[ Includes : compi2.c
File contains most of what has to do with compiling expressions.
Main supporting file: token.c
*/
#include "form3.h"
static struct id_options {
UBYTE *name;
int code;
int dummy;
} IdOptions[] = {
{(UBYTE *)"multi", SUBMULTI ,0}
,{(UBYTE *)"many", SUBMANY ,0}
,{(UBYTE *)"only", SUBONLY ,0}
,{(UBYTE *)"once", SUBONCE ,0}
,{(UBYTE *)"ifmatch", SUBAFTER ,0}
,{(UBYTE *)"ifnomatch", SUBAFTERNOT ,0}
,{(UBYTE *)"ifnotmatch", SUBAFTERNOT ,0}
,{(UBYTE *)"disorder", SUBDISORDER ,0}
,{(UBYTE *)"select", SUBSELECT ,0}
,{(UBYTE *)"all", SUBALL ,0}
};
/*
#] Includes :
#[ CoLocal :
*/
int CoLocal(UBYTE *inp) { return(DoExpr(inp,LOCALEXPRESSION,0)); }
/*
#] CoLocal :
#[ CoGlobal :
*/
int CoGlobal(UBYTE *inp) { return(DoExpr(inp,GLOBALEXPRESSION,0)); }
/*
#] CoGlobal :
#[ CoLocalFactorized :
*/
int CoLocalFactorized(UBYTE *inp) { return(DoExpr(inp,LOCALEXPRESSION,1)); }
/*
#] CoLocalFactorized :
#[ CoGlobalFactorized :
*/
int CoGlobalFactorized(UBYTE *inp) { return(DoExpr(inp,GLOBALEXPRESSION,1)); }
/*
#] CoGlobalFactorized :
#[ DoExpr:
*/
int DoExpr(UBYTE *inp, int type, int par)
{
GETIDENTITY
int error = 0;
UBYTE *p, *q, c;
WORD *w, i, j = 0, c1, c2, *OldWork = AT.WorkPointer, osize;
WORD jold = 0;
POSITION pos;
while ( *inp == ',' ) inp++;
if ( par ) AC.ToBeInFactors = 1;
else AC.ToBeInFactors = 0;
p = inp;
while ( *p && *p != '=' ) {
if ( *p == '(' ) SKIPBRA4(p)
else if ( *p == '{' ) SKIPBRA5(p)
else if ( *p == '[' ) SKIPBRA1(p)
else p++;
}
if ( *p ) { /* Variety with the = sign */
if ( ( q = SkipAName(inp) ) == 0 || q[-1] == '_' ) {
MesPrint("&Illegal name for expression");
error = 1;
if ( q[-1] == '_' ) {
while ( FG.cTable[*q] < 2 || *q == '_' ) q++;
}
}
else {
c = *q; *q = 0;
if ( GetVar(inp,&c1,&c2,ALLVARIABLES,NOAUTO) != NAMENOTFOUND ) {
if ( c1 == CEXPRESSION ) {
if ( Expressions[c2].status == STOREDEXPRESSION ) {
MesPrint("&Illegal attempt to overwrite a stored expression");
error = 1;
}
else {
HighWarning("Expression is replaced by new definition");
if ( AO.OptimizeResult.nameofexpr != NULL &&
StrCmp(inp,AO.OptimizeResult.nameofexpr) == 0 ) {
ClearOptimize();
}
if ( Expressions[c2].status != DROPPEDEXPRESSION ) {
w = &(Expressions[c2].status);
if ( *w == LOCALEXPRESSION || *w == SKIPLEXPRESSION )
*w = DROPLEXPRESSION;
else if ( *w == GLOBALEXPRESSION || *w == SKIPGEXPRESSION )
*w = DROPGEXPRESSION;
else if ( *w == HIDDENLEXPRESSION )
*w = DROPHLEXPRESSION;
else if ( *w == HIDDENGEXPRESSION )
*w = DROPHGEXPRESSION;
}
AC.TransEname = Expressions[c2].name;
j = EntVar(CEXPRESSION,0,type,0,0,0);
Expressions[j].node = Expressions[c2].node;
Expressions[c2].replace = j;
}
}
else {
MesPrint("&name of expression is also name of a variable");
error = 1;
j = EntVar(CEXPRESSION,inp,type,0,0,0);
}
jold = c2;
}
else {
/*
Here we have to worry about reuse of the expression in the
same module. That will need AS.Oldvflags but that may not
be defined or have the proper value.
*/
j = EntVar(CEXPRESSION,inp,type,0,0,0);
jold = j;
}
*q = c;
OldWork = w = AT.WorkPointer;
*w++ = TYPEEXPRESSION;
*w++ = 3+SUBEXPSIZE;
*w++ = j;
AC.ProtoType = w;
AR.CurExpr = j; /* Block expression j */
*w++ = SUBEXPRESSION;
*w++ = SUBEXPSIZE;
*w++ = j;
*w++ = 1;
*w++ = AC.cbufnum;
FILLSUB(w)
if ( c == '(' ) {
while ( *q == ',' || *q == '(' ) {
inp = q+1;
if ( ( q = SkipAName(inp) ) == 0 ) {
MesPrint("&Illegal name for expression argument");
error = 1;
q = p - 1;
break;
}
c = *q; *q = 0;
if ( GetVar(inp,&c1,&c2,ALLVARIABLES,WITHAUTO) < 0 ) c1 = -1;
switch ( c1 ) {
case CSYMBOL :
*w++ = SYMTOSYM; *w++ = 4; *w++ = c2; *w++ = 0;
break;
case CINDEX :
*w++ = INDTOIND; *w++ = 4;
*w++ = c2 + AM.OffsetIndex; *w++ = 0;
break;
case CVECTOR :
*w++ = VECTOVEC; *w++ = 4;
*w++ = c2 + AM.OffsetVector; *w++ = 0;
break;
case CFUNCTION :
*w++ = FUNTOFUN; *w++ = 4; *w++ = c2 + FUNCTION; *w++ = 0;
break;
default :
MesPrint("&Illegal expression parameter: %s",inp);
error = 1;
break;
}
*q = c;
}
if ( *q != ')' || q+1 != p ) {
MesPrint("&Illegal use of arguments for expression");
error = 1;
}
AC.ProtoType[1] = w - AC.ProtoType;
}
else if ( c != '=' ) {
/*
The dummy accepted L F := RHS;
*/
MesPrint("&Illegal LHS for expression definition");
error = 1;
}
*w++ = 1;
*w++ = 1;
*w++ = 3;
*w++ = 0;
SeekScratch(AR.outfile,&pos);
Expressions[j].counter = 1;
Expressions[j].onfile = pos;
Expressions[j].whichbuffer = 0;
#ifdef PARALLELCODE
Expressions[j].partodo = AC.inparallelflag;
#endif
OldWork[2] = w - OldWork - 3;
AT.WorkPointer = w;
/*
Writing the expression prototype to disk and to the compiler
buffer is done only after the RHS has been compiled because
we don't know the number of the main level RHS yet.
*/
}
inp = p+1;
ClearWildcardNames();
osize = AC.ProtoType[1]; AC.ProtoType[1] = SUBEXPSIZE;
PutInVflags(jold);
if ( ( i = CompileAlgebra(inp,RHSIDE,AC.ProtoType) ) < 0 ) {
AC.ProtoType[1] = osize;
error = 1;
}
else if ( error == 0 ) {
AC.ProtoType[1] = osize;
AC.ProtoType[2] = i;
if ( PutOut(BHEAD OldWork+2,&pos,AR.outfile,0) < 0 ) {
MesPrint("&Cannot create expression");
error = -1;
}
else {
Expressions[j].sizeprototype = OldWork[2];
OldWork[2] = 4+SUBEXPSIZE;
OldWork[4] = SUBEXPSIZE;
OldWork[5] = i;
OldWork[SUBEXPSIZE+3] = 1;
OldWork[SUBEXPSIZE+4] = 1;
OldWork[SUBEXPSIZE+5] = 3;
OldWork[SUBEXPSIZE+6] = 0;
if ( PutOut(BHEAD OldWork+2,&pos,AR.outfile,0) < 0
|| FlushOut(&pos,AR.outfile,0) ) {
MesPrint("&Cannot create expression");
error = -1;
}
AR.outfile->POfull = AR.outfile->POfill;
}
OldWork[2] = j;
/*
Seems unnecessary (13-feb-2018)
AddNtoL(OldWork[1],OldWork);
*/
AT.WorkPointer = OldWork;
if ( AC.dumnumflag ) Add2Com(TYPEDETCURDUM)
}
AC.ToBeInFactors = 0;
}
else { /* Variety in which expressions change property */
/*
This code got a major revision because it didn't
take hidden expressions into account. (1-jun-2010 JV)
*/
do {
if ( ( q = SkipAName(inp) ) == 0 ) {
MesPrint("&Illegal name(s) for expression(s)");
return(1);
}
c = *q; *q = 0;
if ( GetName(AC.exprnames,inp,&c2,NOAUTO) == NAMENOTFOUND ) {
MesPrint("&%s is not a valid expression",inp);
error = 1;
}
else {
w = &(Expressions[c2].status);
if ( type == LOCALEXPRESSION ) {
switch ( *w ) {
case GLOBALEXPRESSION:
*w = LOCALEXPRESSION;
break;
case SKIPGEXPRESSION:
*w = SKIPLEXPRESSION;
break;
case DROPGEXPRESSION:
*w = DROPLEXPRESSION;
break;
case HIDDENGEXPRESSION:
*w = HIDDENLEXPRESSION;
break;
case HIDEGEXPRESSION:
*w = HIDELEXPRESSION;
break;
case UNHIDEGEXPRESSION:
*w = UNHIDELEXPRESSION;
break;
case INTOHIDEGEXPRESSION:
*w = INTOHIDELEXPRESSION;
break;
case DROPHGEXPRESSION:
*w = DROPHLEXPRESSION;
break;
}
}
else if ( type == GLOBALEXPRESSION ) {
switch ( *w ) {
case LOCALEXPRESSION:
*w = GLOBALEXPRESSION;
break;
case SKIPLEXPRESSION:
*w = SKIPGEXPRESSION;
break;
case DROPLEXPRESSION:
*w = DROPGEXPRESSION;
break;
case HIDDENLEXPRESSION:
*w = HIDDENGEXPRESSION;
break;
case HIDELEXPRESSION:
*w = HIDEGEXPRESSION;
break;
case UNHIDELEXPRESSION:
*w = UNHIDEGEXPRESSION;
break;
case INTOHIDELEXPRESSION:
*w = INTOHIDEGEXPRESSION;
break;
case DROPHLEXPRESSION:
*w = DROPHGEXPRESSION;
break;
}
}
/*
old code
if ( type != LOCALEXPRESSION || *w != STOREDEXPRESSION )
*w = type;
*/
}
*q = c; inp = q+1;
} while ( c == ',' );
if ( c ) {
MesPrint("&Illegal object in local or global redefinition");
error = 1;
}
}
return(error);
}
/*
#] DoExpr:
#[ CoIdOld :
*/
int CoIdOld(UBYTE *inp)
{
AC.idoption = 0;
return(CoIdExpression(inp,TYPEIDOLD));
}
/*
#] CoIdOld :
#[ CoId :
*/
int CoId(UBYTE *inp)
{
AC.idoption = 0;
return(CoIdExpression(inp,TYPEIDNEW));
}
/*
#] CoId :
#[ CoIdNew :
*/
int CoIdNew(UBYTE *inp)
{
AC.idoption = 0;
return(CoIdExpression(inp,TYPEIDNEW));
}
/*
#] CoIdNew :
#[ CoDisorder :
*/
int CoDisorder(UBYTE *inp)
{
AC.idoption = SUBDISORDER;
return(CoIdExpression(inp,TYPEIDNEW));
}
/*
#] CoDisorder :
#[ CoMany :
*/
int CoMany(UBYTE *inp)
{
AC.idoption = SUBMANY;
return(CoIdExpression(inp,TYPEIDNEW));
}
/*
#] CoMany :
#[ CoMulti :
*/
int CoMulti(UBYTE *inp)
{
AC.idoption = SUBMULTI;
return(CoIdExpression(inp,TYPEIDNEW));
}
/*
#] CoMulti :
#[ CoIfMatch :
*/
int CoIfMatch(UBYTE *inp)
{
AC.idoption = SUBAFTER;
return(CoIdExpression(inp,TYPEIDNEW));
}
/*
#] CoIfMatch :
#[ CoIfNoMatch :
*/
int CoIfNoMatch(UBYTE *inp)
{
AC.idoption = SUBAFTERNOT;
return(CoIdExpression(inp,TYPEIDNEW));
}
/*
#] CoIfNoMatch :
#[ CoOnce :
*/
int CoOnce(UBYTE *inp)
{
AC.idoption = SUBONCE;
return(CoIdExpression(inp,TYPEIDNEW));
}
/*
#] CoOnce :
#[ CoOnly :
*/
int CoOnly(UBYTE *inp)
{
AC.idoption = SUBONLY;
return(CoIdExpression(inp,TYPEIDNEW));
}
/*
#] CoOnly :
#[ CoSelect :
*/
int CoSelect(UBYTE *inp)
{
AC.idoption = SUBSELECT;
return(CoIdExpression(inp,TYPEIDNEW));
}
/*
#] CoSelect :
#[ CoIdExpression :
First finish dealing with secondary keywords
*/
int CoIdExpression(UBYTE *inp, int type)
{
GETIDENTITY
int i, j, idhead, error = 0, MinusSign = 0, opt, retcode;
WORD *w, *s, *m, *mm, *ww, *FirstWork, *OldWork, c1, numsets = 0,
oldnumrhs, *ow, oldEside;
UBYTE *p, *pp, c;
CBUF *C = cbuf+AC.cbufnum;
LONG oldcpointer, x;
FirstWork = OldWork = AT.WorkPointer;
/*
Don't forget to change in StudyPattern if we change/add_to the
following setup.
if ( type == TYPEIF ) idhead = IDHEAD-1;
else
*/
idhead = IDHEAD;
AR.CurExpr = -1;
w = AT.WorkPointer;
*w++ = type;
*w++ = idhead + SUBEXPSIZE;
w++;
if ( idhead >= IDHEAD ) *w++ = -1;
#if IDHEAD > 4
for ( i = 4; i < idhead; i++ ) *w++ = 0;
#endif
while ( *inp == ',' ) inp++;
p = inp;
if ( AC.idoption == SUBSELECT ) {
p--;
goto findsets;
}
else if ( ( AC.idoption == SUBAFTER ) || ( AC.idoption == SUBAFTERNOT ) ) {
while ( *p && *p != '=' && *p != ',' ) {
if ( *p == '(' ) SKIPBRA4(p)
else if ( *p == '{' ) SKIPBRA5(p)
else if ( *p == '[' ) SKIPBRA1(p)
else p++;
}
if ( *p == '=' || *inp != '-' || inp[1] != '>' ) {
MesPrint("&Illegal use if if[no]match in id statement");
error = 1; goto AllDone;
}
if ( *p == 0 ) {
MesPrint("&id-statement without = sign");
error = 1; goto AllDone;
}
inp += 2; pp = inp;
goto readlabel;
}
for(;;) {
while ( *p && *p != '=' && *p != ',' ) {
if ( *p == '(' ) SKIPBRA4(p)
else if ( *p == '{' ) SKIPBRA5(p)
else if ( *p == '[' ) SKIPBRA1(p)
else p++;
}
if ( *p == '=' ) break;
if ( *p == 0 ) {
MesPrint("&id-statement without = sign");
error = 1; goto AllDone;
}
/*
We have either a secondary option or a syntax error
*/
pp = inp;
while ( FG.cTable[*pp] == 0 ) pp++;
c = *pp; *pp = 0;
i = sizeof(IdOptions)/sizeof(struct id_options);
while ( --i >= 0 ) {
if ( StrICmp(inp,IdOptions[i].name) == 0 ) break;
}
if ( i < 0 ) {
MesPrint("&Illegal option %s in id-statement",inp);
*pp = c; error = 1; p++; inp = p; continue;
}
opt = IdOptions[i].code;
*pp = c;
inp = pp+1;
switch ( opt ) {
case SUBDISORDER:
if ( pp != p ) goto IllField;
AC.idoption |= SUBDISORDER;
p++; inp = p;
break;
case SUBSELECT:
if ( p != pp ) goto IllField;
if ( ( AC.idoption & SUBMASK ) != 0 ) {
if ( AC.idoption == SUBMULTI && type == TYPEIF ) {}
else {
MesPrint("&Conflicting options in id-statement");
error = 1;
}
}
findsets:;
/*
Now we read the sets
*/
numsets = 0;
for(;;) {
inp = ++p;
while ( *p && *p != '=' && *p != ',' ) {
if ( *p == '(' ) SKIPBRA4(p)
else if ( *p == '{' ) SKIPBRA5(p)
else if ( *p == '[' ) SKIPBRA1(p)
else p++;
}
if ( *p == '=' ) break;
if ( *p == 0 ) {
MesPrint("&id-statement without = sign");
error = 1; goto AllDone;
}
/*
We have a set at inp.
*/
if ( *inp == '{' ) {
if ( p[-1] != '}' ) {
c = *p; *p = 0;
MesPrint("&Illegal temporary set: %s",inp);
error = 1; *p = c;
}
else {
inp++;
c = p[-1]; p[-1] = 0;
c1 = DoTempSet(inp,p-1);
*w++ = c1;
p[-1] = c;
numsets++;
if ( w[-1] < 0 ) error = 1;
}
}
else {
c = *p; *p = 0;
if ( GetName(AC.varnames,inp,&c1,NOAUTO) != CSET ) {
MesPrint("&%s is not a set",inp);
error = 1;
}
else {
if ( c1 < AM.NumFixedSets ) {
MesPrint("&Built in sets are not allowed in the select option");
error = 1;
}
else if ( Sets[c1].type == CRANGE ) {
MesPrint("&Ranged sets are not allowed in the select option");
error = 1;
}
numsets++;
*w++ = c1;
}
*p = c;
}
}
/*
Now exchange the positions a bit.
Regular stuff at OldWork, numsets sets at FirstWork[idhead]
*/
OldWork = w;
for ( i = 0; i < idhead; i++ ) *w++ = FirstWork[i];
AC.idoption = SUBSELECT;
break;
case SUBAFTER:
case SUBAFTERNOT:
if ( type == TYPEIF ) {
MesPrint("&The if[no]match->label option is not allowed in an if statement");
error = 1; goto AllDone;
}
if ( pp[0] != '-' || pp[1] != '>' ) goto IllField;
pp += 2; /* points now at the label */
inp = pp;
AC.idoption |= opt;
readlabel:
while ( FG.cTable[*pp] <= 1 ) pp++;
if ( pp != p ) {
c = *p; *p = 0;
MesPrint("&Illegal label %s in if[no]match option of id-statement",inp);
*p = c; error = 1; inp = p+1; continue;
}
c = *p; *p = 0;
OldWork[3] = GetLabel(inp);
*p++ = c; inp = p;
break;
case SUBALL:
x = 0;
if ( *pp == '(' ) {
if ( FG.cTable[*inp] == 1 ) {
while ( *inp >= '0' && *inp <= '9' ) x = 10*x+*inp++ - '0';
}
else {
pp++;
while ( FG.cTable[*inp] == 0 ) inp++;
c = *inp; *inp = 0;
if ( StrICont(pp,(UBYTE *)"normalize") != 0 ) goto IllOpt;
*inp = c;
OldWork[4] |= NORMALIZEFLAG;
}
if ( *inp != ')' || inp+1 != p ) {
c = *inp; *inp = 0;
IllOpt:
MesPrint("&Illegal ALL option in id-statement: ",pp);
*inp++ = c;
error = 1;
continue;
}
pp = inp;
inp = pp+1;
}
/*
Note that the following statement limits x to
*/
if ( x > MAXPOSITIVE ) {
MesPrint("&Requested maximum number of matches %l in ALL option in id-statement is greater than %l ",x,MAXPOSITIVE);
error = 1;
}
OldWork[5] = x;
if ( type != TYPEIDNEW ) {
if ( type == TYPEIDOLD ) {
MesPrint("&Requested ALL option not allowed in idold/also statement.");
error = 1;
}
else if ( type == TYPEIF ) {
MesPrint("&Requested ALL option not allowed in if(match())");
error = 1;
}
else {
MesPrint("&ALL option only allowed in regular id-statement.");
error = 1;
}
}
p++; inp = p;
AC.idoption = opt;
break;
default:
if ( pp != p ) {
IllField: c = *p; *p = 0;
MesPrint("&Illegal optionfield %s in id-statement",inp);
*p = c; error = 1; inp = p+1; continue;
}
i = AC.idoption & SUBMASK;
if ( i && i != opt ) {
MesPrint("&Conflicting options in id-statement");
error = 1; continue;
}
else AC.idoption |= opt;
while ( *p == ',' ) p++;
inp = p;
break;
}
}
if ( ( AC.idoption & SUBMASK ) == 0 ) AC.idoption |= SUBMULTI;
OldWork[2] = AC.idoption;
/*
Now we have a field till the = sign
Now the subexpression prototype
*/
AC.ProtoType = w;
*w++ = SUBEXPRESSION;
*w++ = SUBEXPSIZE;
*w++ = C->numrhs+1;
*w++ = 1;
*w++ = AC.cbufnum;
FILLSUB(w)
AC.WildC = w;
AC.NwildC = 0;
AT.WorkPointer = s = w + 4*AM.MaxWildcards + 8;
/*
Now read the LHS
*/
ClearWildcardNames();
oldcpointer = AddLHS(AC.cbufnum) - C->Buffer;
*p = 0;
oldnumrhs = C->numrhs;
if ( ( retcode = CompileAlgebra(inp,LHSIDE,AC.ProtoType) ) < 0 ) { error = 1; }
else AC.ProtoType[2] = retcode;
*p = '='; inp = p+1;
AT.WorkPointer = s;
if ( AC.NwildC && SortWild(w,AC.NwildC) ) error = 1;
/* Make the LHS pointers ready */
OldWork[1] = AC.WildC-OldWork;
OldWork[idhead+1] = OldWork[1] - idhead;
w = AC.WildC;
AT.WorkPointer = w;
s = C->rhs[C->numrhs];
/*
Now check whether wildcards get converted to dollars (for PARALLEL)
*/
{
WORD *tw, *twstop;
tw = AC.ProtoType; twstop = tw + tw[1]; tw += SUBEXPSIZE;
while ( tw < twstop ) {
if ( *tw == LOADDOLLAR ) {
AddPotModdollar(tw[2]);
}
tw += tw[1];
}
}
/*
We have the expression in the compiler buffers.
The main level is at lhs[numlhs]
The partial lhs (including ProtoType) is in OldWork (in WorkSpace)
We need to load the result at w after the prototype
Because these sort routines don't use the WorkSpace
there should not be a conflict
*/
if ( !error && *s == 0 ) {
IllLeft:MesPrint("&Illegal LHS");
AC.lhdollarflag = 0;
return(1);
}
if ( !error && *(s+*s) != 0 ) {
MesPrint("&LHS should be one term only");
return(1);
}
if ( error == 0 ) {
WORD oldpolyfun = AR.PolyFun;
if ( NewSort(BHEAD0) || NewSort(BHEAD0) ) {
if ( !error ) error = 1;
return(error);
}
AN.RepPoint = AT.RepCount + 1;
ow = (WORD *)(((UBYTE *)(AT.WorkPointer)) + AM.MaxTer);
mm = s; ww = ow; i = *mm;
while ( --i >= 0 ) *ww++ = *mm++; AT.WorkPointer = ww;
AC.lhdollarflag = 0; oldEside = AR.Eside; AR.Eside = LHSIDE;
AR.Cnumlhs = C->numlhs;
AR.PolyFun = 0;
if ( Generator(BHEAD ow,C->numlhs) ) {
AR.Eside = oldEside;
LowerSortLevel(); LowerSortLevel(); AR.PolyFun = oldpolyfun; goto IllLeft;
}
AR.Eside = oldEside;
AT.WorkPointer = w;
if ( EndSort(BHEAD w,0) < 0 ) { LowerSortLevel(); AR.PolyFun = oldpolyfun; goto IllLeft; }
AR.PolyFun = oldpolyfun;
if ( *w == 0 || *(w+*w) != 0 ) {
MesPrint("&LHS must be one term");
AC.lhdollarflag = 0;
return(1);
}
LowerSortLevel();
if ( AC.lhdollarflag ) MarkDirty(w,DIRTYFLAG);
}
AT.WorkPointer = w + *w;
AC.DumNum = 0;
/*
Everything is now after OldWork. We can pop the compilerbuffer.
Next test for illegal things like a coefficient
At this point we have:
w = the term of the LHS
*/
C->Pointer = C->Buffer + oldcpointer;
C->numrhs = oldnumrhs;
C->numlhs--;
m = w + *w - 3;
AC.vectorlikeLHS = 0;
if ( !error ) {
if ( m[2] != 3 || m[1] != 1 || *m != 1 ) {
if ( *m == 1 && m[1] == 1 && m[2] == -3 ) {
MinusSign = 1;
}
else {
MesPrint("&Coefficient in LHS");
error = 1;
AC.DumNum = 0;
*w -= ABS(m[2])-3;
}
}
if ( *w == 7 && w[1] == INDEX && w[3] < 0 ) {
if ( ( AC.idoption & SUBMASK ) != 0 && ( AC.idoption & SUBMASK ) !=
SUBMULTI ) {
MesPrint("&Illegal option for substitution of a vector");
error = 1;
}
AC.DumNum = AM.IndDum;
OldWork[2] = ( OldWork[2] - ( OldWork[2] & SUBMASK ) ) | SUBVECTOR;
c1 = w[3];
/* We overwrite the LHS */
*w++ = INDTOIND;
*w++ = 4;
*w++ = AC.DumNum + WILDOFFSET;
*w++ = 0;
w[0] = 5;
w[1] = VECTOR;
w[2] = 4;
w[3] = c1;
w[4] = AC.DumNum + WILDOFFSET;
OldWork[idhead+1] = w - OldWork - idhead;
AC.vectorlikeLHS = 1;
}
else {
AC.DumNum = 0;
*w -= 3;
i = OldWork[2] & SUBMASK;
m = w + *w;
if ( i == 0 || i == SUBMULTI ) {
s = w+1;
while ( s < m ) {
if ( *s == SYMBOL ) {
j = s[1]/2; s += 2;
while ( --j >= 0 ) {
if ( ABS(s[1]) > 2*MAXPOWER ) {
OldWork[2] = ( OldWork[2] - i ) | SUBONCE;
break;
}
s += 2;
}
if ( j >= 0 ) break;
}
else if ( *s == DOTPRODUCT ) {
j = s[1]/3; s += 2;
while ( --j >= 0 ) {
if ( ABS(s[2]) > 2*MAXPOWER ) {
OldWork[2] = ( OldWork[2] - i ) | SUBONCE;
break;
}
else if ( s[1] >= -(2*WILDOFFSET) || s[0] >= -(2*WILDOFFSET) ) {
OldWork[2] = ( OldWork[2] - i ) | SUBMANY;
i = SUBMANY;
}
s += 3;
}
if ( j >= 0 ) break;
}
else {
OldWork[2] = ( OldWork[2] - i ) | SUBMANY;
break;
}
}
}
if ( ( OldWork[2] & SUBMASK ) == 0 ) OldWork[2] |= SUBMULTI;
}
if ( ( OldWork[2] & SUBMASK ) == SUBSELECT ) {
/*
Paste the SETSET information after the pattern.
Important note: We will still get function information for the
smart patternmatching after it. To distinguish them we need to have
that SETSET != m*n+1 in which m is the number of words per function
and n the number of functions. Currently (29-may-1997) m = 4.
*/
*m++ = SETSET;
*m++ = numsets+2;
s = FirstWork + idhead;
while ( --numsets >= 0 ) *m++ = *s++;
}
else {
m = w + *w;
}
}
/*
We keep the whole thing in OldWork for the moment.
We still have to add the number of the RHS expression.
There is also some opportunity now to be smart about the pattern.
This is needed for complicated wildcarding with symmetric functions.
We do this in a special routine during compile time to make sure
that we loose as little time as possible (during running) if there
is no need to be smart.
*/
*m++ = 0;
OldWork[1] = m - OldWork;
AC.ProtoType = OldWork+idhead;
if ( !error ) {
if ( StudyPattern(OldWork) ) error = 1;
}
AT.WorkPointer = OldWork + OldWork[1];
if ( AC.lhdollarflag ) OldWork[4] |= DOLLARFLAG;
AC.lhdollarflag = 0;
/*
Test whether the id/idold configuration is fine.
*/
if ( type == TYPEIDOLD ) {
WORD ci = C->numlhs;
while ( ci >= 1 ) {
if ( C->lhs[ci][0] == TYPEIDNEW ) {
if ( (C->lhs[ci][2] & SUBMASK) == SUBALL ) {
MesPrint("&Idold/also cannot follow an id,all statement.");
error = 1;
}
break;
}
else if ( C->lhs[ci][0] == TYPEDETCURDUM ) { ci--; continue; }
else if ( C->lhs[ci][0] == TYPEIDOLD ) { ci--; continue; }
else ci = 0;
}
if ( ci < 1 ) {
MesPrint("&Idold/also should follow an id/idnew statement.");
error = 1;
}
}
/*
Now the right hand side.
*/
if ( type != TYPEIF ) {
if ( ( retcode = CompileAlgebra(inp,RHSIDE,AC.ProtoType) ) < 0 ) error = 1;
else {
AC.ProtoType[2] = retcode;
AC.DumNum = 0;
if ( MinusSign ) { /* Flip the sign of the RHS */
w = C->rhs[retcode];
while ( *w ) { w += *w; w[-1] = -w[-1]; }
}
if ( AC.dumnumflag ) Add2Com(TYPEDETCURDUM)
}
}
/*
Actual adding happens only now after numrhs insertion
*/
if ( !error ) { AddNtoL(OldWork[1],OldWork); }
AllDone:
AC.lhdollarflag = 0;
AT.WorkPointer = FirstWork;
return(error);
}
/*
#] CoIdExpression :
#[ CoMultiply :
*/
static WORD mularray[13] = { TYPEMULT, SUBEXPSIZE+3, 0, SUBEXPRESSION,
SUBEXPSIZE, 0, 1, 0, 0, 0, 0, 0, 0 };
int CoMultiply(UBYTE *inp)
{
UBYTE *p;
int error = 0, RetCode;
mularray[2] = 0; /* right multiply is default */
while ( *inp == ',' ) inp++;
/* if ( inp[-1] == '-' || inp[-1] == '+' ) inp--; */
p = SkipField(inp,0);
if ( *p ) {
*p = 0;
if ( StrICont(inp,(UBYTE *)"left") == 0 ) mularray[2] = 1;
else if ( StrICont(inp,(UBYTE *)"right") == 0 ) mularray[2] = 0;
else {
MesPrint("&Illegal option in multiply statement or ; forgotten.");
return(1);
}
*p = ',';
inp = p + 1;
}
ClearWildcardNames();
while ( *inp == ',' ) inp++;
AC.ProtoType = mularray+3;
mularray[7] = AC.cbufnum;
if ( ( RetCode = CompileAlgebra(inp,RHSIDE,AC.ProtoType) ) < 0 ) error = 1;
else {
mularray[5] = RetCode;
AddNtoL(SUBEXPSIZE+3,mularray);
if ( AC.dumnumflag ) Add2Com(TYPEDETCURDUM)
}
return(error);
}
/*
#] CoMultiply :
#[ CoFill :
Special additions for tablebase-like tables added 12-aug-2002
*/
int CoFill(UBYTE *inp)
{
GETIDENTITY
WORD error = 0, x, funnum, type, *oldwp = AT.WorkPointer;
int i, oldcbufnum = AC.cbufnum, nofill = 0, numover, redef = 0;
WORD *w, *wold, *Tprototype;
UBYTE *p = inp, c, *inp1;
TABLES T = 0, oldT;
LONG newreservation, sum = 0;
UBYTE *p1, *p2, *p3, *p4, *fake = 0;
int tablestub = 0;
if ( AC.exprfillwarning == 1 ) AC.exprfillwarning = 0;
/*
Read the name of the function and test that it is in the table.
*/
p1 = inp;
if ( ( p = SkipAName(inp) ) == 0 ) return(1);
p2 = p;
c = *p; *p = 0;
if ( ( GetVar(inp,&type,&funnum,CFUNCTION,WITHAUTO) == NAMENOTFOUND )
|| ( T = functions[funnum].tabl ) == 0 || ( T->numind > 0 && c != '(' ) ) {
MesPrint("&%s should be a table with argument(s)",inp);
*p = c; return(1);
}
oldT = T;
*p++ = c;
if ( T->numind == 0 ) {
if ( c == '(' ) {
if ( *p != ')' ) {
c = *p; *p = 0;
MesPrint("&%s should be a table without arguments",inp);
*p = c; return(1);
}
else { p++; }
}
else { p--; }
sum = 0;
p3 = p;
goto andagain;
}
for ( sum = 0, i = 0, w = oldwp; i < T->numind; i++ ) {
ParseSignedNumber(x,p);
if ( FG.cTable[p[-1]] != 1 || ( *p != ',' && *p != ')' ) ) {
MesPrint("&Table arguments in fill statement should be numbers");
return(1);
}
if ( T->sparse ) *w++ = x;
else if ( x < T->mm[i].mini || x > T->mm[i].maxi ) {
MesPrint("&Value %d for argument %d of table out of bounds",x,i+1);
error = 1; nofill = 1;
}
else sum += ( x - T->mm[i].mini ) * T->mm[i].size;
if ( *p == ')' ) break;
p++;
}
p3 = p;
if ( *p != ')' || i < ( T->numind - 1 ) ) {
MesPrint("&Incorrect number of table arguments in fill statement. Should be %d"
,T->numind);
error = 1; nofill = 1;
}
AT.WorkPointer = w;
if ( T->sparse == 0 ) sum *= TABLEEXTENSION;
andagain:;
AC.cbufnum = T->bufnum;
if ( T->sparse ) {
i = FindTableTree(T,oldwp,1);
if ( i >= 0 ) {
sum = i + T->numind;
if ( tablestub == 0 && ( ( T->sparse & 2 ) == 2 ) && ( T->mode != 0 )
&& ( AC.vetotablebasefill == 0 ) ) {
/*
This redefinition does not need a new stub
*/
functions[funnum].tabl = T = T->spare;
tablestub = 1;
goto andagain;
}
redef = 1;
goto redef;
}
if ( T->totind >= T->reserved ) {
if ( T->reserved == 0 ) newreservation = 20;
else newreservation = T->reserved;
/*
while ( T->totind >= newreservation && newreservation <
MAXTABLECOMBUF*(T->numind+TABLEEXTENSION) )
if ( newreservation > MAXTABLECOMBUF*T->numind ) newreservation =
5*(T->numind+TABLEEXTENSION);
*/
while ( T->totind >= newreservation && newreservation < MAXTABLECOMBUF )
newreservation = 2*newreservation;
if ( newreservation > MAXTABLECOMBUF ) newreservation = MAXTABLECOMBUF;
if ( T->totind >= newreservation ) {
MesPrint("@More than %ld elements in sparse table",MAXTABLECOMBUF);
AC.cbufnum = oldcbufnum;
Terminate(-1);
}
wold = (WORD *)Malloc1(newreservation*sizeof(WORD)*
(T->numind+TABLEEXTENSION),"tablepointers");
for ( i = T->reserved*(T->numind+TABLEEXTENSION)-1; i >= 0; i-- )
wold[i] = T->tablepointers[i];
if ( T->tablepointers ) M_free(T->tablepointers,"tablepointers");
T->tablepointers = wold;
T->reserved = newreservation;
}
w = oldwp;
for ( sum = T->totind*(T->numind+TABLEEXTENSION), i = 0; i < T->numind; i++ ) {
T->tablepointers[sum++] = *w++;
}
InsTableTree(T,T->tablepointers+sum-T->numind);
#if TABLEEXTENSION == 2
T->tablepointers[sum+TABLEEXTENSION-1] = -1; /* New element! */
#else
T->tablepointers[sum+1] = T->bufnum;
T->tablepointers[sum+2] = -1;
T->tablepointers[sum+3] = -1;
T->tablepointers[sum+4] = 0;
T->tablepointers[sum+5] = 0;
#endif
}
else {
if ( !nofill && T->tablepointers[sum] >= 0 ) {
redef:;
if ( AC.vetofilling ) nofill = 1;
else {
Warning("Table element was already defined. New definition will be used");
}
}
#if TABLEEXTENSION == 2
T->tablepointers[sum+TABLEEXTENSION-1] = -1; /* New element! */
#else
T->tablepointers[sum+1] = T->bufnum;
T->tablepointers[sum+2] = -1;
T->tablepointers[sum+3] = -1;
T->tablepointers[sum+4] = 0;
T->tablepointers[sum+5] = 0;
#endif
}
if ( T->numind ) { p++; }
if ( *p != '=' ) {
MesPrint("&Fill statement misses = sign after the table element");
AC.cbufnum = oldcbufnum;
AT.WorkPointer = oldwp;
functions[funnum].tabl = oldT;
return(1);
}
if ( tablestub == 0 && T->mode == 1 && AC.vetotablebasefill == 0 ) {
/*
Here we construct a righthandside from the indices and the wildcards
*/
int numfake;
tablestub = 1;
p4 = T->argtail;
while ( *p4 ) p4++;
numfake = (p4-T->argtail)+(p3-p1)+10;
fake = (UBYTE *)Malloc1(numfake*sizeof(UBYTE),"Fill fake rhs");
p = fake;
*p++ = 't'; *p++ = 'b'; *p++ = 'l'; *p++ = '_'; *p++ = '(';
p4 = p1; while ( p4 < p2 ) *p++ = *p4++; *p++ = ',';
p4 = p2+1; while ( p4 < p3 ) *p++ = *p4++;
if ( T->argtail ) {
p4 = T->argtail + 1;
while ( FG.cTable[*p4] == 1 ) p4++;
while ( *p4 ) {
if ( *p4 == '?' && p[-1] != ',' ) {
p4++;
if ( FG.cTable[*p4] == 0 || *p4 == '$' || *p4 == '[' ) {
p4 = SkipAName(p4);
if ( *p4 == '[' ) {
SKIPBRA1(p4);
}
}
else if ( *p4 == '{' ) {
SKIPBRA2(p4);
}
else if ( *p4 ) { *p++ = *p4++; continue; }
}
else *p++ = *p4++;
}
}
*p++ = ')';
*p = 0;
inp1 = fake;
/* AT.WorkPointer += T->numind; */
}
else {
inp1 = ++p;
}
c = 0;
/*
Now we have the indices and p points to the rhs.
*/
numover = 0;
AC.tablefilling = funnum;
while ( *inp1 ) {
p = SkipField(inp1,0);
c = *p; *p = 0;
#ifdef WITHPTHREADS
Tprototype = T->prototype[0];
#else
Tprototype = T->prototype;
#endif
if ( ( i = CompileAlgebra(inp1,RHSIDE,Tprototype) ) < 0 ) { error = 1; i = 0; }
if ( !nofill ) {
T->tablepointers[sum] = i;
T->tablepointers[sum+1] = T->bufnum;
}
AC.DumNum = 0;
*p = c;
if ( T->sparse || c == 0 ) break;
inp1 = ++p;
#if ( TABLEEXTENSION == 2 )
sum++;
#else
sum += 2;
#endif
if ( !nofill && T->tablepointers[sum] >= 0 ) numover++;
#if ( TABLEEXTENSION == 2 )
sum++;
#else
sum += TABLEEXTENSION-2;
#endif
}
if ( AC.exprfillwarning == 1 ) {
AC.exprfillwarning = 2;
Warning("Use of expressions and/or $variables in Fill statements is potentially very dangerous.");
}
AC.tablefilling = 0;
if ( T->sparse && c != 0 ) {
MesPrint("&In sparse tables one can fill only one element at a time");
error = 1;
}
else if ( numover ) {
if ( numover == 1 )
Warning("one element was overwritten. New definition will be used");
else if ( AC.WarnFlag )
MesPrint("&Warning: %d elements were overwritten. New definitions will be used",numover);
}
if ( T->sparse ) {
if ( redef == 0 ) T->totind++;
}
else T->defined++;
/*
NumSets = AC.SetList.numtemp;
NumSetElements = AC.SetElementList.numtemp;
*/
if ( fake ) {
M_free(fake,"Fill fake rhs");
fake = 0;
functions[funnum].tabl = T = T->spare;
p = p3;
goto andagain;
}
AC.cbufnum = oldcbufnum;
AC.SymChangeFlag = 1;
AT.WorkPointer = oldwp;
functions[funnum].tabl = oldT;
return(error);
}
/*
#] CoFill :
#[ CoFillExpression :
Syntax: FillExpression table = expression(x1,...,xn);
The arguments should have been bracketed. Each corresponds to one
of the dimensions of the table. Then the bracket with x1^2*x3^4
will fill the (2,0,4) element of the table (if n=3 of course).
Brackets that don't fit will be skipped. It just gives a warning.
New option (13-jul-2005)
Syntax: FillExpression table = expression(f);
The table indices are arguments of the function f which should
have been bracketed before.
*/
int CoFillExpression(UBYTE *inp)
{
GETIDENTITY
UBYTE *p, c;
WORD type, funnum, expnum, symnum, numsym = 0, *oldwork = AT.WorkPointer;
WORD *brackets, *term, brasize, *b, *m, *w, *pw, *tstop, zero = 0;
WORD oldcbuf = AC.cbufnum, curelement = 0;
int weneedit, i, j, numzero, pow;
TABLES T = 0;
LONG newreservation, numcommu, sum;
POSITION oldposition;
FILEHANDLE *fi;
CBUF *C;
WORD numdummies;
AN.IndDum = AM.IndDum;
if ( ( p = SkipAName(inp) ) == 0 ) return(1);
c = *p; *p = 0;
if ( ( GetVar(inp,&type,&funnum,CFUNCTION,NOAUTO) == NAMENOTFOUND )
|| ( T = functions[funnum].tabl ) == 0 ) {
MesPrint("&%s should be a previously declared table",inp);
*p = c; return(1);
}
*p++ = c;
if ( T->spare ) T = T->spare;
C = cbuf + T->bufnum;
if ( c != '=' ) {
MesPrint("&No = sign in FillExpression statement");
return(1);
}
inp = p;
if ( ( p = SkipAName(inp) ) == 0 ) return(1);
c = *p; *p = 0;
if ( ( type = GetName(AC.exprnames,inp,&expnum,NOAUTO) ) == NAMENOTFOUND
|| c != '(' || (
Expressions[expnum].status != LOCALEXPRESSION &&
Expressions[expnum].status != SKIPLEXPRESSION &&
Expressions[expnum].status != DROPLEXPRESSION &&
Expressions[expnum].status != GLOBALEXPRESSION &&
Expressions[expnum].status != SKIPGEXPRESSION &&
Expressions[expnum].status != DROPGEXPRESSION ) ) {
MesPrint("&%s should be an active expression with arguments",inp);
*p = c; return(1);
}
if ( Expressions[expnum].inmem ) {
MesPrint("&%s cannot be used in a FillExpression statement in the same %n\
module that it has been redefined",inp);
*p = c; return(1);
}
*p++ = c;
while ( *p ) {
inp = p;
if ( ( p = SkipAName(inp) ) == 0 ) return(1);
c = *p; *p = 0;
if ( GetVar(inp,&type,&symnum,-1,NOAUTO) == NAMENOTFOUND ) {
MesPrint("&%s should be a previously declared symbol or function",inp);
*p = c; return(1);
}
else if ( type == CSYMBOL ) {
*p++ = c;
*AT.WorkPointer++ = symnum;
numsym++;
}
else if ( type == CFUNCTION ) {
numsym = -1;
*p++ = c;
if ( c != ')' ) {
MesPrint("&Argument should be a single function or a list of symbols");
return(1);
}
symnum += FUNCTION;
*AT.WorkPointer++ = symnum;
}
else {
MesPrint("&%s should be a previously declared symbol or function",inp);
*p = c; return(1);
}
/*
if ( GetVar(inp,&type,&symnum,CSYMBOL,NOAUTO) == NAMENOTFOUND ) {
if ( numsym > 0 ) {
MesPrint("&%s should be a previously declared symbol",inp);
*p = c; return(1);
}
else {
if ( GetVar(inp,&type,&symnum,CFUNCTION,NOAUTO) == NAMENOTFOUND ) {
MesPrint("&%s should be a previously declared symbol or function",inp);
*p = c; return(1);
}
numsym = -1;
*p++ = c;
if ( c != ')' ) {
MesPrint("&Argument should be a single function or a list of symbols");
*p = c; return(1);
}
symnum += FUNCTION;
*AT.WorkPointer++ = symnum;
break;
}
}
*p++ = c;
*AT.WorkPointer++ = symnum;
numsym++;
*/
if ( c == ')' ) break;
if ( c != ',' ) {
MesPrint("&Illegal separator in FillExpression statement");
goto noway;
}
}
if ( *p ) {
MesPrint("&Illegal end of FillExpression statement");
goto noway;
}
/*
We have the number of the table in funnum.
The number of the expression in expnum, the table struct in T
and either the numbers of the symbols in oldwork (there are numsym of them)
or the number of the function in oldwork (just one and numsym = -1).
We don't sort them!!!!
*/
if ( ( numsym > 0 ) && ( T->numind != numsym ) ) {
MesPrint("&This table needs %d symbols for its array indices");
goto noway;
}
EXCHINOUT
#ifdef WITHMPI
/*
* The workers can't access to the data of the input expression. We need to
* broadcast it to all the workers.
*/
PF_BroadcastExpr(&Expressions[expnum], AR.infile);
if ( PF.me == MASTER ) {
/*
* Restore the file position on the master.
*/
POSITION pos;
SetEndScratch(AR.infile, &pos);
}
#endif
fi = AR.infile;
if ( fi->handle >= 0 ) {
PUTZERO(oldposition);
SeekFile(fi->handle,&oldposition,SEEK_CUR);
SetScratch(fi,&(Expressions[expnum].onfile));
/* SeekFile(fi->handle,&(Expressions[expnum].onfile),SEEK_SET); */
if ( ISNEGPOS(Expressions[expnum].onfile) ) {
MesPrint("&File error in FillExpression");
BACKINOUT
goto noway;
}
}
else {
/*
Note: Because everything fits inside memory we never get problems
with excessive file sizes.
*/
SETBASEPOSITION(oldposition,(UBYTE *)(fi->POfill)-(UBYTE *)(fi->PObuffer));
fi->POfill = (WORD *)((UBYTE *)(fi->PObuffer) + BASEPOSITION(Expressions[expnum].onfile));
}
pw = AT.WorkPointer;
if ( numsym < 0 ) { brackets = pw + 1; }
else { brackets = pw + numsym; }
brasize = -1; weneedit = 0; /* stands for we need it */
term = (WORD *)(((UBYTE *)(brackets)) + AM.MaxTer);
AT.WorkPointer = (WORD *)(((UBYTE *)(term)) + AM.MaxTer);
AC.cbufnum = T->bufnum;
AC.tablefilling = funnum;
if ( GetTerm(BHEAD term) > 0 ) { /* Skip prototype */
while ( GetTerm(BHEAD term) > 0 ) {
GETSTOP(term,tstop);
w = m = term + 1;
while ( m < tstop && *m != HAAKJE ) m += m[1];
if ( *m != HAAKJE ) {
MesPrint("&Illegal attempt to put an expression without brackets in a table");
BACKINOUT
goto noway;
}
if ( brasize == m - w ) {
b = brackets;
while ( *b == *w && w < m ) { b++; w++; }
if ( w == m ) { /* Same as current bracket. Copy. */
if ( weneedit ) {
m += m[1] - 1;
*m = *term - (m-term);
AddNtoC(AC.cbufnum,*m,m,3);
numdummies = DetCurDum(BHEAD term) - AM.IndDum;
if ( numdummies > T->numdummies ) T->numdummies = numdummies;
}
continue; /* Next term */
}
}
if ( weneedit ) {
AddNtoC(AC.cbufnum,1,&zero,4); /* Terminate old bracket */
numcommu = numcommute(C->rhs[curelement],&(C->NumTerms[curelement]));
C->CanCommu[curelement] = numcommu;
}
b = brackets; w = term + 1;
if ( numsym < 0 ) pw = oldwork + 1;
else pw = oldwork + numsym;
while ( w < m ) *b++ = *w++;
brasize = b - brackets;
/*
Now compute the element. See whether we need it
*/
if ( numsym < 0 ) {
WORD *bb;
if ( *brackets != symnum || brasize != brackets[1] ) {
weneedit = 0; continue; /* Cannot work! */
}
/*
Now count the number of arguments and whether they are numbers
*/
b = brackets + FUNHEAD;
bb = brackets+brackets[1];
i = 0;
while ( b < bb ) {
if ( *b != -SNUMBER ) break;
i++;
b += 2;
}
if ( b < bb || i != T->numind ) {
weneedit = 0; continue; /* Cannot work! */
}
}
else if ( brasize > 0 && ( *brackets != SYMBOL
|| brackets[1] < brasize || (brackets[1]-2) > numsym*2 ) ) {
weneedit = 0; continue; /* Cannot work! */
}
numzero = 0; sum = 0;
if ( numsym > 0 ) {
for ( i = 0; i < numsym; i++ ) {
if ( brasize > 0 ) {
b = brackets + 2; j = brackets[1]-2;
while ( j > 0 ) {
if ( *b == oldwork[i] ) break;
j -= 2; b += 2;
}
if ( j <= 0 ) { /* it was not there */
numzero++; pow = 0;
if ( 2*numzero+brackets[1]-2 > numsym*2 ) {
weneedit = 0; goto nextterm;
}
}
else pow = b[1];
}
else pow = 0;
if ( T->sparse ) *pw++ = pow;
else if ( pow < T->mm[i].mini || pow > T->mm[i].maxi ) {
weneedit = 0; goto nextterm;
}
else sum += ( pow - T->mm[i].mini ) * T->mm[i].size;
}
}
else {
b = brackets + FUNHEAD;
sum = 0;
for ( i = 0; i < T->numind; i++ ) {
pow = b[1];
b += 2;
if ( T->sparse ) { *pw++ = pow; }
else if ( pow < T->mm[i].mini || pow > T->mm[i].maxi ) {
weneedit = 0; goto nextterm;
}
else sum += ( pow - T->mm[i].mini ) * T->mm[i].size;
}
}
weneedit = 1;
if ( T->sparse ) {
if ( numsym < 0 ) pw = oldwork + 1;
else pw = oldwork + T->numind;
i = FindTableTree(T,pw,1);
if ( i >= 0 ) {
sum = i+T->numind;
/*
Wrong!!!! C->rhs[T->tablepointers[sum]] = C->Pointer;
*/
C->Pointer--; /* Back up over the zero */
goto newentry;
}
if ( T->totind >= T->reserved ) {
if ( T->reserved == 0 ) newreservation = 20;
else newreservation = T->reserved;
/*
while ( T->totind >= newreservation && newreservation <
MAXTABLECOMBUF*(T->numind+TABLEEXTENSION) )
newreservation = 2*newreservation;
if ( newreservation > MAXTABLECOMBUF*T->numind ) newreservation =
MAXTABLECOMBUF*(T->numind+TABLEEXTENSION);
*/
/*---Copied from Fill---------------------------*/
while ( T->totind >= newreservation && newreservation < MAXTABLECOMBUF )
newreservation = 2*newreservation;
if ( newreservation > MAXTABLECOMBUF ) newreservation = MAXTABLECOMBUF;
if ( T->totind >= newreservation ) {
MesPrint("@More than %ld elements in sparse table",MAXTABLECOMBUF);
AC.cbufnum = oldcbuf;
AT.WorkPointer = oldwork;
Terminate(-1);
}
/*---Copied from Fill---------------------------*/
if ( T->totind >= newreservation ) {
MesPrint("@More than %ld elements in sparse table",MAXTABLECOMBUF);
AC.cbufnum = oldcbuf;
AT.WorkPointer = oldwork;
Terminate(-1);
}
w = (WORD *)Malloc1(newreservation*sizeof(WORD)*
(T->numind+TABLEEXTENSION),"tablepointers");
for ( i = T->reserved*(T->numind+TABLEEXTENSION)-1; i >= 0; i-- )
w[i] = T->tablepointers[i];
if ( T->tablepointers ) M_free(T->tablepointers,"tablepointers");
T->tablepointers = w;
T->reserved = newreservation;
}
if ( numsym < 0 ) pw = oldwork + 1;
else pw = oldwork + numsym;
for ( sum = T->totind*(T->numind+TABLEEXTENSION), i = 0; i < T->numind; i++ ) {
T->tablepointers[sum++] = *pw++;
}
InsTableTree(T,T->tablepointers+sum-T->numind);
(T->totind)++;
}
#if ( TABLEEXTENSION != 2 )
else {
sum *= TABLEEXTENSION;
}
#endif
/*
Start a new entry. Copy the element.
*/
AddRHS(T->bufnum,0);
T->tablepointers[sum] = C->numrhs;
#if ( TABLEEXTENSION == 2 )
T->tablepointers[sum+TABLEEXTENSION-1] = -1;
#else
T->tablepointers[sum+1] = T->bufnum;
T->tablepointers[sum+2] = -1;
T->tablepointers[sum+3] = -1;
T->tablepointers[sum+4] = 0;
T->tablepointers[sum+5] = 0;
#endif
newentry: if ( *m == HAAKJE ) { m += m[1] - 1; }
else m--;
*m = *term - (m-term);
AddNtoC(AC.cbufnum,*m,m,5);
curelement = T->tablepointers[sum];
nextterm:;
}
if ( weneedit ) {
AddNtoC(AC.cbufnum,1,&zero,6); /* Terminate old bracket */
numcommu = numcommute(C->rhs[curelement],&(C->NumTerms[curelement]));
C->CanCommu[curelement] = numcommu;
}
}
if ( fi->handle >= 0 ) {
SetScratch(fi,&(oldposition));
}
else {
fi->POfill = (WORD *)((UBYTE *)(fi->PObuffer) + BASEPOSITION(oldposition));
}
BACKINOUT
AC.cbufnum = oldcbuf;
AC.tablefilling = 0;
AT.WorkPointer = oldwork;
return(0);
noway:
BACKINOUT
AC.cbufnum = oldcbuf;
AC.tablefilling = 0;
AT.WorkPointer = oldwork;
return(1);
}
/*
#] CoFillExpression :
#[ CoPrintTable :
Syntax
PrintTable [+f] [+s] tablename [>[>] file];
All defined elements are written with individual Fill statements.
If a file is specified, the result is written to file only.
The flags of the print statement apply as much as possible.
We make use of the regular write routines.
*/
int CoPrintTable(UBYTE *inp)
{
GETIDENTITY
int fflag = 0, sflag = 0, addflag = 0, error = 0, sum, i, j;
UBYTE *filename, *p, c, buffer[100], *s, *oldoutputline = AO.OutputLine;
WORD type, funnum, *expr, *m, num;
TABLES T = 0;
WORD oldSkip = AO.OutSkip, oldMode = AC.OutputMode, oldHandle = AC.LogHandle;
WORD oldType = AO.PrintType, *oldwork = AT.WorkPointer;
UBYTE *oldFill = AO.OutFill, *oldLine = AO.OutputLine;
#ifdef WITHMPI
if ( PF.me != MASTER ) return 0;
#endif
/*
First the flags
*/
while ( *inp == '+' ) {
inp++;
if ( *inp == 'f' || *inp == 'F' ) { fflag = 1; inp++; }
else if ( *inp == 's' || *inp == 'S' ) { sflag = PRINTONETERM; inp++; }
else {
MesPrint("&Illegal + option in PrintTable statement");
error = 1; inp++;
}
while ( *inp != ',' && *inp && *inp != '+' ) {
if ( !error ) {
if ( *inp ) {
MesPrint("&Illegal + option in PrintTable statement");
inp++;
}
else {
MesPrint("&Unfinished PrintTable statement");
return(1);
}
error = 1;
}
inp++;
}
if ( *inp == ',' ) inp++;
}
/*
Now the name of the table
*/
if ( ( p = SkipAName(inp) ) == 0 ) return(1);
c = *p; *p = 0;
if ( ( GetVar(inp,&type,&funnum,CFUNCTION,NOAUTO) == NAMENOTFOUND )
|| ( T = functions[funnum].tabl ) == 0 ) {
MesPrint("&%s should be a previously declared table",inp);
*p = c; return(1);
}
if ( T->spare && T->mode == 1 ) T = T->spare;
*p++ = c;
/*
Check for a filename. Runs to the end of the statement.
*/
filename = 0;
if ( c == '>' ) {
if ( *p == '>' ) { addflag = 1; p++; }
filename = p;
}
else filename = 0;
if ( filename ) {
if ( addflag ) AC.LogHandle = OpenAddFile((char *)filename);
else AC.LogHandle = CreateFile((char *)filename);
if ( AC.LogHandle < 0 ) {
MesPrint("&Cannot open file '%s' properly",filename);
error = 1; goto finally;
}
AO.PrintType = PRINTLFILE;
}
else if ( fflag && AC.LogHandle >= 0 ) {
AO.PrintType = PRINTLFILE;
}
AO.OutFill = AO.OutputLine = (UBYTE *)AT.WorkPointer;
AT.WorkPointer += 2*AC.LineLength;
AO.PrintType |= sflag;
AC.OutputMode = 0;
AO.IsBracket = 0;
AO.OutSkip = 0;
AR.DeferFlag = 0;
AC.outsidefun = 1;
if ( AC.LogHandle == oldHandle ) FiniLine();
AO.OutputLine = AO.OutFill = (UBYTE *)Malloc1(AC.LineLength+20,"PrintTable");
AO.OutStop = AO.OutFill + AC.LineLength;
for ( i = 0; i < T->totind; i++ ) {
if ( !T->sparse && T->tablepointers[i*TABLEEXTENSION] < 0 ) continue;
TokenToLine((UBYTE *)"Fill ");
TokenToLine((UBYTE *)(VARNAME(functions,funnum)));
TokenToLine((UBYTE *)"(");
AO.OutSkip = 3;
if ( T->sparse ) {
sum = i * ( T->numind + TABLEEXTENSION );
for ( j = 0; j < T->numind; j++, sum++ ) {
if ( j > 0 ) TokenToLine((UBYTE *)",");
num = T->tablepointers[sum];
s = buffer; s = NumCopy(num,s);
TokenToLine(buffer);
}
expr = cbuf[T->bufnum].rhs[T->tablepointers[sum]];
}
else {
for ( j = 0; j < T->numind; j++ ) {
if ( j > 0 ) {
TokenToLine((UBYTE *)",");
num = T->mm[j].mini + ( i % T->mm[j-1].size ) / T->mm[j].size;
}
else {
num = T->mm[j].mini + i / T->mm[j].size;
}
s = buffer; s = NumCopy(num,s);
TokenToLine(buffer);
}
expr = cbuf[T->bufnum].rhs[T->tablepointers[TABLEEXTENSION*i]];
}
TOKENTOLINE(") =",")=");
if ( sflag ) {
FiniLine();
if ( AC.OutputSpaces != NOSPACEFORMAT ) TokenToLine((UBYTE *)" ");
}
m = expr;
/*
WORD lbrac, first;
lbrac = 0; first = 1;
while ( *m ) {
if ( WriteTerm(m,&lbrac,first,1,0) ) {
MesPrint("Error while writing table");
error = 1;
goto finally;
}
first = 0;
m += *m;
}
if ( first ) { TOKENTOLINE(" 0","0") }
else if ( lbrac ) { TOKENTOLINE(" )",")") }
*/
while ( *m ) m += *m;
if ( m > expr ) {
if ( WriteExpression(expr,(LONG)(m-expr)) ) { error = 1; goto finally; }
AO.OutSkip = 0;
}
else {
TokenToLine((UBYTE *)"0");
}
TokenToLine((UBYTE *)";");
FiniLine();
}
M_free(AO.OutputLine,"PrintTable");
AO.OutputLine = AO.OutFill = oldoutputline;
/*
Reset the file pointers and parameters if any. Close file if needed.
*/
finally:
AO.OutSkip = oldSkip;
AC.OutputMode = oldMode;
AC.LogHandle = oldHandle;
AO.PrintType = oldType;
AO.OutFill = oldFill;
AO.OutputLine = oldLine;
AT.WorkPointer = oldwork;
AC.outsidefun = 0;
return(error);
}
/*
#] CoPrintTable :
#[ CoAssign :
This statement has an easy syntax:
$name = expression
*/
static WORD AssignLHS[14] = { TYPEASSIGN, 3+SUBEXPSIZE, 0,
SUBEXPRESSION, SUBEXPSIZE, 0, 1, 0, 0,0,0,0,0 };
int CoAssign(UBYTE *inp)
{
int error = 0, retcode;
UBYTE *name, c;
WORD number;
if ( *inp != '$' ) {
nolhs: MesPrint("&assign statement should have a dollar variable in the LHS");
return(1);
}
inp++; name = inp;
if ( FG.cTable[*inp] != 0 ) goto nolhs;
while ( FG.cTable[*inp] < 2 ) inp++;
if ( AP.PreAssignFlag == 2 ) {
if ( *inp == '_' ) inp++;
}
if ( ( *inp == ',' && inp[1] != '=' ) && ( *inp != '=' ) ) {
MesPrint("&assign statement should have only a dollar variable in the LHS");
return(1);
}
c = *inp;
*inp = 0;
if ( GetName(AC.dollarnames,name,&number,NOAUTO) == NAMENOTFOUND ) {
number = AddDollar(name,DOLUNDEFINED,0,0);
}
*inp = c;
if ( c == ',' ) inp++;
*inp++ = '=';
if ( *inp == ',' ) inp++;
/*
Fake a Prototype and read the RHS
*/
AssignLHS[7] = AC.cbufnum;
retcode = CompileAlgebra(inp,RHSIDE,(AssignLHS+3));
if ( retcode < 0 ) error = 1;
AC.DumNum = 0;
/*
Now add the LHS
*/
AssignLHS[2] = number;
AssignLHS[5] = retcode;
AddNtoL(AssignLHS[1],AssignLHS);
/*
Add to the list of potentially modified dollars (for PARALLEL)
*/
AddPotModdollar(number);
return(error);
}
/*
#] CoAssign :
#[ CoDeallocateTable :
Syntax: DeallocateTable tablename(s);
Should work only for sparse tables.
Action: Cleans all definitions of elements of a table as if there have
never been any fill statements.
*/
int CoDeallocateTable(UBYTE *inp)
{
UBYTE *p, c;
TABLES T = 0;
WORD type, funnum, i;
c = *inp;
while ( c ) {
while ( *inp == ',' ) inp++;
if ( *inp == 0 ) break;
if ( ( p = SkipAName(inp) ) == 0 ) return(1);
c = *p; *p = 0;
if ( ( GetVar(inp,&type,&funnum,CFUNCTION,NOAUTO) == NAMENOTFOUND )
|| ( T = functions[funnum].tabl ) == 0 ) {
MesPrint("&%s should be a previously declared table",inp);
*p = c; return(1);
}
if ( T->sparse == 0 ) {
MesPrint("&%s should be a sparse table",inp);
*p = c; return(1);
}
if ( T->tablepointers ) M_free(T->tablepointers,"tablepointers");
ClearTableTree(T);
for (i = 0; i < T->buffersfill; i++ ) { /* was <= */
finishcbuf(T->buffers[i]);
}
T->bufnum = inicbufs();
T->buffersfill = 0;
T->buffers[T->buffersfill++] = T->bufnum;
T->tablepointers = 0;
T->boomlijst = 0;
T->totind = 0;
T->reserved = 0;
if ( T->spare ) {
TABLES TT = T->spare;
if ( TT->tablepointers ) M_free(TT->tablepointers,"tablepointers");
ClearTableTree(TT);
for (i = 0; i < TT->buffersfill; i++ ) { /* was <= */
finishcbuf(TT->buffers[i]);
}
TT->bufnum = inicbufs();
TT->buffersfill = 0;
TT->buffers[T->buffersfill++] = T->bufnum;
TT->tablepointers = 0;
TT->boomlijst = 0;
TT->totind = 0;
TT->reserved = 0;
}
*p++ = c;
inp = p;
}
return(0);
}
/*
#] CoDeallocateTable :
#[ CoFactorCache :
*/
/**
* Reads the FactorCache statement which is like a fill statement for
* the factorization cache. Syntax:
* FactorCache,expression:factor1,...,factorn;
* This statement is mainly for testing purposes, because there are severe
* restrictions on the use of the expression (no common GCD, no denominators)
* The expression is worked out by FORM and properly normalized and sorted.
*/
/*
int CoFactorCache(UBYTE *inp)
{
Code to be added in due time
We need to read 'expression', get its terms through Generator and sort them.
We store the result in the WorkSpace in argument notation.
This will be argin.
Then we do the same with the sequence of factors. They form argout.
The whole is put in the buffer with the call
InsertArg(BHEAD argin,argout,1)
return(0);
}
*/
/*
#] CoFactorCache :
*/
form-master/sources/compcomm.c 0000664 0000000 0000000 00000540631 13565763364 0017006 0 ustar 00root root 0000000 0000000 /** @file compcomm.c
*
* Compiler routines for most statements that don't involve algebraic
* expressions. Exceptions: all routines involving declarations are in
* the file names.c
* When making new statements one can add the compiler routines here and
* have a look whether there is already a routine that is similar. In that
* case one can make a copy and modify it.
*/
/* #[ License : */
/*
* Copyright (C) 1984-2017 J.A.M. Vermaseren
* When using this file you are requested to refer to the publication
* J.A.M.Vermaseren "New features of FORM" math-ph/0010025
* This is considered a matter of courtesy as the development was paid
* for by FOM the Dutch physics granting agency and we would like to
* be able to track its scientific use to convince FOM of its value
* for the community.
*
* This file is part of FORM.
*
* FORM 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.
*
* FORM 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 FORM. If not, see .
*/
/* #] License : */
/*
#[ includes :
*/
#include "form3.h"
#include "comtool.h"
static KEYWORD formatoptions[] = {
{"allfloat", (TFUN)0, ALLINTEGERDOUBLE, 0}
,{"c", (TFUN)0, CMODE, 0}
,{"doublefortran", (TFUN)0, DOUBLEFORTRANMODE, 0}
,{"float", (TFUN)0, 0, 2}
,{"fortran", (TFUN)0, FORTRANMODE, 0}
,{"fortran90", (TFUN)0, FORTRANMODE, 4}
,{"maple", (TFUN)0, MAPLEMODE, 0}
,{"mathematica", (TFUN)0, MATHEMATICAMODE, 0}
,{"normal", (TFUN)0, NORMALFORMAT, 1}
,{"nospaces", (TFUN)0, NOSPACEFORMAT, 3}
,{"pfortran", (TFUN)0, PFORTRANMODE, 0}
,{"quadfortran", (TFUN)0, QUADRUPLEFORTRANMODE, 0}
,{"quadruplefortran", (TFUN)0, QUADRUPLEFORTRANMODE, 0}
,{"rational", (TFUN)0, RATIONALMODE, 1}
,{"reduce", (TFUN)0, REDUCEMODE, 0}
,{"spaces", (TFUN)0, NORMALFORMAT, 3}
,{"vortran", (TFUN)0, VORTRANMODE, 0}
};
static KEYWORD trace4options[] = {
{"contract", (TFUN)0, CHISHOLM, 0 }
,{"nocontract", (TFUN)0, 0, CHISHOLM }
,{"nosymmetrize",(TFUN)0, 0, ALSOREVERSE}
,{"notrick", (TFUN)0, NOTRICK, 0 }
,{"symmetrize", (TFUN)0, ALSOREVERSE, 0 }
,{"trick", (TFUN)0, 0, NOTRICK }
};
static KEYWORD chisoptions[] = {
{"nosymmetrize",(TFUN)0, 0, ALSOREVERSE}
,{"symmetrize", (TFUN)0, ALSOREVERSE, 0 }
};
static KEYWORDV writeoptions[] = {
{"stats", &(AC.StatsFlag), 1, 0}
,{"statistics", &(AC.StatsFlag), 1, 0}
,{"shortstats", &(AC.ShortStats), 1, 0}
,{"shortstatistics",&(AC.ShortStats), 1, 0}
,{"warnings", &(AC.WarnFlag), 1, 0}
,{"allwarnings", &(AC.WarnFlag), 2, 0}
,{"setup", &(AC.SetupFlag), 1, 0}
,{"names", &(AC.NamesFlag), 1, 0}
,{"allnames", &(AC.NamesFlag), 2, 0}
,{"codes", &(AC.CodesFlag), 1, 0}
,{"highfirst", &(AC.SortType), SORTHIGHFIRST, SORTLOWFIRST}
,{"lowfirst", &(AC.SortType), SORTLOWFIRST, SORTHIGHFIRST}
,{"powerfirst", &(AC.SortType), SORTPOWERFIRST, SORTHIGHFIRST}
,{"tokens", &(AC.TokensWriteFlag),1, 0}
};
static KEYWORDV onoffoptions[] = {
{"compress", &(AC.NoCompress), 0, 1}
,{"checkpoint", &(AC.CheckpointFlag), 1, 0}
,{"insidefirst", &(AC.insidefirst), 1, 0}
,{"propercount", &(AC.BottomLevel), 1, 0}
,{"stats", &(AC.StatsFlag), 1, 0}
,{"statistics", &(AC.StatsFlag), 1, 0}
,{"shortstats", &(AC.ShortStats), 1, 0}
,{"shortstatistics",&(AC.ShortStats), 1, 0}
,{"names", &(AC.NamesFlag), 1, 0}
,{"allnames", &(AC.NamesFlag), 2, 0}
,{"warnings", &(AC.WarnFlag), 1, 0}
,{"allwarnings", &(AC.WarnFlag), 2, 0}
,{"highfirst", &(AC.SortType), SORTHIGHFIRST, SORTLOWFIRST}
,{"lowfirst", &(AC.SortType), SORTLOWFIRST, SORTHIGHFIRST}
,{"powerfirst", &(AC.SortType), SORTPOWERFIRST, SORTHIGHFIRST}
,{"setup", &(AC.SetupFlag), 1, 0}
,{"codes", &(AC.CodesFlag), 1, 0}
,{"tokens", &(AC.TokensWriteFlag),1,0}
,{"properorder", &(AC.properorderflag),1,0}
,{"threadloadbalancing",&(AC.ThreadBalancing),1, 0}
,{"threads", &(AC.ThreadsFlag),1, 0}
,{"threadsortfilesynch",&(AC.ThreadSortFileSynch),1, 0}
,{"threadstats", &(AC.ThreadStats),1, 0}
,{"finalstats", &(AC.FinalStats),1, 0}
,{"fewerstats", &(AC.ShortStatsMax), 10, 0}
,{"fewerstatistics",&(AC.ShortStatsMax), 10, 0}
,{"processstats", &(AC.ProcessStats),1, 0}
,{"oldparallelstats",&(AC.OldParallelStats),1,0}
,{"parallel", &(AC.parallelflag),PARALLELFLAG,NOPARALLEL_USER}
,{"nospacesinnumbers",&(AO.NoSpacesInNumbers),1,0}
,{"indentspace", &(AO.IndentSpace),INDENTSPACE,0}
,{"totalsize", &(AM.PrintTotalSize), 1, 0}
,{"flag", (int *)&(AC.debugFlags), 1, 0}
,{"oldfactarg", &(AC.OldFactArgFlag), 1, 0}
,{"memdebugflag", &(AC.MemDebugFlag), 1, 0}
,{"oldgcd", &(AC.OldGCDflag), 1, 0}
,{"innertest", &(AC.InnerTest), 1, 0}
,{"wtimestats", &(AC.WTimeStatsFlag), 1, 0}
};
static WORD one = 1;
/*
#] includes :
#[ CoCollect :
Collect,functionname
*/
int CoCollect(UBYTE *s)
{
/* --------------change 17-feb-2003 Added percentage */
WORD numfun;
int type,x = 0;
UBYTE *t = SkipAName(s), *t1, *t2;
AC.AltCollectFun = 0;
if ( t == 0 ) goto syntaxerror;
t1 = t; while ( *t1 == ',' || *t1 == ' ' || *t1 == '\t' ) t1++;
*t = 0; t = t1;
if ( *t1 && ( FG.cTable[*t1] == 0 || *t1 == '[' ) ) {
t2 = SkipAName(t1);
if ( t2 == 0 ) goto syntaxerror;
t = t2;
while ( *t == ',' || *t == ' ' || *t == '\t' ) t++;
*t2 = 0;
}
else t1 = 0;
if ( *t && FG.cTable[*t] == 1 ) {
while ( *t >= '0' && *t <= '9' ) x = 10*x + *t++ - '0';
if ( x > 100 ) x = 100;
while ( *t == ',' || *t == ' ' || *t == '\t' ) t++;
if ( *t ) goto syntaxerror;
}
else {
if ( *t ) goto syntaxerror;
x = 100;
}
if ( ( ( type = GetName(AC.varnames,s,&numfun,WITHAUTO) ) != CFUNCTION )
|| ( functions[numfun].spec != 0 ) ) {
MesPrint("&%s should be a regular function",s);
if ( type < 0 ) {
if ( GetName(AC.exprnames,s,&numfun,NOAUTO) == NAMENOTFOUND )
AddFunction(s,0,0,0,0,0,-1,-1);
}
return(1);
}
AC.CollectFun = numfun+FUNCTION;
AC.CollectPercentage = (WORD)x;
if ( t1 ) {
if ( ( ( type = GetName(AC.varnames,t1,&numfun,WITHAUTO) ) != CFUNCTION )
|| ( functions[numfun].spec != 0 ) ) {
MesPrint("&%s should be a regular function",t1);
if ( type < 0 ) {
if ( GetName(AC.exprnames,t1,&numfun,NOAUTO) == NAMENOTFOUND )
AddFunction(t1,0,0,0,0,0,-1,-1);
}
return(1);
}
AC.AltCollectFun = numfun+FUNCTION;
}
return(0);
syntaxerror:
MesPrint("&Collect statement needs one or two functions (and a percentage) for its argument(s)");
return(1);
}
/*
#] CoCollect :
#[ setonoff :
*/
int setonoff(UBYTE *s, int *flag, int onvalue, int offvalue)
{
if ( StrICmp(s,(UBYTE *)"on") == 0 ) *flag = onvalue;
else if ( StrICmp(s,(UBYTE *)"off") == 0 ) *flag = offvalue;
else {
MesPrint("&Unknown option: %s, on or off expected",s);
return(1);
}
return(0);
}
/*
#] setonoff :
#[ CoCompress :
*/
int CoCompress(UBYTE *s)
{
GETIDENTITY
UBYTE *t, c;
if ( StrICmp(s,(UBYTE *)"on") == 0 ) {
AC.NoCompress = 0;
AR.gzipCompress = 0;
}
else if ( StrICmp(s,(UBYTE *)"off") == 0 ) {
AC.NoCompress = 1;
AR.gzipCompress = 0;
}
else {
t = s; while ( FG.cTable[*t] <= 1 ) t++;
c = *t; *t = 0;
if ( StrICmp(s,(UBYTE *)"gzip") == 0 ) {
#ifndef WITHZLIB
Warning("gzip compression not supported on this platform");
#endif
s = t; *s = c;
if ( *s == 0 ) {
AR.gzipCompress = GZIPDEFAULT; /* Normally should be 6 */
return(0);
}
while ( *s == ' ' || *s == ',' || *s == '\t' ) s++;
t = s;
if ( FG.cTable[*s] == 1 ) {
AR.gzipCompress = *s - '0';
s++;
while ( *s == ' ' || *s == ',' || *s == '\t' ) s++;
if ( *s == 0 ) return(0);
}
MesPrint("&Unknown gzip option: %s, a digit was expected",t);
return(1);
}
else {
MesPrint("&Unknown option: %s, on, off or gzip expected",s);
return(1);
}
}
return(0);
}
/*
#] CoCompress :
#[ CoFlags :
*/
int CoFlags(UBYTE *s,int value)
{
int i, error = 0;
if ( *s != ',' ) {
MesPrint("&Proper syntax is: On/Off Flag,number[s];");
error = 1;
}
while ( *s == ',' ) {
do { s++; } while ( *s == ',' );
i = 0;
if ( FG.cTable[*s] != 1 ) {
MesPrint("&Proper syntax is: On/Off Flag,number[s];");
error = 1;
break;
}
while ( FG.cTable[*s] == 1 ) { i = 10*i + *s++ - '0'; }
if ( i <= 0 || i > MAXFLAGS ) {
MesPrint("&The number of a flag in On/Off Flag should be in the range 0-%d",(int)MAXFLAGS);
error = 1;
break;
}
AC.debugFlags[i] = value;
}
if ( *s ) {
MesPrint("&Proper syntax is: On/Off Flag,number[s];");
error = 1;
}
return(error);
}
/*
#] CoFlags :
#[ CoOff :
*/
int CoOff(UBYTE *s)
{
GETIDENTITY
UBYTE *t, c;
int i, num = sizeof(onoffoptions)/sizeof(KEYWORD);
for (;;) {
while ( *s == ' ' || *s == ',' || *s == '\t' ) s++;
if ( *s == 0 ) return(0);
if ( chartype[*s] != 0 ) {
MesPrint("&Illegal character or option encountered in OFF statement");
return(-1);
}
t = s; while ( chartype[*s] == 0 ) s++;
c = *s; *s = 0;
for ( i = 0; i < num; i++ ) {
if ( StrICont(t,(UBYTE *)(onoffoptions[i].name)) == 0 ) break;
}
if ( i >= num ) {
MesPrint("&Unrecognized option in OFF statement: %s",t);
*s = c; return(-1);
}
else if ( StrICont(t,(UBYTE *)"compress") == 0 ) {
AR.gzipCompress = 0;
}
else if ( StrICont(t,(UBYTE *)"checkpoint") == 0 ) {
AC.CheckpointInterval = 0;
if ( AC.CheckpointRunBefore ) { free(AC.CheckpointRunBefore); AC.CheckpointRunBefore = NULL; }
if ( AC.CheckpointRunAfter ) { free(AC.CheckpointRunAfter); AC.CheckpointRunAfter = NULL; }
if ( AC.NoShowInput == 0 ) MesPrint("Checkpoints deactivated.");
}
else if ( StrICont(t,(UBYTE *)"threads") == 0 ) {
AS.MultiThreaded = 0;
}
else if ( StrICont(t,(UBYTE *)"flag") == 0 ) {
*s = c;
return(CoFlags(s,0));
}
else if ( StrICont(t,(UBYTE *)"innertest") == 0 ) {
*s = c;
AC.InnerTest = 0;
if ( AC.TestValue ) {
M_free(AC.TestValue,"InnerTest");
AC.TestValue = 0;
}
}
*s = c;
*onoffoptions[i].var = onoffoptions[i].flags;
AR.SortType = AC.SortType;
AC.mparallelflag = AC.parallelflag | AM.hparallelflag;
}
}
/*
#] CoOff :
#[ CoOn :
*/
int CoOn(UBYTE *s)
{
GETIDENTITY
UBYTE *t, c;
int i, num = sizeof(onoffoptions)/sizeof(KEYWORD);
LONG interval;
for (;;) {
while ( *s == ' ' || *s == ',' || *s == '\t' ) s++;
if ( *s == 0 ) return(0);
if ( chartype[*s] != 0 ) {
MesPrint("&Illegal character or option encountered in ON statement");
return(-1);
}
t = s; while ( chartype[*s] == 0 ) s++;
c = *s; *s = 0;
for ( i = 0; i < num; i++ ) {
if ( StrICont(t,(UBYTE *)(onoffoptions[i].name)) == 0 ) break;
}
if ( i >= num ) {
MesPrint("&Unrecognized option in ON statement: %s",t);
*s = c; return(-1);
}
if ( StrICont(t,(UBYTE *)"compress") == 0 ) {
AR.gzipCompress = 0;
*s = c;
while ( *s == ' ' || *s == ',' || *s == '\t' ) s++;
if ( *s ) {
t = s;
while ( FG.cTable[*s] <= 1 ) s++;
c = *s; *s = 0;
if ( StrICmp(t,(UBYTE *)"gzip") == 0 ) {}
else {
MesPrint("&Unrecognized option in ON compress statement: %s",t);
return(-1);
}
*s = c;
while ( *s == ' ' || *s == ',' || *s == '\t' ) s++;
#ifndef WITHZLIB
Warning("gzip compression not supported on this platform");
#endif
if ( FG.cTable[*s] == 1 ) {
AR.gzipCompress = *s++ - '0';
while ( *s == ' ' || *s == ',' || *s == '\t' ) s++;
if ( *s ) {
MesPrint("&Unrecognized option in ON compress gzip statement: %s",t);
return(-1);
}
}
else if ( *s == 0 ) {
AR.gzipCompress = GZIPDEFAULT;
}
else {
MesPrint("&Unrecognized option in ON compress gzip statement: %s, single digit expected",t);
return(-1);
}
}
}
else if ( StrICont(t,(UBYTE *)"checkpoint") == 0 ) {
AC.CheckpointInterval = 0;
if ( AC.CheckpointRunBefore ) { free(AC.CheckpointRunBefore); AC.CheckpointRunBefore = NULL; }
if ( AC.CheckpointRunAfter ) { free(AC.CheckpointRunAfter); AC.CheckpointRunAfter = NULL; }
*s = c;
while ( *s ) {
while ( *s == ' ' || *s == ',' || *s == '\t' ) s++;
if ( FG.cTable[*s] == 1 ) {
interval = 0;
t = s;
do { interval = 10*interval + *s++ - '0'; } while ( FG.cTable[*s] == 1 );
if ( *s == 's' || *s == 'S' ) {
s++;
}
else if ( *s == 'm' || *s == 'M' ) {
interval *= 60; s++;
}
else if ( *s == 'h' || *s == 'H' ) {
interval *= 3600; s++;
}
else if ( *s == 'd' || *s == 'D' ) {
interval *= 86400; s++;
}
if ( *s != ',' && FG.cTable[*s] != 6 && FG.cTable[*s] != 10 ) {
MesPrint("&Unrecognized time interval in ON Checkpoint statement: %s", t);
return(-1);
}
AC.CheckpointInterval = interval * 100; /* in 1/100 of seconds */
}
else if ( FG.cTable[*s] == 0 ) {
int type;
t = s;
while ( FG.cTable[*s] == 0 ) s++;
c = *s; *s = 0;
if ( StrICmp(t,(UBYTE *)"run") == 0 ) {
type = 3;
}
else if ( StrICmp(t,(UBYTE *)"runafter") == 0 ) {
type = 2;
}
else if ( StrICmp(t,(UBYTE *)"runbefore") == 0 ) {
type = 1;
}
else {
MesPrint("&Unrecognized option in ON Checkpoint statement: %s", t);
*s = c; return(-1);
}
*s = c;
if ( *s != '=' && FG.cTable[*(s+1)] != 9 ) {
MesPrint("&Unrecognized option in ON Checkpoint statement: %s", t);
return(-1);
}
++s;
t = ++s;
while ( *s ) {
if ( FG.cTable[*s] == 9 ) {
c = *s; *s = 0;
if ( type & 1 ) {
if ( AC.CheckpointRunBefore ) {
free(AC.CheckpointRunBefore); AC.CheckpointRunBefore = NULL;
}
if ( s-t > 0 ) {
AC.CheckpointRunBefore = Malloc1(s-t+1, "AC.CheckpointRunBefore");
StrCopy(t, (UBYTE*)AC.CheckpointRunBefore);
}
}
if ( type & 2 ) {
if ( AC.CheckpointRunAfter ) {
free(AC.CheckpointRunAfter); AC.CheckpointRunAfter = NULL;
}
if ( s-t > 0 ) {
AC.CheckpointRunAfter = Malloc1(s-t+1, "AC.CheckpointRunAfter");
StrCopy(t, (UBYTE*)AC.CheckpointRunAfter);
}
}
*s = c;
break;
}
++s;
}
if ( FG.cTable[*s] != 9 ) {
MesPrint("&Unrecognized option in ON Checkpoint statement: %s", t);
return(-1);
}
++s;
}
}
/*
if ( AC.NoShowInput == 0 ) {
MesPrint("Checkpoints activated.");
if ( AC.CheckpointInterval ) {
MesPrint("-> Minimum saving interval: %l seconds.", AC.CheckpointInterval/100);
}
else {
MesPrint("-> No minimum saving interval given. Saving after EVERY module.");
}
if ( AC.CheckpointRunBefore ) {
MesPrint("-> Calling script \"%s\" before saving.", AC.CheckpointRunBefore);
}
if ( AC.CheckpointRunAfter ) {
MesPrint("-> Calling script \"%s\" after saving.", AC.CheckpointRunAfter);
}
}
*/
}
else if ( StrICont(t,(UBYTE *)"indentspace") == 0 ) {
*s = c;
while ( *s == ' ' || *s == ',' || *s == '\t' ) s++;
if ( *s ) {
i = 0;
while ( FG.cTable[*s] == 1 ) { i = 10*i + *s++ - '0'; }
if ( *s ) {
MesPrint("&Unrecognized option in ON IndentSpace statement: %s",t);
return(-1);
}
if ( i > 40 ) {
Warning("IndentSpace parameter adjusted to 40");
i = 40;
}
AO.IndentSpace = i;
}
else {
AO.IndentSpace = AM.ggIndentSpace;
}
return(0);
}
else if ( ( StrICont(t,(UBYTE *)"fewerstats") == 0 ) ||
( StrICont(t,(UBYTE *)"fewerstatistics") == 0 ) ) {
*s = c;
while ( *s == ' ' || *s == ',' || *s == '\t' ) s++;
if ( *s ) {
i = 0;
while ( FG.cTable[*s] == 1 ) { i = 10*i + *s++ - '0'; }
if ( *s ) {
MesPrint("&Unrecognized option in ON FewerStatistics statement: %s",t);
return(-1);
}
if ( i > AM.S0->MaxPatches ) {
if ( AC.WarnFlag )
MesPrint("&Warning: FewerStatistics parameter greater than MaxPatches(=%d). Adjusted to %d"
,AM.S0->MaxPatches,(AM.S0->MaxPatches+1)/2);
i = (AM.S0->MaxPatches+1)/2;
}
AC.ShortStatsMax = i;
}
else {
AC.ShortStatsMax = 10; /* default value */
}
return(0);
}
else if ( StrICont(t,(UBYTE *)"threads") == 0 ) {
if ( AM.totalnumberofthreads > 1 ) AS.MultiThreaded = 1;
}
else if ( StrICont(t,(UBYTE *)"flag") == 0 ) {
*s = c;
return(CoFlags(s,1));
}
else if ( StrICont(t,(UBYTE *)"innertest") == 0 ) {
UBYTE *t;
*s = c;
while ( *s == ' ' || *s == ',' || *s == '\t' ) s++;
if ( *s ) {
t = s; while ( *t ) t++;
while ( t[-1] == ' ' || t[-1] == '\t' ) t--;
c = *t; *t = 0;
if ( AC.TestValue ) M_free(AC.TestValue,"InnerTest");
AC.TestValue = strDup1(s,"InnerTest");
*t = c;
s = t;
while ( *s == ' ' || *s == ',' || *s == '\t' ) s++;
}
else {
if ( AC.TestValue ) {
M_free(AC.TestValue,"InnerTest");
AC.TestValue = 0;
}
}
}
else { *s = c; }
*onoffoptions[i].var = onoffoptions[i].type;
AR.SortType = AC.SortType;
AC.mparallelflag = AC.parallelflag | AM.hparallelflag;
}
}
/*
#] CoOn :
#[ CoInsideFirst :
*/
int CoInsideFirst(UBYTE *s) { return(setonoff(s,&AC.insidefirst,1,0)); }
/*
#] CoInsideFirst :
#[ CoProperCount :
*/
int CoProperCount(UBYTE *s) { return(setonoff(s,&AC.BottomLevel,1,0)); }
/*
#] CoProperCount :
#[ CoDelete :
*/
int CoDelete(UBYTE *s)
{
int error = 0;
if ( StrICmp(s,(UBYTE *)"storage") == 0 ) {
if ( DeleteStore(1) < 0 ) {
MesPrint("&Cannot restart storage file");
error = 1;
}
}
else {
UBYTE *t = s, c;
while ( *t && *t != ',' && *t != '>' ) t++;
c = *t; *t = 0;
if ( ( StrICmp(s,(UBYTE *)"extrasymbols") == 0 )
|| ( StrICmp(s,(UBYTE *)"extrasymbol") == 0 ) ) {
WORD x = 0;
/*
Either deletes all extra symbols or deletes above a given number
*/
*t = c; s = t;
if ( *s == '>' ) {
s++;
if ( FG.cTable[*s] != 1 ) goto unknown;
while ( *s <= '9' && *s >= '0' ) x = 10*x + *s++ - '0';
if ( *s ) goto unknown;
}
else if ( *s ) goto unknown;
if ( x < AM.gnumextrasym ) x = AM.gnumextrasym;
PruneExtraSymbols(x);
}
else {
*t = c;
unknown:
MesPrint("&Unknown option: %s",s);
error = 1;
}
}
return(error);
}
/*
#] CoDelete :
#[ CoFormat :
*/
int CoFormat(UBYTE *s)
{
int error = 0, x;
KEYWORD *key;
UBYTE *ss;
while ( *s == ' ' || *s == ',' ) s++;
if ( *s == 0 ) {
AC.OutputMode = 72;
AC.OutputSpaces = NORMALFORMAT;
return(error);
}
/*
First the optimization level
*/
if ( *s == 'O' || *s == 'o' ) {
if ( ( FG.cTable[s[1]] == 1 ) ||
( s[1] == '=' && FG.cTable[s[2]] == 1 ) ) {
s++; if ( *s == '=' ) s++;
x = 0;
while ( *s >= '0' && *s <= '9' ) x = 10*x + *s++ - '0';
while ( *s == ',' ) s++;
AO.OptimizationLevel = x;
AO.Optimize.greedytimelimit = 0;
AO.Optimize.mctstimelimit = 0;
AO.Optimize.printstats = 0;
AO.Optimize.debugflags = 0;
AO.Optimize.schemeflags = 0;
AO.Optimize.mctsdecaymode = 1; // default is decreasing C_p with iteration number
if ( AO.inscheme ) {
M_free(AO.inscheme,"Horner input scheme");
AO.inscheme = 0; AO.schemenum = 0;
}
switch ( x ) {
case 0:
break;
case 1:
AO.Optimize.mctsconstant.fval = -1.0;
AO.Optimize.horner = O_OCCURRENCE;
AO.Optimize.hornerdirection = O_FORWARDORBACKWARD;
AO.Optimize.method = O_CSE;
break;
case 2:
AO.Optimize.horner = O_OCCURRENCE;
AO.Optimize.hornerdirection = O_FORWARDORBACKWARD;
AO.Optimize.method = O_GREEDY;
AO.Optimize.greedyminnum = 10;
AO.Optimize.greedymaxperc = 5;
break;
case 3:
AO.Optimize.mctsconstant.fval = 1.0;
AO.Optimize.horner = O_MCTS;
AO.Optimize.hornerdirection = O_FORWARDORBACKWARD;
AO.Optimize.method = O_GREEDY;
AO.Optimize.mctsnumexpand = 1000;
AO.Optimize.mctsnumkeep = 10;
AO.Optimize.mctsnumrepeat = 1;
AO.Optimize.greedyminnum = 10;
AO.Optimize.greedymaxperc = 5;
break;
case 4:
AO.Optimize.horner = O_SIMULATED_ANNEALING;
AO.Optimize.saIter = 1000;
AO.Optimize.saMaxT.fval = 2000;
AO.Optimize.saMinT.fval = 1;
break;
default:
error = 1;
MesPrint("&Illegal optimization specification in format statement");
break;
}
if ( error == 0 && *s != 0 && x > 0 ) return(CoOptimizeOption(s));
return(error);
}
#ifdef EXPOPT
{ UBYTE c;
ss = s;
while ( FG.cTable[*s] == 0 ) s++;
c = *s; *s = 0;
if ( StrICont(ss,(UBYTE *)"optimize") == 0 ) {
*s = c;
while ( *s == ',' ) s++;
if ( *s == '=' ) s++;
AO.OptimizationLevel = 3;
AO.Optimize.mctsconstant.fval = 1.0;
AO.Optimize.horner = O_MCTS;
AO.Optimize.hornerdirection = O_FORWARDORBACKWARD;
AO.Optimize.method = O_GREEDY;
AO.Optimize.mctstimelimit = 0;
AO.Optimize.mctsnumexpand = 1000;
AO.Optimize.mctsnumkeep = 10;
AO.Optimize.mctsnumrepeat = 1;
AO.Optimize.greedytimelimit = 0;
AO.Optimize.greedyminnum = 10;
AO.Optimize.greedymaxperc = 5;
AO.Optimize.printstats = 0;
AO.Optimize.debugflags = 0;
AO.Optimize.schemeflags = 0;
AO.Optimize.mctsdecaymode = 1;
if ( AO.inscheme ) {
M_free(AO.inscheme,"Horner input scheme");
AO.inscheme = 0; AO.schemenum = 0;
}
return(CoOptimizeOption(s));
}
else {
error = 1;
MesPrint("&Illegal optimization specification in format statement");
return(error);
}
}
#endif
}
else if ( FG.cTable[*s] == 1 ) {
x = 0;
while ( FG.cTable[*s] == 1 ) x = 10*x + *s++ - '0';
if ( x <= 0 || x >= MAXLINELENGTH ) {
x = 72;
error = 1;
MesPrint("&Illegal value for linesize: %d",x);
}
if ( x < 39 ) {
MesPrint(" ... Too small value for linesize corrected to 39");
x = 39;
}
AO.DoubleFlag = 0;
/*
The next line resets the mode to normal. Because the special modes
reset the line length we have a little problem with the special modes
and customized line length. We try to improve by removing the next line
*/
/* AC.OutputMode = 0; */
AC.LineLength = x;
if ( *s != 0 ) {
error = 1;
MesPrint("&Illegal linesize field in format statement");
}
}
else {
key = FindKeyWord(s,formatoptions,
sizeof(formatoptions)/sizeof(KEYWORD));
if ( key ) {
if ( key->flags == 0 ) {
if ( key->type == FORTRANMODE || key->type == PFORTRANMODE
|| key->type == DOUBLEFORTRANMODE || key->type == ALLINTEGERDOUBLE
|| key->type == QUADRUPLEFORTRANMODE || key->type == VORTRANMODE ) {
AC.IsFortran90 = ISNOTFORTRAN90;
if ( AC.Fortran90Kind ) {
M_free(AC.Fortran90Kind,"Fortran90 Kind");
AC.Fortran90Kind = 0;
}
}
if ( ( key->type == ALLINTEGERDOUBLE ) && AO.DoubleFlag != 0 ) {
AO.DoubleFlag |= 4;
}
else {
AO.DoubleFlag = 0;
AC.OutputMode = key->type & NODOUBLEMASK;
if ( ( key->type & DOUBLEPRECISIONFLAG ) != 0 ) {
AO.DoubleFlag = 1;
}
else if ( ( key->type & QUADRUPLEPRECISIONFLAG ) != 0 ) {
AO.DoubleFlag = 2;
}
}
}
else if ( key->flags == 1 ) {
AC.OutputMode = AC.OutNumberType = key->type;
}
else if ( key->flags == 2 ) {
while ( FG.cTable[*s] == 0 ) s++;
if ( *s == 0 ) AC.OutNumberType = 10;
else if ( *s == ',' ) {
s++;
x = 0;
while ( FG.cTable[*s] == 1 ) x = 10*x + *s++ - '0';
if ( *s != 0 ) {
error = 1;
MesPrint("&Illegal float format specifier");
}
else {
if ( x < 3 ) {
x = 3;
MesPrint("& ... float format value corrected to 3");
}
if ( x > 100 ) {
x = 100;
MesPrint("& ... float format value corrected to 100");
}
AC.OutNumberType = x;
}
}
}
else if ( key->flags == 3 ) {
AC.OutputSpaces = key->type;
}
else if ( key->flags == 4 ) {
AC.IsFortran90 = ISFORTRAN90;
if ( AC.Fortran90Kind ) {
M_free(AC.Fortran90Kind,"Fortran90 Kind");
AC.Fortran90Kind = 0;
}
while ( FG.cTable[*s] <= 1 ) s++;
if ( *s == ',' ) {
s++; ss = s;
while ( *ss && *ss != ',' ) ss++;
if ( *ss == ',' ) {
MesPrint("&No white space or comma's allowed in Fortran90 option: %s",s); error = 1;
}
else {
AC.Fortran90Kind = strDup1(s,"Fortran90 Kind");
}
}
AO.DoubleFlag = 0;
AC.OutputMode = key->type & NODOUBLEMASK;
}
}
else if ( ( *s == 'c' || *s == 'C' ) && ( FG.cTable[s[1]] == 1 ) ) {
UBYTE *ss = s+1;
WORD x = 0;
while ( *ss >= '0' && *ss <= '9' ) x = 10*x + *ss++ - '0';
if ( *ss != 0 ) goto Unknown;
AC.OutputMode = CMODE;
AC.Cnumpows = x;
}
else {
Unknown: MesPrint("&Unknown option: %s",s); error = 1;
}
AC.LineLength = 72;
}
return(error);
}
/*
#] CoFormat :
#[ CoKeep :
*/
int CoKeep(UBYTE *s)
{
if ( StrICmp(s,(UBYTE *)"brackets") == 0 ) AC.ComDefer = 1;
else { MesPrint("&Unknown option: '%s'",s); return(1); }
return(0);
}
/*
#] CoKeep :
#[ CoFixIndex :
*/
int CoFixIndex(UBYTE *s)
{
int x, y, error = 0;
while ( *s ) {
if ( FG.cTable[*s] != 1 ) {
proper: MesPrint("&Proper syntax is: FixIndex,number:value[,number,value];");
return(1);
}
ParseNumber(x,s)
if ( *s != ':' ) goto proper;
s++;
if ( *s != '-' && *s != '+' && FG.cTable[*s] != 1 ) goto proper;
ParseSignedNumber(y,s)
if ( *s && *s != ',' ) goto proper;
while ( *s == ',' ) s++;
if ( x >= AM.OffsetIndex ) {
MesPrint("&Fixed index out of allowed range. Change ConstIndex in setup file?");
MesPrint("&Current value of ConstIndex = %d",AM.OffsetIndex-1);
error = 1;
}
if ( y != (int)((WORD)y) ) {
MesPrint("&Value of d_(%d,%d) outside range for this computer",x,x);
error = 1;
}
if ( error == 0 ) AC.FixIndices[x] = y;
}
return(error);
}
/*
#] CoFixIndex :
#[ CoMetric :
*/
int CoMetric(UBYTE *s)
{ DUMMYUSE(s); MesPrint("&The metric statement does not do anything yet"); return(1); }
/*
#] CoMetric :
#[ DoPrint :
*/
int DoPrint(UBYTE *s, int par)
{
int i, error = 0, numdol = 0, type;
WORD handle = -1;
UBYTE *name, c, *t;
EXPRESSIONS e;
WORD numexpr, tofile = 0, *w, par2 = 0;
CBUF *C = cbuf + AC.cbufnum;
while ( *s == ',' ) s++;
if ( ( *s == '+' || *s == '-' ) && ( s[1] == 'f' || s[1] == 'F' ) ) {
t = s + 2; while ( *t == ' ' || *t == ',' ) t++;
if ( *t == '"' ) {
if ( *s == '+' ) { tofile = 1; handle = AC.LogHandle; }
s = t;
}
}
else if ( *s == '<' ) {
UBYTE *filename;
s++; filename = s;
while ( *s && *s != '>' ) s++;
if ( *s == 0 ) {
MesPrint("&Improper filename in print statement");
return(1);
}
*s++ = 0;
tofile = 1;
if ( ( handle = GetChannel((char *)filename,1) ) < 0 ) return(1);
SKIPBLANKS(s) if ( *s == ',' ) s++; SKIPBLANKS(s)
if ( *s == '+' && ( s[1] == 's' || s[1] == 'S' ) ) {
s += 2;
par2 |= PRINTONETERM;
if ( *s == 's' || *s == 'S' ) {
s++;
par2 |= PRINTONEFUNCTION;
if ( *s == 's' || *s == 'S' ) {
s++;
par2 |= PRINTALL;
}
}
SKIPBLANKS(s) if ( *s == ',' ) s++; SKIPBLANKS(s)
}
}
if ( par == PRINTON && *s == '"' ) {
WORD code[3];
if ( tofile == 1 ) code[0] = TYPEFPRINT;
else code[0] = TYPEPRINT;
code[1] = handle;
code[2] = par2;
s++; name = s;
while ( *s && *s != '"' ) {
if ( *s == '\\' ) s++;
if ( *s == '%' && s[1] == '$' ) numdol++;
s++;
}
if ( *s != '"' ) {
MesPrint("&String in print statement should be enclosed in \"");
return(1);
}
*s = 0;
AddComString(3,code,name,1);
*s++ = '"';
while ( *s == ',' ) {
s++;
if ( *s == '$' ) {
s++; name = s; while ( FG.cTable[*s] <= 1 ) s++;
c = *s; *s = 0;
type = GetName(AC.dollarnames,name,&numexpr,NOAUTO);
if ( type == NAMENOTFOUND ) {
MesPrint("&$ variable %s not (yet) defined",name);
error = 1;
}
else {
C->lhs[C->numlhs][1] += 2;
*(C->Pointer)++ = DOLLAREXPRESSION;
*(C->Pointer)++ = numexpr;
numdol--;
}
}
else {
MesPrint("&Illegal object in print statement");
error = 1;
return(error);
}
*s = c;
if ( c == '[' ) {
w = C->Pointer;
s++;
s = GetDoParam(s,&(C->Pointer),-1);
if ( s == 0 ) return(1);
if ( *s != ']' ) {
MesPrint("&unmatched [] in $ factor");
return(1);
}
C->lhs[C->numlhs][1] += C->Pointer - w;
s++;
}
}
if ( *s != 0 ) {
MesPrint("&Illegal object in print statement");
error = 1;
}
if ( numdol > 0 ) {
MesPrint("&More $ variables asked for than provided");
error = 1;
}
*(C->Pointer)++ = 0;
return(error);
}
if ( *s == 0 ) { /* All active expressions */
AllExpr:
for ( e = Expressions, i = NumExpressions; i > 0; i--, e++ ) {
if ( e->status == LOCALEXPRESSION || e->status ==
GLOBALEXPRESSION || e->status == UNHIDELEXPRESSION
|| e->status == UNHIDEGEXPRESSION ) e->printflag = par;
}
return(error);
}
while ( *s ) {
if ( *s == '+' ) {
s++;
if ( tolower(*s) == 'f' ) par |= PRINTLFILE;
else if ( tolower(*s) == 's' ) {
if ( tolower(s[1]) == 's' ) {
if ( tolower(s[2]) == 's' ) {
par |= PRINTONEFUNCTION | PRINTONETERM | PRINTALL;
s++;
}
else if ( ( par & 3 ) < 2 ) par |= PRINTONEFUNCTION | PRINTONETERM;
s++;
}
else {
if ( ( par & 3 ) < 2 ) par |= PRINTONETERM;
}
}
else {
illeg: MesPrint("&Illegal option in (n)print statement");
error = 1;
}
s++;
if ( *s == 0 ) goto AllExpr;
}
else if ( *s == '-' ) {
s++;
if ( tolower(*s) == 'f' ) par &= ~PRINTLFILE;
else if ( tolower(*s) == 's' ) {
if ( tolower(s[1]) == 's' ) {
if ( tolower(s[2]) == 's' ) {
par &= ~PRINTALL;
s++;
}
else if ( ( par & 3 ) < 2 ) {
par &= ~PRINTONEFUNCTION;
par &= ~PRINTALL;
}
s++;
}
else {
if ( ( par & 3 ) < 2 ) {
par &= ~PRINTONETERM;
par &= ~PRINTONEFUNCTION;
par &= ~PRINTALL;
}
}
}
else goto illeg;
s++;
if ( *s == 0 ) goto AllExpr;
}
else if ( FG.cTable[*s] == 0 || *s == '[' ) {
name = s;
if ( ( s = SkipAName(s) ) == 0 ) {
MesPrint("&Improper name in (n)print statement");
return(1);
}
c = *s; *s = 0;
if ( ( GetName(AC.exprnames,name,&numexpr,NOAUTO) == CEXPRESSION )
&& ( Expressions[numexpr].status == LOCALEXPRESSION
|| Expressions[numexpr].status == GLOBALEXPRESSION ) ) {
FoundExpr:;
if ( c == '[' && s[1] == ']' ) {
Expressions[numexpr].printflag = par | PRINTCONTENTS;
*s++ = c; c = *++s;
}
else
Expressions[numexpr].printflag = par;
}
else if ( GetLastExprName(name,&numexpr)
&& ( Expressions[numexpr].status == LOCALEXPRESSION
|| Expressions[numexpr].status == GLOBALEXPRESSION
|| Expressions[numexpr].status == UNHIDELEXPRESSION
|| Expressions[numexpr].status == UNHIDEGEXPRESSION
) ) {
goto FoundExpr;
}
else {
MesPrint("&%s is not the name of an active expression",name);
error = 1;
}
*s++ = c;
if ( c == 0 ) return(0);
if ( c == '-' || c == '+' ) s--;
}
else if ( *s == ',' ) s++;
else {
MesPrint("&Illegal object in (n)print statement");
return(1);
}
}
return(0);
}
/*
#] DoPrint :
#[ CoPrint :
*/
int CoPrint(UBYTE *s) { return(DoPrint(s,PRINTON)); }
/*
#] CoPrint :
#[ CoPrintB :
*/
int CoPrintB(UBYTE *s) { return(DoPrint(s,PRINTCONTENT)); }
/*
#] CoPrintB :
#[ CoNPrint :
*/
int CoNPrint(UBYTE *s) { return(DoPrint(s,PRINTOFF)); }
/*
#] CoNPrint :
#[ CoPushHide :
*/
int CoPushHide(UBYTE *s)
{
GETIDENTITY
WORD *ScratchBuf;
int i;
if ( AR.Fscr[2].PObuffer == 0 ) {
ScratchBuf = (WORD *)Malloc1(AM.HideSize*sizeof(WORD),"hidesize");
AR.Fscr[2].POsize = AM.HideSize * sizeof(WORD);
AR.Fscr[2].POfull = AR.Fscr[2].POfill = AR.Fscr[2].PObuffer = ScratchBuf;
AR.Fscr[2].POstop = AR.Fscr[2].PObuffer + AM.HideSize;
PUTZERO(AR.Fscr[2].POposition);
}
while ( *s == ' ' || *s == ',' || *s == '\t' ) s++;
AC.HideLevel += 2;
if ( *s ) {
MesPrint("&PushHide statement should have no arguments");
return(-1);
}
for ( i = 0; i < NumExpressions; i++ ) {
switch ( Expressions[i].status ) {
case DROPLEXPRESSION:
case SKIPLEXPRESSION:
case LOCALEXPRESSION:
Expressions[i].status = HIDELEXPRESSION;
Expressions[i].hidelevel = AC.HideLevel-1;
break;
case DROPGEXPRESSION:
case SKIPGEXPRESSION:
case GLOBALEXPRESSION:
Expressions[i].status = HIDEGEXPRESSION;
Expressions[i].hidelevel = AC.HideLevel-1;
break;
default:
break;
}
}
return(0);
}
/*
#] CoPushHide :
#[ CoPopHide :
*/
int CoPopHide(UBYTE *s)
{
int i;
while ( *s == ' ' || *s == ',' || *s == '\t' ) s++;
if ( AC.HideLevel <= 0 ) {
MesPrint("&PopHide statement without corresponding PushHide statement");
return(-1);
}
AC.HideLevel -= 2;
if ( *s ) {
MesPrint("&PopHide statement should have no arguments");
return(-1);
}
for ( i = 0; i < NumExpressions; i++ ) {
switch ( Expressions[i].status ) {
case HIDDENLEXPRESSION:
if ( Expressions[i].hidelevel > AC.HideLevel )
Expressions[i].status = UNHIDELEXPRESSION;
break;
case HIDDENGEXPRESSION:
if ( Expressions[i].hidelevel > AC.HideLevel )
Expressions[i].status = UNHIDEGEXPRESSION;
break;
default:
break;
}
}
return(0);
}
/*
#] CoPopHide :
#[ SetExprCases :
*/
int SetExprCases(int par, int setunset, int val)
{
switch ( par ) {
case SKIP:
switch ( val ) {
case SKIPLEXPRESSION:
if ( !setunset ) val = LOCALEXPRESSION;
break;
case SKIPGEXPRESSION:
if ( !setunset ) val = GLOBALEXPRESSION;
break;
case LOCALEXPRESSION:
if ( setunset ) val = SKIPLEXPRESSION;
break;
case GLOBALEXPRESSION:
if ( setunset ) val = SKIPGEXPRESSION;
break;
case INTOHIDEGEXPRESSION:
case INTOHIDELEXPRESSION:
default:
break;
}
break;
case DROP:
switch ( val ) {
case SKIPLEXPRESSION:
case LOCALEXPRESSION:
case HIDELEXPRESSION:
if ( setunset ) val = DROPLEXPRESSION;
break;
case DROPLEXPRESSION:
if ( !setunset ) val = LOCALEXPRESSION;
break;
case SKIPGEXPRESSION:
case GLOBALEXPRESSION:
case HIDEGEXPRESSION:
if ( setunset ) val = DROPGEXPRESSION;
break;
case DROPGEXPRESSION:
if ( !setunset ) val = GLOBALEXPRESSION;
break;
case HIDDENLEXPRESSION:
case UNHIDELEXPRESSION:
if ( setunset ) val = DROPHLEXPRESSION;
break;
case HIDDENGEXPRESSION:
case UNHIDEGEXPRESSION:
if ( setunset ) val = DROPHGEXPRESSION;
break;
case DROPHLEXPRESSION:
if ( !setunset ) val = HIDDENLEXPRESSION;
break;
case DROPHGEXPRESSION:
if ( !setunset ) val = HIDDENGEXPRESSION;
break;
case INTOHIDEGEXPRESSION:
case INTOHIDELEXPRESSION:
default:
break;
}
break;
case HIDE:
switch ( val ) {
case DROPLEXPRESSION:
case SKIPLEXPRESSION:
case LOCALEXPRESSION:
if ( setunset ) val = HIDELEXPRESSION;
break;
case HIDELEXPRESSION:
if ( !setunset ) val = LOCALEXPRESSION;
break;
case DROPGEXPRESSION:
case SKIPGEXPRESSION:
case GLOBALEXPRESSION:
if ( setunset ) val = HIDEGEXPRESSION;
break;
case HIDEGEXPRESSION:
if ( !setunset ) val = GLOBALEXPRESSION;
break;
case INTOHIDEGEXPRESSION:
case INTOHIDELEXPRESSION:
default:
break;
}
break;
case UNHIDE:
switch ( val ) {
case HIDDENLEXPRESSION:
case DROPHLEXPRESSION:
if ( setunset ) val = UNHIDELEXPRESSION;
break;
case UNHIDELEXPRESSION:
if ( !setunset ) val = HIDDENLEXPRESSION;
break;
case HIDDENGEXPRESSION:
case DROPHGEXPRESSION:
if ( setunset ) val = UNHIDEGEXPRESSION;
break;
case UNHIDEGEXPRESSION:
if ( !setunset ) val = HIDDENGEXPRESSION;
break;
case INTOHIDEGEXPRESSION:
case INTOHIDELEXPRESSION:
default:
break;
}
break;
case INTOHIDE:
switch ( val ) {
case HIDDENLEXPRESSION:
case HIDDENGEXPRESSION:
MesPrint("&Expression is already hidden");
return(-1);
case DROPHLEXPRESSION:
case DROPHGEXPRESSION:
case UNHIDELEXPRESSION:
case UNHIDEGEXPRESSION:
MesPrint("&Cannot unhide and put intohide expression in the same module");
return(-1);
case LOCALEXPRESSION:
case DROPLEXPRESSION:
case SKIPLEXPRESSION:
case HIDELEXPRESSION:
if ( setunset ) val = INTOHIDELEXPRESSION;
break;
case GLOBALEXPRESSION:
case DROPGEXPRESSION:
case SKIPGEXPRESSION:
case HIDEGEXPRESSION:
if ( setunset ) val = INTOHIDEGEXPRESSION;
break;
default:
break;
}
break;
default:
break;
}
return(val);
}
/*
#] SetExprCases :
#[ SetExpr :
*/
int SetExpr(UBYTE *s, int setunset, int par)
{
WORD *w, numexpr;
int error = 0, i;
UBYTE *name, c;
if ( *s == 0 && ( par != INTOHIDE ) ) {
for ( i = 0; i < NumExpressions; i++ ) {
w = &(Expressions[i].status);
*w = SetExprCases(par,setunset,*w);
if ( *w < 0 ) error = 1;
if ( par == HIDE && setunset == 1 )
Expressions[i].hidelevel = AC.HideLevel;
}
return(0);
}
while ( *s ) {
if ( *s == ',' ) { s++; continue; }
if ( *s == '0' ) { s++; continue; }
name = s;
if ( ( s = SkipAName(s) ) == 0 ) {
MesPrint("&Improper name for an expression: '%s'",name);
return(1);
}
c = *s; *s = 0;
if ( GetName(AC.exprnames,name,&numexpr,NOAUTO) == CEXPRESSION ) {
w = &(Expressions[numexpr].status);
*w = SetExprCases(par,setunset,*w);
if ( *w < 0 ) error = 1;
if ( ( par == HIDE || par == INTOHIDE ) && setunset == 1 )
Expressions[numexpr].hidelevel = AC.HideLevel;
}
else if ( GetName(AC.varnames,name,&numexpr,NOAUTO) != NAMENOTFOUND ) {
MesPrint("&%s is not an expression",name);
error = 1;
}
*s = c;
}
return(error);
}
/*
#] SetExpr :
#[ CoDrop :
*/
int CoDrop(UBYTE *s) { return(SetExpr(s,1,DROP)); }
/*
#] CoDrop :
#[ CoNoDrop :
*/
int CoNoDrop(UBYTE *s) { return(SetExpr(s,0,DROP)); }
/*
#] CoNoDrop :
#[ CoSkip :
*/
int CoSkip(UBYTE *s) { return(SetExpr(s,1,SKIP)); }
/*
#] CoSkip :
#[ CoNoSkip :
*/
int CoNoSkip(UBYTE *s) { return(SetExpr(s,0,SKIP)); }
/*
#] CoNoSkip :
#[ CoHide :
*/
int CoHide(UBYTE *inp) {
GETIDENTITY
WORD *ScratchBuf;
if ( AR.Fscr[2].PObuffer == 0 ) {
ScratchBuf = (WORD *)Malloc1(AM.HideSize*sizeof(WORD),"hidesize");
AR.Fscr[2].POsize = AM.HideSize * sizeof(WORD);
AR.Fscr[2].POfull = AR.Fscr[2].POfill = AR.Fscr[2].PObuffer = ScratchBuf;
AR.Fscr[2].POstop = AR.Fscr[2].PObuffer + AM.HideSize;
PUTZERO(AR.Fscr[2].POposition);
}
return(SetExpr(inp,1,HIDE));
}
/*
#] CoHide :
#[ CoIntoHide :
*/
int CoIntoHide(UBYTE *inp) {
GETIDENTITY
WORD *ScratchBuf;
if ( AR.Fscr[2].PObuffer == 0 ) {
ScratchBuf = (WORD *)Malloc1(AM.HideSize*sizeof(WORD),"hidesize");
AR.Fscr[2].POsize = AM.HideSize * sizeof(WORD);
AR.Fscr[2].POfull = AR.Fscr[2].POfill = AR.Fscr[2].PObuffer = ScratchBuf;
AR.Fscr[2].POstop = AR.Fscr[2].PObuffer + AM.HideSize;
PUTZERO(AR.Fscr[2].POposition);
}
return(SetExpr(inp,1,INTOHIDE));
}
/*
#] CoIntoHide :
#[ CoNoHide :
*/
int CoNoHide(UBYTE *inp) { return(SetExpr(inp,0,HIDE)); }
/*
#] CoNoHide :
#[ CoUnHide :
*/
int CoUnHide(UBYTE *inp) { return(SetExpr(inp,1,UNHIDE)); }
/*
#] CoUnHide :
#[ CoNoUnHide :
*/
int CoNoUnHide(UBYTE *inp) { return(SetExpr(inp,0,UNHIDE)); }
/*
#] CoNoUnHide :
#[ AddToCom :
*/
void AddToCom(int n, WORD *array)
{
CBUF *C = cbuf+AC.cbufnum;
#ifdef COMPBUFDEBUG
MesPrint(" %a",n,array);
#endif
while ( C->Pointer+n >= C->Top ) DoubleCbuffer(AC.cbufnum,C->Pointer,18);
while ( --n >= 0 ) *(C->Pointer)++ = *array++;
}
/*
#] AddToCom :
#[ AddComString :
*/
int AddComString(int n, WORD *array, UBYTE *thestring, int par)
{
CBUF *C = cbuf+AC.cbufnum;
UBYTE *s = thestring, *w;
#ifdef COMPBUFDEBUG
WORD *cc;
UBYTE *ww;
#endif
int i, numchars = 0, size, zeroes;
while ( *s ) {
if ( *s == '\\' ) s++;
else if ( par == 1 &&
( ( *s == '%' && s[1] != 't' && s[1] != 'T' && s[1] != '$' &&
s[1] != 'w' && s[1] != 'W' && s[1] != 'r' && s[1] != 0 ) || *s == '#'
|| *s == '@' || *s == '&' ) ) {
numchars++;
}
s++; numchars++;
}
AddLHS(AC.cbufnum);
size = numchars/sizeof(WORD)+1;
while ( C->Pointer+size+n+2 >= C->Top ) DoubleCbuffer(AC.cbufnum,C->Pointer,19);
#ifdef COMPBUFDEBUG
cc = C->Pointer;
#endif
*(C->Pointer)++ = array[0];
*(C->Pointer)++ = size+n+2;
for ( i = 1; i < n; i++ ) *(C->Pointer)++ = array[i];
*(C->Pointer)++ = size;
#ifdef COMPBUFDEBUG
ww =
#endif
w = (UBYTE *)(C->Pointer);
zeroes = size*sizeof(WORD)-numchars;
s = thestring;
while ( *s ) {
if ( *s == '\\' ) s++;
else if ( par == 1 && ( ( *s == '%' &&
s[1] != 't' && s[1] != 'T' && s[1] != '$' &&
s[1] != 'w' && s[1] != 'W' && s[1] != 'r' && s[1] != 0 ) || *s == '#'
|| *s == '@' || *s == '&' ) ) {
*w++ = '%';
}
*w++ = *s++;
}
while ( --zeroes >= 0 ) *w++ = 0;
C->Pointer += size;
#ifdef COMPBUFDEBUG
MesPrint("LH: %a",size+1+n,cc);
MesPrint(" %s",thestring);
#endif
return(0);
}
/*
#] AddComString :
#[ Add2ComStrings :
*/
int Add2ComStrings(int n, WORD *array, UBYTE *string1, UBYTE *string2)
{
CBUF *C = cbuf+AC.cbufnum;
UBYTE *s1 = string1, *s2 = string2, *w;
int i, num1chars = 0, num2chars = 0, size1, size2, zeroes1, zeroes2;
AddLHS(AC.cbufnum);
while ( *s1 ) { s1++; num1chars++; }
size1 = num1chars/sizeof(WORD)+1;
if ( s2 ) {
while ( *s2 ) { s2++; num2chars++; }
size2 = num2chars/sizeof(WORD)+1;
}
else size2 = 0;
while ( C->Pointer+size1+size2+n+3 >= C->Top ) DoubleCbuffer(AC.cbufnum,C->Pointer,20);
*(C->Pointer)++ = array[0];
*(C->Pointer)++ = size1+size2+n+3;
for ( i = 1; i < n; i++ ) *(C->Pointer)++ = array[i];
*(C->Pointer)++ = size1;
w = (UBYTE *)(C->Pointer);
zeroes1 = size1*sizeof(WORD)-num1chars;
s1 = string1;
while ( *s1 ) { *w++ = *s1++; }
while ( --zeroes1 >= 0 ) *w++ = 0;
C->Pointer += size1;
*(C->Pointer)++ = size2;
if ( size2 ) {
w = (UBYTE *)(C->Pointer);
zeroes2 = size2*sizeof(WORD)-num2chars;
s2 = string2;
while ( *s2 ) { *w++ = *s2++; }
while ( --zeroes2 >= 0 ) *w++ = 0;
C->Pointer += size2;
}
return(0);
}
/*
#] Add2ComStrings :
#[ CoDiscard :
*/
int CoDiscard(UBYTE *s)
{
if ( *s == 0 ) {
Add2Com(TYPEDISCARD)
return(0);
}
MesPrint("&Illegal argument in discard statement: '%s'",s);
return(1);
}
/*
#] CoDiscard :
#[ CoContract :
Syntax:
Contract
Contract:#
Contract #
Contract:#,#
*/
static WORD ccarray[5] = { TYPEOPERATION,5,CONTRACT,0,0 };
int CoContract(UBYTE *s)
{
int x;
if ( *s == ':' ) {
s++;
ParseNumber(x,s)
if ( *s != ',' && *s ) {
proper: MesPrint("&Illegal number in contract statement");
return(1);
}
if ( *s ) s++;
ccarray[4] = x;
}
else ccarray[4] = 0;
if ( FG.cTable[*s] == 1 ) {
ParseNumber(x,s)
if ( *s ) goto proper;
ccarray[3] = x;
}
else if ( *s ) goto proper;
else ccarray[3] = -1;
return(AddNtoL(5,ccarray));
}
/*
#] CoContract :
#[ CoGoTo :
*/
int CoGoTo(UBYTE *inp)
{
UBYTE *s = inp;
int x;
while ( FG.cTable[*s] <= 1 ) s++;
if ( *s ) {
MesPrint("&Label should be an alpha-numeric string");
return(1);
}
x = GetLabel(inp);
Add3Com(TYPEGOTO,x);
return(0);
}
/*
#] CoGoTo :
#[ CoLabel :
*/
int CoLabel(UBYTE *inp)
{
UBYTE *s = inp;
int x;
while ( FG.cTable[*s] <= 1 ) s++;
if ( *s ) {
MesPrint("&Label should be an alpha-numeric string");
return(1);
}
x = GetLabel(inp);
if ( AC.Labels[x] >= 0 ) {
MesPrint("&Label %s defined more than once",AC.LabelNames[x]);
return(1);
}
AC.Labels[x] = cbuf[AC.cbufnum].numlhs;
return(0);
}
/*
#] CoLabel :
#[ DoArgument :
Layout:
par,full size,numlhs(+1),par,scale
scale is for normalize
*/
int DoArgument(UBYTE *s, int par)
{
GETIDENTITY
UBYTE *name, *t, *v, c;
WORD *oldworkpointer = AT.WorkPointer, *w, *ww, number, *scale;
int error = 0, zeroflag, type, x;
AC.lhdollarflag = 0;
while ( *s == ',' ) s++;
w = AT.WorkPointer;
*w++ = par;
w++;
switch ( par ) {
case TYPEARG:
if ( AC.arglevel >= MAXNEST ) {
MesPrint("@Nesting of argument statements more than %d levels"
,(WORD)MAXNEST);
return(-1);
}
AC.argsumcheck[AC.arglevel] = NestingChecksum();
AC.argstack[AC.arglevel] = cbuf[AC.cbufnum].Pointer
- cbuf[AC.cbufnum].Buffer + 2;
AC.arglevel++;
*w++ = cbuf[AC.cbufnum].numlhs;
break;
case TYPENORM:
case TYPENORM4:
case TYPESPLITARG:
case TYPESPLITFIRSTARG:
case TYPESPLITLASTARG:
case TYPEFACTARG:
case TYPEARGTOEXTRASYMBOL:
*w++ = cbuf[AC.cbufnum].numlhs+1;
break;
}
*w++ = par;
scale = w;
*w++ = 1;
*w++ = 0;
if ( *s == '^' ) {
s++; ParseSignedNumber(x,s)
while ( *s == ',' ) s++;
*scale = x;
}
if ( *s == '(' ) {
t = s+1; SKIPBRA3(s) /* We did check the brackets already */
if ( par == TYPEARG ) {
MesPrint("&Illegal () entry in argument statement");
error = 1; s++; goto skipbracks;
}
else if ( par == TYPESPLITFIRSTARG ) {
MesPrint("&Illegal () entry in splitfirstarg statement");
error = 1; s++; goto skipbracks;
}
else if ( par == TYPESPLITLASTARG ) {
MesPrint("&Illegal () entry in splitlastarg statement");
error = 1; s++; goto skipbracks;
}
v = t;
while ( v < s ) {
if ( *v == '?' ) {
MesPrint("&Wildcarding not allowed in this type of statement");
error = 1; break;
}
v++;
}
v = s++;
if ( *t == '(' && v[-1] == ')' ) {
t++; v--;
if ( par == TYPESPLITARG ) oldworkpointer[0] = TYPESPLITARG2;
else if ( par == TYPEFACTARG ) oldworkpointer[0] = TYPEFACTARG2;
else if ( par == TYPENORM4 ) oldworkpointer[0] = TYPENORM4;
else if ( par == TYPENORM ) {
if ( *t == '-' ) { oldworkpointer[0] = TYPENORM3; t++; }
else { oldworkpointer[0] = TYPENORM2; *scale = 0; }
}
}
if ( error == 0 ) {
CBUF *C = cbuf+AC.cbufnum;
WORD oldnumrhs = C->numrhs, oldnumlhs = C->numlhs;
WORD prototype[SUBEXPSIZE+40]; /* Up to 10 nested sums! */
WORD *m, *mm;
int i, retcode;
LONG oldpointer = C->Pointer - C->Buffer;
*v = 0;
prototype[0] = SUBEXPRESSION;
prototype[1] = SUBEXPSIZE;
prototype[2] = C->numrhs+1;
prototype[3] = 1;
prototype[4] = AC.cbufnum;
AT.WorkPointer += TYPEARGHEADSIZE+1;
AddLHS(AC.cbufnum);
if ( ( retcode = CompileAlgebra(t,LHSIDE,prototype) ) < 0 )
error = 1;
else {
prototype[2] = retcode;
ww = C->lhs[retcode];
AC.lhdollarflag = 0;
if ( *ww == 0 ) {
*w++ = -2; *w++ = 0;
}
else if ( ww[ww[0]] != 0 ) {
MesPrint("&There should be only one term between ()");
error = 1;
}
else if ( NewSort(BHEAD0) ) { if ( !error ) error = 1; }
else if ( NewSort(BHEAD0) ) {
LowerSortLevel();
if ( !error ) error = 1;
}
else {
AN.RepPoint = AT.RepCount + 1;
m = AT.WorkPointer;
mm = ww; i = *mm;
while ( --i >= 0 ) *m++ = *mm++;
mm = AT.WorkPointer; AT.WorkPointer = m;
AR.Cnumlhs = C->numlhs;
if ( Generator(BHEAD mm,C->numlhs) ) {
LowerSortLevel(); error = 1;
}
else if ( EndSort(BHEAD mm,0) < 0 ) {
error = 1;
AT.WorkPointer = mm;
}
else if ( *mm == 0 ) {
*w++ = -2; *w++ = 0;
AT.WorkPointer = mm;
}
else if ( mm[mm[0]] != 0 ) {
error = 1;
AT.WorkPointer = mm;
}
else {
AT.WorkPointer = mm;
m = mm+*mm;
if ( par == TYPEFACTARG ) {
if ( *mm != ABS(m[-1])+1 ) {
*mm -= ABS(m[-1]); /* Strip coefficient */
}
mm[-1] = -*mm-1; w += *mm+1;
}
else {
*mm -= ABS(m[-1]); /* Strip coefficient */
/*
if ( *mm == 1 ) { *w++ = -2; *w++ = 0; }
else
*/
{ mm[-1] = -*mm-1; w += *mm+1; }
}
oldworkpointer[1] = w - oldworkpointer;
}
LowerSortLevel();
}
oldworkpointer[5] = AC.lhdollarflag;
}
*v = ')';
C->numrhs = oldnumrhs;
C->numlhs = oldnumlhs;
C->Pointer = C->Buffer + oldpointer;
}
}
skipbracks:
if ( *s == 0 ) { *w++ = 0; *w++ = 2; *w++ = 1; }
else {
do {
if ( *s == ',' ) { s++; continue; }
ww = w; *w++ = 0; w++;
if ( FG.cTable[*s] > 1 && *s != '[' && *s != '{' ) {
MesPrint("&Illegal parameters in statement");
error = 1;
break;
}
while ( FG.cTable[*s] == 0 || *s == '[' || *s == '{' ) {
if ( *s == '{' ) {
name = s+1;
SKIPBRA2(s)
c = *s; *s = 0;
number = DoTempSet(name,s);
name--; *s++ = c; c = *s; *s = 0;
goto doset;
}
else {
name = s;
if ( ( s = SkipAName(s) ) == 0 ) {
MesPrint("&Illegal name '%s'",name);
return(1);
}
c = *s; *s = 0;
if ( ( type = GetName(AC.varnames,name,&number,WITHAUTO) ) == CSET ) {
doset: if ( Sets[number].type != CFUNCTION ) goto nofun;
*w++ = CSET; *w++ = number;
}
else if ( type == CFUNCTION ) {
*w++ = CFUNCTION; *w++ = number + FUNCTION;
}
else {
nofun: MesPrint("&%s is not a function or a set of functions"
,name);
error = 1;
}
}
*s = c;
while ( *s == ',' ) s++;
}
ww[1] = w - ww;
ww = w; w++; zeroflag = 0;
while ( FG.cTable[*s] == 1 ) {
ParseNumber(x,s)
if ( *s && *s != ',' ) {
MesPrint("&Illegal separator after number");
error = 1;
while ( *s && *s != ',' ) s++;
}
while ( *s == ',' ) s++;
if ( x == 0 ) zeroflag = 1;
if ( !zeroflag ) *w++ = (WORD)x;
}
*ww = w - ww;
} while ( *s );
}
oldworkpointer[1] = w - oldworkpointer;
if ( par == TYPEARG ) { /* To make sure. The Pointer might move in the future */
AC.argstack[AC.arglevel-1] = cbuf[AC.cbufnum].Pointer
- cbuf[AC.cbufnum].Buffer + 2;
}
AddNtoL(oldworkpointer[1],oldworkpointer);
AT.WorkPointer = oldworkpointer;
return(error);
}
/*
#] DoArgument :
#[ CoArgument :
*/
int CoArgument(UBYTE *s) { return(DoArgument(s,TYPEARG)); }
/*
#] CoArgument :
#[ CoEndArgument :
*/
int CoEndArgument(UBYTE *s)
{
CBUF *C = cbuf+AC.cbufnum;
while ( *s == ',' ) s++;
if ( *s ) {
MesPrint("&Illegal syntax for EndArgument statement");
return(1);
}
if ( AC.arglevel <= 0 ) {
MesPrint("&EndArgument without corresponding Argument statement");
return(1);
}
AC.arglevel--;
cbuf[AC.cbufnum].Buffer[AC.argstack[AC.arglevel]] = C->numlhs;
if ( AC.argsumcheck[AC.arglevel] != NestingChecksum() ) {
MesNesting();
return(1);
}
return(0);
}
/*
#] CoEndArgument :
#[ CoInside :
*/
int CoInside(UBYTE *s) { return(ExecInside(s)); }
/*
#] CoInside :
#[ CoEndInside :
*/
int CoEndInside(UBYTE *s)
{
CBUF *C = cbuf+AC.cbufnum;
while ( *s == ',' ) s++;
if ( *s ) {
MesPrint("&Illegal syntax for EndInside statement");
return(1);
}
if ( AC.insidelevel <= 0 ) {
MesPrint("&EndInside without corresponding Inside statement");
return(1);
}
AC.insidelevel--;
cbuf[AC.cbufnum].Buffer[AC.insidestack[AC.insidelevel]] = C->numlhs;
if ( AC.insidesumcheck[AC.insidelevel] != NestingChecksum() ) {
MesNesting();
return(1);
}
return(0);
}
/*
#] CoEndInside :
#[ CoNormalize :
*/
int CoNormalize(UBYTE *s) { return(DoArgument(s,TYPENORM)); }
/*
#] CoNormalize :
#[ CoMakeInteger :
*/
int CoMakeInteger(UBYTE *s) { return(DoArgument(s,TYPENORM4)); }
/*
#] CoMakeInteger :
#[ CoSplitArg :
*/
int CoSplitArg(UBYTE *s) { return(DoArgument(s,TYPESPLITARG)); }
/*
#] CoSplitArg :
#[ CoSplitFirstArg :
*/
int CoSplitFirstArg(UBYTE *s) { return(DoArgument(s,TYPESPLITFIRSTARG)); }
/*
#] CoSplitFirstArg :
#[ CoSplitLastArg :
*/
int CoSplitLastArg(UBYTE *s) { return(DoArgument(s,TYPESPLITLASTARG)); }
/*
#] CoSplitLastArg :
#[ CoFactArg :
*/
int CoFactArg(UBYTE *s) {
if ( ( AC.topolynomialflag & TOPOLYNOMIALFLAG ) != 0 ) {
MesPrint("&ToPolynomial statement and FactArg statement are not allowed in the same module");
return(1);
}
AC.topolynomialflag |= FACTARGFLAG;
return(DoArgument(s,TYPEFACTARG));
}
/*
#] CoFactArg :
#[ DoSymmetrize :
Syntax:
Symmetrize Fun[:[number]] [Fields] -> par = 0;
AntiSymmetrize Fun[:[number]] [Fields] -> par = 1;
CycleSymmetrize Fun[:[number]] [Fields] -> par = 2;
RCycleSymmetrize Fun[:[number]] [Fields]-> par = 3;
*/
int DoSymmetrize(UBYTE *s, int par)
{
GETIDENTITY
int extra = 0, error = 0, err, fix, x, groupsize, num, i;
UBYTE *name, c;
WORD funnum, *w, *ww, type;
for(;;) {
name = s;
if ( ( s = SkipAName(s) ) == 0 ) {
MesPrint("&Improper function name");
return(1);
}
c = *s; *s = 0;
if ( c != ',' || ( FG.cTable[s[1]] != 0 && s[1] != '[' ) ) break;
if ( par <= 0 && StrICmp(name,(UBYTE *)"cyclic") == 0 ) extra = 2;
else if ( par <= 0 && StrICmp(name,(UBYTE *)"rcyclic") == 0 ) extra = 6;
else {
MesPrint("&Illegal option: '%s'",name);
error = 1;
}
*s++ = c;
}
if ( ( err = GetVar(name,&type,&funnum,CFUNCTION,WITHAUTO) ) == NAMENOTFOUND ) {
MesPrint("&Undefined function: %s",name);
AddFunction(name,0,0,0,0,0,-1,-1);
*s++ = c;
return(1);
}
funnum += FUNCTION;
if ( err == -1 ) error = 1;
*s = c;
if ( *s == ':' ) {
s++;
if ( *s == ',' || *s == '(' || *s == 0 ) fix = -1;
else if ( FG.cTable[*s] == 1 ) {
ParseNumber(fix,s)
if ( fix == 0 )
Warning("Restriction to zero arguments removed");
}
else {
MesPrint("&Illegal character after :");
return(1);
}
}
else fix = 0;
w = AT.WorkPointer;
*w++ = TYPEOPERATION;
w++;
*w++ = SYMMETRIZE;
*w++ = par | extra;
*w++ = funnum;
*w++ = fix;
/*
And now the argument lists. We have either ,#,#,... or (#,#,..,#),(#,...
*/
w += 2; ww = w; groupsize = -1;
while ( *s == ',' ) s++;
while ( *s ) {
if ( *s == '(' ) {
s++; num = 0;
while ( *s && *s != ')' ) {
if ( *s == ',' ) { s++; continue; }
if ( FG.cTable[*s] != 1 ) goto illarg;
ParseNumber(x,s)
if ( x <= 0 || ( fix > 0 && x > fix ) ) goto illnum;
num++;
*w++ = x-1;
}
if ( *s == 0 ) {
MesPrint("&Improper termination of statement");
return(1);
}
if ( groupsize < 0 ) groupsize = num;
else if ( groupsize != num ) goto group;
s++;
}
else if ( FG.cTable[*s] == 1 ) {
if ( groupsize < 0 ) groupsize = 1;
else if ( groupsize != 1 ) {
group: MesPrint("&All groups should have the same number of arguments");
return(1);
}
ParseNumber(x,s)
if ( x <= 0 || ( fix > 0 && x > fix ) ) {
illnum: MesPrint("&Illegal argument number: %d",x);
return(1);
}
*w++ = x-1;
}
else {
illarg: MesPrint("&Illegal argument");
return(1);
}
while ( *s == ',' ) s++;
}
/*
Now the completion
*/
if ( w == ww ) {
ww[-1] = 1;
ww[-2] = 0;
if ( fix > 0 ) {
for ( i = 0; i < fix; i++ ) *w++ = i;
ww[-2] = fix; /* Bugfix 31-oct-2001. Reported by York Schroeder */
}
}
else {
ww[-1] = groupsize;
ww[-2] = (w-ww)/groupsize;
}
AT.WorkPointer[1] = w - AT.WorkPointer;
AddNtoL(AT.WorkPointer[1],AT.WorkPointer);
return(error);
}
/*
#] DoSymmetrize :
#[ CoSymmetrize :
*/
int CoSymmetrize(UBYTE *s) { return(DoSymmetrize(s,SYMMETRIC)); }
/*
#] CoSymmetrize :
#[ CoAntiSymmetrize :
*/
int CoAntiSymmetrize(UBYTE *s) { return(DoSymmetrize(s,ANTISYMMETRIC)); }
/*
#] CoAntiSymmetrize :
#[ CoCycleSymmetrize :
*/
int CoCycleSymmetrize(UBYTE *s) { return(DoSymmetrize(s,CYCLESYMMETRIC)); }
/*
#] CoCycleSymmetrize :
#[ CoRCycleSymmetrize :
*/
int CoRCycleSymmetrize(UBYTE *s) { return(DoSymmetrize(s,RCYCLESYMMETRIC)); }
/*
#] CoRCycleSymmetrize :
#[ CoWrite :
*/
int CoWrite(UBYTE *s)
{
GETIDENTITY
UBYTE *option;
KEYWORDV *key;
option = s;
if ( ( ( s = SkipAName(s) ) == 0 ) || *s != 0 ) {
MesPrint("&Proper use of write statement is: write option");
return(1);
}
key = (KEYWORDV *)FindInKeyWord(option,(KEYWORD *)writeoptions,sizeof(writeoptions)/sizeof(KEYWORD));
if ( key == 0 ) {
MesPrint("&Unrecognized option in write statement");
return(1);
}
*key->var = key->type;
AR.SortType = AC.SortType;
return(0);
}
/*
#] CoWrite :
#[ CoNWrite :
*/
int CoNWrite(UBYTE *s)
{
GETIDENTITY
UBYTE *option;
KEYWORDV *key;
option = s;
if ( ( ( s = SkipAName(s) ) == 0 ) || *s != 0 ) {
MesPrint("&Proper use of nwrite statement is: nwrite option");
return(1);
}
key = (KEYWORDV *)FindInKeyWord(option,(KEYWORD *)writeoptions,sizeof(writeoptions)/sizeof(KEYWORD));
if ( key == 0 ) {
MesPrint("&Unrecognized option in nwrite statement");
return(1);
}
*key->var = key->flags;
AR.SortType = AC.SortType;
return(0);
}
/*
#] CoNWrite :
#[ CoRatio :
*/
static WORD ratstring[6] = { TYPEOPERATION, 6, RATIO, 0, 0, 0 };
int CoRatio(UBYTE *s)
{
UBYTE c, *t;
int i, type, error = 0;
WORD numsym, *rs;
rs = ratstring+3;
for ( i = 0; i < 3; i++ ) {
if ( *s ) {
t = s;
s = SkipAName(s);
c = *s; *s = 0;
if ( ( ( type = GetName(AC.varnames,t,&numsym,WITHAUTO) ) != CSYMBOL )
&& type != CDUBIOUS ) {
MesPrint("&%s is not a symbol",t);
error = 4;
if ( type < 0 ) numsym = AddSymbol(t,-MAXPOWER,MAXPOWER,0,0);
}
*s = c;
if ( *s == ',' ) s++;
}
else {
if ( error == 0 )
MesPrint("&The ratio statement needs three symbols for its arguments");
error++;
numsym = 0;
}
*rs++ = numsym;
}
AddNtoL(6,ratstring);
return(error);
}
/*
#] CoRatio :
#[ CoRedefine :
We have a preprocessor variable and a (new) value for it.
This value is inside a string that must be stored.
*/
int CoRedefine(UBYTE *s)
{
UBYTE *name, c, *args = 0;
int numprevar;
WORD code[2];
name = s;
if ( FG.cTable[*s] || ( s = SkipAName(s) ) == 0 || s[-1] == '_' ) {
MesPrint("&Illegal name for preprocessor variable in redefine statement");
return(1);
}
c = *s; *s = 0;
for ( numprevar = NumPre-1; numprevar >= 0; numprevar-- ) {
if ( StrCmp(name,PreVar[numprevar].name) == 0 ) break;
}
if ( numprevar < 0 ) {
MesPrint("&There is no preprocessor variable with the name `%s'",name);
*s = c;
return(1);
}
*s = c;
/*
The next code worries about arguments.
It is a direct copy of the code in TheDefine in the preprocessor.
*/
if ( *s == '(' ) { /* arguments. scan for correctness */
s++; args = s;
for (;;) {
if ( chartype[*s] != 0 ) goto illarg;
s++;
while ( chartype[*s] <= 1 ) s++;
while ( *s == ' ' || *s == '\t' ) s++;
if ( *s == ')' ) break;
if ( *s != ',' ) goto illargs;
s++;
while ( *s == ' ' || *s == '\t' ) s++;
}
*s++ = 0;
while ( *s == ' ' || *s == '\t' ) s++;
}
while ( *s == ',' ) s++;
if ( *s != '"' ) {
encl: MesPrint("&Value for %s should be enclosed in double quotes"
,PreVar[numprevar].name);
return(1);
}
s++; name = s; /* actually name points to the new string */
while ( *s && *s != '"' ) { if ( *s == '\\' ) s++; s++; }
if ( *s != '"' ) goto encl;
*s = 0;
code[0] = TYPEREDEFPRE; code[1] = numprevar;
/*
AddComString(2,code,name,0);
*/
Add2ComStrings(2,code,name,args);
*s = '"';
#ifdef PARALLELCODE
/*
Now we prepare the input numbering system for pthreads.
We need a list of preprocessor variables that are redefined in this
module.
*/
{
int j;
WORD *newpf;
LONG *newin;
for ( j = 0; j < AC.numpfirstnum; j++ ) {
if ( numprevar == AC.pfirstnum[j] ) break;
}
if ( j >= AC.numpfirstnum ) { /* add to list */
if ( j >= AC.sizepfirstnum ) {
if ( AC.sizepfirstnum <= 0 ) { AC.sizepfirstnum = 10; }
else { AC.sizepfirstnum = 2 * AC.sizepfirstnum; }
newin = (LONG *)Malloc1(AC.sizepfirstnum*(sizeof(WORD)+sizeof(LONG)),"AC.pfirstnum");
newpf = (WORD *)(newin+AC.sizepfirstnum);
for ( j = 0; j < AC.numpfirstnum; j++ ) {
newpf[j] = AC.pfirstnum[j];
newin[j] = AC.inputnumbers[j];
}
if ( AC.inputnumbers ) M_free(AC.inputnumbers,"AC.pfirstnum");
AC.inputnumbers = newin;
AC.pfirstnum = newpf;
}
AC.pfirstnum[AC.numpfirstnum] = numprevar;
AC.inputnumbers[AC.numpfirstnum] = -1;
AC.numpfirstnum++;
}
}
#endif
return(0);
illarg:;
MesPrint("&Illegally formed name in argument of redefine statement");
return(1);
illargs:;
MesPrint("&Illegally formed arguments in redefine statement");
return(1);
}
/*
#] CoRedefine :
#[ CoRenumber :
renumber or renumber,0 Only exchanges (n^2 until no improvement)
renumber,1 All permutations (could be slow)
*/
int CoRenumber(UBYTE *s)
{
int x;
UBYTE *inp;
while ( *s == ',' ) s++;
inp = s;
if ( *s == 0 ) { x = 0; }
else ParseNumber(x,s)
if ( *s == 0 && x >= 0 && x <= 1 ) {
Add3Com(TYPERENUMBER,x);
return(0);
}
MesPrint("&Illegal argument in Renumber statement: '%s'",inp);
return(1);
}
/*
#] CoRenumber :
#[ CoSum :
*/
int CoSum(UBYTE *s)
{
CBUF *C = cbuf+AC.cbufnum;
UBYTE *ss = 0, c, *t;
int error = 0, i = 0, type, x;
WORD numindex,number;
while ( *s ) {
t = s;
if ( *s == '$' ) {
t++; s++; while ( FG.cTable[*s] < 2 ) s++;
c = *s; *s = 0;
if ( ( number = GetDollar(t) ) < 0 ) {
MesPrint("&Undefined variable $%s",t);
if ( !error ) error = 1;
number = AddDollar(t,0,0,0);
}
numindex = -number;
}
else {
if ( ( s = SkipAName(s) ) == 0 ) return(1);
c = *s; *s = 0;
if ( ( ( type = GetOName(AC.exprnames,t,&numindex,NOAUTO) ) != NAMENOTFOUND )
|| ( ( type = GetOName(AC.varnames,t,&numindex,WITHAUTO) ) != CINDEX ) ) {
if ( type != NAMENOTFOUND ) error = NameConflict(type,t);
else {
MesPrint("&%s should have been declared as an index",t);
error = 1;
numindex = AddIndex(s,AC.lDefDim,AC.lDefDim4) + AM.OffsetIndex;
}
}
}
Add3Com(TYPESUM,numindex);
i = 3; *s = c;
if ( *s == 0 ) break;
if ( *s != ',' ) {
MesPrint("&Illegal separator between objects in sum statement.");
return(1);
}
s++;
if ( FG.cTable[*s] == 0 || *s == '[' || *s == '$' ) {
while ( FG.cTable[*s] == 0 || *s == '[' || *s == '$' ) {
if ( *s == '$' ) {
s++;
ss = t = s;
while ( FG.cTable[*s] < 2 ) s++;
c = *s; *s = 0;
if ( ( number = GetDollar(t) ) < 0 ) {
MesPrint("&Undefined variable $%s",t);
if ( !error ) error = 1;
number = AddDollar(t,0,0,0);
}
numindex = -number;
}
else {
ss = t = s;
if ( ( s = SkipAName(s) ) == 0 ) return(1);
c = *s; *s = 0;
if ( ( ( type = GetOName(AC.exprnames,t,&numindex,NOAUTO) ) != NAMENOTFOUND )
|| ( ( type = GetOName(AC.varnames,t,&numindex,WITHAUTO) ) != CINDEX ) ) {
if ( type != NAMENOTFOUND ) error = NameConflict(type,t);
else {
MesPrint("&%s should have been declared as an index",t);
error = 1;
numindex = AddIndex(s,AC.lDefDim,AC.lDefDim4) + AM.OffsetIndex;
}
}
}
AddToCB(C,numindex)
i++;
C->Pointer[-i+1] = i;
*s = c;
if ( *s == 0 ) return(error);
if ( *s != ',' ) {
MesPrint("&Illegal separator between objects in sum statement.");
return(1);
}
s++;
}
if ( FG.cTable[*s] == 1 ) {
C->Pointer[-i+1]--; C->Pointer--; s = ss;
}
}
else if ( FG.cTable[*s] == 1 ) {
while ( FG.cTable[*s] == 1 ) {
t = s;
x = *s++ - '0';
while( FG.cTable[*s] == 1 ) x = 10*x + *s++ - '0';
if ( *s && *s != ',' ) {
MesPrint("&%s is not a legal fixed index",t);
return(1);
}
else if ( x >= AM.OffsetIndex ) {
MesPrint("&%d is too large to be a fixed index",x);
error = 1;
}
else {
AddToCB(C,x)
i++;
C->Pointer[-i] = TYPESUMFIX;
C->Pointer[-i+1] = i;
}
if ( *s == 0 ) break;
s++;
}
}
else {
MesPrint("&Illegal object in sum statement");
error = 1;
}
}
return(error);
}
/*
#] CoSum :
#[ CoToTensor :
*/
static WORD cttarray[7] = { TYPEOPERATION,7,TENVEC,0,0,1,0 };
int CoToTensor(UBYTE *s)
{
UBYTE c, *t;
int type, j, nargs, error = 0;
WORD number, dol[2] = { 0, 0 };
cttarray[1] = 6; /* length */
cttarray[3] = 0; /* tensor */
cttarray[4] = 0; /* vector */
cttarray[5] = 1; /* option flags */
/* cttarray[6] = 0; set veto */
/*
Count the number of the arguments. The validity of them is not checked here.
*/
nargs = 0;
t = s;
for (;;) {
while ( *s == ' ' || *s == ',' || *s == '\t' ) s++;
if ( *s == 0 ) break;
if ( *s == '!' ) {
s++;
if ( *s == '{' ) {
SKIPBRA2(s)
s++;
} else {
if ( ( s = SkipAName(s) ) == 0 ) goto syntax_error;
}
} else {
if ( ( s = SkipAName(s) ) == 0 ) goto syntax_error;
}
nargs++;
}
if ( nargs < 2 ) goto not_enough_arguments;
s = t;
/*
Parse options, which are given as the arguments except the last two.
*/
for ( j = 2; j < nargs; j++ ) {
while ( *s == ' ' || *s == ',' || *s == '\t' ) s++;
if ( *s == '!' ) {
/*
Handle !set or !{vector,...}. Note: If two or more sets are
specified, then only the last one is used.
*/
s++;
cttarray[1] = 7;
cttarray[5] |= 8;
if ( FG.cTable[*s] == 0 || *s == '[' || *s == '_' ) {
t = s;
if ( ( s = SkipAName(s) ) == 0 ) goto syntax_error;
c = *s; *s = 0;
type = GetName(AC.varnames,t,&number,WITHAUTO);
if ( type == CVECTOR ) {
/*
As written in the manual, "!p" (without "{}") should work.
*/
cttarray[6] = DoTempSet(t,s);
*s = c;
goto check_tempset;
}
else if ( type != CSET ) {
MesPrint("&%s is not the name of a set or a vector",t);
error = 1;
}
*s = c;
cttarray[6] = number;
}
else if ( *s == '{' ) {
t = ++s; SKIPBRA2(s) *s = 0;
cttarray[6] = DoTempSet(t,s);
*s++ = '}';
check_tempset:
if ( cttarray[6] < 0 ) {
error = 1;
}
if ( AC.wildflag ) {
MesPrint("&Improper use of wildcard(s) in set specification");
error = 1;
}
}
} else {
/*
Other options.
*/
t = s;
if ( ( s = SkipAName(s) ) == 0 ) goto syntax_error;
c = *s; *s = 0;
if ( StrICmp(t,(UBYTE *)"nosquare") == 0 ) cttarray[5] |= 2;
else if ( StrICmp(t,(UBYTE *)"functions") == 0 ) cttarray[5] |= 4;
else {
MesPrint("&Unrecognized option in ToTensor statement: '%s'",t);
*s = c;
return(1);
}
*s = c;
}
}
/*
Now parse a vector and a tensor. The ordering doesn't matter.
*/
for ( j = 0; j < 2; j++ ) {
while ( *s == ' ' || *s == ',' || *s == '\t' ) s++;
t = s;
if ( ( s = SkipAName(s) ) == 0 ) goto syntax_error;
c = *s; *s = 0;
if ( t[0] == '$' ) {
dol[j] = GetDollar(t+1);
if ( dol[j] < 0 ) dol[j] = AddDollar(t+1,DOLUNDEFINED,0,0);
} else {
type = GetName(AC.varnames,t,&number,WITHAUTO);
if ( type == CVECTOR ) {
cttarray[4] = number + AM.OffsetVector;
}
else if ( type == CFUNCTION && ( functions[number].spec > 0 ) ) {
cttarray[3] = number + FUNCTION;
}
else {
MesPrint("&%s is not a vector or a tensor",t);
error = 1;
}
}
*s = c;
}
if ( cttarray[3] == 0 || cttarray[4] == 0 ) {
if ( dol[0] == 0 && dol[1] == 0 ) {
goto not_enough_arguments;
}
else if ( cttarray[3] ) {
if ( dol[1] ) cttarray[4] = dol[1];
else if ( dol[0] ) { cttarray[4] = dol[0]; }
else {
goto not_enough_arguments;
}
}
else if ( cttarray[4] ) {
if ( dol[1] ) { cttarray[3] = -dol[1]; }
else if ( dol[0] ) cttarray[3] = -dol[0];
else {
goto not_enough_arguments;
}
}
else {
if ( dol[0] == 0 || dol[1] == 0 ) {
goto not_enough_arguments;
}
else {
cttarray[3] = -dol[0]; cttarray[4] = dol[1];
}
}
}
AddNtoL(cttarray[1],cttarray);
return(error);
syntax_error:
MesPrint("&Syntax error in ToTensor statement");
return(1);
not_enough_arguments:
MesPrint("&ToTensor statement needs a vector and a tensor");
return(1);
}
/*
#] CoToTensor :
#[ CoToVector :
*/
static WORD ctvarray[6] = { TYPEOPERATION,6,TENVEC,0,0,0 };
int CoToVector(UBYTE *s)
{
UBYTE *t, c;
int j, type, error = 0;
WORD number, dol[2];
dol[0] = dol[1] = 0;
ctvarray[3] = ctvarray[4] = ctvarray[5] = 0;
for ( j = 0; j < 2; j++ ) {
t = s;
if ( ( s = SkipAName(s) ) == 0 ) {
proper: MesPrint("&Arguments of ToVector statement should be a vector and a tensor");
return(1);
}
c = *s; *s = 0;
if ( *t == '$' ) {
dol[j] = GetDollar(t+1);
if ( dol[j] < 0 ) dol[j] = AddDollar(t+1,DOLUNDEFINED,0,0);
}
else if ( ( type = GetName(AC.varnames,t,&number,WITHAUTO) ) == CVECTOR )
ctvarray[4] = number + AM.OffsetVector;
else if ( type == CFUNCTION && ( functions[number].spec > 0 ) )
ctvarray[3] = number+FUNCTION;
else {
MesPrint("&%s is not a vector or a tensor",t);
error = 1;
}
*s = c; if ( *s && *s != ',' ) goto proper;
if ( *s ) s++;
}
if ( *s != 0 ) goto proper;
if ( ctvarray[3] == 0 || ctvarray[4] == 0 ) {
if ( dol[0] == 0 && dol[1] == 0 ) {
MesPrint("&ToVector statement needs a vector and a tensor");
error = 1;
}
else if ( ctvarray[3] ) {
if ( dol[1] ) ctvarray[4] = dol[1];
else if ( dol[0] ) ctvarray[4] = dol[0];
else {
MesPrint("&ToVector statement needs a vector and a tensor");
error = 1;
}
}
else if ( ctvarray[4] ) {
if ( dol[1] ) ctvarray[3] = -dol[1];
else if ( dol[0] ) ctvarray[3] = -dol[0];
else {
MesPrint("&ToVector statement needs a vector and a tensor");
error = 1;
}
}
else {
if ( dol[0] == 0 || dol[1] == 0 ) {
MesPrint("&ToVector statement needs a vector and a tensor");
error = 1;
}
else {
ctvarray[3] = -dol[0]; ctvarray[4] = dol[1];
}
}
}
AddNtoL(6,ctvarray);
return(error);
}
/*
#] CoToVector :
#[ CoTrace4 :
*/
int CoTrace4(UBYTE *s)
{
int error = 0, type, option = CHISHOLM;
UBYTE *t, c;
WORD numindex, one = 1;
KEYWORD *key;
for (;;) {
t = s;
if ( FG.cTable[*s] == 1 ) break;
if ( ( s = SkipAName(s) ) == 0 ) {
proper: MesPrint("&Proper syntax for Trace4 is 'Trace4[,options],index;'");
return(1);
}
if ( *s == 0 ) break;
c = *s; *s = 0;
if ( ( key = FindKeyWord(t,trace4options,
sizeof(trace4options)/sizeof(KEYWORD)) ) == 0 ) break;
else {
option |= key->type;
option &= ~key->flags;
}
if ( ( *s++ = c ) != ',' ) {
MesPrint("&Illegal separator in Trace4 statement");
return(1);
}
if ( *s == 0 ) goto proper;
}
s = t;
if ( FG.cTable[*s] == 1 ) {
retry:
ParseNumber(numindex,s)
if ( *s != 0 ) {
MesPrint("&Last argument of Trace4 should be an index");
return(1);
}
if ( numindex >= AM.OffsetIndex ) {
MesPrint("&fixed index >= %d. Change value of OffsetIndex in setup file"
,AM.OffsetIndex);
return(1);
}
}
else if ( *s == '$' ) {
if ( ( type = GetName(AC.dollarnames,s+1,&numindex,NOAUTO) ) == CDOLLAR )
numindex = -numindex;
else {
MesPrint("&%s is undefined",s);
numindex = AddDollar(s+1,DOLINDEX,&one,1);
return(1);
}
tests: s = SkipAName(s);
if ( *s != 0 ) {
MesPrint("&Trace4 should have a single index or $variable for its argument");
return(1);
}
}
else if ( ( type = GetName(AC.varnames,s,&numindex,WITHAUTO) ) == CINDEX ) {
numindex += AM.OffsetIndex;
goto tests;
}
else if ( type != -1 ) {
if ( type != CDUBIOUS ) {
if ( ( FG.cTable[*s] != 0 ) && ( *s != '[' ) ) {
if ( *s == '+' && FG.cTable[s[1]] == 1 ) { s++; goto retry; }
goto proper;
}
NameConflict(type,s);
type = MakeDubious(AC.varnames,s,&numindex);
}
return(1);
}
else {
MesPrint("&%s is not an index",s);
numindex = AddIndex(s,AC.lDefDim,AC.lDefDim4) + AM.OffsetIndex;
return(1);
}
if ( error ) return(error);
if ( ( option & CHISHOLM ) != 0 )
Add4Com(TYPECHISHOLM,numindex,(option & ALSOREVERSE));
Add5Com(TYPEOPERATION,TAKETRACE,4 + (option & NOTRICK),numindex);
return(0);
}
/*
#] CoTrace4 :
#[ CoTraceN :
*/
int CoTraceN(UBYTE *s)
{
WORD numindex, one = 1;
int type;
if ( FG.cTable[*s] == 1 ) {
retry:
ParseNumber(numindex,s)
if ( *s != 0 ) {
proper: MesPrint("&TraceN should have a single index for its argument");
return(1);
}
if ( numindex >= AM.OffsetIndex ) {
MesPrint("&fixed index >= %d. Change value of OffsetIndex in setup file"
,AM.OffsetIndex);
return(1);
}
}
else if ( *s == '$' ) {
if ( ( type = GetName(AC.dollarnames,s+1,&numindex,NOAUTO) ) == CDOLLAR )
numindex = -numindex;
else {
MesPrint("&%s is undefined",s);
numindex = AddDollar(s+1,DOLINDEX,&one,1);
return(1);
}
tests: s = SkipAName(s);
if ( *s != 0 ) {
MesPrint("&TraceN should have a single index or $variable for its argument");
return(1);
}
}
else if ( ( type = GetName(AC.varnames,s,&numindex,WITHAUTO) ) == CINDEX ) {
numindex += AM.OffsetIndex;
goto tests;
}
else if ( type != -1 ) {
if ( type != CDUBIOUS ) {
if ( ( FG.cTable[*s] != 0 ) && ( *s != '[' ) ) {
if ( *s == '+' && FG.cTable[s[1]] == 1 ) { s++; goto retry; }
goto proper;
}
NameConflict(type,s);
type = MakeDubious(AC.varnames,s,&numindex);
}
return(1);
}
else {
MesPrint("&%s is not an index",s);
numindex = AddIndex(s,AC.lDefDim,AC.lDefDim4) + AM.OffsetIndex;
return(1);
}
Add5Com(TYPEOPERATION,TAKETRACE,0,numindex);
return(0);
}
/*
#] CoTraceN :
#[ CoChisholm :
*/
int CoChisholm(UBYTE *s)
{
int error = 0, type, option = CHISHOLM;
UBYTE *t, c;
WORD numindex, one = 1;
KEYWORD *key;
for (;;) {
t = s;
if ( FG.cTable[*s] == 1 ) break;
if ( ( s = SkipAName(s) ) == 0 ) {
proper: MesPrint("&Proper syntax for Chisholm is 'Chisholm[,options],index;'");
return(1);
}
if ( *s == 0 ) break;
c = *s; *s = 0;
if ( ( key = FindKeyWord(t,chisoptions,
sizeof(chisoptions)/sizeof(KEYWORD)) ) == 0 ) break;
else {
option |= key->type;
option &= ~key->flags;
}
if ( ( *s++ = c ) != ',' ) {
MesPrint("&Illegal separator in Chisholm statement");
return(1);
}
if ( *s == 0 ) goto proper;
}
s = t;
if ( FG.cTable[*s] == 1 ) {
ParseNumber(numindex,s)
if ( *s != 0 ) {
MesPrint("&Last argument of Chisholm should be an index");
return(1);
}
if ( numindex >= AM.OffsetIndex ) {
MesPrint("&fixed index >= %d. Change value of OffsetIndex in setup file"
,AM.OffsetIndex);
return(1);
}
}
else if ( *s == '$' ) {
if ( ( type = GetName(AC.dollarnames,s+1,&numindex,NOAUTO) ) == CDOLLAR )
numindex = -numindex;
else {
MesPrint("&%s is undefined",s);
numindex = AddDollar(s+1,DOLINDEX,&one,1);
return(1);
}
tests: s = SkipAName(s);
if ( *s != 0 ) {
MesPrint("&Chisholm should have a single index or $variable for its argument");
return(1);
}
}
else if ( ( type = GetName(AC.varnames,s,&numindex,WITHAUTO) ) == CINDEX ) {
numindex += AM.OffsetIndex;
goto tests;
}
else if ( type != -1 ) {
if ( type != CDUBIOUS ) {
NameConflict(type,s);
type = MakeDubious(AC.varnames,s,&numindex);
}
return(1);
}
else {
MesPrint("&%s is not an index",s);
numindex = AddIndex(s,AC.lDefDim,AC.lDefDim4) + AM.OffsetIndex;
return(1);
}
if ( error ) return(error);
Add4Com(TYPECHISHOLM,numindex,(option & ALSOREVERSE));
return(0);
}
/*
#] CoChisholm :
#[ DoChain :
Syntax: Chainxx functionname;
*/
int DoChain(UBYTE *s, int option)
{
WORD numfunc,type;
if ( *s == '$' ) {
if ( ( type = GetName(AC.dollarnames,s+1,&numfunc,NOAUTO) ) == CDOLLAR )
numfunc = -numfunc;
else {
MesPrint("&%s is undefined",s);
numfunc = AddDollar(s+1,DOLINDEX,&one,1);
return(1);
}
tests: s = SkipAName(s);
if ( *s != 0 ) {
MesPrint("&ChainIn/ChainOut should have a single function or $variable for its argument");
return(1);
}
}
else if ( ( type = GetName(AC.varnames,s,&numfunc,WITHAUTO) ) == CFUNCTION ) {
numfunc += FUNCTION;
goto tests;
}
else if ( type != -1 ) {
if ( type != CDUBIOUS ) {
NameConflict(type,s);
type = MakeDubious(AC.varnames,s,&numfunc);
}
return(1);
}
else {
MesPrint("&%s is not a function",s);
numfunc = AddFunction(s,0,0,0,0,0,-1,-1) + FUNCTION;
return(1);
}
Add3Com(option,numfunc);
return(0);
}
/*
#] DoChain :
#[ CoChainin :
Syntax: Chainin functionname;
*/
int CoChainin(UBYTE *s)
{
return(DoChain(s,TYPECHAININ));
}
/*
#] CoChainin :
#[ CoChainout :
Syntax: Chainout functionname;
*/
int CoChainout(UBYTE *s)
{
return(DoChain(s,TYPECHAINOUT));
}
/*
#] CoChainout :
#[ CoExit :
*/
int CoExit(UBYTE *s)
{
UBYTE *name;
WORD code = TYPEEXIT;
while ( *s == ',' ) s++;
if ( *s == 0 ) {
Add3Com(TYPEEXIT,0);
return(0);
}
name = s+1;
s++;
while ( *s ) { if ( *s == '\\' ) s++; s++; }
if ( name[-1] != '"' || s[-1] != '"' ) {
MesPrint("&Illegal syntax for exit statement");
return(1);
}
s[-1] = 0;
AddComString(1,&code,name,0);
s[-1] = '"';
return(0);
}
/*
#] CoExit :
#[ CoInParallel :
*/
int CoInParallel(UBYTE *s)
{
return(DoInParallel(s,1));
}
/*
#] CoInParallel :
#[ CoNotInParallel :
*/
int CoNotInParallel(UBYTE *s)
{
return(DoInParallel(s,0));
}
/*
#] CoNotInParallel :
#[ DoInParallel :
InParallel;
InParallel,names;
NotInParallel;
NotInParallel,names;
*/
int DoInParallel(UBYTE *s, int par)
{
#ifdef PARALLELCODE
EXPRESSIONS e;
WORD i;
#endif
WORD number;
UBYTE *t, c;
int error = 0;
#ifndef WITHPTHREADS
DUMMYUSE(par);
#endif
if ( *s == 0 ) {
AC.inparallelflag = par;
#ifdef PARALLELCODE
for ( i = NumExpressions-1; i >= 0; i-- ) {
e = Expressions+i;
if ( e->status == LOCALEXPRESSION || e->status == GLOBALEXPRESSION
|| e->status == UNHIDELEXPRESSION || e->status == UNHIDEGEXPRESSION
) {
e->partodo = par;
}
}
#endif
}
else {
for(;;) { /* Look for a (comma separated) list of variables */
while ( *s == ',' ) s++;
if ( *s == 0 ) break;
if ( *s == '[' || FG.cTable[*s] == 0 ) {
t = s;
if ( ( s = SkipAName(s) ) == 0 ) {
MesPrint("&Improper name for an expression: '%s'",t);
return(1);
}
c = *s; *s = 0;
if ( GetName(AC.exprnames,t,&number,NOAUTO) == CEXPRESSION ) {
#ifdef PARALLELCODE
e = Expressions+number;
if ( e->status == LOCALEXPRESSION || e->status == GLOBALEXPRESSION
|| e->status == UNHIDELEXPRESSION || e->status == UNHIDEGEXPRESSION
) {
e->partodo = par;
}
#endif
}
else if ( GetName(AC.varnames,t,&number,NOAUTO) != NAMENOTFOUND ) {
MesPrint("&%s is not an expression",t);
error = 1;
}
*s = c;
}
else {
MesPrint("&Illegal object in InExpression statement");
error = 1;
while ( *s && *s != ',' ) s++;
if ( *s == 0 ) break;
}
}
}
return(error);
}
/*
#] DoInParallel :
#[ CoInExpression :
*/
int CoInExpression(UBYTE *s)
{
GETIDENTITY
UBYTE *t, c;
WORD *w, number;
int error = 0;
w = AT.WorkPointer;
if ( AC.inexprlevel >= MAXNEST ) {
MesPrint("@Nesting of inexpression statements more than %d levels",(WORD)MAXNEST);
return(-1);
}
AC.inexprsumcheck[AC.inexprlevel] = NestingChecksum();
AC.inexprstack[AC.inexprlevel] = cbuf[AC.cbufnum].Pointer
- cbuf[AC.cbufnum].Buffer + 2;
AC.inexprlevel++;
*w++ = TYPEINEXPRESSION;
w++; w++;
for(;;) { /* Look for a (comma separated) list of variables */
while ( *s == ',' ) s++;
if ( *s == 0 ) break;
if ( *s == '[' || FG.cTable[*s] == 0 ) {
t = s;
if ( ( s = SkipAName(s) ) == 0 ) {
MesPrint("&Improper name for an expression: '%s'",t);
return(1);
}
c = *s; *s = 0;
if ( GetName(AC.exprnames,t,&number,NOAUTO) == CEXPRESSION ) {
*w++ = number;
}
else if ( GetName(AC.varnames,t,&number,NOAUTO) != NAMENOTFOUND ) {
MesPrint("&%s is not an expression",t);
error = 1;
}
*s = c;
}
else {
MesPrint("&Illegal object in InExpression statement");
error = 1;
while ( *s && *s != ',' ) s++;
if ( *s == 0 ) break;
}
}
AT.WorkPointer[1] = w - AT.WorkPointer;
AddNtoL(AT.WorkPointer[1],AT.WorkPointer);
return(error);
}
/*
#] CoInExpression :
#[ CoEndInExpression :
*/
int CoEndInExpression(UBYTE *s)
{
CBUF *C = cbuf+AC.cbufnum;
while ( *s == ',' ) s++;
if ( *s ) {
MesPrint("&Illegal syntax for EndInExpression statement");
return(1);
}
if ( AC.inexprlevel <= 0 ) {
MesPrint("&EndInExpression without corresponding InExpression statement");
return(1);
}
AC.inexprlevel--;
cbuf[AC.cbufnum].Buffer[AC.inexprstack[AC.inexprlevel]] = C->numlhs;
if ( AC.inexprsumcheck[AC.inexprlevel] != NestingChecksum() ) {
MesNesting();
return(1);
}
return(0);
}
/*
#] CoEndInExpression :
#[ CoSetExitFlag :
*/
int CoSetExitFlag(UBYTE *s)
{
if ( *s ) {
MesPrint("&Illegal syntax for the SetExitFlag statement");
return(1);
}
Add2Com(TYPESETEXIT);
return(0);
}
/*
#] CoSetExitFlag :
#[ CoTryReplace :
*/
int CoTryReplace(UBYTE *p)
{
GETIDENTITY
UBYTE *name, c;
WORD *w, error = 0, i, which = -1, c1, minvec = 0;
w = AT.WorkPointer;
*w++ = TYPETRY;
*w++ = 3;
*w++ = 0;
*w++ = REPLACEMENT;
*w++ = FUNHEAD;
FILLFUN(w)
/*
Now we have to read a function argument for the replace_ function.
Current arguments that we allow involve only single arguments
that do not expand further. No brackets!
*/
while ( *p ) {
/*
No numbers yet
*/
if ( *p == '-' && minvec == 0 && which == (CVECTOR+1) ) {
minvec = 1; p++;
}
if ( *p == '[' || FG.cTable[*p] == 0 ) {
name = p;
if ( ( p = SkipAName(p) ) == 0 ) return(1);
c = *p; *p = 0;
i = GetName(AC.varnames,name,&c1,WITHAUTO);
if ( which >= 0 && i >= 0 && i != CDUBIOUS && which != (i+1) ) {
MesPrint("&Illegal combination of objects in TryReplace");
error = 1;
}
else if ( minvec && i != CVECTOR && i != CDUBIOUS ) {
MesPrint("&Currently a - sign can be used only with a vector in TryReplace");
error = 1;
}
else switch ( i ) {
case CSYMBOL: *w++ = -SYMBOL; *w++ = c1; break;
case CVECTOR:
if ( minvec ) *w++ = -MINVECTOR;
else *w++ = -VECTOR;
*w++ = c1 + AM.OffsetVector;
minvec = 0;
break;
case CINDEX: *w++ = -INDEX; *w++ = c1 + AM.OffsetIndex;
if ( c1 >= AM.WilInd && c == '?' ) { *p++ = c; c = *p; }
break;
case CFUNCTION: *w++ = -c1-FUNCTION; break;
case CDUBIOUS: minvec = 0; error = 1; break;
default:
MesPrint("&Illegal object type in TryReplace: %s",name);
error = 1;
i = 0;
break;
}
if ( which < 0 ) which = i+1;
else which = -1;
*p = c;
if ( *p == ',' ) p++;
continue;
}
else {
MesPrint("&Illegal object in TryReplace");
error = 1;
while ( *p && *p != ',' ) {
if ( *p == '(' ) SKIPBRA3(p)
else if ( *p == '{' ) SKIPBRA2(p)
else if ( *p == '[' ) SKIPBRA1(p)
else p++;
}
}
if ( *p == ',' ) p++;
if ( which < 0 ) which = 0;
else which = -1;
}
if ( which >= 0 ) {
MesPrint("&Odd number of arguments in TryReplace");
error = 1;
}
i = w - AT.WorkPointer;
AT.WorkPointer[1] = i;
AT.WorkPointer[2] = i - 3;
AT.WorkPointer[4] = i - 3;
AddNtoL((int)i,AT.WorkPointer);
return(error);
}
/*
#] CoTryReplace :
#[ CoModulus :
Old syntax: Modulus [-] number [:number]
New syntax: Modulus [option(s)] number
Options are: NoFunctions/CoefficientsOnly/AlsoFunctions
PlusMin/Positive
InverseTable
PrintPowersOf(number)
AlsoPowers/NoPowers
AlsoDollars/NoDollars
Notice: We change the defaults. This may cause problems to some.
*/
int CoModulus(UBYTE *inp)
{
#ifdef OLDMODULUS
/* #[ Old Syntax : */
UBYTE *p, c;
WORD sign = 1, Retval;
while ( *inp == '-' || *inp == '+' ) {
if ( *inp == '-' ) sign = -sign;
inp++;
}
p = inp;
if ( FG.cTable[*inp] != 1 ) {
MesPrint("&Invalid value for modulus:%s",inp);
if ( AC.modpowers ) M_free(AC.modpowers,"AC.modpowers");
AC.modpowers = 0;
return(1);
}
do { inp++; } while ( FG.cTable[*inp] == 1 );
c = *inp; *inp = 0;
Retval = GetLong(p,(UWORD *)AC.cmod,&AC.ncmod);
if ( sign < 0 ) AC.ncmod = -AC.ncmod;
*p = c;
if ( c == 0 ) goto regular;
else if ( c != ':' ) {
MesPrint("&Illegal option for modulus %s",inp);
if ( AC.modpowers ) M_free(AC.modpowers,"AC.modpowers");
AC.modpowers = 0;
return(1);
}
inp++;
p = inp;
while ( FG.cTable[*inp] == 1 ) inp++;
if ( *inp ) {
MesPrint("&Illegal character in option for modulus %s",inp);
if ( AC.modpowers ) M_free(AC.modpowers,"AC.modpowers");
AC.modpowers = 0;
return(1);
}
if ( GetLong(p,(UWORD *)AC.powmod,&AC.npowmod) ) Retval = -1;
if ( TakeModulus((UWORD *)AC.powmod,&AC.npowmod,AC.cmod,AC.ncmod,NOUNPACK) ) Retval = -1;
if ( AC.npowmod == 0 ) {
MesPrint("&Improper value for generator");
Retval = -1;
}
if ( MakeModTable() ) Retval = -1;
AC.DirtPow = 1;
regular:
AN.ncmod = AC.ncmod;
if ( AC.halfmod ) {
M_free(AC.halfmod,"halfmod");
AC.halfmod = 0; AC.nhalfmod = 0;
}
if ( AC.modinverses ) {
M_free(AC.halfmod,"modinverses");
AC.modinverses = 0;
}
return(Retval);
/* #] Old Syntax : */
#else
GETIDENTITY
int Retval = 0, sign = 1;
UBYTE *p, c;
while ( *inp == ',' || *inp == ' ' || *inp == '\t' ) inp++;
if ( *inp == 0 ) {
SwitchOff:
if ( AC.modpowers ) M_free(AC.modpowers,"AC.modpowers");
AC.modpowers = 0;
AN.ncmod = AC.ncmod = 0;
if ( AC.halfmod ) M_free(AC.halfmod,"halfmod");
AC.halfmod = 0; AC.nhalfmod = 0;
if ( AC.modinverses ) M_free(AC.modinverses,"modinverses");
AC.modinverses = 0;
AC.modmode = 0;
return(0);
}
AC.modmode = 0;
if ( *inp == '-' ) {
sign = -1;
inp++;
}
else {
while ( FG.cTable[*inp] == 0 ) {
p = inp;
while ( FG.cTable[*inp] == 0 ) inp++;
c = *inp; *inp = 0;
if ( StrICmp(p,(UBYTE *)"nofunctions") == 0 ) {
AC.modmode &= ~ALSOFUNARGS;
}
else if ( StrICmp(p,(UBYTE *)"alsofunctions") == 0 ) {
AC.modmode |= ALSOFUNARGS;
}
else if ( StrICmp(p,(UBYTE *)"coefficientsonly") == 0 ) {
AC.modmode &= ~ALSOFUNARGS;
AC.modmode &= ~ALSOPOWERS;
sign = -1;
}
else if ( StrICmp(p,(UBYTE *)"plusmin") == 0 ) {
AC.modmode |= POSNEG;
}
else if ( StrICmp(p,(UBYTE *)"positive") == 0 ) {
AC.modmode &= ~POSNEG;
}
else if ( StrICmp(p,(UBYTE *)"inversetable") == 0 ) {
AC.modmode |= INVERSETABLE;
}
else if ( StrICmp(p,(UBYTE *)"noinversetable") == 0 ) {
AC.modmode &= ~INVERSETABLE;
}
else if ( StrICmp(p,(UBYTE *)"nodollars") == 0 ) {
AC.modmode &= ~ALSODOLLARS;
}
else if ( StrICmp(p,(UBYTE *)"alsodollars") == 0 ) {
AC.modmode |= ALSODOLLARS;
}
else if ( StrICmp(p,(UBYTE *)"printpowersof") == 0 ) {
*inp = c;
if ( *inp != '(' ) {
badsyntax:
MesPrint("&Bad syntax in argument of PrintPowersOf(number) in Modulus statement");
return(1);
}
while ( *inp == ',' || *inp == ' ' || *inp == '\t' ) inp++;
inp++; p = inp;
if ( FG.cTable[*inp] != 1 ) goto badsyntax;
do { inp++; } while ( FG.cTable[*inp] == 1 );
c = *inp; *inp = 0;
if ( GetLong(p,(UWORD *)AC.powmod,&AC.npowmod) ) Retval = -1;
if ( TakeModulus((UWORD *)AC.powmod,&AC.npowmod,AC.cmod,AC.ncmod,NOUNPACK) ) Retval = -1;
if ( AC.npowmod == 0 ) {
MesPrint("&Improper value for generator");
Retval = -1;
}
if ( MakeModTable() ) Retval = -1;
AC.DirtPow = 1;
*inp = c;
while ( *inp == ',' || *inp == ' ' || *inp == '\t' ) inp++;
if ( *inp != ')' ) goto badsyntax;
inp++;
c = *inp;
}
else if ( StrICmp(p,(UBYTE *)"alsopowers") == 0 ) {
AC.modmode |= ALSOPOWERS;
sign = 1;
}
else if ( StrICmp(p,(UBYTE *)"nopowers") == 0 ) {
AC.modmode &= ~ALSOPOWERS;
sign = -1;
}
else {
MesPrint("&Unrecognized option %s in Modulus statement",inp);
return(1);
}
*inp = c;
while ( *inp == ',' || *inp == ' ' || *inp == '\t' ) inp++;
if ( *inp == 0 ) {
MesPrint("&Modulus statement with no value!!!");
return(1);
}
}
}
p = inp;
if ( FG.cTable[*inp] != 1 ) {
MesPrint("&Invalid value for modulus:%s",inp);
if ( AC.modpowers ) M_free(AC.modpowers,"AC.modpowers");
AC.modpowers = 0;
AN.ncmod = AC.ncmod = 0;
if ( AC.halfmod ) M_free(AC.halfmod,"halfmod");
AC.halfmod = 0; AC.nhalfmod = 0;
if ( AC.modinverses ) M_free(AC.modinverses,"modinverses");
AC.modinverses = 0;
return(1);
}
do { inp++; } while ( FG.cTable[*inp] == 1 );
c = *inp; *inp = 0;
Retval = GetLong(p,(UWORD *)AC.cmod,&AC.ncmod);
if ( Retval == 0 && AC.ncmod == 0 ) goto SwitchOff;
if ( sign < 0 ) AC.ncmod = -AC.ncmod;
AN.ncmod = AC.ncmod;
if ( ( AC.modmode & INVERSETABLE ) != 0 ) MakeInverses();
if ( AC.halfmod ) M_free(AC.halfmod,"halfmod");
AC.halfmod = 0; AC.nhalfmod = 0;
return(Retval);
#endif
}
/*
#] CoModulus :
#[ CoRepeat :
*/
int CoRepeat(UBYTE *inp)
{
int error = 0;
AC.RepSumCheck[AC.RepLevel] = NestingChecksum();
AC.RepLevel++;
if ( AC.RepLevel > AM.RepMax ) {
MesPrint("&Too many repeat levels. Maximum is %d",AM.RepMax);
return(1);
}
Add3Com(TYPEREPEAT,-1) /* Means indefinite */
while ( *inp == ' ' || *inp == ',' || *inp == '\t' ) inp++;
if ( *inp ) {
error = CompileStatement(inp);
if ( CoEndRepeat(inp) ) error = 1;
}
return(error);
}
/*
#] CoRepeat :
#[ CoEndRepeat :
*/
int CoEndRepeat(UBYTE *inp)
{
CBUF *C = cbuf+AC.cbufnum;
int level, error = 0, repeatlevel = 0;
DUMMYUSE(inp);
AC.RepLevel--;
if ( AC.RepLevel < 0 ) {
MesPrint("&EndRepeat without Repeat");
AC.RepLevel = 0;
return(1);
}
else if ( AC.RepSumCheck[AC.RepLevel] != NestingChecksum() ) {
MesNesting();
error = 1;
}
level = C->numlhs+1;
while ( level > 0 ) {
if ( C->lhs[--level][0] == TYPEREPEAT ) {
if ( repeatlevel == 0 ) {
Add3Com(TYPEENDREPEAT,level)
return(error);
}
repeatlevel--;
}
else if ( C->lhs[level][0] == TYPEENDREPEAT ) repeatlevel++;
}
return(1);
}
/*
#] CoEndRepeat :
#[ DoBrackets :
Reads in the bracket information.
Storage is in the form of a regular term.
No subterms and arguments are allowed.
*/
int DoBrackets(UBYTE *inp, int par)
{
GETIDENTITY
UBYTE *p, *pp, c;
WORD *to, i, type, *w, error = 0;
WORD c1,c2, *WorkSave;
int biflag;
p = inp;
WorkSave = to = AT.WorkPointer;
to++;
if ( AT.BrackBuf == 0 ) {
AR.MaxBracket = 100;
AT.BrackBuf = (WORD *)Malloc1(sizeof(WORD)*(AR.MaxBracket+1),"bracket buffer");
}
*AT.BrackBuf = 0;
AR.BracketOn = 0;
AC.bracketindexflag = 0;
AT.bracketindexflag = 0;
if ( *p == '+' || *p == '-' ) p++;
if ( p[-1] == ',' && *p ) p--;
if ( p[-1] == '+' && *p ) { biflag = 1; if ( *p != ',' ) { *--p = ','; } }
else if ( p[-1] == '-' && *p ) { biflag = -1; if ( *p != ',' ) { *--p = ','; } }
else biflag = 0;
while ( *p == ',' ) {
redo: AR.BracketOn++;
while ( *p == ',' ) p++;
if ( *p == 0 ) break;
if ( *p == '0' ) {
p++; while ( *p == '0' ) p++;
continue;
}
inp = pp = p;
p = SkipAName(p);
if ( p == 0 ) return(1);
c = *p;
*p = 0;
type = GetName(AC.varnames,inp,&c1,WITHAUTO);
if ( c == '.' ) {
if ( type == CVECTOR || type == CDUBIOUS ) {
*p++ = c;
inp = p;
p = SkipAName(p);
if ( p == 0 ) return(1);
c = *p;
*p = 0;
type = GetName(AC.varnames,inp,&c2,WITHAUTO);
if ( type != CVECTOR && type != CDUBIOUS ) {
MesPrint("&Not a vector in dotproduct in bracket statement: %s",inp);
error = 1;
}
else type = CDOTPRODUCT;
}
else {
MesPrint("&Illegal use of . after %s in bracket statement",inp);
error = 1;
*p++ = c;
goto redo;
}
}
switch ( type ) {
case CSYMBOL :
*to++ = SYMBOL; *to++ = 4; *to++ = c1; *to++ = 1; break;
case CVECTOR :
*to++ = INDEX; *to++ = 3; *to++ = AM.OffsetVector + c1; break;
case CFUNCTION :
*to++ = c1+FUNCTION; *to++ = FUNHEAD; *to++ = 0;
FILLFUN3(to)
break;
case CDOTPRODUCT :
*to++ = DOTPRODUCT; *to++ = 5; *to++ = c1 + AM.OffsetVector;
*to++ = c2 + AM.OffsetVector; *to++ = 1; break;
case CDELTA :
*to++ = DELTA; *to++ = 4; *to++ = EMPTYINDEX; *to++ = EMPTYINDEX; break;
case CSET :
*to++ = SETSET; *to++ = 4; *to++ = c1; *to++ = Sets[c1].type; break;
default :
MesPrint("&Illegal bracket request for %s",pp);
error = 1; break;
}
*p = c;
}
if ( *p ) {
MesCerr("separator",p);
AC.BracketNormalize = 0;
AT.WorkPointer = WorkSave;
error = 1;
return(error);
}
*to++ = 1; *to++ = 1; *to++ = 3;
*AT.WorkPointer = to - AT.WorkPointer;
AT.WorkPointer = to;
AC.BracketNormalize = 1;
if ( BracketNormalize(BHEAD WorkSave) ) { error = 1; AR.BracketOn = 0; }
else {
w = WorkSave;
if ( *w == 4 || !*w ) { AR.BracketOn = 0; }
else {
i = *(w+*w-1);
if ( i < 0 ) i = -i;
*w -= i;
i = *w;
if ( i > AR.MaxBracket ) {
WORD *newbuf;
newbuf = (WORD *)Malloc1(sizeof(WORD)*(i+1),"bracket buffer");
AR.MaxBracket = i;
if ( AT.BrackBuf != 0 ) M_free(AT.BrackBuf,"bracket buffer");
AT.BrackBuf = newbuf;
}
to = AT.BrackBuf;
NCOPY(to,w,i);
}
}
AC.BracketNormalize = 0;
if ( par == 1 ) AR.BracketOn = -AR.BracketOn;
if ( error == 0 ) {
AC.bracketindexflag = biflag;
AT.bracketindexflag = biflag;
}
AT.WorkPointer = WorkSave;
return(error);
}
/*
#] DoBrackets :
#[ CoBracket :
*/
int CoBracket(UBYTE *inp)
{ return(DoBrackets(inp,0)); }
/*
#] CoBracket :
#[ CoAntiBracket :
*/
int CoAntiBracket(UBYTE *inp)
{ return(DoBrackets(inp,1)); }
/*
#] CoAntiBracket :
#[ CoMultiBracket :
Syntax:
MultiBracket:{A|B} bracketinfo:...:{A|B} bracketinfo;
*/
int CoMultiBracket(UBYTE *inp)
{
GETIDENTITY
int i, error = 0, error1, type, num;
UBYTE *s, c;
WORD *to, *from;
if ( *inp != ':' ) {
MesPrint("&Illegal Multiple Bracket separator: %s",inp);
return(1);
}
inp++;
if ( AC.MultiBracketBuf == 0 ) {
AC.MultiBracketBuf = (WORD **)Malloc1(sizeof(WORD *)*MAXMULTIBRACKETLEVELS,"multi bracket buffer");
for ( i = 0; i < MAXMULTIBRACKETLEVELS; i++ ) {
AC.MultiBracketBuf[i] = 0;
}
}
else {
for ( i = 0; i < MAXMULTIBRACKETLEVELS; i++ ) {
if ( AC.MultiBracketBuf[i] ) {
M_free(AC.MultiBracketBuf[i],"bracket buffer i");
AC.MultiBracketBuf[i] = 0;
}
}
AC.MultiBracketLevels = 0;
}
AC.MultiBracketLevels = 0;
/*
Start with disabling the regular brackets.
*/
if ( AT.BrackBuf == 0 ) {
AR.MaxBracket = 100;
AT.BrackBuf = (WORD *)Malloc1(sizeof(WORD)*(AR.MaxBracket+1),"bracket buffer");
}
*AT.BrackBuf = 0;
AR.BracketOn = 0;
AC.bracketindexflag = 0;
AT.bracketindexflag = 0;
/*
Now loop through the various levels, separated by the colons.
*/
for ( i = 0; i < MAXMULTIBRACKETLEVELS; i++ ) {
if ( *inp == 0 ) goto RegEnd;
/*
1: skip to ':', determine bracket or antibracket
*/
s = inp;
while ( *s && *s != ':' ) {
if ( *s == '[' ) { SKIPBRA1(s) s++; }
else if ( *s == '{' ) { SKIPBRA2(s) s++; }
else s++;
}
c = *s; *s = 0;
if ( StrICont(inp,(UBYTE *)"antibrackets") == 0 ) { type = 1; }
else if ( StrICont(inp,(UBYTE *)"brackets") == 0 ) { type = 0; }
else {
MesPrint("&Illegal (anti)bracket specification in MultiBracket statement");
if ( error == 0 ) error = 1;
goto NextLevel;
}
while ( FG.cTable[*inp] == 0 ) inp++;
if ( *inp != ',' ) {
MesPrint("&Illegal separator after (anti)bracket specification in MultiBracket statement");
if ( error == 0 ) error = 1;
goto NextLevel;
}
inp++;
/*
2: call DoBrackets.
*/
error1 = DoBrackets(inp, type);
if ( error < 0 ) return(error1);
if ( error1 > error ) error = error1;
/*
3: copy bracket information to the multi bracket arrays
*/
if ( AR.BracketOn ) {
num = AT.BrackBuf[0];
to = AC.MultiBracketBuf[i] = (WORD *)Malloc1((num+2)*sizeof(WORD),"bracket buffer i");
from = AT.BrackBuf;
*to++ = AR.BracketOn;
NCOPY(to,from,num);
*to = 0;
}
/*
4: set ready for the next level
*/
NextLevel:
*s = c; if ( c == ':' ) s++;
inp = s;
*AT.BrackBuf = 0;
AR.BracketOn = 0;
}
if ( *inp != 0 ) {
MesPrint("&More than %d levels in MultiBracket statement",(WORD)MAXMULTIBRACKETLEVELS);
if ( error == 0 ) error = 1;
}
RegEnd:
AC.MultiBracketLevels = i;
*AT.BrackBuf = 0;
AR.BracketOn = 0;
AC.bracketindexflag = 0;
AT.bracketindexflag = 0;
return(error);
}
/*
#] CoMultiBracket :
#[ CountComp :
This routine reads the count statement. The syntax is:
count minimum,object,size[,object,size]
Objects can be:
symbol
dotproduct
vector
function
Vectors can have the auxiliary flags:
+v +f +d +?setname
Output for the compiler:
TYPECOUNT,size,minimum,objects
with the objects:
SYMBOL,4,number,size
DOTPRODUCT,5,v1,v2,size
FUNCTION,4,number,size
VECTOR,5,number,bits,size or VECTOR,6,number,bits,setnumber,size
Currently only used in the if statement
*/
WORD *CountComp(UBYTE *inp, WORD *to)
{
GETIDENTITY
UBYTE *p, c;
WORD *w, mini = 0, type, c1, c2;
int error = 0;
p = inp;
w = to;
AR.Eside = 2;
*w++ = TYPECOUNT;
*w++ = 0;
*w++ = 0;
while ( *p == ',' ) {
p++; inp = p;
if ( *p == '[' || FG.cTable[*p] == 0 ) {
if ( ( p = SkipAName(inp) ) == 0 ) return(0);
c = *p; *p = 0;
type = GetName(AC.varnames,inp,&c1,WITHAUTO);
if ( c == '.' ) {
if ( type == CVECTOR || type == CDUBIOUS ) {
*p++ = c;
inp = p;
p = SkipAName(p);
if ( p == 0 ) return(0);
c = *p;
*p = 0;
type = GetName(AC.varnames,inp,&c2,WITHAUTO);
if ( type != CVECTOR && type != CDUBIOUS ) {
MesPrint("&Not a vector in dotproduct in if statement: %s",inp);
error = 1;
}
else type = CDOTPRODUCT;
}
else {
MesPrint("&Illegal use of . after %s in if statement",inp);
if ( type == NAMENOTFOUND )
MesPrint("&%s is not a properly declared variable",inp);
error = 1;
*p++ = c;
while ( *p && *p != ')' && *p != ',' ) p++;
if ( *p == ',' && FG.cTable[p[1]] == 1 ) {
p++;
while ( *p && *p != ')' && *p != ',' ) p++;
}
continue;
}
}
*p = c;
switch ( type ) {
case CSYMBOL:
*w++ = SYMBOL; *w++ = 4; *w++ = c1;
Sgetnum: if ( *p != ',' ) {
MesCerr("sequence",p);
while ( *p && *p != ')' && *p != ',' ) p++;
error = 1;
}
p++; inp = p;
ParseSignedNumber(mini,p)
if ( FG.cTable[p[-1]] != 1 || ( *p && *p != ')' && *p != ',' ) ) {
while ( *p && *p != ')' && *p != ',' ) p++;
error = 1;
c = *p; *p = 0;
MesPrint("&Improper value in count: %s",inp);
*p = c;
while ( *p && *p != ')' && *p != ',' ) p++;
}
*w++ = mini;
break;
case CFUNCTION:
*w++ = FUNCTION; *w++ = 4; *w++ = c1+FUNCTION; goto Sgetnum;
case CDOTPRODUCT:
*w++ = DOTPRODUCT; *w++ = 5;
*w++ = c2 + AM.OffsetVector;
*w++ = c1 + AM.OffsetVector;
goto Sgetnum;
case CVECTOR:
*w++ = VECTOR; *w++ = 5;
*w++ = c1 + AM.OffsetVector;
if ( *p == ',' ) {
*w++ = VECTBIT | DOTPBIT | FUNBIT;
goto Sgetnum;
}
else if ( *p == '+' ) {
p++;
*w = 0;
while ( *p && *p != ',' ) {
if ( *p == 'v' || *p == 'V' ) {
*w |= VECTBIT; p++;
}
else if ( *p == 'd' || *p == 'D' ) {
*w |= DOTPBIT; p++;
}
else if ( *p == 'f' || *p == 'F'
|| *p == 't' || *p == 'T' ) {
*w |= FUNBIT; p++;
}
else if ( *p == '?' ) {
p++; inp = p;
if ( *p == '{' ) { /* } */
SKIPBRA2(p)
if ( p == 0 ) return(0);
if ( ( c1 = DoTempSet(inp+1,p) ) < 0 ) return(0);
if ( Sets[c1].type != CFUNCTION ) {
MesPrint("&set type conflict: Function expected");
return(0);
}
type = CSET;
c = *++p;
}
else {
p = SkipAName(p);
if ( p == 0 ) return(0);
c = *p; *p = 0;
type = GetName(AC.varnames,inp,&c1,WITHAUTO);
}
if ( type != CSET && type != CDUBIOUS ) {
MesPrint("&%s is not a set",inp);
error = 1;
}
w[-2] = 6;
*w++ |= SETBIT;
*w++ = c1;
*p = c;
goto Sgetnum;
}
else {
MesCerr("specifier for vector",p);
error = 1;
}
}
w++;
goto Sgetnum;
}
else {
MesCerr("specifier for vector",p);
while ( *p && *p != ')' && *p != ',' ) p++;
error = 1;
*w++ = VECTBIT | DOTPBIT | FUNBIT;
goto Sgetnum;
}
case CDUBIOUS:
goto skipfield;
default:
*p = 0;
MesPrint("&%s is not a symbol, function, vector or dotproduct",inp);
error = 1;
skipfield: while ( *p && *p != ')' && *p != ',' ) p++;
if ( *p && FG.cTable[p[1]] == 1 ) {
p++;
while ( *p && *p != ')' && *p != ',' ) p++;
}
break;
}
}
else {
MesCerr("name",p);
while ( *p && *p != ',' ) p++;
error = 1;
}
}
to[1] = w-to;
if ( *p == ')' ) p++;
if ( *p ) { MesCerr("end of statement",p); return(0); }
if ( error ) return(0);
return(w);
}
/*
#] CountComp :
#[ CoIf :
Reads the if statement: There must be a pair of parentheses.
Much work is delegated to the routines in compi2 and CountComp.
The goto is kept hanging as it is forward.
The address in which the label must be written is pushed on
the AC.IfStack.
Here we allow statements of the type
if ( condition ) single statement;
compile the if statement.
test character at end
if not ; or )
copy the statement after the proper parenthesis to the
beginning of the AC.iBuffer.
Have it compiled.
generate an endif statement.
*/
static UWORD *CIscratC = 0;
int CoIf(UBYTE *inp)
{
GETIDENTITY
int error = 0, level;
WORD *w, *ww, *u, *s, *OldWork, *OldSpace = AT.WorkSpace;
WORD gotexp = 0; /* Indicates whether there can be a condition */
WORD lenpp, lenlev, ncoef, i, number;
UBYTE *p, *pp, *ppp, c;
CBUF *C = cbuf+AC.cbufnum;
LONG x;
if ( *inp == '(' && inp[1] == ',' ) inp += 2;
else if ( *inp == '(' ) inp++; /* Usually we enter at the bracket */
if ( CIscratC == 0 )
CIscratC = (UWORD *)Malloc1((AM.MaxTal+2)*sizeof(UWORD),"CoIf");
lenpp = 0;
lenlev = 1;
if ( AC.IfLevel >= AC.MaxIf ) DoubleIfBuffers();
AC.IfCount[lenpp++] = 0;
/*
IfStack is used for organizing the 'go to' for the various if levels
*/
*AC.IfStack++ = C->Pointer-C->Buffer+2;
/*
IfSumCheck is used to test for illegal nesting of if, argument or repeat.
*/
AC.IfSumCheck[AC.IfLevel] = NestingChecksum();
AC.IfLevel++;
w = OldWork = AT.WorkPointer;
*w++ = TYPEIF;
w += 2;
p = inp;
for(;;) {
inp = p;
level = 0;
ReDo:
if ( FG.cTable[*p] == 1 ) { /* Number */
if ( gotexp == 1 ) { MesCerr("position for )",p); error = 1; }
u = w;
*w++ = LONGNUMBER;
w += 2;
if ( GetLong(p,(UWORD *)w,&ncoef) ) { ncoef = 1; error = 1; }
w[-1] = ncoef;
while ( FG.cTable[*++p] == 1 );
if ( *p == '/' ) {
p++;
if ( FG.cTable[*p] != 1 ) {
MesCerr("sequence",p); error = 1; goto OnlyNum;
}
if ( GetLong(p,CIscratC,&ncoef) ) {
ncoef = 1; error = 1;
}
while ( FG.cTable[*++p] == 1 );
if ( ncoef == 0 ) {
MesPrint("&Division by zero!");
error = 1;
}
else {
if ( w[-1] != 0 ) {
if ( Simplify(BHEAD (UWORD *)w,(WORD *)(w-1),
CIscratC,&ncoef) ) error = 1;
else {
i = w[-1];
if ( i >= ncoef ) {
i = w[-1];
w += i;
i -= ncoef;
s = (WORD *)CIscratC;
NCOPY(w,s,ncoef);
while ( --i >= 0 ) *w++ = 0;
}
else {
w += i;
i = ncoef - i;
while ( --i >= 0 ) *w++ = 0;
s = (WORD *)CIscratC;
NCOPY(w,s,ncoef);
}
}
}
}
}
else {
OnlyNum:
w += ncoef;
if ( ncoef > 0 ) {
ncoef--; *w++ = 1;
while ( --ncoef >= 0 ) *w++ = 0;
}
}
u[1] = WORDDIF(w,u);
u[2] = (u[1] - 3)/2;
if ( level ) u[2] = -u[2];
gotexp = 1;
}
else if ( *p == '+' ) { p++; goto ReDo; }
else if ( *p == '-' ) { level ^= 1; p++; goto ReDo; }
else if ( *p == 'c' || *p == 'C' ) { /* Count or Coefficient */
if ( gotexp == 1 ) { MesCerr("position for )",p); error = 1; }
while ( FG.cTable[*++p] == 0 );
c = *p; *p = 0;
if ( !StrICmp(inp,(UBYTE *)"count") ) {
*p = c;
if ( c != '(' ) {
MesPrint("&no ( after count");
error = 1;
goto endofif;
}
inp = p;
SKIPBRA4(p);
c = *++p; *p = 0; *inp = ',';
w = CountComp(inp,w);
*p = c; *inp = '(';
if ( w == 0 ) { error = 1; goto endofif; }
gotexp = 1;
}
else if ( ConWord(inp,(UBYTE *)"coefficient") && ( p - inp ) > 3 ) {
*w++ = COEFFI;
*w++ = 2;
*p = c;
gotexp = 1;
}
else goto NoGood;
inp = p;
}
else if ( *p == 'm' || *p == 'M' ) { /* match */
if ( gotexp == 1 ) { MesCerr("position for )",p); error = 1; }
while ( !FG.cTable[*++p] );
c = *p; *p = 0;
if ( !StrICmp(inp,(UBYTE *)"match") ) {
*p = c;
if ( c != '(' ) {
MesPrint("&no ( after match");
error = 1;
goto endofif;
}
p++; inp = p;
SKIPBRA4(p);
*p = '=';
/*
Now we can call the reading of the lhs of an id statement.
This has to be modified in the future.
*/
AT.WorkSpace = AT.WorkPointer = w;
ppp = inp;
while ( FG.cTable[*ppp] == 0 && ppp < p ) ppp++;
if ( *ppp == ',' ) AC.idoption = 0;
else AC.idoption = SUBMULTI;
level = CoIdExpression(inp,TYPEIF);
AT.WorkSpace = OldSpace;
AT.WorkPointer = OldWork;
if ( level != 0 ) {
if ( level < 0 ) { error = -1; goto endofif; }
error = 1;
}
/*
If we pop numlhs we are in good shape
*/
s = u = C->lhs[C->numlhs];
while ( u < C->Pointer ) *w++ = *u++;
C->numlhs--; C->Pointer = s;
*p++ = ')';
inp = p;
gotexp = 1;
}
else if ( !StrICmp(inp,(UBYTE *)"multipleof") ) {
if ( gotexp == 1 ) { MesCerr("position for )",p); error = 1; }
*p = c;
if ( c != '(' ) {
MesPrint("&no ( after multipleof");
error = 1; goto endofif;
}
p++;
if ( FG.cTable[*p] != 1 ) {
Nomulof: MesPrint("&multipleof needs a short positive integer argument");
error = 1; goto endofif;
}
ParseNumber(x,p)
if ( *p != ')' || x <= 0 || x > MAXPOSITIVE ) goto Nomulof;
p++;
*w++ = MULTIPLEOF; *w++ = 3; *w++ = (WORD)x;
inp = p;
gotexp = 1;
}
else {
NoGood: MesPrint("&Unrecognized word: %s",inp);
*p = c;
error = 1;
level = 0;
if ( c == '(' ) SKIPBRA4(p)
inp = ++p;
gotexp = 1;
}
}
else if ( *p == 'f' || *p == 'F' ) { /* FindLoop */
if ( gotexp == 1 ) { MesCerr("position for )",p); error = 1; }
while ( FG.cTable[*++p] == 0 );
c = *p; *p = 0;
if ( !StrICmp(inp,(UBYTE *)"findloop") ) {
*p = c;
if ( c != '(' ) {
MesPrint("&no ( after findloop");
error = 1;
goto endofif;
}
inp = p;
SKIPBRA4(p);
c = *++p; *p = 0; *inp = ',';
if ( CoFindLoop(inp) ) goto endofif;
s = u = C->lhs[C->numlhs];
while ( u < C->Pointer ) *w++ = *u++;
C->numlhs--; C->Pointer = s;
*p = c; *inp = '(';
if ( w == 0 ) { error = 1; goto endofif; }
gotexp = 1;
}
else goto NoGood;
inp = p;
}
else if ( *p == 'e' || *p == 'E' ) { /* Expression */
if ( gotexp == 1 ) { MesCerr("position for )",p); error = 1; }
while ( FG.cTable[*++p] == 0 );
c = *p; *p = 0;
if ( !StrICmp(inp,(UBYTE *)"expression") ) {
*p = c;
if ( c != '(' ) {
MesPrint("&no ( after expression");
error = 1;
goto endofif;
}
p++; ww = w; *w++ = IFEXPRESSION; w++;
while ( *p != ')' ) {
if ( *p == ',' ) { p++; continue; }
if ( *p == '[' || FG.cTable[*p] == 0 ) {
pp = p;
if ( ( p = SkipAName(p) ) == 0 ) {
MesPrint("&Improper name for an expression: '%s'",pp);
error = 1;
goto endofif;
}
c = *p; *p = 0;
if ( GetName(AC.exprnames,pp,&number,NOAUTO) == CEXPRESSION ) {
*w++ = number;
}
else if ( GetName(AC.varnames,pp,&number,NOAUTO) != NAMENOTFOUND ) {
MesPrint("&%s is not an expression",pp);
error = 1;
*w++ = number;
}
*p = c;
}
else {
MesPrint("&Illegal object in Expression in if-statement");
error = 1;
while ( *p && *p != ',' && *p != ')' ) p++;
if ( *p == 0 || *p == ')' ) break;
}
}
ww[1] = w - ww;
p++;
gotexp = 1;
}
else goto NoGood;
inp = p;
}
else if ( *p == 'i' || *p == 'I' ) { /* IsFactorized */
if ( gotexp == 1 ) { MesCerr("position for )",p); error = 1; }
while ( FG.cTable[*++p] == 0 );
c = *p; *p = 0;
if ( !StrICmp(inp,(UBYTE *)"isfactorized") ) {
*p = c;
if ( c != '(' ) { /* No expression means current expression */
ww = w; *w++ = IFISFACTORIZED; w++;
}
else {
p++; ww = w; *w++ = IFISFACTORIZED; w++;
while ( *p != ')' ) {
if ( *p == ',' ) { p++; continue; }
if ( *p == '[' || FG.cTable[*p] == 0 ) {
pp = p;
if ( ( p = SkipAName(p) ) == 0 ) {
MesPrint("&Improper name for an expression: '%s'",pp);
error = 1;
goto endofif;
}
c = *p; *p = 0;
if ( GetName(AC.exprnames,pp,&number,NOAUTO) == CEXPRESSION ) {
*w++ = number;
}
else if ( GetName(AC.varnames,pp,&number,NOAUTO) != NAMENOTFOUND ) {
MesPrint("&%s is not an expression",pp);
error = 1;
*w++ = number;
}
*p = c;
}
else {
MesPrint("&Illegal object in IsFactorized in if-statement");
error = 1;
while ( *p && *p != ',' && *p != ')' ) p++;
if ( *p == 0 || *p == ')' ) break;
}
}
p++;
}
ww[1] = w - ww;
gotexp = 1;
}
else goto NoGood;
inp = p;
}
else if ( *p == 'o' || *p == 'O' ) { /* Occurs */
/*
Tests whether variables occur inside a term.
At the moment this is done one by one.
If we want to do them in groups we should do the reading
a bit different: each as a variable in a term, and then
use Normalize to get the variables grouped and in order.
That way FindVar (in if.c) can work more efficiently.
Still to be done!!!
TASK: Nice little task for someone to learn.
*/
UBYTE cc;
if ( gotexp == 1 ) { MesCerr("position for )",p); error = 1; }
while ( FG.cTable[*++p] == 0 );
c = cc = *p; *p = 0;
if ( !StrICmp(inp,(UBYTE *)"occurs") ) {
WORD c1, c2, type;
*p = cc;
if ( cc != '(' ) {
MesPrint("&no ( after occurs");
error = 1;
goto endofif;
}
inp = p;
SKIPBRA4(p);
cc = *++p; *p = 0; *inp = ','; pp = p;
ww = w;
*w++ = IFOCCURS; *w++ = 0;
while ( *inp ) {
while ( *inp == ',' ) inp++;
if ( *inp == 0 || *inp == ')' ) break;
/*
Now read a list of names
We can have symbols, vectors, dotproducts, indices, functions.
There could also be dummy indices and/or extra symbols.
*/
if ( *inp == '[' || FG.cTable[*inp] == 0 ) {
if ( ( p = SkipAName(inp) ) == 0 ) return(0);
c = *p; *p = 0;
type = GetName(AC.varnames,inp,&c1,WITHAUTO);
if ( c == '.' ) {
if ( type == CVECTOR || type == CDUBIOUS ) {
*p++ = c;
inp = p;
p = SkipAName(p);
if ( p == 0 ) return(0);
c = *p;
*p = 0;
type = GetName(AC.varnames,inp,&c2,WITHAUTO);
if ( type != CVECTOR && type != CDUBIOUS ) {
MesPrint("&Not a vector in dotproduct in if statement: %s",inp);
error = 1;
}
else type = CDOTPRODUCT;
}
else {
MesPrint("&Illegal use of . after %s in if statement",inp);
if ( type == NAMENOTFOUND )
MesPrint("&%s is not a properly declared variable",inp);
error = 1;
*p++ = c;
while ( *p && *p != ')' && *p != ',' ) p++;
if ( *p == ',' && FG.cTable[p[1]] == 1 ) {
p++;
while ( *p && *p != ')' && *p != ',' ) p++;
}
continue;
}
}
*p = c;
switch ( type ) {
case CSYMBOL: /* To worry about extra symbols */
*w++ = SYMBOL;
*w++ = c1;
break;
case CINDEX:
*w++ = INDEX;
*w++ = c1 + AM.OffsetIndex;
break;
case CVECTOR:
*w++ = VECTOR;
*w++ = c1 + AM.OffsetVector;
break;
case CDOTPRODUCT:
*w++ = DOTPRODUCT;
*w++ = c1 + AM.OffsetVector;
*w++ = c2 + AM.OffsetVector;
break;
case CFUNCTION:
*w++ = FUNCTION;
*w++ = c1+FUNCTION;
break;
default:
MesPrint("&Illegal variable %s in occurs condition in if statement",inp);
error = 1;
break;
}
inp = p;
}
else {
MesPrint("&Illegal object %s in occurs condition in if statement",inp);
error = 1;
break;
}
}
ww[1] = w-ww;
p = pp; *p = cc; *inp = '(';
gotexp = 1;
if ( ww[1] <= 2 ) {
MesPrint("&The occurs condition in the if statement needs arguments.");
error = 1;
}
}
else goto NoGood;
inp = p;
}
else if ( *p == '$' ) {
if ( gotexp == 1 ) { MesCerr("position for )",p); error = 1; }
p++; inp = p;
while ( FG.cTable[*p] == 0 || FG.cTable[*p] == 1 ) p++;
c = *p; *p = 0;
if ( ( i = GetDollar(inp) ) < 0 ) {
MesPrint("&undefined dollar expression %s",inp);
error = 1;
i = AddDollar(inp,DOLUNDEFINED,0,0);
}
*p = c;
*w++ = IFDOLLAR; *w++ = 3; *w++ = i;
/*
And then the IFDOLLAREXTRA pieces for [1] [$y] etc
*/
if ( *p == '[' ) {
p++;
if ( ( w = GetIfDollarFactor(&p,w) ) == 0 ) {
error = 1;
goto endofif;
}
else if ( *p != ']' ) {
error = 1;
goto endofif;
}
p++;
}
inp = p;
gotexp = 1;
}
else if ( *p == '(' ) {
if ( gotexp ) {
MesCerr("parenthesis",p);
error = 1;
goto endofif;
}
gotexp = 0;
if ( ++lenlev >= AC.MaxIf ) DoubleIfBuffers();
AC.IfCount[lenpp++] = w-OldWork;
*w++ = SUBEXPR;
w += 2;
p++;
}
else if ( *p == ')' ) {
if ( gotexp == 0 ) { MesCerr("position for )",p); error = 1; }
gotexp = 1;
u = AC.IfCount[--lenpp]+OldWork;
lenlev--;
u[1] = w - u;
if ( lenlev <= 0 ) { /* End if condition */
AT.WorkSpace = OldSpace;
AT.WorkPointer = OldWork;
AddNtoL(OldWork[1],OldWork);
p++;
if ( *p == ')' ) {
MesPrint("&unmatched parenthesis in if/while ()");
error = 1;
while ( *++p == ')' );
}
if ( *p ) {
level = CompileStatement(p);
if ( level ) error = level;
while ( *p ) p++;
if ( CoEndIf(p) && error == 0 ) error = 1;
}
return(error);
}
p++;
}
else if ( *p == '>' ) {
if ( gotexp == 0 ) goto NoExp;
if ( p[1] == '=' ) { *w++ = GREATEREQUAL; *w++ = 2; p += 2; }
else { *w++ = GREATER; *w++ = 2; p++; }
gotexp = 0;
}
else if ( *p == '<' ) {
if ( gotexp == 0 ) goto NoExp;
if ( p[1] == '=' ) { *w++ = LESSEQUAL; *w++ = 2; p += 2; }
else { *w++ = LESS; *w++ = 2; p++; }
gotexp = 0;
}
else if ( *p == '=' ) {
if ( gotexp == 0 ) goto NoExp;
if ( p[1] == '=' ) p++;
*w++ = EQUAL; *w++ = 2; p++;
gotexp = 0;
}
else if ( *p == '!' && p[1] == '=' ) {
if ( gotexp == 0 ) { p++; goto NoExp; }
*w++ = NOTEQUAL; *w++ = 2; p += 2;
gotexp = 0;
}
else if ( *p == '|' && p[1] == '|' ) {
if ( gotexp == 0 ) { p++; goto NoExp; }
*w++ = ORCOND; *w++ = 2; p += 2;
gotexp = 0;
}
else if ( *p == '&' && p[1] == '&' ) {
if ( gotexp == 0 ) {
p++;
NoExp: p++;
MesCerr("sequence",p);
error = 1;
}
else {
*w++ = ANDCOND; *w++ = 2; p += 2;
gotexp = 0;
}
}
else if ( *p == 0 ) {
MesPrint("&Unmatched parentheses");
error = 1;
goto endofif;
}
else {
if ( FG.cTable[*p] == 0 ) {
WORD ij;
inp = p;
while ( ( ij = FG.cTable[*++p] ) == 0 || ij == 1 );
c = *p; *p = 0;
goto NoGood;
}
MesCerr("sequence",p);
error = 1;
p++;
}
}
endofif:;
return(error);
}
/*
#] CoIf :
#[ CoElse :
*/
int CoElse(UBYTE *p)
{
int error = 0;
CBUF *C = cbuf+AC.cbufnum;
if ( *p != 0 ) {
while ( *p == ',' ) p++;
if ( tolower(*p) == 'i' && tolower(p[1]) == 'f' && p[2] == '(' )
return(CoElseIf(p+2));
MesPrint("&No extra text allowed as part of an else statement");
error = 1;
}
if ( AC.IfLevel <= 0 ) { MesPrint("&else statement without if"); return(1); }
if ( AC.IfSumCheck[AC.IfLevel-1] != NestingChecksum() - 1 ) {
MesNesting();
error = 1;
}
Add3Com(TYPEELSE,AC.IfLevel)
C->Buffer[AC.IfStack[-1]] = C->numlhs;
AC.IfStack[-1] = C->Pointer - C->Buffer - 1;
return(error);
}
/*
#] CoElse :
#[ CoElseIf :
*/
int CoElseIf(UBYTE *inp)
{
CBUF *C = cbuf+AC.cbufnum;
if ( AC.IfLevel <= 0 ) { MesPrint("&elseif statement without if"); return(1); }
Add3Com(TYPEELSE,-AC.IfLevel)
AC.IfLevel--;
C->Buffer[*--AC.IfStack] = C->numlhs;
return(CoIf(inp));
}
/*
#] CoElseIf :
#[ CoEndIf :
It puts a RHS-level at the position indicated in the AC.IfStack.
This corresponds to the label belonging to a forward goto.
It is the goto that belongs either to the failing condition
of the if (no else statement), or the completion of the
success path (with else statement)
The code is a jump to the next statement. It is there to prevent
problems with
if ( .. )
if ( .. )
endif;
elseif ( .. )
*/
int CoEndIf(UBYTE *inp)
{
CBUF *C = cbuf+AC.cbufnum;
WORD i = C->numlhs, to, k = -AC.IfLevel;
int error = 0;
while ( *inp == ',' ) inp++;
if ( *inp != 0 ) {
error = 1;
MesPrint("&No extra text allowed as part of an endif/elseif statement");
}
if ( AC.IfLevel <= 0 ) {
MesPrint("&Endif statement without corresponding if"); return(1);
}
AC.IfLevel--;
C->Buffer[*--AC.IfStack] = i+1;
if ( AC.IfSumCheck[AC.IfLevel] != NestingChecksum() ) {
MesNesting();
error = 1;
}
Add3Com(TYPEENDIF,i+1)
/*
Now the search for the TYPEELSE in front of the elseif statements
*/
to = C->numlhs;
while ( i > 0 ) {
if ( C->lhs[i][0] == TYPEELSE && C->lhs[i][2] == to ) to = i;
if ( C->lhs[i][0] == TYPEIF ) {
if ( C->lhs[i][2] == to ) {
i--;
if ( i <= 0 || C->lhs[i][0] != TYPEELSE
|| C->lhs[i][2] != k ) break;
C->lhs[i][2] = C->numlhs;
to = i;
}
}
i--;
}
return(error);
}
/*
#] CoEndIf :
#[ CoWhile :
*/
int CoWhile(UBYTE *inp)
{
CBUF *C = cbuf+AC.cbufnum;
WORD startnum = C->numlhs + 1;
int error;
AC.WhileLevel++;
error = CoIf(inp);
if ( C->numlhs > startnum && C->lhs[startnum][2] == C->numlhs
&& C->lhs[C->numlhs][0] == TYPEENDIF ) {
C->lhs[C->numlhs][2] = startnum-1;
AC.WhileLevel--;
}
else C->lhs[startnum][2] = startnum;
return(error);
}
/*
#] CoWhile :
#[ CoEndWhile :
*/
int CoEndWhile(UBYTE *inp)
{
int error = 0;
WORD i;
CBUF *C = cbuf+AC.cbufnum;
if ( AC.WhileLevel <= 0 ) {
MesPrint("&EndWhile statement without corresponding While"); return(1);
}
AC.WhileLevel--;
i = C->Buffer[AC.IfStack[-1]];
error = CoEndIf(inp);
C->lhs[C->numlhs][2] = i - 1;
return(error);
}
/*
#] CoEndWhile :
#[ DoFindLoop :
Function,arguments=number,loopsize=number,outfun=function,include=index;
*/
static char *messfind[] = {
"Findloop(function,arguments=#,loopsize(=#|<#)[,include=index])"
,"Replaceloop,function,arguments=#,loopsize(=#|<#),outfun=function[,include=index]"
};
static WORD comfindloop[7] = { TYPEFINDLOOP,7,0,0,0,0,0 };
int DoFindLoop(UBYTE *inp, int mode)
{
UBYTE *s, c;
WORD funnum, nargs = 0, nloop = 0, indexnum = 0, outfun = 0;
int type, aflag, lflag, indflag, outflag, error = 0, sym;
while ( *inp == ',' ) inp++;
if ( ( s = SkipAName(inp) ) == 0 ) {
syntax: MesPrint("&Proper syntax is:");
MesPrint("%s",messfind[mode]);
return(1);
}
c = *s; *s = 0;
if ( ( ( type = GetName(AC.varnames,inp,&funnum,WITHAUTO) ) == NAMENOTFOUND )
|| type != CFUNCTION || ( ( sym = (functions[funnum].symmetric) & ~REVERSEORDER )
!= SYMMETRIC && sym != ANTISYMMETRIC ) ) {
MesPrint("&%s should be a (anti)symmetric function or tensor",inp);
}
funnum += FUNCTION;
*s = c; inp = s;
aflag = lflag = indflag = outflag = 0;
while ( *inp == ',' ) {
while ( *inp == ',' ) inp++;
s = inp;
if ( ( s = SkipAName(inp) ) == 0 ) goto syntax;
c = *s; *s = 0;
if ( StrICont(inp,(UBYTE *)"arguments") == 0 ) {
if ( c != '=' ) goto syntax;
*s++ = c;
NeedNumber(nargs,s,syntax)
aflag++;
inp = s;
}
else if ( StrICont(inp,(UBYTE *)"loopsize") == 0 ) {
if ( c != '=' && c != '<' ) goto syntax;
*s++ = c;
if ( FG.cTable[*s] == 1 ) {
NeedNumber(nloop,s,syntax)
if ( nloop < 2 ) {
MesPrint("&loopsize should be at least 2");
error = 1;
}
if ( c == '<' ) nloop = -nloop;
}
else if ( tolower(*s) == 'a' && tolower(s[1]) == 'l'
&& tolower(s[2]) == 'l' && FG.cTable[s[3]] > 1 ) {
nloop = -1; s += 3;
if ( c != '=' ) goto syntax;
}
inp = s;
lflag++;
}
else if ( StrICont(inp,(UBYTE *)"include") == 0 ) {
if ( c != '=' ) goto syntax;
*s++ = c;
if ( ( inp = SkipAName(s) ) == 0 ) goto syntax;
c = *inp; *inp = 0;
if ( ( type = GetName(AC.varnames,s,&indexnum,WITHAUTO) ) != CINDEX ) {
MesPrint("&%s is not a proper index",s);
error = 1;
}
else if ( indexnum < WILDOFFSET
&& indices[indexnum].dimension == 0 ) {
MesPrint("&%s should be a summable index",s);
error = 1;
}
indexnum += AM.OffsetIndex;
*inp = c;
indflag++;
}
else if ( StrICont(inp,(UBYTE *)"outfun") == 0 ) {
if ( c != '=' ) goto syntax;
*s++ = c;
if ( ( inp = SkipAName(s) ) == 0 ) goto syntax;
c = *inp; *inp = 0;
if ( ( type = GetName(AC.varnames,s,&outfun,WITHAUTO) ) != CFUNCTION ) {
MesPrint("&%s is not a proper function or tensor",s);
error = 1;
}
outfun += FUNCTION;
outflag++;
*inp = c;
}
else {
MesPrint("&Unrecognized option in FindLoop or ReplaceLoop: %s",inp);
*s = c; inp = s;
while ( *inp && *inp != ',' ) inp++;
}
}
if ( *inp != 0 && mode == REPLACELOOP ) goto syntax;
if ( mode == FINDLOOP && outflag > 0 ) {
MesPrint("&outflag option is illegal in FindLoop");
error = 1;
}
if ( mode == REPLACELOOP && outflag == 0 ) goto syntax;
if ( aflag == 0 || lflag == 0 ) goto syntax;
comfindloop[3] = funnum;
comfindloop[4] = nloop;
comfindloop[5] = nargs;
comfindloop[6] = outfun;
comfindloop[1] = 7;
if ( indflag ) {
if ( mode == 0 ) comfindloop[2] = indexnum + 5;
else comfindloop[2] = -indexnum - 5;
}
else comfindloop[2] = mode;
AddNtoL(comfindloop[1],comfindloop);
return(error);
}
/*
#] DoFindLoop :
#[ CoFindLoop :
*/
int CoFindLoop(UBYTE *inp)
{ return(DoFindLoop(inp,FINDLOOP)); }
/*
#] CoFindLoop :
#[ CoReplaceLoop :
*/
int CoReplaceLoop(UBYTE *inp)
{ return(DoFindLoop(inp,REPLACELOOP)); }
/*
#] CoReplaceLoop :
#[ CoFunPowers :
*/
static UBYTE *FunPowOptions[] = {
(UBYTE *)"nofunpowers"
,(UBYTE *)"commutingonly"
,(UBYTE *)"allfunpowers"
};
int CoFunPowers(UBYTE *inp)
{
UBYTE *option, c;
int i, maxoptions = sizeof(FunPowOptions)/sizeof(UBYTE *);
while ( *inp == ',' ) inp++;
option = inp;
inp = SkipAName(inp); c = *inp; *inp = 0;
for ( i = 0; i < maxoptions; i++ ) {
if ( StrICont(option,FunPowOptions[i]) == 0 ) {
if ( c ) {
*inp = c;
MesPrint("&Illegal FunPowers statement");
return(1);
}
AC.funpowers = i;
return(0);
}
}
MesPrint("&Illegal option in FunPowers statement: %s",option);
return(1);
}
/*
#] CoFunPowers :
#[ CoUnitTrace :
*/
int CoUnitTrace(UBYTE *s)
{
WORD num;
if ( FG.cTable[*s] == 1 ) {
ParseNumber(num,s)
if ( *s != 0 ) {
nogood: MesPrint("&Value of UnitTrace should be a (positive) number or a symbol");
return(1);
}
AC.lUniTrace[0] = SNUMBER;
AC.lUniTrace[2] = num;
}
else {
if ( GetName(AC.varnames,s,&num,WITHAUTO) == CSYMBOL ) {
AC.lUniTrace[0] = SYMBOL;
AC.lUniTrace[2] = num;
num = -num;
}
else goto nogood;
s = SkipAName(s);
if ( *s ) goto nogood;
}
AC.lUnitTrace = num;
return(0);
}
/*
#] CoUnitTrace :
#[ CoTerm :
Note: termstack holds the offset of the term statement in the compiler
buffer. termsortstack holds the offset of the last sort statement
(or the corresponding term statement)
*/
int CoTerm(UBYTE *s)
{
GETIDENTITY
WORD *w = AT.WorkPointer;
int error = 0;
while ( *s == ',' ) s++;
if ( *s ) {
MesPrint("&Illegal syntax for Term statement");
return(1);
}
if ( AC.termlevel+1 >= AC.maxtermlevel ) {
if ( AC.maxtermlevel <= 0 ) {
AC.maxtermlevel = 20;
AC.termstack = (LONG *)Malloc1(AC.maxtermlevel*sizeof(LONG),"termstack");
AC.termsortstack = (LONG *)Malloc1(AC.maxtermlevel*sizeof(LONG),"termsortstack");
AC.termsumcheck = (WORD *)Malloc1(AC.maxtermlevel*sizeof(WORD),"termsumcheck");
}
else {
DoubleBuffer((void **)AC.termstack,(void **)AC.termstack+AC.maxtermlevel,
sizeof(LONG),"doubling termstack");
DoubleBuffer((void **)AC.termsortstack,
(void **)AC.termsortstack+AC.maxtermlevel,
sizeof(LONG),"doubling termsortstack");
DoubleBuffer((void **)AC.termsumcheck,
(void **)AC.termsumcheck+AC.maxtermlevel,
sizeof(LONG),"doubling termsumcheck");
AC.maxtermlevel *= 2;
}
}
AC.termsumcheck[AC.termlevel] = NestingChecksum();
AC.termstack[AC.termlevel] = cbuf[AC.cbufnum].Pointer
- cbuf[AC.cbufnum].Buffer + 2;
AC.termsortstack[AC.termlevel] = AC.termstack[AC.termlevel] + 1;
AC.termlevel++;
*w++ = TYPETERM;
w++;
*w++ = cbuf[AC.cbufnum].numlhs;
*w++ = cbuf[AC.cbufnum].numlhs;
AT.WorkPointer[1] = w - AT.WorkPointer;
AddNtoL(AT.WorkPointer[1],AT.WorkPointer);
return(error);
}
/*
#] CoTerm :
#[ CoEndTerm :
*/
int CoEndTerm(UBYTE *s)
{
CBUF *C = cbuf+AC.cbufnum;
while ( *s == ',' ) s++;
if ( *s ) {
MesPrint("&Illegal syntax for EndTerm statement");
return(1);
}
if ( AC.termlevel <= 0 ) {
MesPrint("&EndTerm without corresponding Argument statement");
return(1);
}
AC.termlevel--;
cbuf[AC.cbufnum].Buffer[AC.termstack[AC.termlevel]] = C->numlhs;
cbuf[AC.cbufnum].Buffer[AC.termsortstack[AC.termlevel]] = C->numlhs;
if ( AC.termsumcheck[AC.termlevel] != NestingChecksum() ) {
MesNesting();
return(1);
}
return(0);
}
/*
#] CoEndTerm :
#[ CoSort :
*/
int CoSort(UBYTE *s)
{
GETIDENTITY
WORD *w = AT.WorkPointer;
int error = 0;
while ( *s == ',' ) s++;
if ( *s ) {
MesPrint("&Illegal syntax for Sort statement");
error = 1;
}
if ( AC.termlevel <= 0 ) {
MesPrint("&The Sort statement can only be used inside a term environment");
error = 1;
}
if ( error ) return(error);
*w++ = TYPESORT;
w++;
w++;
cbuf[AC.cbufnum].Buffer[AC.termsortstack[AC.termlevel-1]] =
*w = cbuf[AC.cbufnum].numlhs+1;
w++;
AC.termsortstack[AC.termlevel-1] = cbuf[AC.cbufnum].Pointer
- cbuf[AC.cbufnum].Buffer + 3;
if ( AC.termsumcheck[AC.termlevel-1] != NestingChecksum() - 1 ) {
MesNesting();
return(1);
}
AT.WorkPointer[1] = w - AT.WorkPointer;
AddNtoL(AT.WorkPointer[1],AT.WorkPointer);
return(error);
}
/*
#] CoSort :
#[ CoPolyFun :
Collect,functionname
*/
int CoPolyFun(UBYTE *s)
{
GETIDENTITY
WORD numfun;
int type;
UBYTE *t;
AR.PolyFun = AC.lPolyFun = 0;
AR.PolyFunInv = AC.lPolyFunInv = 0;
AR.PolyFunType = AC.lPolyFunType = 0;
AR.PolyFunExp = AC.lPolyFunExp = 0;
AR.PolyFunVar = AC.lPolyFunVar = 0;
AR.PolyFunPow = AC.lPolyFunPow = 0;
if ( *s == 0 ) { return(0); }
t = SkipAName(s);
if ( t == 0 || *t != 0 ) {
MesPrint("&PolyFun statement needs a single commuting function for its argument");
return(1);
}
if ( ( ( type = GetName(AC.varnames,s,&numfun,WITHAUTO) ) != CFUNCTION )
|| ( functions[numfun].spec != 0 ) || ( functions[numfun].commute != 0 ) ) {
MesPrint("&%s should be a regular commuting function",s);
if ( type < 0 ) {
if ( GetName(AC.exprnames,s,&numfun,NOAUTO) == NAMENOTFOUND )
AddFunction(s,0,0,0,0,0,-1,-1);
}
return(1);
}
AR.PolyFun = AC.lPolyFun = numfun+FUNCTION;
AR.PolyFunType = AC.lPolyFunType = 1;
return(0);
}
/*
#] CoPolyFun :
#[ CoPolyRatFun :
PolyRatFun [,functionname[,functionname](option)]
*/
int CoPolyRatFun(UBYTE *s)
{
GETIDENTITY
WORD numfun;
int type;
UBYTE *t, c;
AR.PolyFun = AC.lPolyFun = 0;
AR.PolyFunInv = AC.lPolyFunInv = 0;
AR.PolyFunType = AC.lPolyFunType = 0;
AR.PolyFunExp = AC.lPolyFunExp = 0;
AR.PolyFunVar = AC.lPolyFunVar = 0;
AR.PolyFunPow = AC.lPolyFunPow = 0;
if ( *s == 0 ) return(0);
t = SkipAName(s);
if ( t == 0 ) goto NumErr;
c = *t; *t = 0;
if ( ( ( type = GetName(AC.varnames,s,&numfun,WITHAUTO) ) != CFUNCTION )
|| ( functions[numfun].spec != 0 ) || ( functions[numfun].commute != 0 ) ) {
MesPrint("&%s should be a regular commuting function",s);
if ( type < 0 ) {
if ( GetName(AC.exprnames,s,&numfun,NOAUTO) == NAMENOTFOUND )
AddFunction(s,0,0,0,0,0,-1,-1);
}
return(1);
}
AR.PolyFun = AC.lPolyFun = numfun+FUNCTION;
AR.PolyFunInv = AC.lPolyFunInv = 0;
AR.PolyFunType = AC.lPolyFunType = 2;
AC.PolyRatFunChanged = 1;
if ( c == 0 ) return(0);
*t = c;
if ( *t == '-' ) { AC.PolyRatFunChanged = 0; t++; }
while ( *t == ',' || *t == ' ' || *t == '\t' ) t++;
if ( *t == 0 ) return(0);
if ( *t != '(' ) {
s = t;
t = SkipAName(s);
if ( t == 0 ) goto NumErr;
c = *t; *t = 0;
if ( ( ( type = GetName(AC.varnames,s,&numfun,WITHAUTO) ) != CFUNCTION )
|| ( functions[numfun].spec != 0 ) || ( functions[numfun].commute != 0 ) ) {
MesPrint("&%s should be a regular commuting function",s);
if ( type < 0 ) {
if ( GetName(AC.exprnames,s,&numfun,NOAUTO) == NAMENOTFOUND )
AddFunction(s,0,0,0,0,0,-1,-1);
}
return(1);
}
AR.PolyFunInv = AC.lPolyFunInv = numfun+FUNCTION;
if ( c == 0 ) return(0);
*t = c;
if ( *t == '-' ) { AC.PolyRatFunChanged = 0; t++; }
while ( *t == ',' || *t == ' ' || *t == '\t' ) t++;
if ( *t == 0 ) return(0);
}
if ( *t == '(' ) {
t++;
while ( *t == ',' || *t == ' ' || *t == '\t' ) t++;
/*
Next we need a keyword like
(divergence,ep)
(expand,ep,maxpow)
*/
s = t;
t = SkipAName(s);
if ( t == 0 ) goto NumErr;
c = *t; *t = 0;
if ( ( StrICmp(s,(UBYTE *)"divergence") == 0 )
|| ( StrICmp(s,(UBYTE *)"finddivergence") == 0 ) ) {
if ( c != ',' ) {
MesPrint("&Illegal option field in PolyRatFun statement.");
return(1);
}
*t = c;
while ( *t == ',' || *t == ' ' || *t == '\t' ) t++;
s = t;
t = SkipAName(s);
if ( t == 0 ) goto NumErr;
c = *t; *t = 0;
if ( ( type = GetName(AC.varnames,s,&AC.lPolyFunVar,WITHAUTO) ) != CSYMBOL ) {
MesPrint("&Illegal symbol %s in option field in PolyRatFun statement.",s);
return(1);
}
*t = c;
while ( *t == ',' || *t == ' ' || *t == '\t' ) t++;
if ( *t != ')' ) {
MesPrint("&Illegal termination of option in PolyRatFun statement.");
return(1);
}
AR.PolyFunExp = AC.lPolyFunExp = 1;
AR.PolyFunVar = AC.lPolyFunVar;
symbols[AC.lPolyFunVar].minpower = -MAXPOWER;
symbols[AC.lPolyFunVar].maxpower = MAXPOWER;
}
else if ( StrICmp(s,(UBYTE *)"expand") == 0 ) {
WORD x = 0, etype = 2;
if ( c != ',' ) {
MesPrint("&Illegal option field in PolyRatFun statement.");
return(1);
}
*t = c;
while ( *t == ',' || *t == ' ' || *t == '\t' ) t++;
s = t;
t = SkipAName(s);
if ( t == 0 ) goto NumErr;
c = *t; *t = 0;
if ( ( type = GetName(AC.varnames,s,&AC.lPolyFunVar,WITHAUTO) ) != CSYMBOL ) {
MesPrint("&Illegal symbol %s in option field in PolyRatFun statement.",s);
return(1);
}
*t = c;
while ( *t == ',' || *t == ' ' || *t == '\t' ) t++;
if ( *t > '9' || *t < '0' ) {
MesPrint("&Illegal option field in PolyRatFun statement.");
return(1);
}
while ( *t <= '9' && *t >= '0' ) x = 10*x + *t++ - '0';
while ( *t == ',' || *t == ' ' || *t == '\t' ) t++;
if ( *t != ')' ) {
s = t;
t = SkipAName(s);
if ( t == 0 ) goto ParErr;
c = *t; *t = 0;
if ( StrICmp(s,(UBYTE *)"fixed") == 0 ) {
etype = 3;
}
else if ( StrICmp(s,(UBYTE *)"relative") == 0 ) {
etype = 2;
}
else {
MesPrint("&Illegal termination of option in PolyRatFun statement.");
return(1);
}
*t = c;
while ( *t == ',' || *t == ' ' || *t == '\t' ) t++;
if ( *t != ')' ) {
MesPrint("&Illegal termination of option in PolyRatFun statement.");
return(1);
}
}
AR.PolyFunExp = AC.lPolyFunExp = etype;
AR.PolyFunVar = AC.lPolyFunVar;
AR.PolyFunPow = AC.lPolyFunPow = x;
symbols[AC.lPolyFunVar].minpower = -MAXPOWER;
symbols[AC.lPolyFunVar].maxpower = MAXPOWER;
}
else {
ParErr: MesPrint("&Illegal option %s in PolyRatFun statement.",s);
return(1);
}
t++;
while ( *t == ',' || *t == ' ' || *t == '\t' ) t++;
if ( *t == 0 ) return(0);
}
NumErr:;
MesPrint("&PolyRatFun statement needs one or two commuting function(s) for its argument(s)");
return(1);
}
/*
#] CoPolyRatFun :
#[ CoMerge :
*/
int CoMerge(UBYTE *inp)
{
UBYTE *s = inp;
int type;
WORD numfunc, option = 0;
if ( tolower(s[0]) == 'o' && tolower(s[1]) == 'n' && tolower(s[2]) == 'c' &&
tolower(s[3]) == 'e' && tolower(s[4]) == ',' ) {
option = 1; s += 5;
}
else if ( tolower(s[0]) == 'a' && tolower(s[1]) == 'l' && tolower(s[2]) == 'l' &&
tolower(s[3]) == ',' ) {
option = 0; s += 4;
}
if ( *s == '$' ) {
if ( ( type = GetName(AC.dollarnames,s+1,&numfunc,NOAUTO) ) == CDOLLAR )
numfunc = -numfunc;
else {
MesPrint("&%s is undefined",s);
numfunc = AddDollar(s+1,DOLINDEX,&one,1);
return(1);
}
tests: s = SkipAName(s);
if ( *s != 0 ) {
MesPrint("&Merge/shuffle should have a single function or $variable for its argument");
return(1);
}
}
else if ( ( type = GetName(AC.varnames,s,&numfunc,WITHAUTO) ) == CFUNCTION ) {
numfunc += FUNCTION;
goto tests;
}
else if ( type != -1 ) {
if ( type != CDUBIOUS ) {
NameConflict(type,s);
type = MakeDubious(AC.varnames,s,&numfunc);
}
return(1);
}
else {
MesPrint("&%s is not a function",s);
numfunc = AddFunction(s,0,0,0,0,0,-1,-1) + FUNCTION;
return(1);
}
Add4Com(TYPEMERGE,numfunc,option);
return(0);
}
/*
#] CoMerge :
#[ CoStuffle :
Important for future options: The bit, given by 256 (bit 8) is reserved
internally for keeping track of the sign in the number of Stuffle
additions.
*/
int CoStuffle(UBYTE *inp)
{
UBYTE *s = inp, *ss, c;
int type;
WORD numfunc, option = 0;
if ( tolower(s[0]) == 'o' && tolower(s[1]) == 'n' && tolower(s[2]) == 'c' &&
tolower(s[3]) == 'e' && tolower(s[4]) == ',' ) {
option = 1; s += 5;
}
else if ( tolower(s[0]) == 'a' && tolower(s[1]) == 'l' && tolower(s[2]) == 'l' &&
tolower(s[3]) == ',' ) {
option = 0; s += 4;
}
ss = SkipAName(s);
c = *ss; *ss = 0;
if ( *s == '$' ) {
if ( ( type = GetName(AC.dollarnames,s+1,&numfunc,NOAUTO) ) == CDOLLAR )
numfunc = -numfunc;
else {
MesPrint("&%s is undefined",s);
numfunc = AddDollar(s+1,DOLINDEX,&one,1);
return(1);
}
tests: *ss = c;
if ( *ss != '+' && *ss != '-' && ss[1] != 0 ) {
MesPrint("&Stuffle should have a single function or $variable for its argument, followed by either + or -");
return(1);
}
if ( *ss == '-' ) option += 2;
}
else if ( ( type = GetName(AC.varnames,s,&numfunc,WITHAUTO) ) == CFUNCTION ) {
numfunc += FUNCTION;
goto tests;
}
else if ( type != -1 ) {
if ( type != CDUBIOUS ) {
NameConflict(type,s);
type = MakeDubious(AC.varnames,s,&numfunc);
}
return(1);
}
else {
MesPrint("&%s is not a function",s);
numfunc = AddFunction(s,0,0,0,0,0,-1,-1) + FUNCTION;
return(1);
}
Add4Com(TYPESTUFFLE,numfunc,option);
return(0);
}
/*
#] CoStuffle :
#[ CoProcessBucket :
*/
int CoProcessBucket(UBYTE *s)
{
LONG x;
while ( *s == ',' || *s == '=' ) s++;
ParseNumber(x,s)
if ( *s && *s != ' ' && *s != '\t' ) {
MesPrint("&Numerical value expected for ProcessBucketSize");
return(1);
}
AC.ProcessBucketSize = x;
return(0);
}
/*
#] CoProcessBucket :
#[ CoThreadBucket :
*/
int CoThreadBucket(UBYTE *s)
{
LONG x;
while ( *s == ',' || *s == '=' ) s++;
ParseNumber(x,s)
if ( *s && *s != ' ' && *s != '\t' ) {
MesPrint("&Numerical value expected for ThreadBucketSize");
return(1);
}
if ( x <= 0 ) {
Warning("Negative of zero value not allowed for ThreadBucketSize. Adjusted to 1.");
x = 1;
}
AC.ThreadBucketSize = x;
#ifdef WITHPTHREADS
if ( AS.MultiThreaded ) MakeThreadBuckets(-1,1);
#endif
return(0);
}
/*
#] CoThreadBucket :
#[ DoArgPlode :
Syntax: a list of functions.
If the functions have an argument it must be a function.
In the case f(g) we treat f(g(...)) with g any argument.
(not yet implemented)
*/
int DoArgPlode(UBYTE *s, int par)
{
GETIDENTITY
WORD numfunc, type, error = 0, *w, n;
UBYTE *t,c;
int i;
w = AT.WorkPointer;
*w++ = par;
w++;
while ( *s == ',' ) s++;
while ( *s ) {
if ( *s == '$' ) {
MesPrint("&We don't do dollar variables yet in ArgImplode/ArgExplode");
return(1);
}
t = s;
if ( ( s = SkipAName(s) ) == 0 ) return(1);
c = *s; *s = 0;
if ( ( type = GetName(AC.varnames,t,&numfunc,WITHAUTO) ) == CFUNCTION ) {
numfunc += FUNCTION;
}
else if ( type != -1 ) {
if ( type != CDUBIOUS ) {
NameConflict(type,t);
type = MakeDubious(AC.varnames,t,&numfunc);
}
error = 1;
}
else {
MesPrint("&%s is not a function",t);
numfunc = AddFunction(s,0,0,0,0,0,-1,-1) + FUNCTION;
return(1);
}
*s = c;
*w++ = numfunc;
*w++ = FUNHEAD;
#if FUNHEAD > 2
for ( i = 2; i < FUNHEAD; i++ ) *w++ = 0;
#endif
if ( *s && *s != ',' ) {
MesPrint("&Illegal character in ArgImplode/ArgExplode statement: %s",s);
return(1);
}
while ( *s == ',' ) s++;
}
n = w - AT.WorkPointer;
AT.WorkPointer[1] = n;
AddNtoL(n,AT.WorkPointer);
return(error);
}
/*
#] DoArgPlode :
#[ CoArgExplode :
*/
int CoArgExplode(UBYTE *s) { return(DoArgPlode(s,TYPEARGEXPLODE)); }
/*
#] CoArgExplode :
#[ CoArgImplode :
*/
int CoArgImplode(UBYTE *s) { return(DoArgPlode(s,TYPEARGIMPLODE)); }
/*
#] CoArgImplode :
#[ CoClearTable :
*/
int CoClearTable(UBYTE *s)
{
UBYTE c, *t;
int j, type, error = 0;
WORD numfun;
TABLES T, TT;
if ( *s == 0 ) {
MesPrint("&The ClearTable statement needs at least one (table) argument.");
return(1);
}
while ( *s ) {
t = s;
s = SkipAName(s);
c = *s; *s = 0;
if ( ( ( type = GetName(AC.varnames,t,&numfun,WITHAUTO) ) != CFUNCTION )
&& type != CDUBIOUS ) {
nofunc: MesPrint("&%s is not a table",t);
error = 4;
if ( type < 0 ) numfun = AddFunction(t,0,0,0,0,0,-1,-1);
*s = c;
if ( *s == ',' ) s++;
continue;
}
/*
else if ( ( ( T = functions[numfun].tabl ) == 0 )
|| ( T->sparse == 0 ) ) goto nofunc;
*/
else if ( ( T = functions[numfun].tabl ) == 0 ) goto nofunc;
numfun += FUNCTION;
*s = c;
if ( *s == ',' ) s++;
/*
Now we clear the table.
*/
if ( T->sparse ) {
if ( T->boomlijst ) M_free(T->boomlijst,"TableTree");
for (j = 0; j < T->buffersfill; j++ ) { /* was <= */
finishcbuf(T->buffers[j]);
}
if ( T->buffers ) M_free(T->buffers,"Table buffers");
finishcbuf(T->bufnum);
T->boomlijst = 0;
T->numtree = 0; T->rootnum = 0; T->MaxTreeSize = 0;
T->boomlijst = 0;
T->bufnum = inicbufs();
T->bufferssize = 8;
T->buffers = (WORD *)Malloc1(sizeof(WORD)*T->bufferssize,"Table buffers");
T->buffersfill = 0;
T->buffers[T->buffersfill++] = T->bufnum;
T->totind = 0; /* At the moment there are this many */
T->reserved = 0;
ClearTableTree(T);
if ( T->spare ) {
if ( T->tablepointers ) M_free(T->tablepointers,"tablepointers");
T->tablepointers = 0;
TT = T->spare;
if ( TT->tablepointers ) M_free(TT->tablepointers,"tablepointers");
for (j = 0; j < TT->buffersfill; j++ ) {
finishcbuf(TT->buffers[j]);
}
if ( TT->boomlijst ) M_free(TT->boomlijst,"TableTree");
if ( TT->buffers )M_free(TT->buffers,"Table buffers");
if ( TT->mm ) M_free(TT->mm,"tableminmax");
if ( TT->flags ) M_free(TT->flags,"tableflags");
M_free(TT,"table");
SpareTable(T);
}
}
else EmptyTable(T);
}
return(error);
}
/*
#] CoClearTable :
#[ CoDenominators :
*/
int CoDenominators(UBYTE *s)
{
WORD numfun;
int type;
UBYTE *t = SkipAName(s), *t1;
if ( t == 0 ) goto syntaxerror;
t1 = t; while ( *t1 == ',' || *t1 == ' ' || *t1 == '\t' ) t1++;
if ( *t1 ) goto syntaxerror;
*t = 0;
if ( ( ( type = GetName(AC.varnames,s,&numfun,WITHAUTO) ) != CFUNCTION )
|| ( functions[numfun].spec != 0 ) ) {
if ( type < 0 ) {
if ( GetName(AC.exprnames,s,&numfun,NOAUTO) == NAMENOTFOUND )
AddFunction(s,0,0,0,0,0,-1,-1);
}
goto syntaxerror;
}
Add3Com(TYPEDENOMINATORS,numfun+FUNCTION);
return(0);
syntaxerror:
MesPrint("&Denominators statement needs one regular function for its argument");
return(1);
}
/*
#] CoDenominators :
#[ CoDropCoefficient :
*/
int CoDropCoefficient(UBYTE *s)
{
if ( *s == 0 ) {
Add2Com(TYPEDROPCOEFFICIENT)
return(0);
}
MesPrint("&Illegal argument in DropCoefficient statement: '%s'",s);
return(1);
}
/*
#] CoDropCoefficient :
#[ CoDropSymbols :
*/
int CoDropSymbols(UBYTE *s)
{
if ( *s == 0 ) {
Add2Com(TYPEDROPSYMBOLS)
return(0);
}
MesPrint("&Illegal argument in DropSymbols statement: '%s'",s);
return(1);
}
/*
#] CoDropSymbols :
#[ CoToPolynomial :
Converts the current term as much as possible to symbols.
Keeps a list of all objects converted to symbols in AM.sbufnum.
Note that this cannot be executed in parallel because we have only
a single compiler buffer for this. Hence we switch on the noparallel
module option.
Option(s):
OnlyFunctions [,name1][,name2][,...,namem];
*/
int CoToPolynomial(UBYTE *inp)
{
int error = 0;
while ( *inp == ' ' || *inp == ',' || *inp == '\t' ) inp++;
if ( ( AC.topolynomialflag & ~TOPOLYNOMIALFLAG ) != 0 ) {
MesPrint("&ToPolynomial statement and FactArg statement are not allowed in the same module");
return(1);
}
if ( AO.OptimizeResult.code != NULL ) {
MesPrint("&Using ToPolynomial statement when there are still optimization results active.");
MesPrint("&Please use #ClearOptimize instruction first.");
MesPrint("&This will loose the optimized expression.");
return(1);
}
if ( *inp == 0 ) {
Add3Com(TYPETOPOLYNOMIAL,DOALL)
}
else {
int numargs = 0;
WORD *funnums = 0, type, num;
UBYTE *s, c;
s = SkipAName(inp);
if ( s == 0 ) return(1);
c = *s; *s = 0;
if ( StrICmp(inp,(UBYTE *)"onlyfunctions") ) {
MesPrint("&Illegal option %s in ToPolynomial statement",inp);
*s = c;
return(1);
}
*s = c;
inp = s;
while ( *inp == ' ' || *inp == ',' || *inp == '\t' ) inp++;
s = inp;
while ( *s ) s++;
/*
Get definitely enough space for the numbers of the functions
*/
funnums = (WORD *)Malloc1(((LONG)(s-inp)+3)*sizeof(WORD),"ToPlynomial");
while ( *inp ) {
s = SkipAName(inp);
if ( s == 0 ) return(1);
c = *s; *s = 0;
type = GetName(AC.varnames,inp,&num,WITHAUTO);
if ( type != CFUNCTION ) {
MesPrint("&%s is not a function in ToPolynomial statement",inp);
error = 1;
}
funnums[3+numargs++] = num+FUNCTION;
*s = c;
inp = s;
while ( *inp == ' ' || *inp == ',' || *inp == '\t' ) inp++;
}
funnums[0] = TYPETOPOLYNOMIAL;
funnums[1] = numargs+3;
funnums[2] = ONLYFUNCTIONS;
AddNtoL(numargs+3,funnums);
if ( funnums ) M_free(funnums,"ToPolynomial");
}
AC.topolynomialflag |= TOPOLYNOMIALFLAG;
#ifdef WITHMPI
/* In ParFORM, ToPolynomial has to be executed on the master. */
AC.mparallelflag |= NOPARALLEL_CONVPOLY;
#endif
return(error);
}
/*
#] CoToPolynomial :
#[ CoFromPolynomial :
Converts the current term as much as possible back from extra symbols
to their original values. Does not look inside functions.
*/
int CoFromPolynomial(UBYTE *inp)
{
while ( *inp == ' ' || *inp == ',' || *inp == '\t' ) inp++;
if ( *inp == 0 ) {
if ( AO.OptimizeResult.code != NULL ) {
MesPrint("&Using FromPolynomial statement when there are still optimization results active.");
MesPrint("&Please use #ClearOptimize instruction first.");
MesPrint("&This will loose the optimized expression.");
return(1);
}
Add2Com(TYPEFROMPOLYNOMIAL)
return(0);
}
MesPrint("&Illegal argument in FromPolynomial statement: '%s'",inp);
return(1);
}
/*
#] CoFromPolynomial :
#[ CoArgToExtraSymbol :
Converts the specified function arguments into extra symbols.
Syntax: ArgToExtraSymbol [ToNumber] []
*/
int CoArgToExtraSymbol(UBYTE *s)
{
CBUF *C = cbuf + AC.cbufnum;
WORD *lhs;
/* TODO: resolve interference with rational arithmetic. (#138) */
if ( ( AC.topolynomialflag & ~TOPOLYNOMIALFLAG ) != 0 ) {
MesPrint("&ArgToExtraSymbol statement and FactArg statement are not allowed in the same module");
return(1);
}
if ( AO.OptimizeResult.code != NULL ) {
MesPrint("&Using ArgToExtraSymbol statement when there are still optimization results active.");
MesPrint("&Please use #ClearOptimize instruction first.");
MesPrint("&This will loose the optimized expression.");
return(1);
}
SkipSpaces(&s);
int tonumber = ConsumeOption(&s, "tonumber");
int ret = DoArgument(s,TYPEARGTOEXTRASYMBOL);
if ( ret ) return(ret);
/*
* The "scale" parameter is unused. Instead, we put the "tonumber"
* parameter.
*/
lhs = C->lhs[C->numlhs];
if ( lhs[4] != 1 ) {
Warning("scale parameter (^n) is ignored in ArgToExtraSymbol");
}
lhs[4] = tonumber;
AC.topolynomialflag |= TOPOLYNOMIALFLAG; /* This flag is also used in ParFORM. */
#ifdef WITHMPI
/*
* In ParFORM, the conversion to extra symbols has to be performed on
* the master.
*/
AC.mparallelflag |= NOPARALLEL_CONVPOLY;
#endif
return(0);
}
/*
#] CoArgToExtraSymbol :
#[ CoExtraSymbols :
*/
int CoExtraSymbols(UBYTE *inp)
{
UBYTE *arg1, *arg2, c, *s;
WORD i, j, type, number;
while ( *inp == ' ' || *inp == ',' || *inp == '\t' ) inp++;
if ( FG.cTable[*inp] != 0 ) {
MesPrint("&Illegal argument in ExtraSymbols statement: '%s'",inp);
return(1);
}
arg1 = inp;
while ( FG.cTable[*inp] == 0 ) inp++;
c = *inp; *inp = 0;
if ( ( StrICmp(arg1,(UBYTE *)"array") == 0 )
|| ( StrICmp(arg1,(UBYTE *)"vector") == 0 ) ) {
AC.extrasymbols = 1;
}
else if ( StrICmp(arg1,(UBYTE *)"underscore") == 0 ) {
AC.extrasymbols = 0;
}
/*
else if ( StrICmp(arg1,(UBYTE *)"nothing") == 0 ) {
AC.extrasymbols = 2;
}
*/
else {
MesPrint("&Illegal keyword in ExtraSymbols statement: '%s'",arg1);
return(1);
}
*inp = c;
while ( *inp == ' ' || *inp == ',' || *inp == '\t' ) inp++;
if ( FG.cTable[*inp] != 0 ) {
MesPrint("&Illegal argument in ExtraSymbols statement: '%s'",inp);
return(1);
}
arg2 = inp;
while ( FG.cTable[*inp] <= 1 ) inp++;
if ( *inp != 0 ) {
MesPrint("&Illegal end of ExtraSymbols statement: '%s'",inp);
return(1);
}
/*
Now check whether this object has been declared already.
That would not be allowed.
*/
if ( AC.extrasymbols == 1 ) {
type = GetName(AC.varnames,arg2,&number,NOAUTO);
if ( type != NAMENOTFOUND ) {
MesPrint("&ExtraSymbols statement: '%s' has already been declared before",arg2);
return(1);
}
}
else if ( AC.extrasymbols == 0 ) {
if ( *arg2 == 'N' ) {
s = arg2+1;
while ( FG.cTable[*s] == 1 ) s++;
if ( *s == 0 ) {
MesPrint("&ExtraSymbols statement: '%s' creates conflicts with summed indices",arg2);
return(1);
}
}
}
if ( AC.extrasym ) { M_free(AC.extrasym,"extrasym"); AC.extrasym = 0; }
i = inp - arg2 + 1;
AC.extrasym = (UBYTE *)Malloc1(i*sizeof(UBYTE),"extrasym");
for ( j = 0; j < i; j++ ) AC.extrasym[j] = arg2[j];
return(0);
}
/*
#] CoExtraSymbols :
#[ GetIfDollarFactor :
*/
WORD *GetIfDollarFactor(UBYTE **inp, WORD *w)
{
LONG x;
WORD number;
UBYTE *name, c, *s;
s = *inp;
if ( FG.cTable[*s] == 1 ) {
x = 0;
while ( FG.cTable[*s] == 1 ) {
x = 10*x + *s++ - '0';
if ( x >= MAXPOSITIVE ) {
MesPrint("&Value in dollar factor too large");
while ( FG.cTable[*s] == 1 ) s++;
*inp = s;
return(0);
}
}
*w++ = IFDOLLAREXTRA;
*w++ = 3;
*w++ = -x-1;
*inp = s;
return(w);
}
if ( *s != '$' ) {
MesPrint("&Factor indicator for $-variable should be a number or a $-variable.");
return(0);
}
s++; name = s;
while ( FG.cTable[*s] < 2 ) s++;
c = *s; *s = 0;
if ( GetName(AC.dollarnames,name,&number,NOAUTO) == NAMENOTFOUND ) {
MesPrint("&dollar in if statement should have been defined previously");
return(0);
}
*s = c;
*w++ = IFDOLLAREXTRA;
*w++ = 3;
*w++ = number;
if ( c == '[' ) {
s++;
*inp = s;
if ( ( w = GetIfDollarFactor(inp,w) ) == 0 ) return(0);
s = *inp;
if ( *s != ']' ) {
MesPrint("&unmatched [] in $ in if statement");
return(0);
}
s++;
*inp = s;
}
return(w);
}
/*
#] GetIfDollarFactor :
#[ GetDoParam :
*/
UBYTE *GetDoParam(UBYTE *inp, WORD **wp, int par)
{
LONG x;
WORD number;
UBYTE *name, c;
if ( FG.cTable[*inp] == 1 ) {
x = 0;
while ( *inp >= '0' && *inp <= '9' ) {
x = 10*x + *inp++ - '0';
if ( x > MAXPOSITIVE ) {
if ( par == -1 ) {
MesPrint("&Value in dollar factor too large");
}
else {
MesPrint("&Value in do loop boundaries too large");
}
while ( FG.cTable[*inp] == 1 ) inp++;
return(0);
}
}
if ( par > 0 ) {
*(*wp)++ = SNUMBER;
*(*wp)++ = (WORD)x;
}
else {
*(*wp)++ = DOLLAREXPR2;
*(*wp)++ = -((WORD)x)-1;
}
return(inp);
}
if ( *inp != '$' ) {
return(0);
}
inp++; name = inp;
while ( FG.cTable[*inp] < 2 ) inp++;
c = *inp; *inp = 0;
if ( GetName(AC.dollarnames,name,&number,NOAUTO) == NAMENOTFOUND ) {
if ( par == -1 ) {
MesPrint("&dollar in print statement should have been defined previously");
}
else {
MesPrint("&dollar in do loop boundaries should have been defined previously");
}
return(0);
}
*inp = c;
if ( par > 0 ) {
*(*wp)++ = DOLLAREXPRESSION;
*(*wp)++ = number;
}
else {
*(*wp)++ = DOLLAREXPR2;
*(*wp)++ = number;
}
if ( c == '[' ) {
inp++;
inp = GetDoParam(inp,wp,0);
if ( inp == 0 ) return(0);
if ( *inp != ']' ) {
if ( par == -1 ) {
MesPrint("&unmatched [] in $ in print statement");
}
else {
MesPrint("&unmatched [] in do loop boundaries");
}
return(0);
}
inp++;
}
return(inp);
}
/*
#] GetDoParam :
#[ CoDo :
*/
int CoDo(UBYTE *inp)
{
GETIDENTITY
CBUF *C = cbuf+AC.cbufnum;
WORD *w, numparam;
int error = 0, i;
UBYTE *name, c;
if ( AC.doloopstack == 0 ) {
AC.doloopstacksize = 20;
AC.doloopstack = (WORD *)Malloc1(AC.doloopstacksize*2*sizeof(WORD),"doloop stack");
AC.doloopnest = AC.doloopstack + AC.doloopstacksize;
}
if ( AC.dolooplevel >= AC.doloopstacksize ) {
WORD *newstack, *newnest, newsize;
newsize = AC.doloopstacksize * 2;
newstack = (WORD *)Malloc1(newsize*2*sizeof(WORD),"doloop stack");
newnest = newstack + newsize;
for ( i = 0; i < newsize; i++ ) {
newstack[i] = AC.doloopstack[i];
newnest[i] = AC.doloopnest[i];
}
M_free(AC.doloopstack,"doloop stack");
AC.doloopstack = newstack;
AC.doloopnest = newnest;
AC.doloopstacksize = newsize;
}
AC.doloopnest[AC.dolooplevel] = NestingChecksum();
w = AT.WorkPointer;
*w++ = TYPEDOLOOP;
w++; /* Space for the length of the statement */
/*
Now the $loopvariable
*/
while ( *inp == ',' ) inp++;
if ( *inp != '$' ) {
error = 1;
MesPrint("&do loop parameter should be a dollar variable");
}
else {
inp++;
name = inp;
if ( FG.cTable[*inp] != 0 ) {
error = 1;
MesPrint("&illegal name for do loop parameter");
}
while ( FG.cTable[*inp] < 2 ) inp++;
c = *inp; *inp = 0;
if ( GetName(AC.dollarnames,name,&numparam,NOAUTO) == NAMENOTFOUND ) {
numparam = AddDollar(name,DOLUNDEFINED,0,0);
}
*w++ = numparam;
*inp = c;
AddPotModdollar(numparam);
}
w++; /* space for the level of the enddo statement */
while ( *inp == ',' ) inp++;
if ( *inp != '=' ) goto IllSyntax;
inp++;
while ( *inp == ',' ) inp++;
/*
The start value
*/
inp = GetDoParam(inp,&w,1);
if ( inp == 0 || *inp != ',' ) goto IllSyntax;
while ( *inp == ',' ) inp++;
/*
The end value
*/
inp = GetDoParam(inp,&w,1);
if ( inp == 0 || ( *inp != 0 && *inp != ',' ) ) goto IllSyntax;
/*
The increment value
*/
if ( *inp != ',' ) {
if ( *inp == 0 ) { *w++ = SNUMBER; *w++ = 1; }
else goto IllSyntax;
}
else {
while ( *inp == ',' ) inp++;
inp = GetDoParam(inp,&w,1);
}
if ( inp == 0 || *inp != 0 ) goto IllSyntax;
*w = 0;
AT.WorkPointer[1] = w - AT.WorkPointer;
/*
Put away and set information for placing enddo information.
*/
AddNtoL(AT.WorkPointer[1],AT.WorkPointer);
AC.doloopstack[AC.dolooplevel++] = C->numlhs;
return(error);
IllSyntax:
MesPrint("&Illegal syntax for do statement");
return(1);
}
/*
#] CoDo :
#[ CoEndDo :
*/
int CoEndDo(UBYTE *inp)
{
CBUF *C = cbuf+AC.cbufnum;
WORD scratch[3];
while ( *inp == ',' ) inp++;
if ( *inp ) {
MesPrint("&Illegal syntax for EndDo statement");
return(1);
}
if ( AC.dolooplevel <= 0 ) {
MesPrint("&EndDo without corresponding Do statement");
return(1);
}
AC.dolooplevel--;
scratch[0] = TYPEENDDOLOOP;
scratch[1] = 3;
scratch[2] = AC.doloopstack[AC.dolooplevel];
AddNtoL(3,scratch);
cbuf[AC.cbufnum].lhs[AC.doloopstack[AC.dolooplevel]][3] = C->numlhs;
if ( AC.doloopnest[AC.dolooplevel] != NestingChecksum() ) {
MesNesting();
return(1);
}
return(0);
}
/*
#] CoEndDo :
#[ CoFactDollar :
*/
int CoFactDollar(UBYTE *inp)
{
WORD numdollar;
if ( *inp == '$' ) {
if ( GetName(AC.dollarnames,inp+1,&numdollar,NOAUTO) != CDOLLAR ) {
MesPrint("&%s is undefined",inp);
numdollar = AddDollar(inp+1,DOLINDEX,&one,1);
return(1);
}
inp = SkipAName(inp+1);
if ( *inp != 0 ) {
MesPrint("&FactDollar should have a single $variable for its argument");
return(1);
}
AddPotModdollar(numdollar);
}
else {
MesPrint("&%s is not a $-variable",inp);
return(1);
}
Add3Com(TYPEFACTOR,numdollar);
return(0);
}
/*
#] CoFactDollar :
#[ CoFactorize :
*/
int CoFactorize(UBYTE *s) { return(DoFactorize(s,1)); }
/*
#] CoFactorize :
#[ CoNFactorize :
*/
int CoNFactorize(UBYTE *s) { return(DoFactorize(s,0)); }
/*
#] CoNFactorize :
#[ CoUnFactorize :
*/
int CoUnFactorize(UBYTE *s) { return(DoFactorize(s,3)); }
/*
#] CoUnFactorize :
#[ CoNUnFactorize :
*/
int CoNUnFactorize(UBYTE *s) { return(DoFactorize(s,2)); }
/*
#] CoNUnFactorize :
#[ DoFactorize :
*/
int DoFactorize(UBYTE *s,int par)
{
EXPRESSIONS e;
WORD i;
WORD number;
UBYTE *t, c;
int error = 0, keepzeroflag = 0;
if ( *s == '(' ) {
s++;
while ( *s != ')' && *s ) {
if ( FG.cTable[*s] == 0 ) {
t = s; while ( FG.cTable[*s] == 0 ) s++;
c = *s; *s = 0;
if ( StrICmp((UBYTE *)"keepzero",t) == 0 ) {
keepzeroflag = 1;
}
else {
MesPrint("&Illegal option in [N][Un]Factorize statement: %s",t);
error = 1;
}
*s = c;
}
while ( *s == ',' ) s++;
if ( *s && *s != ')' && FG.cTable[*s] != 0 ) {
MesPrint("&Illegal character in option field of [N][Un]Factorize statement");
error = 1;
return(error);
}
}
if ( *s ) s++;
while ( *s == ',' || *s == ' ' ) s++;
}
if ( *s == 0 ) {
for ( i = NumExpressions-1; i >= 0; i-- ) {
e = Expressions+i;
if ( e->replace >= 0 ) {
e = Expressions + e->replace;
}
if ( e->status == LOCALEXPRESSION || e->status == GLOBALEXPRESSION
|| e->status == UNHIDELEXPRESSION || e->status == UNHIDEGEXPRESSION
|| e->status == INTOHIDELEXPRESSION || e->status == INTOHIDEGEXPRESSION
) {
switch ( par ) {
case 0:
e->vflags &= ~TOBEFACTORED;
break;
case 1:
e->vflags |= TOBEFACTORED;
e->vflags &= ~TOBEUNFACTORED;
break;
case 2:
e->vflags &= ~TOBEUNFACTORED;
break;
case 3:
e->vflags |= TOBEUNFACTORED;
e->vflags &= ~TOBEFACTORED;
break;
}
}
if ( ( e->vflags & TOBEFACTORED ) != 0 ) {
if ( keepzeroflag ) e->vflags |= KEEPZERO;
else e->vflags &= ~KEEPZERO;
}
else e->vflags &= ~KEEPZERO;
}
}
else {
for(;;) { /* Look for a (comma separated) list of variables */
while ( *s == ',' ) s++;
if ( *s == 0 ) break;
if ( *s == '[' || FG.cTable[*s] == 0 ) {
t = s;
if ( ( s = SkipAName(s) ) == 0 ) {
MesPrint("&Improper name for an expression: '%s'",t);
return(1);
}
c = *s; *s = 0;
if ( GetName(AC.exprnames,t,&number,NOAUTO) == CEXPRESSION ) {
e = Expressions+number;
if ( e->replace >= 0 ) {
e = Expressions + e->replace;
}
if ( e->status == LOCALEXPRESSION || e->status == GLOBALEXPRESSION
|| e->status == UNHIDELEXPRESSION || e->status == UNHIDEGEXPRESSION
|| e->status == INTOHIDELEXPRESSION || e->status == INTOHIDEGEXPRESSION
) {
switch ( par ) {
case 0:
e->vflags &= ~TOBEFACTORED;
break;
case 1:
e->vflags |= TOBEFACTORED;
e->vflags &= ~TOBEUNFACTORED;
break;
case 2:
e->vflags &= ~TOBEUNFACTORED;
break;
case 3:
e->vflags |= TOBEUNFACTORED;
e->vflags &= ~TOBEFACTORED;
break;
}
}
if ( ( e->vflags & TOBEFACTORED ) != 0 ) {
if ( keepzeroflag ) e->vflags |= KEEPZERO;
else e->vflags &= ~KEEPZERO;
}
else e->vflags &= ~KEEPZERO;
}
else if ( GetName(AC.varnames,t,&number,NOAUTO) != NAMENOTFOUND ) {
MesPrint("&%s is not an expression",t);
error = 1;
}
*s = c;
}
else {
MesPrint("&Illegal object in (N)Factorize statement");
error = 1;
while ( *s && *s != ',' ) s++;
if ( *s == 0 ) break;
}
}
}
return(error);
}
/*
#] DoFactorize :
#[ CoOptimizeOption :
*/
int CoOptimizeOption(UBYTE *s)
{
UBYTE *name, *t1, *t2, c1, c2, *value, *u;
int error = 0, x;
double d;
while ( *s == ' ' || *s == ',' || *s == '\t' ) s++;
while ( *s ) {
name = s; while ( FG.cTable[*s] == 0 ) s++;
t1 = s; c1 = *t1;
while ( *s == ' ' || *s == '\t' ) s++;
if ( *s != '=' ) {
correctuse:
MesPrint("&Correct use in Format,Optimize statement is Optionname=value");
error = 1;
while ( *s == ' ' || *s == ',' || *s == '\t' || *s == '=' ) s++;
*t1 = c1;
continue;
}
*t1 = 0;
s++;
while ( *s == ' ' || *s == '\t' ) s++;
if ( *s == 0 ) goto correctuse;
value = s;
while ( FG.cTable[*s] <= 1 || *s=='.' || *s=='*' || *s == '(' || *s == ')' ) {
if ( *s == '(' ) { SKIPBRA4(s) }
s++;
}
t2 = s; c2 = *t2;
while ( *s == ' ' || *s == '\t' ) s++;
if ( *s && *s != ',' ) goto correctuse;
if ( *s ) {
s++;
while ( *s == ' ' || *s == '\t' ) s++;
}
*t2 = 0;
/*
Now we have name=value with name and value zero terminated strings.
*/
if ( StrICmp(name,(UBYTE *)"horner") == 0 ) {
if ( StrICmp(value,(UBYTE *)"occurrence") == 0 ) {
AO.Optimize.horner = O_OCCURRENCE;
}
else if ( StrICmp(value,(UBYTE *)"mcts") == 0 ) {
AO.Optimize.horner = O_MCTS;
}
else if ( StrICmp(value,(UBYTE *)"sa") == 0 ) {
AO.Optimize.horner = O_SIMULATED_ANNEALING;
}
else {
AO.Optimize.horner = -1;
MesPrint("&Unrecognized option value in Format,Optimize statement: %s=%s",name,value);
error = 1;
}
}
else if ( StrICmp(name,(UBYTE *)"hornerdirection") == 0 ) {
if ( StrICmp(value,(UBYTE *)"forward") == 0 ) {
AO.Optimize.hornerdirection = O_FORWARD;
}
else if ( StrICmp(value,(UBYTE *)"backward") == 0 ) {
AO.Optimize.hornerdirection = O_BACKWARD;
}
else if ( StrICmp(value,(UBYTE *)"forwardorbackward") == 0 ) {
AO.Optimize.hornerdirection = O_FORWARDORBACKWARD;
}
else if ( StrICmp(value,(UBYTE *)"forwardandbackward") == 0 ) {
AO.Optimize.hornerdirection = O_FORWARDANDBACKWARD;
}
else {
AO.Optimize.method = -1;
MesPrint("&Unrecognized option value in Format,Optimize statement: %s=%s",name,value);
error = 1;
}
}
else if ( StrICmp(name,(UBYTE *)"method") == 0 ) {
if ( StrICmp(value,(UBYTE *)"none") == 0 ) {
AO.Optimize.method = O_NONE;
}
else if ( StrICmp(value,(UBYTE *)"cse") == 0 ) {
AO.Optimize.method = O_CSE;
}
else if ( StrICmp(value,(UBYTE *)"csegreedy") == 0 ) {
AO.Optimize.method = O_CSEGREEDY;
}
else if ( StrICmp(value,(UBYTE *)"greedy") == 0 ) {
AO.Optimize.method = O_GREEDY;
}
else {
AO.Optimize.method = -1;
MesPrint("&Unrecognized option value in Format,Optimize statement: %s=%s",name,value);
error = 1;
}
}
else if ( StrICmp(name,(UBYTE *)"timelimit") == 0 ) {
x = 0;
u = value; while ( *u >= '0' && *u <= '9' ) x = 10*x + *u++ - '0';
if ( *u != 0 ) {
MesPrint("&Option TimeLimit in Format,Optimize statement should be a positive number: %s",value);
AO.Optimize.mctstimelimit = 0;
AO.Optimize.greedytimelimit = 0;
error = 1;
}
else {
AO.Optimize.mctstimelimit = x/2;
AO.Optimize.greedytimelimit = x/2;
}
}
else if ( StrICmp(name,(UBYTE *)"mctstimelimit") == 0 ) {
x = 0;
u = value; while ( *u >= '0' && *u <= '9' ) x = 10*x + *u++ - '0';
if ( *u != 0 ) {
MesPrint("&Option MCTSTimeLimit in Format,Optimize statement should be a positive number: %s",value);
AO.Optimize.mctstimelimit = 0;
error = 1;
}
else {
AO.Optimize.mctstimelimit = x;
}
}
else if ( StrICmp(name,(UBYTE *)"mctsnumexpand") == 0 ) {
int y;
x = 0;
u = value; while ( *u >= '0' && *u <= '9' ) x = 10*x + *u++ - '0';
if ( *u == '*' || *u == 'x' || *u == 'X' ) {
u++; y = x;
x = 0;
while ( *u >= '0' && *u <= '9' ) x = 10*x + *u++ - '0';
}
else { y = 1; }
if ( *u != 0 ) {
MesPrint("&Option MCTSNumExpand in Format,Optimize statement should be a positive number: %s",value);
AO.Optimize.mctsnumexpand= 0;
AO.Optimize.mctsnumrepeat= 1;
error = 1;
}
else {
AO.Optimize.mctsnumexpand= x;
AO.Optimize.mctsnumrepeat= y;
}
}
else if ( StrICmp(name,(UBYTE *)"mctsnumrepeat") == 0 ) {
x = 0;
u = value; while ( *u >= '0' && *u <= '9' ) x = 10*x + *u++ - '0';
if ( *u != 0 ) {
MesPrint("&Option MCTSNumExpand in Format,Optimize statement should be a positive number: %s",value);
AO.Optimize.mctsnumrepeat= 1;
error = 1;
}
else {
AO.Optimize.mctsnumrepeat= x;
}
}
else if ( StrICmp(name,(UBYTE *)"mctsnumkeep") == 0 ) {
x = 0;
u = value; while ( *u >= '0' && *u <= '9' ) x = 10*x + *u++ - '0';
if ( *u != 0 ) {
MesPrint("&Option MCTSNumKeep in Format,Optimize statement should be a positive number: %s",value);
AO.Optimize.mctsnumkeep= 0;
error = 1;
}
else {
AO.Optimize.mctsnumkeep= x;
}
}
else if ( StrICmp(name,(UBYTE *)"mctsconstant") == 0 ) {
d = 0;
if ( sscanf ((char*)value, "%lf", &d) != 1 ) {
MesPrint("&Option MCTSConstant in Format,Optimize statement should be a positive number: %s",value);
AO.Optimize.mctsconstant.fval = 0;
error = 1;
}
else {
AO.Optimize.mctsconstant.fval = d;
}
}
else if ( StrICmp(name,(UBYTE *)"greedytimelimit") == 0 ) {
x = 0;
u = value; while ( *u >= '0' && *u <= '9' ) x = 10*x + *u++ - '0';
if ( *u != 0 ) {
MesPrint("&Option GreedyTimeLimit in Format,Optimize statement should be a positive number: %s",value);
AO.Optimize.greedytimelimit = 0;
error = 1;
}
else {
AO.Optimize.greedytimelimit = x;
}
}
else if ( StrICmp(name,(UBYTE *)"greedyminnum") == 0 ) {
x = 0;
u = value; while ( *u >= '0' && *u <= '9' ) x = 10*x + *u++ - '0';
if ( *u != 0 ) {
MesPrint("&Option GreedyMinNum in Format,Optimize statement should be a positive number: %s",value);
AO.Optimize.greedyminnum= 0;
error = 1;
}
else {
AO.Optimize.greedyminnum= x;
}
}
else if ( StrICmp(name,(UBYTE *)"greedymaxperc") == 0 ) {
x = 0;
u = value; while ( *u >= '0' && *u <= '9' ) x = 10*x + *u++ - '0';
if ( *u != 0 ) {
MesPrint("&Option GreedyMaxPerc in Format,Optimize statement should be a positive number: %s",value);
AO.Optimize.greedymaxperc= 0;
error = 1;
}
else {
AO.Optimize.greedymaxperc= x;
}
}
else if ( StrICmp(name,(UBYTE *)"stats") == 0 ) {
if ( StrICmp(value,(UBYTE *)"on") == 0 ) {
AO.Optimize.printstats = 1;
}
else if ( StrICmp(value,(UBYTE *)"off") == 0 ) {
AO.Optimize.printstats = 0;
}
else {
AO.Optimize.printstats = 0;
MesPrint("&Unrecognized option value in Format,Optimize statement: %s=%s",name,value);
error = 1;
}
}
else if ( StrICmp(name,(UBYTE *)"printscheme") == 0 ) {
if ( StrICmp(value,(UBYTE *)"on") == 0 ) {
AO.Optimize.schemeflags |= 1;
}
else if ( StrICmp(value,(UBYTE *)"off") == 0 ) {
AO.Optimize.schemeflags &= ~1;
}
else {
AO.Optimize.schemeflags &= ~1;
MesPrint("&Unrecognized option value in Format,Optimize statement: %s=%s",name,value);
error = 1;
}
}
else if ( StrICmp(name,(UBYTE *)"debugflag") == 0 ) {
/*
This option is for debugging purposes only. Not in the manual!
0x1: Print statements in reverse order.
0x2: Print the scheme of the variables.
*/
x = 0;
u = value;
if ( FG.cTable[*u] == 1 ) {
while ( *u >= '0' && *u <= '9' ) x = 10*x + *u++ - '0';
if ( *u != 0 ) {
MesPrint("&Numerical value for DebugFlag in Format,Optimize statement should be a nonnegative number: %s",value);
AO.Optimize.debugflags = 0;
error = 1;
}
else {
AO.Optimize.debugflags = x;
}
}
else if ( StrICmp(value,(UBYTE *)"on") == 0 ) {
AO.Optimize.debugflags = 1;
}
else if ( StrICmp(value,(UBYTE *)"off") == 0 ) {
AO.Optimize.debugflags = 0;
}
else {
AO.Optimize.debugflags = 0;
MesPrint("&Unrecognized option value in Format,Optimize statement: %s=%s",name,value);
error = 1;
}
}
else if ( StrICmp(name,(UBYTE *)"scheme") == 0 ) {
UBYTE *ss, *s1, c;
WORD type, numsym;
AO.schemenum = 0;
u = value;
if ( *u != '(' ) {
noscheme:
MesPrint("&Option Scheme in Format,Optimize statement should be an array of names or integers between (): %s",value);
error = 1;
break;
}
u++; ss = u;
while ( *ss == ' ' || *ss == '\t' || *ss == ',' ) ss++;
if ( FG.cTable[*ss] == 0 || *ss == '$' || *ss == '[' ) { /* Name */
s1 = u; SKIPBRA3(s1)
if ( *s1 != ')' ) goto noscheme;
while ( ss < s1 ) { if ( *ss++ == ',' ) AO.schemenum++; }
*ss++ = 0; while ( *ss == ' ' ) ss++;
if ( *ss != 0 ) goto noscheme;
ss = u;
if ( AO.schemenum < 1 ) {
MesPrint("&Option Scheme in Format,Optimize statement should have at least one name or number between ()");
error = 1;
break;
}
if ( AO.inscheme ) M_free(AO.inscheme,"Horner input scheme");
AO.inscheme = (WORD *)Malloc1((AO.schemenum+1)*sizeof(WORD),"Horner input scheme");
while ( *ss == ' ' || *ss == '\t' || *ss == ',' ) ss++;
AO.schemenum = 0;
for(;;) {
if ( *ss == 0 ) break;
s1 = ss; ss = SkipAName(s1); c = *ss; *ss = 0;
if ( ss[-1] == '_' ) {
/*
Now AC.extrasym followed by a number and _
*/
UBYTE *u1, *u2;
u1 = s1; u2 = AC.extrasym;
while ( *u1 == *u2 ) { u1++; u2++; }
if ( *u2 == 0 ) { /* Good start */
numsym = 0;
while ( *u1 >= '0' && *u1 <= '9' ) numsym = 10*numsym + *u1++ - '0';
if ( u1 != ss-1 || numsym == 0 || AC.extrasymbols != 0 ) {
MesPrint("&Improper use of extra symbol in scheme format option");
goto noscheme;
}
numsym = MAXVARIABLES-numsym;
ss++;
goto GotTheNumber;
}
}
else if ( *s1 == '$' ) {
GETIDENTITY
int numdollar;
if ( ( numdollar = GetDollar(s1+1) ) < 0 ) {
MesPrint("&Undefined variable %s",s1);
error = 5;
}
else if ( ( numsym = DolToSymbol(BHEAD numdollar) ) < 0 ) {
MesPrint("&$%s does not evaluate to a symbol",s1);
error = 5;
}
*ss = c;
goto GotTheNumber;
}
else if ( c == '(' ) {
if ( StrCmp(s1,AC.extrasym) == 0 ) {
if ( (AC.extrasymbols&1) != 1 ) {
MesPrint("&Improper use of extra symbol in scheme format option");
goto noscheme;
}
*ss++ = c;
numsym = 0;
while ( *ss >= '0' && *ss <= '9' ) numsym = 10*numsym + *ss++ - '0';
if ( *ss != ')' ) {
MesPrint("&Extra symbol should have a number for its argument.");
goto noscheme;
}
numsym = MAXVARIABLES-numsym;
ss++;
goto GotTheNumber;
}
}
type = GetName(AC.varnames,s1,&numsym,WITHAUTO);
if ( ( type != CSYMBOL ) && type != CDUBIOUS ) {
MesPrint("&%s is not a symbol",s1);
error = 4;
if ( type < 0 ) numsym = AddSymbol(s1,-MAXPOWER,MAXPOWER,0,0);
}
*ss = c;
GotTheNumber:
AO.inscheme[AO.schemenum++] = numsym;
while ( *ss == ' ' || *ss == '\t' || *ss == ',' ) ss++;
}
}
}
else if ( StrICmp(name,(UBYTE *)"mctsdecaymode") == 0 ) {
x = 0;
u = value;
if ( FG.cTable[*u] == 1 ) {
while ( *u >= '0' && *u <= '9' ) x = 10*x + *u++ - '0';
if ( *u != 0 ) {
MesPrint("&Option MCTSDecayMode in Format,Optimize statement should be a nonnegative integer: %s",value);
AO.Optimize.mctsdecaymode = 0;
error = 1;
}
else {
AO.Optimize.mctsdecaymode = x;
}
}
else {
AO.Optimize.mctsdecaymode = 0;
MesPrint("&Unrecognized option value in Format,Optimize statement: %s=%s",name,value);
error = 1;
}
}
else if ( StrICmp(name,(UBYTE *)"saiter") == 0 ) {
x = 0;
u = value; while ( *u >= '0' && *u <= '9' ) x = 10*x + *u++ - '0';
if ( *u != 0 ) {
MesPrint("&Option SAIter in Format,Optimize statement should be a positive integer: %s",value);
AO.Optimize.saIter = 0;
error = 1;
}
else {
AO.Optimize.saIter= x;
}
}
else if ( StrICmp(name,(UBYTE *)"samaxt") == 0 ) {
d = 0;
if ( sscanf ((char*)value, "%lf", &d) != 1 ) {
MesPrint("&Option SAMaxT in Format,Optimize statement should be a positive number: %s",value);
AO.Optimize.saMaxT.fval = 0;
error = 1;
}
else {
AO.Optimize.saMaxT.fval = d;
}
}
else if ( StrICmp(name,(UBYTE *)"samint") == 0 ) {
d = 0;
if ( sscanf ((char*)value, "%lf", &d) != 1 ) {
MesPrint("&Option SAMinT in Format,Optimize statement should be a positive number: %s",value);
AO.Optimize.saMinT.fval = 0;
error = 1;
}
else {
AO.Optimize.saMinT.fval = d;
}
}
else {
MesPrint("&Unrecognized option name in Format,Optimize statement: %s",name);
error = 1;
}
*t1 = c1; *t2 = c2;
}
return(error);
}
/*
#] CoOptimizeOption :
#[ DoPutInside :
Syntax:
PutIn[side],functionname[,brackets] -> par = 1
AntiPutIn[side],functionname,antibrackets -> par = -1
*/
int CoPutInside(UBYTE *inp) { return(DoPutInside(inp,1)); }
int CoAntiPutInside(UBYTE *inp) { return(DoPutInside(inp,-1)); }
int DoPutInside(UBYTE *inp, int par)
{
GETIDENTITY
UBYTE *p, c;
WORD *to, type, c1,c2,funnum, *WorkSave;
int error = 0;
while ( *inp == ' ' || *inp == '\t' || *inp == ',' ) inp++;
/*
First we need the name of a function. (Not a tensor or table!)
*/
p = SkipAName(inp);
if ( p == 0 ) return(1);
c = *p; *p = 0;
type = GetName(AC.varnames,inp,&funnum,WITHAUTO);
if ( type != CFUNCTION || functions[funnum].tabl != 0 || functions[funnum].spec ) {
MesPrint("&PutInside/AntiPutInside expects a regular function for its first argument");
MesPrint("&Argument is %s",inp);
error = 1;
}
funnum += FUNCTION;
*p = c;
inp = p;
while ( *inp == ' ' || *inp == '\t' || *inp == ',' ) inp++;
if ( *inp == 0 ) {
if ( par == 1 ) {
WORD tocompiler[4];
tocompiler[0] = TYPEPUTINSIDE;
tocompiler[1] = 4;
tocompiler[2] = 0;
tocompiler[3] = funnum;
AddNtoL(4,tocompiler);
}
else {
MesPrint("&AntiPutInside needs inside information.");
error = 1;
}
return(error);
}
WorkSave = to = AT.WorkPointer;
*to++ = TYPEPUTINSIDE;
*to++ = 4;
*to++ = par;
*to++ = funnum;
to++;
while ( *inp ) {
while ( *inp == ' ' || *inp == '\t' || *inp == ',' ) inp++;
if ( *inp == 0 ) break;
p = SkipAName(inp);
if ( p == 0 ) { error = 1; break; }
c = *p; *p = 0;
type = GetName(AC.varnames,inp,&c1,WITHAUTO);
if ( c == '.' ) {
if ( type == CVECTOR || type == CDUBIOUS ) {
*p++ = c;
inp = p;
p = SkipAName(inp);
if ( p == 0 ) return(1);
c = *p; *p = 0;
type = GetName(AC.varnames,inp,&c2,WITHAUTO);
if ( type != CVECTOR && type != CDUBIOUS ) {
MesPrint("&Not a vector in dotproduct in PutInside/AntiPutInside statement: %s",inp);
error = 1;
}
else type = CDOTPRODUCT;
}
else {
MesPrint("&Illegal use of . after %s in PutInside/AntiPutInside statement",inp);
error = 1;
*p = c; inp = p;
continue;
}
}
switch ( type ) {
case CSYMBOL :
*to++ = SYMBOL; *to++ = 4; *to++ = c1; *to++ = 1; break;
case CVECTOR :
*to++ = INDEX; *to++ = 3; *to++ = AM.OffsetVector + c1; break;
case CFUNCTION :
*to++ = c1+FUNCTION; *to++ = FUNHEAD; *to++ = 0;
FILLFUN3(to)
break;
case CDOTPRODUCT :
*to++ = DOTPRODUCT; *to++ = 5; *to++ = c1 + AM.OffsetVector;
*to++ = c2 + AM.OffsetVector; *to++ = 1; break;
case CDELTA :
*to++ = DELTA; *to++ = 4; *to++ = EMPTYINDEX; *to++ = EMPTYINDEX; break;
default :
MesPrint("&Illegal variable request for %s in PutInside/AntiPutInside statement",inp);
error = 1; break;
}
*p = c;
inp = p;
}
*to++ = 1; *to++ = 1; *to++ = 3;
AT.WorkPointer[1] = to - AT.WorkPointer;
AT.WorkPointer[4] = AT.WorkPointer[1]-4;
AT.WorkPointer = to;
AC.BracketNormalize = 1;
if ( Normalize(BHEAD WorkSave+4) ) { error = 1; }
else {
WorkSave[1] = WorkSave[4]+4;
to = WorkSave + WorkSave[1] - 1;
c1 = ABS(*to);
WorkSave[1] -= c1;
WorkSave[4] -= c1;
AddNtoL(WorkSave[1],WorkSave);
}
AC.BracketNormalize = 0;
AT.WorkPointer = WorkSave;
return(error);
}
/*
#] DoPutInside :
#[ CoSwitch :
Syntax: Switch $var;
Be carefull with illegal nestings with repeat, if, while.
*/
int CoSwitch(UBYTE *s)
{
WORD numdollar;
SWITCH *sw;
if ( *s == '$' ) {
if ( GetName(AC.dollarnames,s+1,&numdollar,NOAUTO) != CDOLLAR ) {
MesPrint("&%s is undefined in switch statement",s);
numdollar = AddDollar(s+1,DOLINDEX,&one,1);
return(1);
}
s = SkipAName(s+1);
if ( *s != 0 ) {
MesPrint("&Switch should have a single $variable for its argument");
return(1);
}
/* AddPotModdollar(numdollar); */
}
else {
MesPrint("&%s is not a $-variable in switch statement",s);
return(1);
}
/*
Now create the switch table. We will add to it each time we run
into a new case. It will all be sorted out the moment we run into
the endswitch statement.
*/
AC.SwitchLevel++;
if ( AC.SwitchInArray >= AC.MaxSwitch ) DoubleSwitchBuffers();
AC.SwitchHeap[AC.SwitchLevel] = AC.SwitchInArray;
sw = AC.SwitchArray + AC.SwitchInArray;
sw->iflevel = AC.IfLevel;
sw->whilelevel = AC.WhileLevel;
sw->nestingsum = NestingChecksum();
Add4Com(TYPESWITCH,numdollar,AC.SwitchInArray);
AC.SwitchInArray++;
return(0);
}
/*
#] CoSwitch :
#[ CoCase :
*/
int CoCase(UBYTE *s)
{
SWITCH *sw = AC.SwitchArray + AC.SwitchHeap[AC.SwitchLevel];
WORD x = 0, sign = 1;
while ( *s == ',' ) s++;
SKIPBLANKS(s);
while ( *s == '-' || *s == '+' ) {
if ( *s == '-' ) sign = -sign;
s++;
}
while ( FG.cTable[*s] == 1 ) { x = 10*x + *s++ - '0'; }
x = sign*x;
if ( sw->iflevel != AC.IfLevel || sw->whilelevel != AC.WhileLevel
|| sw->nestingsum != NestingChecksum() ) {
MesPrint("&Illegal nesting of switch/case/default with if/while/repeat/loop/argument/term/...");
return(-1);
}
/*
Now add a case to the table with the current 'address'.
*/
if ( sw->numcases >= sw->tablesize ) {
int i;
SWITCHTABLE *newtable;
WORD newsize;
if ( sw->tablesize == 0 ) newsize = 10;
else newsize = 2*sw->tablesize;
newtable = (SWITCHTABLE *)Malloc1(newsize*sizeof(SWITCHTABLE),"Switch table");
if ( sw->table ) {
for ( i = 0; i < sw->tablesize; i++ ) newtable[i] = sw->table[i];
M_free(sw->table,"Switch table");
}
sw->table = newtable;
sw->tablesize = newsize;
}
if ( sw->numcases == 0 ) { sw->mincase = sw->maxcase = x; }
else if ( x > sw->maxcase ) sw->maxcase = x;
else if ( x < sw->mincase ) sw->mincase = x;
sw->table[sw->numcases].ncase = x;
sw->table[sw->numcases].value = cbuf[AC.cbufnum].numlhs;
sw->table[sw->numcases].compbuffer = AC.cbufnum;
sw->numcases++;
return(0);
}
/*
#] CoCase :
#[ CoBreak :
*/
int CoBreak(UBYTE *s)
{
/*
This involves a 'postponed' jump to the end. This can be done
in a special routine during execution.
That routine should also pop the switch level.
*/
SWITCH *sw = AC.SwitchArray + AC.SwitchHeap[AC.SwitchLevel];
if ( sw->iflevel != AC.IfLevel || sw->whilelevel != AC.WhileLevel
|| sw->nestingsum != NestingChecksum() ) {
MesPrint("&Illegal nesting of switch/case/default with if/while/repeat/loop/argument/term/...");
return(-1);
}
if ( *s ) {
MesPrint("&No parameters allowed in Break statement");
return(-1);
}
Add3Com(TYPEENDSWITCH,AC.SwitchHeap[AC.SwitchLevel]);
return(0);
}
/*
#] CoBreak :
#[ CoDefault :
*/
int CoDefault(UBYTE *s)
{
/*
A bit like case, except that the address gets stored directly in the
SWITCH struct.
*/
SWITCH *sw = AC.SwitchArray + AC.SwitchHeap[AC.SwitchLevel];
if ( sw->iflevel != AC.IfLevel || sw->whilelevel != AC.WhileLevel
|| sw->nestingsum != NestingChecksum() ) {
MesPrint("&Illegal nesting of switch/case/default with if/while/repeat/loop/argument/term/...");
return(-1);
}
if ( *s ) {
MesPrint("&No parameters allowed in Default statement");
return(-1);
}
sw->defaultcase.ncase = 0;
sw->defaultcase.value = cbuf[AC.cbufnum].numlhs;
sw->defaultcase.compbuffer = AC.cbufnum;
return(0);
}
/*
#] CoDefault :
#[ CoEndSwitch :
*/
int CoEndSwitch(UBYTE *s)
{
/*
We store this address in the SWITCH struct.
Next we sort the table by ncase.
Then we decide whether the table is DENSE or SPARSE.
If it is dense we change the allocation and spread the cases is necessary.
Finally we pop levels.
*/
SWITCH *sw = AC.SwitchArray + AC.SwitchHeap[AC.SwitchLevel];
WORD i;
WORD totcases = sw->maxcase-sw->mincase+1;
while ( *s == ',' ) s++;
SKIPBLANKS(s)
if ( *s ) {
MesPrint("&No parameters allowed in EndSwitch statement");
return(-1);
}
if ( sw->iflevel != AC.IfLevel || sw->whilelevel != AC.WhileLevel
|| sw->nestingsum != NestingChecksum() ) {
MesPrint("&Illegal nesting of switch/case/default with if/while/repeat/loop/argument/term/...");
return(-1);
}
if ( sw->defaultcase.value == 0 ) CoDefault(s);
if ( totcases > sw->numcases*AM.jumpratio ) { /* The factor is experimental */
sw->caseoffset = 0;
sw->typetable = SPARSETABLE;
/*
Now we need to sort sw->table
*/
SwitchSplitMerge(sw->table,sw->numcases);
}
else { /* DENSE */
SWITCHTABLE *ntable;
sw->caseoffset = sw->mincase;
sw->typetable = DENSETABLE;
ntable = (SWITCHTABLE *)Malloc1(totcases*sizeof(SWITCHTABLE),"Switch table");
for ( i = 0; i < totcases; i++ ) {
ntable[i].ncase = i+sw->caseoffset;
ntable[i].value = sw->defaultcase.value;
ntable[i].compbuffer = sw->defaultcase.compbuffer;
}
for ( i = 0; i < sw->numcases; i++ ) {
ntable[sw->table[i].ncase-sw->caseoffset] = sw->table[i];
}
M_free(sw->table,"Switch table");
sw->table = ntable;
sw->numcases = totcases;
}
sw->endswitch.ncase = 0;
sw->endswitch.value = cbuf[AC.cbufnum].numlhs;
sw->endswitch.compbuffer = AC.cbufnum;
if ( sw->defaultcase.value == 0 ) {
sw->defaultcase = sw->endswitch;
}
Add3Com(TYPEENDSWITCH,AC.SwitchHeap[AC.SwitchLevel]);
/*
Now we need to pop.
*/
AC.SwitchLevel--;
return(0);
}
/*
#] CoEndSwitch :
*/
form-master/sources/compiler.c 0000664 0000000 0000000 00000215360 13565763364 0017004 0 ustar 00root root 0000000 0000000 /** @file compiler.c
*
* The heart of the compiler.
* It contains the tables of statements.
* It finds the statements in the tables and calls the proper routines.
* For algebraic expressions it runs the compilation by first calling
* the tokenizer, splitting things into subexpressions and generating
* the code. There is a system for recognizing already existing
* subexpressions. This economizes on the length of the output.
*
* Note: the compiler of FORM doesn't attempt to normalize the input.
* Hence x+1 and 1+x are different objects during compilation.
* Similarly (a+b-b) will not be simplified to (a).
*/
/* #[ License : */
/*
* Copyright (C) 1984-2017 J.A.M. Vermaseren
* When using this file you are requested to refer to the publication
* J.A.M.Vermaseren "New features of FORM" math-ph/0010025
* This is considered a matter of courtesy as the development was paid
* for by FOM the Dutch physics granting agency and we would like to
* be able to track its scientific use to convince FOM of its value
* for the community.
*
* This file is part of FORM.
*
* FORM 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.
*
* FORM 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 FORM. If not, see .
*/
/* #] License : */
/*
#[ includes :
*/
#include "form3.h"
/*
com1commands are the commands of which only part of the word has to
be present. The order is rather important here.
com2commands are the commands that must have their whole word match.
here we can do a binary search.
{[(
*/
static KEYWORD com1commands[] = {
{"also", (TFUN)CoIdOld, STATEMENT, PARTEST}
,{"abrackets", (TFUN)CoAntiBracket, TOOUTPUT, PARTEST}
,{"antisymmetrize", (TFUN)CoAntiSymmetrize, STATEMENT, PARTEST}
,{"antibrackets", (TFUN)CoAntiBracket, TOOUTPUT, PARTEST}
,{"brackets", (TFUN)CoBracket, TOOUTPUT, PARTEST}
,{"cfunctions", (TFUN)CoCFunction, DECLARATION, PARTEST|WITHAUTO}
,{"commuting", (TFUN)CoCFunction, DECLARATION, PARTEST|WITHAUTO}
,{"compress", (TFUN)CoCompress, DECLARATION, PARTEST}
,{"ctensors", (TFUN)CoCTensor, DECLARATION, PARTEST|WITHAUTO}
,{"cyclesymmetrize",(TFUN)CoCycleSymmetrize, STATEMENT, PARTEST}
,{"dimension", (TFUN)CoDimension, DECLARATION, PARTEST}
,{"discard", (TFUN)CoDiscard, STATEMENT, PARTEST}
,{"functions", (TFUN)CoNFunction, DECLARATION, PARTEST|WITHAUTO}
,{"format", (TFUN)CoFormat, TOOUTPUT, PARTEST}
,{"fixindex", (TFUN)CoFixIndex, DECLARATION, PARTEST}
,{"global", (TFUN)CoGlobal, DEFINITION, PARTEST}
,{"gfactorized", (TFUN)CoGlobalFactorized, DEFINITION, PARTEST}
,{"globalfactorized",(TFUN)CoGlobalFactorized,DEFINITION, PARTEST}
,{"goto", (TFUN)CoGoTo, STATEMENT, PARTEST}
,{"indexes", (TFUN)CoIndex, DECLARATION, PARTEST|WITHAUTO}
,{"indices", (TFUN)CoIndex, DECLARATION, PARTEST|WITHAUTO}
,{"identify", (TFUN)CoId, STATEMENT, PARTEST}
,{"idnew", (TFUN)CoIdNew, STATEMENT, PARTEST}
,{"idold", (TFUN)CoIdOld, STATEMENT, PARTEST}
,{"local", (TFUN)CoLocal, DEFINITION, PARTEST}
,{"lfactorized", (TFUN)CoLocalFactorized, DEFINITION, PARTEST}
,{"localfactorized",(TFUN)CoLocalFactorized, DEFINITION, PARTEST}
,{"load", (TFUN)CoLoad, DECLARATION, PARTEST}
,{"label", (TFUN)CoLabel, STATEMENT, PARTEST}
,{"modulus", (TFUN)CoModulus, DECLARATION, PARTEST}
,{"multiply", (TFUN)CoMultiply, STATEMENT, PARTEST}
,{"nfunctions", (TFUN)CoNFunction, DECLARATION, PARTEST|WITHAUTO}
,{"nprint", (TFUN)CoNPrint, TOOUTPUT, PARTEST}
,{"ntensors", (TFUN)CoNTensor, DECLARATION, PARTEST|WITHAUTO}
,{"nwrite", (TFUN)CoNWrite, DECLARATION, PARTEST}
,{"print", (TFUN)CoPrint, MIXED, 0}
,{"redefine", (TFUN)CoRedefine, STATEMENT, 0}
,{"rcyclesymmetrize",(TFUN)CoRCycleSymmetrize,STATEMENT, PARTEST}
,{"symbols", (TFUN)CoSymbol, DECLARATION, PARTEST|WITHAUTO}
,{"save", (TFUN)CoSave, DECLARATION, PARTEST}
,{"symmetrize", (TFUN)CoSymmetrize, STATEMENT, PARTEST}
,{"tensors", (TFUN)CoCTensor, DECLARATION, PARTEST|WITHAUTO}
,{"unittrace", (TFUN)CoUnitTrace, DECLARATION, PARTEST}
,{"vectors", (TFUN)CoVector, DECLARATION, PARTEST|WITHAUTO}
,{"write", (TFUN)CoWrite, DECLARATION, PARTEST}
};
static KEYWORD com2commands[] = {
{"antiputinside", (TFUN)CoAntiPutInside, STATEMENT, PARTEST}
,{"apply", (TFUN)CoApply, STATEMENT, PARTEST}
,{"aputinside", (TFUN)CoAntiPutInside, STATEMENT, PARTEST}
,{"argexplode", (TFUN)CoArgExplode, STATEMENT, PARTEST}
,{"argimplode", (TFUN)CoArgImplode, STATEMENT, PARTEST}
,{"argtoextrasymbol",(TFUN)CoArgToExtraSymbol,STATEMENT, PARTEST}
,{"argument", (TFUN)CoArgument, STATEMENT, PARTEST}
,{"assign", (TFUN)CoAssign, STATEMENT, PARTEST}
,{"auto", (TFUN)CoAuto, DECLARATION, PARTEST}
,{"autodeclare", (TFUN)CoAuto, DECLARATION, PARTEST}
,{"break", (TFUN)CoBreak, STATEMENT, PARTEST}
,{"canonicalize", (TFUN)CoCanonicalize, STATEMENT, PARTEST}
,{"case", (TFUN)CoCase, STATEMENT, PARTEST}
,{"chainin", (TFUN)CoChainin, STATEMENT, PARTEST}
,{"chainout", (TFUN)CoChainout, STATEMENT, PARTEST}
,{"chisholm", (TFUN)CoChisholm, STATEMENT, PARTEST}
,{"cleartable", (TFUN)CoClearTable, DECLARATION, PARTEST}
,{"collect", (TFUN)CoCollect, SPECIFICATION,PARTEST}
,{"commuteinset", (TFUN)CoCommuteInSet, DECLARATION, PARTEST}
,{"contract", (TFUN)CoContract, STATEMENT, PARTEST}
,{"copyspectator" ,(TFUN)CoCopySpectator, DEFINITION, PARTEST}
,{"createspectator",(TFUN)CoCreateSpectator, DECLARATION, PARTEST}
,{"ctable", (TFUN)CoCTable, DECLARATION, PARTEST}
,{"deallocatetable",(TFUN)CoDeallocateTable, DECLARATION, PARTEST}
,{"default", (TFUN)CoDefault, STATEMENT, PARTEST}
,{"delete", (TFUN)CoDelete, SPECIFICATION,PARTEST}
,{"denominators", (TFUN)CoDenominators, STATEMENT, PARTEST}
,{"disorder", (TFUN)CoDisorder, STATEMENT, PARTEST}
,{"do", (TFUN)CoDo, STATEMENT, PARTEST}
,{"drop", (TFUN)CoDrop, SPECIFICATION,PARTEST}
,{"dropcoefficient",(TFUN)CoDropCoefficient, STATEMENT, PARTEST}
,{"dropsymbols", (TFUN)CoDropSymbols, STATEMENT, PARTEST}
,{"else", (TFUN)CoElse, STATEMENT, PARTEST}
,{"elseif", (TFUN)CoElseIf, STATEMENT, PARTEST}
,{"emptyspectator", (TFUN)CoEmptySpectator, SPECIFICATION,PARTEST}
,{"endargument", (TFUN)CoEndArgument, STATEMENT, PARTEST}
,{"enddo", (TFUN)CoEndDo, STATEMENT, PARTEST}
,{"endif", (TFUN)CoEndIf, STATEMENT, PARTEST}
,{"endinexpression",(TFUN)CoEndInExpression, STATEMENT, PARTEST}
,{"endinside", (TFUN)CoEndInside, STATEMENT, PARTEST}
,{"endrepeat", (TFUN)CoEndRepeat, STATEMENT, PARTEST}
,{"endswitch", (TFUN)CoEndSwitch, STATEMENT, PARTEST}
,{"endterm", (TFUN)CoEndTerm, STATEMENT, PARTEST}
,{"endwhile", (TFUN)CoEndWhile, STATEMENT, PARTEST}
,{"exit", (TFUN)CoExit, STATEMENT, PARTEST}
,{"extrasymbols", (TFUN)CoExtraSymbols, DECLARATION, PARTEST}
,{"factarg", (TFUN)CoFactArg, STATEMENT, PARTEST}
,{"factdollar", (TFUN)CoFactDollar, STATEMENT, PARTEST}
,{"factorize", (TFUN)CoFactorize, TOOUTPUT, PARTEST}
,{"fill", (TFUN)CoFill, DECLARATION, PARTEST}
,{"fillexpression", (TFUN)CoFillExpression, DECLARATION, PARTEST}
,{"frompolynomial", (TFUN)CoFromPolynomial, STATEMENT, PARTEST}
,{"funpowers", (TFUN)CoFunPowers, DECLARATION, PARTEST}
,{"hide", (TFUN)CoHide, SPECIFICATION,PARTEST}
,{"if", (TFUN)CoIf, STATEMENT, PARTEST}
,{"ifmatch", (TFUN)CoIfMatch, STATEMENT, PARTEST}
,{"ifnomatch", (TFUN)CoIfNoMatch, STATEMENT, PARTEST}
,{"ifnotmatch", (TFUN)CoIfNoMatch, STATEMENT, PARTEST}
,{"inexpression", (TFUN)CoInExpression, STATEMENT, PARTEST}
,{"inparallel", (TFUN)CoInParallel, SPECIFICATION,PARTEST}
,{"inside", (TFUN)CoInside, STATEMENT, PARTEST}
,{"insidefirst", (TFUN)CoInsideFirst, DECLARATION, PARTEST}
,{"intohide", (TFUN)CoIntoHide, SPECIFICATION,PARTEST}
,{"keep", (TFUN)CoKeep, SPECIFICATION,PARTEST}
,{"makeinteger", (TFUN)CoMakeInteger, STATEMENT, PARTEST}
,{"many", (TFUN)CoMany, STATEMENT, PARTEST}
,{"merge", (TFUN)CoMerge, STATEMENT, PARTEST}
,{"metric", (TFUN)CoMetric, DECLARATION, PARTEST}
,{"moduleoption", (TFUN)CoModuleOption, ATENDOFMODULE,PARTEST}
,{"multi", (TFUN)CoMulti, STATEMENT, PARTEST}
,{"multibracket", (TFUN)CoMultiBracket, STATEMENT, PARTEST}
,{"ndrop", (TFUN)CoNoDrop, SPECIFICATION,PARTEST}
,{"nfactorize", (TFUN)CoNFactorize, TOOUTPUT, PARTEST}
,{"nhide", (TFUN)CoNoHide, SPECIFICATION,PARTEST}
,{"normalize", (TFUN)CoNormalize, STATEMENT, PARTEST}
,{"notinparallel", (TFUN)CoNotInParallel, SPECIFICATION,PARTEST}
,{"nskip", (TFUN)CoNoSkip, SPECIFICATION,PARTEST}
,{"ntable", (TFUN)CoNTable, DECLARATION, PARTEST}
,{"nunfactorize", (TFUN)CoNUnFactorize, TOOUTPUT, PARTEST}
,{"nunhide", (TFUN)CoNoUnHide, SPECIFICATION,PARTEST}
,{"off", (TFUN)CoOff, DECLARATION, PARTEST}
,{"on", (TFUN)CoOn, DECLARATION, PARTEST}
,{"once", (TFUN)CoOnce, STATEMENT, PARTEST}
,{"only", (TFUN)CoOnly, STATEMENT, PARTEST}
,{"polyfun", (TFUN)CoPolyFun, DECLARATION, PARTEST}
,{"polyratfun", (TFUN)CoPolyRatFun, DECLARATION, PARTEST}
,{"pophide", (TFUN)CoPopHide, SPECIFICATION,PARTEST}
,{"print[]", (TFUN)CoPrintB, TOOUTPUT, PARTEST}
,{"printtable", (TFUN)CoPrintTable, MIXED, PARTEST}
,{"processbucketsize",(TFUN)CoProcessBucket, DECLARATION, PARTEST}
,{"propercount", (TFUN)CoProperCount, DECLARATION, PARTEST}
,{"pushhide", (TFUN)CoPushHide, SPECIFICATION,PARTEST}
,{"putinside", (TFUN)CoPutInside, STATEMENT, PARTEST}
,{"ratio", (TFUN)CoRatio, STATEMENT, PARTEST}
,{"removespectator",(TFUN)CoRemoveSpectator, SPECIFICATION,PARTEST}
,{"renumber", (TFUN)CoRenumber, STATEMENT, PARTEST}
,{"repeat", (TFUN)CoRepeat, STATEMENT, PARTEST}
,{"replaceloop", (TFUN)CoReplaceLoop, STATEMENT, PARTEST}
,{"select", (TFUN)CoSelect, STATEMENT, PARTEST}
,{"set", (TFUN)CoSet, DECLARATION, PARTEST}
,{"setexitflag", (TFUN)CoSetExitFlag, STATEMENT, PARTEST}
,{"shuffle", (TFUN)CoMerge, STATEMENT, PARTEST}
,{"skip", (TFUN)CoSkip, SPECIFICATION,PARTEST}
,{"sort", (TFUN)CoSort, STATEMENT, PARTEST}
,{"splitarg", (TFUN)CoSplitArg, STATEMENT, PARTEST}
,{"splitfirstarg", (TFUN)CoSplitFirstArg, STATEMENT, PARTEST}
,{"splitlastarg", (TFUN)CoSplitLastArg, STATEMENT, PARTEST}
,{"stuffle", (TFUN)CoStuffle, STATEMENT, PARTEST}
,{"sum", (TFUN)CoSum, STATEMENT, PARTEST}
,{"switch", (TFUN)CoSwitch, STATEMENT, PARTEST}
,{"table", (TFUN)CoTable, DECLARATION, PARTEST}
,{"tablebase", (TFUN)CoTableBase, DECLARATION, PARTEST}
,{"tb", (TFUN)CoTableBase, DECLARATION, PARTEST}
,{"term", (TFUN)CoTerm, STATEMENT, PARTEST}
,{"testuse", (TFUN)CoTestUse, STATEMENT, PARTEST}
,{"threadbucketsize",(TFUN)CoThreadBucket, DECLARATION, PARTEST}
,{"topolynomial", (TFUN)CoToPolynomial, STATEMENT, PARTEST}
,{"tospectator", (TFUN)CoToSpectator, STATEMENT, PARTEST}
,{"totensor", (TFUN)CoToTensor, STATEMENT, PARTEST}
,{"tovector", (TFUN)CoToVector, STATEMENT, PARTEST}
,{"trace4", (TFUN)CoTrace4, STATEMENT, PARTEST}
,{"tracen", (TFUN)CoTraceN, STATEMENT, PARTEST}
,{"transform", (TFUN)CoTransform, STATEMENT, PARTEST}
,{"tryreplace", (TFUN)CoTryReplace, STATEMENT, PARTEST}
,{"unfactorize", (TFUN)CoUnFactorize, TOOUTPUT, PARTEST}
,{"unhide", (TFUN)CoUnHide, SPECIFICATION,PARTEST}
,{"while", (TFUN)CoWhile, STATEMENT, PARTEST}
};
int alfatable1[27];
#define OPTION0 1
#define OPTION1 2
#define OPTION2 3
typedef struct SuBbUf {
WORD subexpnum;
WORD buffernum;
} SUBBUF;
SUBBUF *subexpbuffers = 0;
SUBBUF *topsubexpbuffers = 0;
LONG insubexpbuffers = 0;
#define REDUCESUBEXPBUFFERS { if ( (topsubexpbuffers-subexpbuffers) > 256 ) {\
M_free(subexpbuffers,"subexpbuffers");\
subexpbuffers = (SUBBUF *)Malloc1(256*sizeof(SUBBUF),"subexpbuffers");\
topsubexpbuffers = subexpbuffers+256; } insubexpbuffers = 0; }
#if defined(ILP32)
#define PUTNUMBER128(t,n) { if ( n >= 16384 ) { \
*t++ = n/(128*128); *t++ = (n/128)%128; *t++ = n%128; } \
else if ( n >= 128 ) { *t++ = n/128; *t++ = n%128; } \
else *t++ = n; }
#define PUTNUMBER100(t,n) { if ( n >= 10000 ) { \
*t++ = n/10000; *t++ = (n/100)%100; *t++ = n%100; } \
else if ( n >= 100 ) { *t++ = n/100; *t++ = n%100; } \
else *t++ = n; }
#elif ( defined(LLP64) || defined(LP64) )
#define PUTNUMBER128(t,n) { if ( n >= 2097152 ) { \
*t++ = ((n/128)/128)/128; *t++ = ((n/128)/128)%128; *t++ = (n/128)%128; *t++ = n%128; } \
else if ( n >= 16384 ) { \
*t++ = n/(128*128); *t++ = (n/128)%128; *t++ = n%128; } \
else if ( n >= 128 ) { *t++ = n/128; *t++ = n%128; } \
else *t++ = n; }
#define PUTNUMBER100(t,n) { if ( n >= 1000000 ) { \
*t++ = ((n/100)/100)/100; *t++ = ((n/100)/100)%100; *t++ = (n/100)%100; *t++ = n%100; } \
else if ( n >= 10000 ) { \
*t++ = n/10000; *t++ = (n/100)%100; *t++ = n%100; } \
else if ( n >= 100 ) { *t++ = n/100; *t++ = n%100; } \
else *t++ = n; }
#endif
/*
)]}
#] includes :
#[ Compiler :
#[ inictable :
Routine sets the table for 1-st characters that allow a faster
start in the search in table 1 which should be sequential.
Search in table 2 can be binary.
*/
VOID inictable()
{
KEYWORD *k = com1commands;
int i, j, ksize;
ksize = sizeof(com1commands)/sizeof(KEYWORD);
j = 0;
alfatable1[0] = 0;
for ( i = 0; i < 26; i++ ) {
while ( j < ksize && k[j].name[0] == 'a'+i ) j++;
alfatable1[i+1] = j;
}
}
/*
#] inictable :
#[ findcommand :
Checks whether a command is in the command table.
If so a pointer to the table element is returned.
If not we return 0.
Note that when a command is not in the table, we have
to test whether it is an id command without id. It should
then have the structure pattern = rhs. This should be done
in the calling routine.
*/
KEYWORD *findcommand(UBYTE *in)
{
int hi, med, lo, i;
UBYTE *s, c;
s = in;
while ( FG.cTable[*s] <= 1 ) s++;
if ( s > in && *s == '[' && s[1] == ']' ) s += 2;
if ( *s ) { c = *s; *s = 0; }
else c = 0;
/*
First do a binary search in the second table
*/
lo = 0;
hi = sizeof(com2commands)/sizeof(KEYWORD)-1;
do {
med = ( hi + lo ) / 2;
i = StrICmp(in,(UBYTE *)com2commands[med].name);
if ( i == 0 ) { if ( c ) *s = c; return(com2commands+med); }
if ( i < 0 ) hi = med-1;
else lo = med+1;
} while ( hi >= lo );
/*
Now do a 'hashed' search in the first table. It is sequential.
*/
i = tolower(*in) - 'a';
med = alfatable1[i];
hi = alfatable1[i+1];
while ( med < hi ) {
if ( StrICont(in,(UBYTE *)com1commands[med].name) == 0 )
{ if ( c ) *s = c; return(com1commands+med); }
med++;
}
if ( c ) *s = c;
/*
Unrecognized. Too bad!
*/
return(0);
}
/*
#] findcommand :
#[ ParenthesesTest :
*/
int ParenthesesTest(UBYTE *sin)
{
WORD L1 = 0, L2 = 0, L3 = 0;
UBYTE *s = sin;
while ( *s ) {
if ( *s == '[' ) L1++;
else if ( *s == ']' ) {
L1--;
if ( L1 < 0 ) { MesPrint("&Unmatched []"); return(1); }
}
s++;
}
if ( L1 > 0 ) { MesPrint("&Unmatched []"); return(1); }
s = sin;
while ( *s ) {
if ( *s == '[' ) SKIPBRA1(s)
else if ( *s == '(' ) { L2++; s++; }
else if ( *s == ')' ) {
L2--; s++;
if ( L2 < 0 ) { MesPrint("&Unmatched ()"); return(1); }
}
else s++;
}
if ( L2 > 0 ) { MesPrint("&Unmatched ()"); return(1); }
s = sin;
while ( *s ) {
if ( *s == '[' ) SKIPBRA1(s)
else if ( *s == '[' ) SKIPBRA4(s)
else if ( *s == '{' ) { L3++; s++; }
else if ( *s == '}' ) {
L3--; s++;
if ( L3 < 0 ) { MesPrint("&Unmatched {}"); return(1); }
}
else s++;
}
if ( L3 > 0 ) { MesPrint("&Unmatched {}"); return(1); }
return(0);
}
/*
#] ParenthesesTest :
#[ SkipAName :
Skips a name and gives a pointer to the object after the name.
If there is not a proper name, it returns a zero pointer.
In principle the brackets match already, so the `if ( *s == 0 )'
code is not really needed, but you never know how the program
is extended later.
*/
UBYTE *SkipAName(UBYTE *s)
{
UBYTE *t = s;
if ( *s == '[' ) {
SKIPBRA1(s)
if ( *s == 0 ) {
MesPrint("&Illegal name: '%s'",t);
return(0);
}
s++;
}
else if ( FG.cTable[*s] == 0 || *s == '_' || *s == '$' ) {
if ( *s == '$' ) s++;
while ( FG.cTable[*s] <= 1 ) s++;
if ( *s == '_' ) s++;
}
else {
MesPrint("&Illegal name: '%s'",t);
return(0);
}
return(s);
}
/*
#] SkipAName :
#[ IsRHS :
*/
UBYTE *IsRHS(UBYTE *s, UBYTE c)
{
while ( *s && *s != c ) {
if ( *s == '[' ) {
SKIPBRA1(s);
if ( *s != ']' ) {
MesPrint("&Unmatched []");
return(0);
}
}
else if ( *s == '{' ) {
SKIPBRA2(s);
if ( *s != '}' ) {
MesPrint("&Unmatched {}");
return(0);
}
}
else if ( *s == '(' ) {
SKIPBRA3(s);
if ( *s != ')' ) {
MesPrint("&Unmatched ()");
return(0);
}
}
else if ( *s == ')' ) {
MesPrint("&Unmatched ()");
return(0);
}
else if ( *s == '}' ) {
MesPrint("&Unmatched {}");
return(0);
}
else if ( *s == ']' ) {
MesPrint("&Unmatched []");
return(0);
}
s++;
}
return(s);
}
/*
#] IsRHS :
#[ IsIdStatement :
*/
int IsIdStatement(UBYTE *s)
{
DUMMYUSE(s);
return(0);
}
/*
#] IsIdStatement :
#[ CompileAlgebra :
Returns either the number of the main level RHS (>= 0)
or an error code (< 0)
*/
int CompileAlgebra(UBYTE *s, int leftright, WORD *prototype)
{
GETIDENTITY
int error;
WORD *oldproto = AC.ProtoType;
AC.ProtoType = prototype;
if ( AC.TokensWriteFlag ) {
MesPrint("To tokenize: %s",s);
error = tokenize(s,leftright);
MesPrint(" The contents of the token buffer are:");
WriteTokens(AC.tokens);
}
else error = tokenize(s,leftright);
if ( error == 0 ) {
AR.Eside = leftright;
AC.CompileLevel = 0;
if ( leftright == LHSIDE ) { AC.DumNum = AR.CurDum = 0; }
error = CompileSubExpressions(AC.tokens);
REDUCESUBEXPBUFFERS
}
else {
AC.ProtoType = oldproto;
return(-1);
}
AC.ProtoType = oldproto;
if ( error < 0 ) return(-1);
else if ( leftright == LHSIDE ) return(cbuf[AC.cbufnum].numlhs);
else return(cbuf[AC.cbufnum].numrhs);
}
/*
#] CompileAlgebra :
#[ CompileStatement :
*/
int CompileStatement(UBYTE *in)
{
KEYWORD *k;
UBYTE *s;
int error1 = 0, error2;
/* A.iStatement = */ s = in;
if ( *s == 0 ) return(0);
if ( *s == '$' ) {
k = findcommand((UBYTE *)"assign");
}
else {
if ( ( k = findcommand(s) ) == 0 && IsIdStatement(s) == 0 ) {
MesPrint("&Unrecognized statement");
return(1);
}
if ( k == 0 ) { /* Id statement without id. Note: id must be in table */
k = com1commands + alfatable1['i'-'a'];
while ( k->name[1] != 'd' || k->name[2] ) k++;
}
else {
while ( FG.cTable[*s] <= 1 ) s++;
if ( s > in && *s == '[' && s[1] == ']' ) s += 2;
/*
The next statement is rather mysterious
It is undone in DoPrint and CoMultiply, but it also causes effects
in other (wrong) statements like dimension -4; or Trace4 -1;
The code in pre.c (LoadStatement) has been changed 8-sep-2009
to force a comma after the keyword. This means that the
'mysterious' line is automatically inactive. Hence it is taken out.
if ( *s == '+' || *s == '-' ) s++;
*/
if ( *s == ',' ) s++;
}
}
/*
First the test on the order of the statements.
This is relatively new (2.2c) and may cause some problems with old
programs. Hence the first error message should explain!
*/
if ( AP.PreAssignFlag == 0 && AM.OldOrderFlag == 0 ) {
if ( AP.PreInsideLevel ) {
if ( k->type != STATEMENT && k->type != MIXED ) {
MesPrint("&Only executable and print statements are allowed in an %#inside/%#endinside construction");
return(-1);
}
}
else {
if ( ( AC.compiletype == DECLARATION || AC.compiletype == SPECIFICATION )
&& ( k->type == STATEMENT || k->type == DEFINITION || k->type == TOOUTPUT ) ) {
if ( AC.tablecheck == 0 ) {
AC.tablecheck = 1;
if ( TestTables() ) error1 = 1;
}
}
if ( k->type == MIXED ) {
if ( AC.compiletype <= DEFINITION ) {
AC.compiletype = STATEMENT;
}
}
else if ( k->type > AC.compiletype ) {
if ( StrCmp((UBYTE *)(k->name),(UBYTE *)"format") != 0 )
AC.compiletype = k->type;
}
else if ( k->type < AC.compiletype ) {
switch ( k->type ) {
case DECLARATION:
MesPrint("&Declaration out of order");
MesPrint("& %s",in);
break;
case DEFINITION:
MesPrint("&Definition out of order");
MesPrint("& %s",in);
break;
case SPECIFICATION:
MesPrint("&Specification out of order");
MesPrint("& %s",in);
break;
case STATEMENT:
MesPrint("&Statement out of order");
break;
case TOOUTPUT:
MesPrint("&Output control statement out of order");
MesPrint("& %s",in);
break;
}
AC.compiletype = k->type;
if ( AC.firstctypemessage == 0 ) {
MesPrint("&Proper order inside a module is:");
MesPrint("Declarations, specifications, definitions, statements, output control statements");
AC.firstctypemessage = 1;
}
error1 = 1;
}
}
}
/*
Now we execute the tests that are prescribed by the flags.
*/
if ( AC.AutoDeclareFlag && ( ( k->flags & WITHAUTO ) == 0 ) ) {
MesPrint("&Illegal type of auto-declaration");
return(1);
}
if ( ( ( k->flags & PARTEST ) != 0 ) && ParenthesesTest(s) ) return(1);
error2 = (*k->func)(s);
if ( error2 == 0 ) return(error1);
return(error2);
}
/*
#] CompileStatement :
#[ TestTables :
*/
int TestTables()
{
FUNCTIONS f = functions;
TABLES t;
WORD j;
int error = 0, i;
LONG x;
i = NumFunctions + FUNCTION - MAXBUILTINFUNCTION - 1;
f = f + MAXBUILTINFUNCTION - FUNCTION + 1;
if ( AC.MustTestTable > 0 ) {
while ( i > 0 ) {
if ( ( t = f->tabl ) != 0 && t->strict > 0 && !t->sparse ) {
for ( x = 0, j = 0; x < t->totind; x++ ) {
if ( t->tablepointers[TABLEEXTENSION*x] < 0 ) j++;
}
if ( j > 0 ) {
if ( j > 1 ) {
MesPrint("&In table %s there are %d unfilled elements",
AC.varnames->namebuffer+f->name,j);
}
else {
MesPrint("&In table %s there is one unfilled element",
AC.varnames->namebuffer+f->name);
}
error = 1;
}
}
i--; f++;
}
AC.MustTestTable--;
}
return(error);
}
/*
#] TestTables :
#[ CompileSubExpressions :
Now we attack the subexpressions from inside out.
We try to see whether we had any of them already.
We have to worry about adding the wildcard sum parameter
to the prototype.
*/
int CompileSubExpressions(SBYTE *tokens)
{
GETIDENTITY
SBYTE *fill = tokens, *s = tokens, *t;
WORD number[MAXNUMSIZE], *oldwork, *w1, *w2;
int level, num, i, sumlevel = 0, sumtype = SYMTOSYM;
int retval, error = 0;
/*
Eliminate all subexpressions. They are marked by LPARENTHESIS,RPARENTHESIS
*/
AC.CompileLevel++;
while ( *s != TENDOFIT ) {
if ( *s == TFUNOPEN ) {
if ( fill < s ) *fill = TENDOFIT;
t = fill - 1;
while ( t >= tokens && t[0] >= 0 ) t--;
if ( t >= tokens && *t == TFUNCTION ) {
t++; i = 0; while ( *t >= 0 ) i = 128*i + *t++;
if ( i == AM.sumnum || i == AM.sumpnum ) {
t = s + 1;
if ( *t == TSYMBOL || *t == TINDEX ) {
t++; i = 0; while ( *t >= 0 ) i = 128*i + *t++;
if ( s[1] == TINDEX ) {
i += AM.OffsetIndex;
sumtype = INDTOIND;
}
else sumtype = SYMTOSYM;
sumlevel = i;
}
}
}
*fill++ = *s++;
}
else if ( *s == TFUNCLOSE ) { sumlevel = 0; *fill++ = *s++; }
else if ( *s == LPARENTHESIS ) {
/*
We must make an exception here.
If the subexpression is just an integer, whatever its length,
we should try to keep it.
This is important when we have a function with an integer
argument. In particular this is relevant for the MZV program.
*/
t = s; level = 0;
while ( level >= 0 ) {
s++;
if ( *s == LPARENTHESIS ) level++;
else if ( *s == RPARENTHESIS ) level--;
else if ( *s == TENDOFIT ) {
MesPrint("&Unbalanced subexpression parentheses");
return(-1);
}
}
t++; *s = TENDOFIT;
if ( sumlevel > 0 ) { /* Inside sum. Add wildcard to prototype */
oldwork = w1 = AT.WorkPointer;
w2 = AC.ProtoType;
i = w2[1];
while ( --i >= 0 ) *w1++ = *w2++;
oldwork[1] += 4;
*w1++ = sumtype; *w1++ = 4; *w1++ = sumlevel; *w1++ = sumlevel;
w2 = AC.ProtoType; AT.WorkPointer = w1;
AC.ProtoType = oldwork;
num = CompileSubExpressions(t);
AC.ProtoType = w2; AT.WorkPointer = oldwork;
}
else num = CompileSubExpressions(t);
if ( num < 0 ) return(-1);
/*
Note that the subexpression code should always fit.
We had two parentheses and at least two bytes contents.
There cannot be more than 2^21 subexpressions or we get outside
this minimum. Ignoring this might lead to really rare and
hard to find errors, years from now.
*/
if ( insubexpbuffers >= MAXSUBEXPRESSIONS ) {
MesPrint("&More than %d subexpressions inside one expression",(WORD)MAXSUBEXPRESSIONS);
Terminate(-1);
}
if ( subexpbuffers+insubexpbuffers >= topsubexpbuffers ) {
DoubleBuffer((void **)((VOID *)(&subexpbuffers))
,(void **)((VOID *)(&topsubexpbuffers)),sizeof(SUBBUF),"subexpbuffers");
}
subexpbuffers[insubexpbuffers].subexpnum = num;
subexpbuffers[insubexpbuffers].buffernum = AC.cbufnum;
num = insubexpbuffers++;
*fill++ = TSUBEXP;
i = 0;
do { number[i++] = num & 0x7F; num >>= 7; } while ( num );
while ( --i >= 0 ) *fill++ = (SBYTE)(number[i]);
s++;
}
else if ( *s == TEMPTY ) s++;
else *fill++ = *s++;
}
*fill = TENDOFIT;
/*
At this stage there are no more subexpressions.
Hence we can do the basic compilation.
*/
if ( AC.CompileLevel == 1 && AC.ToBeInFactors ) {
error = CodeFactors(tokens);
}
AC.CompileLevel--;
retval = CodeGenerator(tokens);
if ( error < 0 ) return(error);
return(retval);
}
/*
#] CompileSubExpressions :
#[ CodeGenerator :
This routine does the real code generation.
It returns the number of the rhs subexpression.
At this point we do not have to worry about subexpressions,
sets, setelements, simple vs complicated function arguments
simple vs complicated powers etc.
The variable 'first' indicates whether we are starting a new term
The major complication are the set elements of type set[n].
We have marked them as TSETNUM,n,Ttype,setnum
They go into
SETSET,size,subterm,relocation list
in which the subterm should be ready to become a regular
subterm in which the sets have been replaced by their element
The relocation list consists of pairs of numbers:
1: offset in the subterm, 2: the symbol n.
Note that such a subterm can be a whole function with its arguments.
We use the variable inset to indicate that we have something going.
The relocation list is collected in the top of the WorkSpace.
*/
static UWORD *CGscrat7 = 0;
int CodeGenerator(SBYTE *tokens)
{
GETIDENTITY
SBYTE *s = tokens, c;
int i, sign = 1, first = 1, deno = 1, error = 0, minus, n, needarg, numexp, cc;
int base, sumlevel = 0, sumtype = SYMTOSYM, firstsumarg, inset = 0;
int funflag = 0, settype, x1, x2, mulflag = 0;
WORD *t, *v, *r, *term, nnumerator, ndenominator, *oldwork, x3, y, nin;
WORD *w1, *w2, *tsize = 0, *relo = 0;
UWORD *numerator, *denominator, *innum;
CBUF *C;
POSITION position;
WORD TMproto[SUBEXPSIZE];
/*
#ifdef WITHPTHREADS
RENUMBER renumber;
#endif
*/
RENUMBER renumber;
if ( AC.TokensWriteFlag ) WriteTokens(tokens);
if ( CGscrat7 == 0 )
CGscrat7 = (UWORD *)Malloc1((AM.MaxTal+2)*sizeof(WORD),"CodeGenerator");
AddRHS(AC.cbufnum,0);
C = cbuf + AC.cbufnum;
numexp = C->numrhs;
C->NumTerms[numexp] = 0;
C->numdum[numexp] = 0;
oldwork = AT.WorkPointer;
numerator = (UWORD *)(AT.WorkPointer);
denominator = numerator + 2*AM.MaxTal;
innum = denominator + 2*AM.MaxTal;
term = (WORD *)(innum + 2*AM.MaxTal);
AT.WorkPointer = term + AM.MaxTer/sizeof(WORD);
if ( AT.WorkPointer > AT.WorkTop ) goto OverWork;
cc = 0;
t = term+1;
numerator[0] = denominator[0] = 1;
nnumerator = ndenominator = 1;
while ( *s != TENDOFIT ) {
if ( *s == TPLUS || *s == TMINUS ) {
if ( first || mulflag ) { if ( *s == TMINUS ) sign = -sign; }
else {
*term = t-term;
C->NumTerms[numexp]++;
if ( cc && sign ) C->CanCommu[numexp]++;
CompleteTerm(term,numerator,denominator,nnumerator,ndenominator,sign);
first = 1; cc = 0; t = term + 1; deno = 1;
numerator[0] = denominator[0] = 1;
nnumerator = ndenominator = 1;
if ( *s == TMINUS ) sign = -1;
else sign = 1;
}
s++;
}
else {
mulflag = first = 0; c = *s++;
switch ( c ) {
case TSYMBOL:
x1 = 0; while ( *s >= 0 ) { x1 = x1*128 + *s++; }
if ( *s == TWILDCARD ) { s++; x1 += 2*MAXPOWER; }
*t++ = SYMBOL; *t++ = 4; *t++ = x1;
if ( inset ) *relo = 2;
TryPower: if ( *s == TPOWER ) {
s++;
if ( *s == TMINUS ) { s++; deno = -deno; }
c = *s++;
base = ( c == TNUMBER ) ? 100: 128;
x2 = 0; while ( *s >= 0 ) { x2 = base*x2 + *s++; }
if ( c == TSYMBOL ) {
if ( *s == TWILDCARD ) s++;
x2 += 2*MAXPOWER;
}
*t++ = deno*x2;
}
else *t++ = deno;
fin: deno = 1;
if ( inset ) {
while ( relo < AT.WorkTop ) *t++ = *relo++;
inset = 0; tsize[1] = t - tsize;
}
break;
case TINDEX:
x1 = 0; while ( *s >= 0 ) { x1 = x1*128 + *s++; }
*t++ = INDEX; *t++ = 3;
if ( *s == TWILDCARD ) { s++; x1 += WILDOFFSET; }
if ( inset ) { *t++ = x1; *relo = 2; }
else *t++ = x1 + AM.OffsetIndex;
if ( t[-1] > AM.IndDum ) {
x1 = t[-1] - AM.IndDum;
if ( x1 > C->numdum[numexp] ) C->numdum[numexp] = x1;
}
goto fin;
case TGENINDEX:
*t++ = INDEX; *t++ = 3; *t++ = AC.DumNum+WILDOFFSET;
deno = 1;
break;
case TVECTOR:
x1 = 0; while ( *s >= 0 ) { x1 = x1*128 + *s++; }
dovector: if ( inset == 0 ) x1 += AM.OffsetVector;
if ( *s == TWILDCARD ) { s++; x1 += WILDOFFSET; }
if ( inset ) *relo = 2;
if ( *s == TDOT ) { /* DotProduct ? */
s++;
if ( *s == TSETNUM || *s == TSETDOL ) {
settype = ( *s == TSETDOL );
s++; x2 = 0; while ( *s >= 0 ) { x2 = x2*128 + *s++; }
if ( settype ) x2 = -x2;
if ( inset == 0 ) {
tsize = t; *t++ = SETSET; *t++ = 0;
relo = AT.WorkTop;
}
inset += 2;
*--relo = x2; *--relo = 3;
}
if ( *s != TVECTOR && *s != TDUBIOUS ) {
MesPrint("&Illegally formed dotproduct");
error = 1;
}
s++; x2 = 0; while ( *s >= 0 ) { x2 = x2*128 + *s++; }
if ( inset < 2 ) x2 += AM.OffsetVector;
if ( *s == TWILDCARD ) { s++; x2 += WILDOFFSET; }
*t++ = DOTPRODUCT; *t++ = 5; *t++ = x1; *t++ = x2;
goto TryPower;
}
else if ( *s == TFUNOPEN ) {
s++;
if ( *s == TSETNUM || *s == TSETDOL ) {
settype = ( *s == TSETDOL );
s++; x2 = 0; while ( *s >= 0 ) { x2 = x2*128 + *s++; }
if ( settype ) x2 = -x2;
if ( inset == 0 ) {
tsize = t; *t++ = SETSET; *t++ = 0;
relo = AT.WorkTop;
}
inset += 2;
*--relo = x2; *--relo = 3;
}
if ( *s == TINDEX || *s == TDUBIOUS ) {
s++;
x2 = 0; while ( *s >= 0 ) { x2 = x2*128 + *s++; }
if ( inset < 2 ) x2 += AM.OffsetIndex;
if ( *s == TWILDCARD ) { s++; x2 += WILDOFFSET; }
*t++ = VECTOR; *t++ = 4; *t++ = x1; *t++ = x2;
if ( t[-1] > AM.IndDum ) {
x2 = t[-1] - AM.IndDum;
if ( x2 > C->numdum[numexp] ) C->numdum[numexp] = x2;
}
}
else if ( *s == TGENINDEX ) {
*t++ = VECTOR; *t++ = 4; *t++ = x1;
*t++ = AC.DumNum + WILDOFFSET;
}
else if ( *s == TNUMBER || *s == TNUMBER1 ) {
base = ( *s == TNUMBER ) ? 100: 128;
s++;
x2 = 0; while ( *s >= 0 ) { x2 = x2*base + *s++; }
if ( x2 >= AM.OffsetIndex && inset < 2 ) {
MesPrint("&Fixed index in vector greater than %d",
AM.OffsetIndex);
return(-1);
}
*t++ = VECTOR; *t++ = 4; *t++ = x1; *t++ = x2;
}
else if ( *s == TVECTOR || ( *s == TMINUS && s[1] == TVECTOR ) ) {
if ( *s == TMINUS ) { s++; sign = -sign; }
s++;
x2 = 0; while ( *s >= 0 ) { x2 = x2*128 + *s++; }
if ( inset < 2 ) x2 += AM.OffsetVector;
if ( *s == TWILDCARD ) { s++; x2 += WILDOFFSET; }
*t++ = DOTPRODUCT; *t++ = 5; *t++ = x1; *t++ = x2; *t++ = deno;
}
else {
MesPrint("&Illegal argument for vector");
return(-1);
}
if ( *s != TFUNCLOSE ) {
MesPrint("&Illegal argument for vector");
return(-1);
}
s++;
}
else {
if ( AC.DumNum ) {
*t++ = VECTOR; *t++ = 4; *t++ = x1;
*t++ = AC.DumNum + WILDOFFSET;
}
else {
*t++ = INDEX; *t++ = 3; *t++ = x1;
}
}
goto fin;
case TDELTA:
if ( *s != TFUNOPEN ) {
MesPrint("&d_ needs two arguments");
error = -1;
}
v = t; *t++ = DELTA; *t++ = 4;
needarg = 2; x3 = x1 = -1;
goto dotensor;
case TFUNCTION:
x1 = 0; while ( *s >= 0 ) { x1 = x1*128 + *s++; }
if ( x1 == AM.sumnum || x1 == AM.sumpnum ) sumlevel = x1;
x1 += FUNCTION;
if ( x1 == FIRSTBRACKET ) {
if ( s[0] == TFUNOPEN && s[1] == TEXPRESSION ) {
doexpr: s += 2;
*t++ = x1; *t++ = FUNHEAD+2; *t++ = 0;
if ( x1 == AR.PolyFun && AR.PolyFunType == 2 && AR.Eside != LHSIDE )
t[-1] |= MUSTCLEANPRF;
FILLFUN3(t)
x2 = 0; while ( *s >= 0 ) { x2 = x2*128 + *s++; }
*t++ = -EXPRESSION; *t++ = x2;
/*
The next code is added to facilitate parallel processing
We need to call GetTable here to make sure all processors
have the same numbering of all variables.
*/
if ( Expressions[x2].status == STOREDEXPRESSION ) {
TMproto[0] = EXPRESSION;
TMproto[1] = SUBEXPSIZE;
TMproto[2] = x2;
TMproto[3] = 1;
{ int ie; for ( ie = 4; ie < SUBEXPSIZE; ie++ ) TMproto[ie] = 0; }
AT.TMaddr = TMproto;
PUTZERO(position);
/*
if ( (
#ifdef WITHPTHREADS
renumber =
#endif
GetTable(x2,&position,0) ) == 0 ) {
error = 1;
MesPrint("&Problems getting information about stored expression %s(1)"
,EXPRNAME(x2));
}
#ifdef WITHPTHREADS
M_free(renumber->symb.lo,"VarSpace");
M_free(renumber,"Renumber");
#endif
*/
if ( ( renumber = GetTable(x2,&position,0) ) == 0 ) {
error = 1;
MesPrint("&Problems getting information about stored expression %s(1)"
,EXPRNAME(x2));
}
if ( renumber->symb.lo != AN.dummyrenumlist )
M_free(renumber->symb.lo,"VarSpace");
M_free(renumber,"Renumber");
AR.StoreData.dirtyflag = 1;
}
if ( *s != TFUNCLOSE ) {
if ( x1 == FIRSTBRACKET )
MesPrint("&Problems with argument of FirstBracket_");
else if ( x1 == FIRSTTERM )
MesPrint("&Problems with argument of FirstTerm_");
else if ( x1 == CONTENTTERM )
MesPrint("&Problems with argument of FirstTerm_");
else if ( x1 == TERMSINEXPR )
MesPrint("&Problems with argument of TermsIn_");
else if ( x1 == SIZEOFFUNCTION )
MesPrint("&Problems with argument of SizeOf_");
else if ( x1 == NUMFACTORS )
MesPrint("&Problems with argument of NumFactors_");
else
MesPrint("&Problems with argument of FactorIn_");
error = 1;
while ( *s != TENDOFIT && *s != TFUNCLOSE ) s++;
}
if ( *s == TFUNCLOSE ) s++;
goto fin;
}
}
else if ( x1 == TERMSINEXPR || x1 == SIZEOFFUNCTION || x1 == FACTORIN
|| x1 == NUMFACTORS || x1 == FIRSTTERM || x1 == CONTENTTERM ) {
if ( s[0] == TFUNOPEN && s[1] == TEXPRESSION ) goto doexpr;
if ( s[0] == TFUNOPEN && s[1] == TDOLLAR ) {
s += 2;
*t++ = x1; *t++ = FUNHEAD+2; *t++ = 0;
FILLFUN3(t)
x2 = 0; while ( *s >= 0 ) { x2 = x2*128 + *s++; }
*t++ = -DOLLAREXPRESSION; *t++ = x2;
if ( *s != TFUNCLOSE ) {
if ( x1 == TERMSINEXPR )
MesPrint("&Problems with argument of TermsIn_");
else if ( x1 == SIZEOFFUNCTION )
MesPrint("&Problems with argument of SizeOf_");
else if ( x1 == NUMFACTORS )
MesPrint("&Problems with argument of NumFactors_");
else
MesPrint("&Problems with argument of FactorIn_");
error = 1;
while ( *s != TENDOFIT && *s != TFUNCLOSE ) s++;
}
if ( *s == TFUNCLOSE ) s++;
goto fin;
}
}
x3 = x1;
if ( inset && ( t-tsize == 2 ) ) x1 -= FUNCTION;
if ( *s == TWILDCARD ) { x1 += WILDOFFSET; s++; }
if ( functions[x3-FUNCTION].commute ) cc = 1;
if ( *s != TFUNOPEN ) {
*t++ = x1; *t++ = FUNHEAD; *t++ = 0;
if ( x1 == AR.PolyFun && AR.PolyFunType == 2 && AR.Eside != LHSIDE )
t[-1] |= MUSTCLEANPRF;
FILLFUN3(t) sumlevel = 0; goto fin;
}
v = t; *t++ = x1; *t++ = FUNHEAD; *t++ = DIRTYFLAG;
if ( x1 == AR.PolyFun && AR.PolyFunType == 2 && AR.Eside != LHSIDE )
t[-1] |= MUSTCLEANPRF;
FILLFUN3(t)
needarg = -1;
if ( !inset && functions[x3-FUNCTION].spec >= TENSORFUNCTION ) {
dotensor:
do {
if ( needarg == 0 ) {
if ( x1 >= 0 ) {
x3 = x1;
if ( x3 >= FUNCTION+WILDOFFSET ) x3 -= WILDOFFSET;
MesPrint("&Too many arguments in function %s",
VARNAME(functions,(x3-FUNCTION)) );
}
else
MesPrint("&d_ needs exactly two arguments");
error = -1;
needarg--;
}
else if ( needarg > 0 ) needarg--;
s++;
c = *s++;
if ( c == TMINUS && *s == TVECTOR ) { sign = -sign; c = *s++; }
base = ( c == TNUMBER ) ? 100: 128;
x2 = 0; while ( *s >= 0 ) { x2 = base*x2 + *s++; }
if ( *s == TWILDCARD && c != TNUMBER ) { x2 += WILDOFFSET; s++; }
if ( c == TSETNUM || c == TSETDOL ) {
if ( c == TSETDOL ) x2 = -x2;
if ( inset == 0 ) {
w1 = t; t += 2; w2 = t;
while ( w1 > v ) *--w2 = *--w1;
tsize = v; relo = AT.WorkTop;
*v++ = SETSET; *v++ = 0;
}
inset = 2; *--relo = x2; *--relo = t - v;
c = *s++;
x2 = 0; while ( *s >= 0 ) x2 = 128*x2 + *s++;
switch ( c ) {
case TINDEX:
*t++ = x2;
if ( t[-1]+AM.OffsetIndex > AM.IndDum ) {
x2 = t[-1]+AM.OffsetIndex - AM.IndDum;
if ( x2 > C->numdum[numexp] ) C->numdum[numexp] = x2;
}
break;
case TVECTOR:
*t++ = x2; break;
case TNUMBER1:
if ( x2 >= 0 && x2 < AM.OffsetIndex ) {
*t++ = x2; break;
}
/* fall through */
default:
MesPrint("&Illegal type of set inside tensor");
error = 1;
*t++ = x2;
break;
}
}
else { switch ( c ) {
case TINDEX:
if ( inset < 2 ) *t++ = x2 + AM.OffsetIndex;
else *t++ = x2;
if ( x2+AM.OffsetIndex > AM.IndDum ) {
x2 = x2+AM.OffsetIndex - AM.IndDum;
if ( x2 > C->numdum[numexp] ) C->numdum[numexp] = x2;
}
break;
case TGENINDEX:
*t++ = AC.DumNum + WILDOFFSET;
break;
case TVECTOR:
if ( inset < 2 ) *t++ = x2 + AM.OffsetVector;
else *t++ = x2;
break;
case TWILDARG:
*t++ = FUNNYWILD; *t++ = x2;
/* v[2] = 0; */
break;
case TDOLLAR:
*t++ = FUNNYDOLLAR; *t++ = x2;
break;
case TDUBIOUS:
if ( inset < 2 ) *t++ = x2 + AM.OffsetVector;
else *t++ = x2;
break;
case TSGAMMA: /* Special gamma's */
if ( x3 != GAMMA ) {
MesPrint("&5_,6_,7_ can only be used inside g_");
error = -1;
}
*t++ = -x2;
break;
case TNUMBER:
case TNUMBER1:
if ( x2 >= AM.OffsetIndex && inset < 2 ) {
MesPrint("&Value of constant index in tensor too large");
error = -1;
}
*t++ = x2;
break;
default:
MesPrint("&Illegal object in tensor");
error = -1;
break;
}}
if ( inset >= 2 ) inset = 1;
} while ( *s == TCOMMA );
}
else {
dofunction: firstsumarg = 1;
do {
unsigned int ux2;
s++;
c = *s++;
if ( c == TMINUS && ( *s == TVECTOR || *s == TNUMBER
|| *s == TNUMBER1 || *s == TSUBEXP ) ) {
minus = 1; c = *s++;
}
else minus = 0;
base = ( c == TNUMBER ) ? 100: 128;
ux2 = 0; while ( *s >= 0 ) { ux2 = base*ux2 + *s++; }
x2 = ux2; /* may cause an implementation-defined behaviour */
/*
!!!!!!!! What if it does not fit?
*/
if ( firstsumarg ) {
firstsumarg = 0;
if ( sumlevel > 0 ) {
if ( c == TSYMBOL ) {
sumlevel = x2; sumtype = SYMTOSYM;
}
else if ( c == TINDEX ) {
sumlevel = x2+AM.OffsetIndex; sumtype = INDTOIND;
if ( sumlevel > AM.IndDum ) {
x2 = sumlevel - AM.IndDum;
if ( x2 > C->numdum[numexp] ) C->numdum[numexp] = x2;
}
}
}
}
if ( *s == TWILDCARD ) {
if ( c == TSYMBOL ) x2 += 2*MAXPOWER;
else if ( c != TNUMBER ) x2 += WILDOFFSET;
s++;
}
switch ( c ) {
case TSYMBOL:
*t++ = -SYMBOL; *t++ = x2; break;
case TDOLLAR:
*t++ = -DOLLAREXPRESSION; *t++ = x2; break;
case TEXPRESSION:
*t++ = -EXPRESSION; *t++ = x2;
/*
The next code is added to facilitate parallel processing
We need to call GetTable here to make sure all processors
have the same numbering of all variables.
*/
if ( Expressions[x2].status == STOREDEXPRESSION ) {
TMproto[0] = EXPRESSION;
TMproto[1] = SUBEXPSIZE;
TMproto[2] = x2;
TMproto[3] = 1;
{ int ie; for ( ie = 4; ie < SUBEXPSIZE; ie++ ) TMproto[ie] = 0; }
AT.TMaddr = TMproto;
PUTZERO(position);
/*
if ( (
#ifdef WITHPTHREADS
renumber =
#endif
GetTable(x2,&position,0) ) == 0 ) {
error = 1;
MesPrint("&Problems getting information about stored expression %s(2)"
,EXPRNAME(x2));
}
#ifdef WITHPTHREADS
M_free(renumber->symb.lo,"VarSpace");
M_free(renumber,"Renumber");
#endif
*/
if ( ( renumber = GetTable(x2,&position,0) ) == 0 ) {
error = 1;
MesPrint("&Problems getting information about stored expression %s(2)"
,EXPRNAME(x2));
}
if ( renumber->symb.lo != AN.dummyrenumlist )
M_free(renumber->symb.lo,"VarSpace");
M_free(renumber,"Renumber");
AR.StoreData.dirtyflag = 1;
}
break;
case TINDEX:
*t++ = -INDEX; *t++ = x2 + AM.OffsetIndex;
if ( t[-1] > AM.IndDum ) {
x2 = t[-1] - AM.IndDum;
if ( x2 > C->numdum[numexp] ) C->numdum[numexp] = x2;
}
break;
case TGENINDEX:
*t++ = -INDEX; *t++ = AC.DumNum + WILDOFFSET;
break;
case TVECTOR:
if ( minus ) *t++ = -MINVECTOR;
else *t++ = -VECTOR;
*t++ = x2 + AM.OffsetVector;
break;
case TSGAMMA: /* Special gamma's */
MesPrint("&5_,6_,7_ can only be used inside g_");
error = -1;
*t++ = -INDEX;
*t++ = -x2;
break;
case TDUBIOUS:
*t++ = -SYMBOL; *t++ = x2; break;
case TFUNCTION:
*t++ = -x2-FUNCTION;
break;
case TSET:
*t++ = -SETSET;
*t++ = x2;
break;
case TWILDARG:
*t++ = -ARGWILD; *t++ = x2; break;
case TSETDOL:
x2 = -x2;
/* fall through */
case TSETNUM:
if ( inset == 0 ) {
w1 = t; t += 2; w2 = t;
while ( w1 > v ) *--w2 = *--w1;
tsize = v; relo = AT.WorkTop;
*v++ = SETSET; *v++ = 0;
inset = 1;
}
*--relo = x2; *--relo = t-v+1;
c = *s++;
x2 = 0; while ( *s >= 0 ) x2 = 128*x2 + *s++;
switch ( c ) {
case TFUNCTION:
(*relo)--; *t++ = -x2-1; break;
case TSYMBOL:
*t++ = -SYMBOL; *t++ = x2; break;
case TINDEX:
*t++ = -INDEX; *t++ = x2;
if ( x2+AM.OffsetIndex > AM.IndDum ) {
x2 = x2+AM.OffsetIndex - AM.IndDum;
if ( x2 > C->numdum[numexp] ) C->numdum[numexp] = x2;
}
break;
case TVECTOR:
*t++ = -VECTOR; *t++ = x2; break;
case TNUMBER1:
*t++ = -SNUMBER; *t++ = x2; break;
default:
MesPrint("&Internal error 435");
error = 1;
*t++ = -SYMBOL; *t++ = x2; break;
}
break;
case TSUBEXP:
w2 = AC.ProtoType; i = w2[1];
w1 = t;
*t++ = i+ARGHEAD+4;
*t++ = 1;
FILLARG(t);
*t++ = i + 4;
while ( --i >= 0 ) *t++ = *w2++;
w1[ARGHEAD+3] = subexpbuffers[x2].subexpnum;
w1[ARGHEAD+5] = subexpbuffers[x2].buffernum;
if ( sumlevel > 0 ) {
w1[0] += 4;
w1[ARGHEAD] += 4;
w1[ARGHEAD+2] += 4;
*t++ = sumtype; *t++ = 4;
*t++ = sumlevel; *t++ = sumlevel;
}
*t++ = 1; *t++ = 1;
if ( minus ) *t++ = -3;
else *t++ = 3;
break;
case TNUMBER:
case TNUMBER1:
if ( minus ) x2 = UnsignedToInt(-IntAbs(x2));
*t++ = -SNUMBER;
*t++ = x2;
break;
default:
MesPrint("&Illegal object in function");
error = -1;
break;
}
} while ( *s == TCOMMA );
}
if ( *s != TFUNCLOSE ) {
MesPrint("&Illegal argument field for function. Expected )");
return(-1);
}
s++; sumlevel = 0;
v[1] = t-v;
/*
if ( *v == AM.termfunnum && ( v[1] != FUNHEAD+2 ||
v[FUNHEAD] != -DOLLAREXPRESSION ) ) {
MesPrint("&The function term_ can only have one argument with a single $-expression");
error = 1;
}
*/
goto fin;
case TDUBIOUS:
x1 = 0; while ( *s >= 0 ) x1 = 128*x1 + *s++;
if ( *s == TWILDCARD ) s++;
if ( *s == TDOT ) goto dovector;
if ( *s == TFUNOPEN ) {
x1 += FUNCTION;
cc = 1;
v = t; *t++ = x1; *t++ = FUNHEAD; *t++ = DIRTYFLAG;
if ( x1 == AR.PolyFun && AR.PolyFunType == 2 && AR.Eside != LHSIDE )
t[-1] |= MUSTCLEANPRF;
FILLFUN3(t)
needarg = -1; goto dofunction;
}
*t++ = SYMBOL; *t++ = 4; *t++ = 0;
if ( inset ) *relo = 2;
goto TryPower;
case TSUBEXP:
x1 = 0; while ( *s >= 0 ) { x1 = x1*128 + *s++; }
if ( *s == TPOWER ) {
s++; c = *s++;
base = ( c == TNUMBER ) ? 100: 128;
x2 = 0; while ( *s >= 0 ) { x2 = base*x2 + *s++; }
if ( *s == TWILDCARD ) { x2 += 2*MAXPOWER; s++; }
else if ( c == TSYMBOL ) x2 += 2*MAXPOWER;
}
else x2 = 1;
r = AC.ProtoType; n = r[1] - 5; r += 5;
*t++ = SUBEXPRESSION; *t++ = r[-4];
*t++ = subexpbuffers[x1].subexpnum;
*t++ = x2*deno;
*t++ = subexpbuffers[x1].buffernum;
NCOPY(t,r,n);
if ( cbuf[subexpbuffers[x1].buffernum].CanCommu[subexpbuffers[x1].subexpnum] ) cc = 1;
deno = 1;
break;
case TMULTIPLY:
mulflag = 1;
break;
case TDIVIDE:
mulflag = 1;
deno = -deno;
break;
case TEXPRESSION:
cc = 1;
x1 = 0; while ( *s >= 0 ) { x1 = x1*128 + *s++; }
v = t;
*t++ = EXPRESSION; *t++ = SUBEXPSIZE; *t++ = x1; *t++ = deno;
*t++ = 0; FILLSUB(t)
/*
Here we had some erroneous code before. It should be after
the reading of the parameters as it is now (after 15-jan-2007).
Thomas Hahn noticed this and reported it.
*/
if ( *s == TFUNOPEN ) {
do {
s++; c = *s++;
base = ( c == TNUMBER ) ? 100: 128;
x2 = 0; while ( *s >= 0 ) { x2 = base*x2 + *s++; }
switch ( c ) {
case TSYMBOL:
*t++ = SYMBOL; *t++ = 4; *t++ = x2; *t++ = 1;
break;
case TINDEX:
*t++ = INDEX; *t++ = 3; *t++ = x2+AM.OffsetIndex;
if ( t[-1] > AM.IndDum ) {
x2 = t[-1] - AM.IndDum;
if ( x2 > C->numdum[numexp] ) C->numdum[numexp] = x2;
}
break;
case TVECTOR:
*t++ = INDEX; *t++ = 3; *t++ = x2+AM.OffsetVector;
break;
case TFUNCTION:
*t++ = x2+FUNCTION; *t++ = 2; break;
case TNUMBER:
case TNUMBER1:
if ( x2 >= AM.OffsetIndex || x2 < 0 ) {
MesPrint("&Index as argument of expression has illegal value");
error = -1;
}
*t++ = INDEX; *t++ = 3; *t++ = x2; break;
case TSETDOL:
x2 = -x2;
/* fall through */
case TSETNUM:
if ( inset == 0 ) {
w1 = t; t += 2; w2 = t;
while ( w1 > v ) *--w2 = *--w1;
tsize = v; relo = AT.WorkTop;
*v++ = SETSET; *v++ = 0;
inset = 1;
}
*--relo = x2; *--relo = t-v+2;
c = *s++;
x2 = 0; while ( *s >= 0 ) x2 = 128*x2 + *s++;
switch ( c ) {
case TFUNCTION:
*relo -= 2; *t++ = -x2-1; break;
case TSYMBOL:
*t++ = SYMBOL; *t++ = 4; *t++ = x2; *t++ = 1; break;
case TINDEX:
*t++ = INDEX; *t++ = 3; *t++ = x2;
if ( x2+AM.OffsetIndex > AM.IndDum ) {
x2 = x2+AM.OffsetIndex - AM.IndDum;
if ( x2 > C->numdum[numexp] ) C->numdum[numexp] = x2;
}
break;
case TVECTOR:
*t++ = VECTOR; *t++ = 3; *t++ = x2; break;
case TNUMBER1:
*t++ = SNUMBER; *t++ = 4; *t++ = x2; *t++ = 1; break;
default:
MesPrint("&Internal error 435");
error = 1;
*t++ = SYMBOL; *t++ = 4; *t++ = x2; *t++ = 1; break;
}
break;
default:
MesPrint("&Argument of expression can only be symbol, index, vector or function");
error = -1;
break;
}
} while ( *s == TCOMMA );
if ( *s != TFUNCLOSE ) {
MesPrint("&Illegal object in argument field for expression");
error = -1;
while ( *s != TFUNCLOSE ) s++;
}
s++;
}
r = AC.ProtoType; n = r[1];
if ( n > SUBEXPSIZE ) {
*t++ = WILDCARDS; *t++ = n+2;
NCOPY(t,r,n);
}
/*
Code added for parallel processing.
This is different from the other occurrences to test immediately
for renumbering. Here we have to read the parameters first.
*/
if ( Expressions[x1].status == STOREDEXPRESSION ) {
v[1] = t-v;
AT.TMaddr = v;
PUTZERO(position);
/*
if ( (
#ifdef WITHPTHREADS
renumber =
#endif
GetTable(x1,&position,0) ) == 0 ) {
error = 1;
MesPrint("&Problems getting information about stored expression %s(3)"
,EXPRNAME(x1));
}
#ifdef WITHPTHREADS
M_free(renumber->symb.lo,"VarSpace");
M_free(renumber,"Renumber");
#endif
*/
if ( ( renumber = GetTable(x1,&position,0) ) == 0 ) {
error = 1;
MesPrint("&Problems getting information about stored expression %s(3)"
,EXPRNAME(x1));
}
if ( renumber->symb.lo != AN.dummyrenumlist )
M_free(renumber->symb.lo,"VarSpace");
M_free(renumber,"Renumber");
AR.StoreData.dirtyflag = 1;
}
if ( *s == LBRACE ) {
/*
This should be one term that should be inserted
FROMBRAC size+2 ( term )
Because this term should have been translated
already we can copy it from the 'subexpression'
*/
s++;
if ( *s != TSUBEXP ) {
MesPrint("&Internal error 23");
Terminate(-1);
}
s++; x2 = 0; while ( *s >= 0 ) { x2 = 128*x2 + *s++; }
r = cbuf[subexpbuffers[x2].buffernum].rhs[subexpbuffers[x2].subexpnum];
*t++ = FROMBRAC; *t++ = *r+2;
n = *r;
NCOPY(t,r,n);
if ( *r != 0 ) {
MesPrint("&Object between [] in expression should be a single term");
error = -1;
}
if ( *s != RBRACE ) {
MesPrint("&Internal error 23b");
Terminate(-1);
}
s++;
}
if ( *s == TPOWER ) {
s++; c = *s++;
base = ( c == TNUMBER ) ? 100: 128;
x2 = 0; while ( *s >= 0 ) { x2 = base*x2 + *s++; }
if ( *s == TWILDCARD || c == TSYMBOL ) { x2 += 2*MAXPOWER; s++; }
v[3] = x2;
}
v[1] = t - v;
deno = 1;
break;
case TNUMBER:
if ( *s == 0 ) {
s++;
if ( *s == TPOWER ) {
s++; if ( *s == TMINUS ) { s++; deno = -deno; }
c = *s++; base = ( c == TNUMBER ) ? 100: 128;
x2 = 0; while ( *s >= 0 ) { x2 = x2*base + *s++; }
if ( x2 == 0 ) {
error = -1;
MesPrint("&Encountered 0^0 during compilation");
}
if ( deno < 0 ) {
error = -1;
MesPrint("&Division by zero during compilation (0 to the power negative number)");
}
}
else if ( deno < 0 ) {
error = -1;
MesPrint("&Division by zero during compilation");
}
sign = 0; break; /* term is zero */
}
y = *s++;
if ( *s >= 0 ) { y = 100*y + *s++; }
innum[0] = y; nin = 1;
while ( *s >= 0 ) {
y = *s++; x2 = 100;
if ( *s >= 0 ) { y = 100*y + *s++; x2 = 10000; }
Product(innum,&nin,(WORD)x2);
if ( y ) AddLong(innum,nin,(UWORD *)(&y),(WORD)1,innum,&nin);
}
docoef:
if ( *s == TPOWER ) {
s++; if ( *s == TMINUS ) { s++; deno = -deno; }
c = *s++; base = ( c == TNUMBER ) ? 100: 128;
x2 = 0; while ( *s >= 0 ) { x2 = x2*base + *s++; }
if ( x2 == 0 ) {
innum[0] = 1; nin = 1;
}
else if ( RaisPow(BHEAD innum,&nin,x2) ) {
error = -1; innum[0] = 1; nin = 1;
}
}
if ( deno > 0 ) {
Simplify(BHEAD innum,&nin,denominator,&ndenominator);
for ( i = 0; i < nnumerator; i++ ) CGscrat7[i] = numerator[i];
MulLong(innum,nin,CGscrat7,nnumerator,numerator,&nnumerator);
}
else if ( deno < 0 ) {
Simplify(BHEAD innum,&nin,numerator,&nnumerator);
for ( i = 0; i < ndenominator; i++ ) CGscrat7[i] = denominator[i];
MulLong(innum,nin,CGscrat7,ndenominator,denominator,&ndenominator);
}
deno = 1;
break;
case TNUMBER1:
if ( *s == 0 ) { s++; sign = 0; break; /* term is zero */ }
y = *s++;
if ( *s >= 0 ) { y = 128*y + *s++; }
if ( inset == 0 ) {
innum[0] = y; nin = 1;
while ( *s >= 0 ) {
y = *s++; x2 = 128;
if ( *s >= 0 ) { y = 128*y + *s++; x2 = 16384; }
Product(innum,&nin,(WORD)x2);
if ( y ) AddLong(innum,nin,(UWORD *)&y,(WORD)1,innum,&nin);
}
goto docoef;
}
*relo = 2; *t++ = SNUMBER; *t++ = 4; *t++ = y;
goto TryPower;
case TDOLLAR:
{
WORD *powplace;
x1 = 0; while ( *s >= 0 ) { x1 = x1*128 + *s++; }
if ( AR.Eside != LHSIDE ) {
*t++ = SUBEXPRESSION; *t++ = SUBEXPSIZE; *t++ = x1;
}
else {
*t++ = DOLLAREXPRESSION; *t++ = SUBEXPSIZE; *t++ = x1;
}
powplace = t; t++;
*t++ = AM.dbufnum; FILLSUB(t)
/*
Now we have to test for factors of dollars with [ ] and [ [ ]]
*/
if ( *s == LBRACE ) {
int bracelevel = 1;
s++;
while ( bracelevel > 0 ) {
if ( *s == RBRACE ) {
bracelevel--; s++;
}
else if ( *s == TNUMBER ) {
s++;
x2 = 0; while ( *s >= 0 ) { x2 = 100*x2 + *s++; }
*t++ = DOLLAREXPR2; *t++ = 3; *t++ = -x2-1;
CloseBraces:
while ( bracelevel > 0 ) {
if ( *s != RBRACE ) {
ErrorBraces:
error = -1;
MesPrint("&Improper use of [] in $-variable.");
return(error);
}
else {
s++; bracelevel--;
}
}
}
else if ( *s == TDOLLAR ) {
s++;
x1 = 0; while ( *s >= 0 ) { x1 = x1*128 + *s++; }
*t++ = DOLLAREXPR2; *t++ = 3; *t++ = x1;
if ( *s == RBRACE ) goto CloseBraces;
else if ( *s == LBRACE ) {
s++; bracelevel++;
}
}
else goto ErrorBraces;
}
}
/*
Finally we can continue with the power
*/
if ( *s == TPOWER ) {
s++;
if ( *s == TMINUS ) { s++; deno = -deno; }
c = *s++;
base = ( c == TNUMBER ) ? 100: 128;
x2 = 0; while ( *s >= 0 ) { x2 = base*x2 + *s++; }
if ( c == TSYMBOL ) {
if ( *s == TWILDCARD ) s++;
x2 += 2*MAXPOWER;
}
*powplace = deno*x2;
}
else *powplace = deno;
deno = 1;
/*
if ( inset ) {
while ( relo < AT.WorkTop ) *t++ = *relo++;
inset = 0; tsize[1] = t - tsize;
}
*/
}
break;
case TSETNUM:
inset = 1; tsize = t; relo = AT.WorkTop;
*t++ = SETSET; *t++ = 0;
x1 = 0; while ( *s >= 0 ) x1 = x1*128 + *s++;
*--relo = x1; *--relo = 0;
break;
case TSETDOL:
inset = 1; tsize = t; relo = AT.WorkTop;
*t++ = SETSET; *t++ = 0;
x1 = 0; while ( *s >= 0 ) x1 = x1*128 + *s++;
*--relo = -x1; *--relo = 0;
break;
case TFUNOPEN:
MesPrint("&Illegal use of function arguments");
error = -1;
funflag = 1;
deno = 1;
break;
case TFUNCLOSE:
if ( funflag == 0 )
MesPrint("&Illegal use of function arguments");
error = -1;
funflag = 0;
deno = 1;
break;
case TSGAMMA:
MesPrint("&Illegal use special gamma symbols 5_, 6_, 7_");
error = -1;
funflag = 0;
deno = 1;
break;
default:
MesPrint("&Internal error in code generator. Unknown object: %d",c);
error = -1;
deno = 1;
break;
}
}
}
if ( mulflag ) {
MesPrint("&Irregular end of statement.");
error = 1;
}
if ( !first && error == 0 ) {
*term = t-term;
C->NumTerms[numexp]++;
if ( cc && sign ) C->CanCommu[numexp]++;
error = CompleteTerm(term,numerator,denominator,nnumerator,ndenominator,sign);
}
AT.WorkPointer = oldwork;
if ( error ) return(-1);
AddToCB(C,0)
if ( AC.CompileLevel > 0 && AR.Eside != LHSIDE ) {
/* See whether we have this one already */
error = InsTree(AC.cbufnum,C->numrhs);
if ( error < (C->numrhs) ) {
C->Pointer = C->rhs[C->numrhs--];
return(error);
}
}
return(C->numrhs);
OverWork:
MLOCK(ErrorMessageLock);
MesWork();
MUNLOCK(ErrorMessageLock);
return(-1);
}
/*
#] CodeGenerator :
#[ CompleteTerm :
Completes the term
Puts it in the buffer
*/
int CompleteTerm(WORD *term, UWORD *numer, UWORD *denom, WORD nnum, WORD nden, int sign)
{
int nsize, i;
WORD *t;
if ( sign == 0 ) return(0); /* Term is zero */
if ( nnum >= nden ) nsize = nnum;
else nsize = nden;
t = term + *term;
for ( i = 0; i < nnum; i++ ) *t++ = numer[i];
for ( ; i < nsize; i++ ) *t++ = 0;
for ( i = 0; i < nden; i++ ) *t++ = denom[i];
for ( ; i < nsize; i++ ) *t++ = 0;
*t++ = (2*nsize+1)*sign;
*term = t - term;
AddNtoC(AC.cbufnum,*term,term,7);
return(0);
}
/*
#] CompleteTerm :
#[ CodeFactors :
This routine does the part of reading in in terms of factors.
If there is more than one term at this level we have only one
factor. In that case any expression should first be unfactorized.
Then the whole expression gets read as a new subexpression and finally
we generate factor_*subexpression.
If the whole has only multiplications we have factors. Then the
nasty thing is powers of objects and in particular powers of
factorized expressions or dollars.
For a power we generate a new subexpression of the type
1+factor_+...+factor_^(power-1)
with which we multiply.
WE HAVE NOT YET WORRIED ABOUT SETS
*/
int CodeFactors(SBYTE *tokens)
{
GETIDENTITY
EXPRESSIONS e = Expressions + AR.CurExpr;
int nfactor = 1, nparenthesis, i, last = 0, error = 0;
SBYTE *t, *startobject, *tt, *s1, *out, *outtokens;
WORD nexp, subexp = 0, power, pow, x2, powfactor, first;
/*
First scan the number of factors
*/
t = tokens;
while ( *t != TENDOFIT ) {
if ( *t >= 0 ) { while ( *t >= 0 ) t++; continue; }
if ( *t == LPARENTHESIS || *t == LBRACE || *t == TSETOPEN || *t == TFUNOPEN ) {
nparenthesis = 0; t++;
while ( nparenthesis >= 0 ) {
if ( *t == LPARENTHESIS || *t == LBRACE || *t == TSETOPEN || *t == TFUNOPEN ) nparenthesis++;
else if ( *t == RPARENTHESIS || *t == RBRACE || *t == TSETCLOSE || *t == TFUNCLOSE ) nparenthesis--;
t++;
}
continue;
}
else if ( ( *t == TPLUS || *t == TMINUS ) && ( t > tokens )
&& ( t[-1] != TPLUS && t[-1] != TMINUS ) ) {
if ( t[-1] >= 0 || t[-1] == RPARENTHESIS || t[-1] == RBRACE
|| t[-1] == TSETCLOSE || t[-1] == TFUNCLOSE ) {
subexp = CodeGenerator(tokens);
if ( subexp < 0 ) error = -1;
if ( insubexpbuffers >= MAXSUBEXPRESSIONS ) {
MesPrint("&More than %d subexpressions inside one expression",(WORD)MAXSUBEXPRESSIONS);
Terminate(-1);
}
if ( subexpbuffers+insubexpbuffers >= topsubexpbuffers ) {
DoubleBuffer((void **)((VOID *)(&subexpbuffers))
,(void **)((VOID *)(&topsubexpbuffers)),sizeof(SUBBUF),"subexpbuffers");
}
subexpbuffers[insubexpbuffers].subexpnum = subexp;
subexpbuffers[insubexpbuffers].buffernum = AC.cbufnum;
subexp = insubexpbuffers++;
t = tokens;
*t++ = TSYMBOL; *t++ = FACTORSYMBOL;
*t++ = TMULTIPLY; *t++ = TSUBEXP;
PUTNUMBER128(t,subexp)
*t++ = TENDOFIT;
e->numfactors = 1;
e->vflags |= ISFACTORIZED;
return(subexp);
}
}
else if ( ( *t == TMULTIPLY || *t == TDIVIDE ) && t > tokens ) {
nfactor++;
}
else if ( *t == TEXPRESSION ) {
t++;
nexp = 0; while ( *t >= 0 ) { nexp = nexp*128 + *t++; }
if ( *t == LBRACE ) continue;
if ( ( AS.Oldvflags[nexp] & ISFACTORIZED ) != 0 ) {
nfactor += AS.OldNumFactors[nexp];
}
else { nfactor++; }
continue;
}
else if ( *t == TDOLLAR ) {
t++;
nexp = 0; while ( *t >= 0 ) { nexp = nexp*128 + *t++; }
if ( *t == LBRACE ) continue;
if ( Dollars[nexp].nfactors > 0 ) {
nfactor += Dollars[nexp].nfactors;
}
else { nfactor++; }
continue;
}
t++;
}
/*
Now the real pass.
nfactor is a not so reliable measure for the space we need.
*/
outtokens = (SBYTE *)Malloc1(((t-tokens)+(nfactor+2)*25)*sizeof(SBYTE),"CodeFactors");
out = outtokens;
t = tokens; first = 1; powfactor = 1;
while ( *t == TPLUS || *t == TMINUS ) { if ( *t == TMINUS ) first = -first; t++; }
if ( first < 0 ) {
*out++ = TMINUS; *out++ = TSYMBOL; *out++ = FACTORSYMBOL;
*out++ = TPOWER; *out++ = TNUMBER; PUTNUMBER100(out,powfactor)
powfactor++;
}
startobject = t; power = 1;
while ( *t != TENDOFIT ) {
if ( *t >= 0 ) { while ( *t >= 0 ) t++; continue; }
if ( *t == LPARENTHESIS || *t == LBRACE || *t == TSETOPEN || *t == TFUNOPEN ) {
nparenthesis = 0; t++;
while ( nparenthesis >= 0 ) {
if ( *t == LPARENTHESIS || *t == LBRACE || *t == TSETOPEN || *t == TFUNOPEN ) nparenthesis++;
else if ( *t == RPARENTHESIS || *t == RBRACE || *t == TSETCLOSE || *t == TFUNCLOSE ) nparenthesis--;
t++;
}
continue;
}
else if ( ( *t == TMULTIPLY || *t == TDIVIDE ) && ( t > tokens ) ) {
if ( t[-1] >= 0 || t[-1] == RPARENTHESIS || t[-1] == RBRACE
|| t[-1] == TSETCLOSE || t[-1] == TFUNCLOSE ) {
dolast:
if ( startobject ) { /* apparently power is 1 or -1 */
*out++ = TPLUS;
if ( power < 0 ) { *out++ = TNUMBER; *out++ = 1; *out++ = TDIVIDE; }
s1 = startobject;
while ( s1 < t ) *out++ = *s1++;
*out++ = TMULTIPLY; *out++ = TSYMBOL; *out++ = FACTORSYMBOL;
*out++ = TPOWER; *out++ = TNUMBER; PUTNUMBER100(out,powfactor)
powfactor++;
}
if ( last ) { startobject = 0; break; }
startobject = t+1;
if ( *t == TDIVIDE ) power = -1;
if ( *t == TMULTIPLY ) power = 1;
}
}
else if ( *t == TPOWER ) {
pow = 1;
tt = t+1;
while ( ( *tt == TMINUS ) || ( *tt == TPLUS ) ) {
if ( *tt == TMINUS ) pow = -pow;
tt++;
}
if ( *tt == TSYMBOL ) {
tt++; while ( *tt >= 0 ) tt++;
t = tt; continue;
}
tt++; x2 = 0; while ( *tt >= 0 ) { x2 = 100*x2 + *tt++; }
/*
We have an object in startobject till t. The power is
power*pow*x2
*/
power = power*pow*x2;
if ( power < 0 ) { pow = -power; power = -1; }
else if ( power == 0 ) { t = tt; startobject = tt; continue; }
else { pow = power; power = 1; }
*out++ = TPLUS;
if ( pow > 1 ) {
subexp = GenerateFactors(pow,1);
if ( subexp < 0 ) { error = -1; subexp = 0; }
*out++ = TSUBEXP; PUTNUMBER128(out,subexp);
}
*out++ = TSYMBOL; *out++ = FACTORSYMBOL;
*out++ = TPOWER; *out++ = TNUMBER; PUTNUMBER100(out,powfactor)
powfactor += pow;
if ( power > 0 ) *out++ = TMULTIPLY;
else *out++ = TDIVIDE;
s1 = startobject; while ( s1 < t ) *out++ = *s1++;
startobject = 0; t = tt; continue;
}
else if ( *t == TEXPRESSION ) {
startobject = t;
t++;
nexp = 0; while ( *t >= 0 ) { nexp = nexp*128 + *t++; }
if ( *t == LBRACE ) continue;
if ( *t == LPARENTHESIS ) {
nparenthesis = 0; t++;
while ( nparenthesis >= 0 ) {
if ( *t == LPARENTHESIS ) nparenthesis++;
else if ( *t == RPARENTHESIS ) nparenthesis--;
t++;
}
}
if ( ( AS.Oldvflags[nexp] & ISFACTORIZED ) == 0 ) continue;
if ( *t == TPOWER ) {
pow = 1;
tt = t+1;
while ( ( *tt == TMINUS ) || ( *tt == TPLUS ) ) {
if ( *tt == TMINUS ) pow = -pow;
tt++;
}
if ( *tt != TNUMBER ) {
MesPrint("Internal problems(1) in CodeFactors");
return(-1);
}
tt++; x2 = 0; while ( *tt >= 0 ) { x2 = 100*x2 + *tt++; }
/*
We have an object in startobject till t. The power is
power*pow*x2
*/
dopower:
power = power*pow*x2;
if ( power < 0 ) { pow = -power; power = -1; }
else if ( power == 0 ) { t = tt; startobject = tt; continue; }
else { pow = power; power = 1; }
*out++ = TPLUS;
if ( pow > 1 ) {
subexp = GenerateFactors(pow,AS.OldNumFactors[nexp]);
if ( subexp < 0 ) { error = -1; subexp = 0; }
*out++ = TSUBEXP; PUTNUMBER128(out,subexp)
*out++ = TMULTIPLY;
}
i = powfactor-1;
if ( i > 0 ) {
*out++ = TSYMBOL; *out++ = FACTORSYMBOL;
if ( i > 1 ) {
*out++ = TPOWER; *out++ = TNUMBER; PUTNUMBER100(out,i)
}
*out++ = TMULTIPLY;
}
powfactor += AS.OldNumFactors[nexp]*pow;
s1 = startobject;
while ( s1 < t ) *out++ = *s1++;
startobject = 0; t = tt; continue;
}
else {
tt = t; pow = 1; x2 = 1; goto dopower;
}
}
else if ( *t == TDOLLAR ) {
startobject = t;
t++;
nexp = 0; while ( *t >= 0 ) { nexp = nexp*128 + *t++; }
if ( *t == LBRACE ) continue;
if ( Dollars[nexp].nfactors == 0 ) continue;
if ( *t == TPOWER ) {
pow = 1;
tt = t+1;
while ( ( *tt == TMINUS ) || ( *tt == TPLUS ) ) {
if ( *tt == TMINUS ) pow = -pow;
tt++;
}
if ( *tt != TNUMBER ) {
MesPrint("Internal problems(2) in CodeFactors");
return(-1);
}
tt++; x2 = 0; while ( *tt >= 0 ) { x2 = 100*x2 + *tt++; }
/*
We have an object in startobject till t. The power is
power*pow*x2
*/
dopowerd:
power = power*pow*x2;
if ( power < 0 ) { pow = -power; power = -1; }
else if ( power == 0 ) { t = tt; startobject = tt; continue; }
else { pow = power; power = 1; }
if ( pow > 1 ) {
subexp = GenerateFactors(pow,1);
if ( subexp < 0 ) { error = -1; subexp = 0; }
}
for ( i = 1; i <= Dollars[nexp].nfactors; i++ ) {
s1 = startobject; *out++ = TPLUS;
while ( s1 < t ) *out++ = *s1++;
*out++ = LBRACE; *out++ = TNUMBER; PUTNUMBER128(out,i)
*out++ = RBRACE;
*out++ = TMULTIPLY;
*out++ = TSYMBOL; *out++ = FACTORSYMBOL;
*out++ = TPOWER; *out++ = TNUMBER; PUTNUMBER100(out,powfactor)
powfactor += pow;
if ( pow > 1 ) {
*out++ = TSUBEXP; PUTNUMBER128(out,subexp)
}
}
startobject = 0; t = tt; continue;
}
else {
tt = t; pow = 1; x2 = 1; goto dopowerd;
}
}
t++;
}
if ( last == 0 ) { last = 1; goto dolast; }
*out = TENDOFIT;
e->numfactors = powfactor-1;
e->vflags |= ISFACTORIZED;
subexp = CodeGenerator(outtokens);
if ( subexp < 0 ) error = -1;
if ( insubexpbuffers >= MAXSUBEXPRESSIONS ) {
MesPrint("&More than %d subexpressions inside one expression",(WORD)MAXSUBEXPRESSIONS);
Terminate(-1);
}
if ( subexpbuffers+insubexpbuffers >= topsubexpbuffers ) {
DoubleBuffer((void **)((VOID *)(&subexpbuffers))
,(void **)((VOID *)(&topsubexpbuffers)),sizeof(SUBBUF),"subexpbuffers");
}
subexpbuffers[insubexpbuffers].subexpnum = subexp;
subexpbuffers[insubexpbuffers].buffernum = AC.cbufnum;
subexp = insubexpbuffers++;
M_free(outtokens,"CodeFactors");
s1 = tokens;
*s1++ = TSUBEXP; PUTNUMBER128(s1,subexp); *s1++ = TENDOFIT;
if ( error < 0 ) return(-1);
else return(subexp);
}
/*
#] CodeFactors :
#[ GenerateFactors :
Generates an expression of the type
1+factor_+factor_^2+...+factor_^(n-1)
(this is if inc=1)
Returns the subexpression pointer of it.
*/
WORD GenerateFactors(WORD n,WORD inc)
{
WORD subexp;
int i, error = 0;
SBYTE *s;
SBYTE *tokenbuffer = (SBYTE *)Malloc1(8*n*sizeof(SBYTE),"GenerateFactors");
s = tokenbuffer;
*s++ = TNUMBER; *s++ = 1;
for ( i = inc; i < n*inc; i += inc ) {
*s++ = TPLUS; *s++ = TSYMBOL; *s++ = FACTORSYMBOL;
if ( i > 1 ) {
*s++ = TPOWER; *s++ = TNUMBER;
PUTNUMBER100(s,i)
}
}
*s++ = TENDOFIT;
subexp = CodeGenerator(tokenbuffer);
if ( subexp < 0 ) error = -1;
M_free(tokenbuffer,"GenerateFactors");
if ( insubexpbuffers >= MAXSUBEXPRESSIONS ) {
MesPrint("&More than %d subexpressions inside one expression",(WORD)MAXSUBEXPRESSIONS);
Terminate(-1);
}
if ( subexpbuffers+insubexpbuffers >= topsubexpbuffers ) {
DoubleBuffer((void **)((VOID *)(&subexpbuffers))
,(void **)((VOID *)(&topsubexpbuffers)),sizeof(SUBBUF),"subexpbuffers");
}
subexpbuffers[insubexpbuffers].subexpnum = subexp;
subexpbuffers[insubexpbuffers].buffernum = AC.cbufnum;
subexp = insubexpbuffers++;
if ( error < 0 ) return(error);
return(subexp);
}
/*
#] GenerateFactors :
#] Compiler :
*/
form-master/sources/compress.c 0000664 0000000 0000000 00000045741 13565763364 0017031 0 ustar 00root root 0000000 0000000 /** @file compress.c
*
* The routines for the use of gzip (de)compression of the information
* in the sort file.
*/
/* #[ License : */
/*
* Copyright (C) 1984-2017 J.A.M. Vermaseren
* When using this file you are requested to refer to the publication
* J.A.M.Vermaseren "New features of FORM" math-ph/0010025
* This is considered a matter of courtesy as the development was paid
* for by FOM the Dutch physics granting agency and we would like to
* be able to track its scientific use to convince FOM of its value
* for the community.
*
* This file is part of FORM.
*
* FORM 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.
*
* FORM 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 FORM. If not, see .
*/
/* #] License : */
#include "form3.h"
#ifdef WITHZLIB
/*
#define GZIPDEBUG
Low level routines for dealing with zlib during sorting and handling
the scratch files. Work started 5-sep-2005.
The .sor file handling was more or less completed on 8-sep-2005
The handling of the scratch files still needs some thinking.
Complications are:
gzip compression should be per expression, not per buffer.
No gzip compression for expressions with a bracket index.
Separate decompression buffers for expressions in the rhs.
This last one will involve more buffer work and organization.
Information about compression should be stored for each expr.
(including what method/program was used)
Note: Be careful with compression. By far the most compact method
is the original problem....
#[ Variables :
The following variables are to contain the intermediate buffers
for the inflation of the various patches in the sort file.
There can be up to MaxFpatches (FilePatches in the setup) and hence
we can have that many streams simultaneously. We set this up once
and only when needed.
(in struct A.N or AB[threadnum].N)
Bytef **AN.ziobufnum;
Bytef *AN.ziobuffers;
*/
/*
#] Variables :
#[ SetupOutputGZIP :
Routine prepares a gzip output stream for the given file.
*/
int SetupOutputGZIP(FILEHANDLE *f)
{
GETIDENTITY
if ( AT.SS != AT.S0 ) return(0);
if ( AR.NoCompress == 1 ) return(0);
if ( AR.gzipCompress <= 0 ) return(0);
if ( f->ziobuffer == 0 ) {
/*
1: Allocate a struct for the gzip stream:
*/
f->zsp = Malloc1(sizeof(z_stream),"output zstream");
/*
2: Allocate the output buffer.
*/
f->ziobuffer =
(Bytef *)Malloc1(f->ziosize*sizeof(char),"output zbuffer");
if ( f->zsp == 0 || f->ziobuffer == 0 ) {
MLOCK(ErrorMessageLock);
MesCall("SetupOutputGZIP");
MUNLOCK(ErrorMessageLock);
Terminate(-1);
}
}
/*
3: Set the default fields:
*/
f->zsp->zalloc = Z_NULL;
f->zsp->zfree = Z_NULL;
f->zsp->opaque = Z_NULL;
/*
4: Set the output space:
*/
f->zsp->next_out = f->ziobuffer;
f->zsp->avail_out = f->ziosize;
f->zsp->total_out = 0;
/*
5: Set the input space:
*/
f->zsp->next_in = (Bytef *)(f->PObuffer);
f->zsp->avail_in = (Bytef *)(f->POfill) - (Bytef *)(f->PObuffer);
f->zsp->total_in = 0;
/*
6: Initiate the deflation
*/
if ( deflateInit(f->zsp,AR.gzipCompress) != Z_OK ) {
MLOCK(ErrorMessageLock);
MesPrint("Error from zlib: %s",f->zsp->msg);
MesCall("SetupOutputGZIP");
MUNLOCK(ErrorMessageLock);
Terminate(-1);
}
return(0);
}
/*
#] SetupOutputGZIP :
#[ PutOutputGZIP :
Routine is called when the PObuffer of f is full.
The contents of it will be compressed and whenever the output buffer
f->ziobuffer is full it will be written and the output buffer
will be reset.
Upon exit the input buffer will be cleared.
*/
int PutOutputGZIP(FILEHANDLE *f)
{
GETIDENTITY
int zerror;
/*
First set the number of bytes in the input
*/
f->zsp->next_in = (Bytef *)(f->PObuffer);
f->zsp->avail_in = (Bytef *)(f->POfill) - (Bytef *)(f->PObuffer);
f->zsp->total_in = 0;
while ( ( zerror = deflate(f->zsp,Z_NO_FLUSH) ) == Z_OK ) {
if ( f->zsp->avail_out == 0 ) {
/*
ziobuffer is full. Write the output.
*/
#ifdef GZIPDEBUG
{
char *s = (char *)((UBYTE *)(f->ziobuffer)+f->ziosize);
MLOCK(ErrorMessageLock);
MesPrint("%wWriting %l bytes at %10p: %d %d %d %d %d"
,f->ziosize,&(f->POposition),s[-5],s[-4],s[-3],s[-2],s[-1]);
MUNLOCK(ErrorMessageLock);
}
#endif
#ifdef ALLLOCK
LOCK(f->pthreadslock);
#endif
if ( f == AR.hidefile ) {
LOCK(AS.inputslock);
}
SeekFile(f->handle,&(f->POposition),SEEK_SET);
if ( WriteFile(f->handle,(UBYTE *)(f->ziobuffer),f->ziosize)
!= f->ziosize ) {
if ( f == AR.hidefile ) {
UNLOCK(AS.inputslock);
}
#ifdef ALLLOCK
UNLOCK(f->pthreadslock);
#endif
MLOCK(ErrorMessageLock);
MesPrint("%wWrite error during compressed sort. Disk full?");
MUNLOCK(ErrorMessageLock);
return(-1);
}
if ( f == AR.hidefile ) {
UNLOCK(AS.inputslock);
}
#ifdef ALLLOCK
UNLOCK(f->pthreadslock);
#endif
ADDPOS(f->filesize,f->ziosize);
ADDPOS(f->POposition,f->ziosize);
#ifdef WITHPTHREADS
if ( AS.MasterSort && AC.ThreadSortFileSynch ) {
if ( f->handle >= 0 ) SynchFile(f->handle);
}
#endif
/*
Reset the output
*/
f->zsp->next_out = f->ziobuffer;
f->zsp->avail_out = f->ziosize;
f->zsp->total_out = 0;
}
else if ( f->zsp->avail_in == 0 ) {
/*
We compressed everything and it sits in ziobuffer. Finish
*/
return(0);
}
else {
MLOCK(ErrorMessageLock);
MesPrint("%w avail_in = %d, avail_out = %d.",f->zsp->avail_in,f->zsp->avail_out);
MUNLOCK(ErrorMessageLock);
break;
}
}
MLOCK(ErrorMessageLock);
MesPrint("%wError in gzip handling of output. zerror = %d",zerror);
MUNLOCK(ErrorMessageLock);
return(-1);
}
/*
#] PutOutputGZIP :
#[ FlushOutputGZIP :
Routine is called to flush a stream. The compression of the input buffer
will be completed and the contents of f->ziobuffer will be written.
Both buffers will be cleared.
*/
int FlushOutputGZIP(FILEHANDLE *f)
{
GETIDENTITY
int zerror;
/*
Set the proper parameters
*/
f->zsp->next_in = (Bytef *)(f->PObuffer);
f->zsp->avail_in = (Bytef *)(f->POfill) - (Bytef *)(f->PObuffer);
f->zsp->total_in = 0;
while ( ( zerror = deflate(f->zsp,Z_FINISH) ) == Z_OK ) {
if ( f->zsp->avail_out == 0 ) {
/*
Write the output
*/
#ifdef GZIPDEBUG
MLOCK(ErrorMessageLock);
MesPrint("%wWriting %l bytes at %10p",f->ziosize,&(f->POposition));
MUNLOCK(ErrorMessageLock);
#endif
#ifdef ALLLOCK
LOCK(f->pthreadslock);
#endif
if ( f == AR.hidefile ) {
UNLOCK(AS.inputslock);
}
SeekFile(f->handle,&(f->POposition),SEEK_SET);
if ( WriteFile(f->handle,(UBYTE *)(f->ziobuffer),f->ziosize)
!= f->ziosize ) {
if ( f == AR.hidefile ) {
UNLOCK(AS.inputslock);
}
#ifdef ALLLOCK
UNLOCK(f->pthreadslock);
#endif
MLOCK(ErrorMessageLock);
MesPrint("%wWrite error during compressed sort. Disk full?");
MUNLOCK(ErrorMessageLock);
return(-1);
}
if ( f == AR.hidefile ) {
UNLOCK(AS.inputslock);
}
#ifdef ALLLOCK
UNLOCK(f->pthreadslock);
#endif
ADDPOS(f->filesize,f->ziosize);
ADDPOS(f->POposition,f->ziosize);
#ifdef WITHPTHREADS
if ( AS.MasterSort && AC.ThreadSortFileSynch ) {
if ( f->handle >= 0 ) SynchFile(f->handle);
}
#endif
/*
Reset the output
*/
f->zsp->next_out = f->ziobuffer;
f->zsp->avail_out = f->ziosize;
f->zsp->total_out = 0;
}
}
if ( zerror == Z_STREAM_END ) {
/*
Write the output
*/
#ifdef GZIPDEBUG
MLOCK(ErrorMessageLock);
MesPrint("%wWriting %l bytes at %10p",(LONG)(f->zsp->avail_out),&(f->POposition));
MUNLOCK(ErrorMessageLock);
#endif
#ifdef ALLLOCK
LOCK(f->pthreadslock);
#endif
if ( f == AR.hidefile ) {
LOCK(AS.inputslock);
}
SeekFile(f->handle,&(f->POposition),SEEK_SET);
if ( WriteFile(f->handle,(UBYTE *)(f->ziobuffer),f->zsp->total_out)
!= (LONG)(f->zsp->total_out) ) {
if ( f == AR.hidefile ) {
UNLOCK(AS.inputslock);
}
#ifdef ALLLOCK
UNLOCK(f->pthreadslock);
#endif
MLOCK(ErrorMessageLock);
MesPrint("%wWrite error during compressed sort. Disk full?");
MUNLOCK(ErrorMessageLock);
return(-1);
}
if ( f == AR.hidefile ) {
LOCK(AS.inputslock);
}
#ifdef ALLLOCK
UNLOCK(f->pthreadslock);
#endif
ADDPOS(f->filesize,f->zsp->total_out);
ADDPOS(f->POposition,f->zsp->total_out);
#ifdef WITHPTHREADS
if ( AS.MasterSort && AC.ThreadSortFileSynch ) {
if ( f->handle >= 0 ) SynchFile(f->handle);
}
#endif
#ifdef GZIPDEBUG
MLOCK(ErrorMessageLock);
{ char *s = f->ziobuffer+f->zsp->total_out;
MesPrint("%w Last bytes written: %d %d %d %d %d",s[-5],s[-4],s[-3],s[-2],s[-1]);
}
MesPrint("%w Perceived position in FlushOutputGZIP is %10p",&(f->POposition));
MUNLOCK(ErrorMessageLock);
#endif
/*
Reset the output
*/
f->zsp->next_out = f->ziobuffer;
f->zsp->avail_out = f->ziosize;
f->zsp->total_out = 0;
if ( ( zerror = deflateEnd(f->zsp) ) == Z_OK ) return(0);
MLOCK(ErrorMessageLock);
if ( f->zsp->msg ) {
MesPrint("%wError in finishing gzip handling of output: %s",f->zsp->msg);
}
else {
MesPrint("%wError in finishing gzip handling of output.");
}
MUNLOCK(ErrorMessageLock);
}
else {
MLOCK(ErrorMessageLock);
MesPrint("%wError in gzip handling of output.");
MUNLOCK(ErrorMessageLock);
}
return(-1);
}
/*
#] FlushOutputGZIP :
#[ SetupAllInputGZIP :
Routine prepares all gzip input streams for a merge.
Problem (29-may-2008): If we never use GZIP compression, this routine
will still allocate the array space. This is an enormous amount!
It places an effective restriction on the value of SortIOsize
*/
int SetupAllInputGZIP(SORTING *S)
{
GETIDENTITY
int i, NumberOpened = 0;
z_streamp zsp;
/*
This code was added 29-may-2008 by JV to prevent further processing if
there is no compression at all (usually).
*/
for ( i = 0; i < S->inNum; i++ ) {
if ( S->fpincompressed[i] ) break;
}
if ( i >= S->inNum ) return(0);
if ( S->zsparray == 0 ) {
S->zsparray = (z_streamp)Malloc1(sizeof(z_stream)*S->MaxFpatches,"input zstreams");
if ( S->zsparray == 0 ) {
MLOCK(ErrorMessageLock);
MesCall("SetupAllInputGZIP");
MUNLOCK(ErrorMessageLock);
Terminate(-1);
}
/*
We add 128 bytes in the hope that if it can happen that it goes
outside the buffer during decompression, it does not do damage.
*/
AN.ziobuffers = (Bytef *)Malloc1(S->MaxFpatches*(S->file.ziosize+128)*sizeof(Bytef),"input raw buffers");
/*
This seems to be one of the really stupid errors:
We allocate way too much space. Way way way too much.
AN.ziobufnum = (Bytef **)Malloc1(S->MaxFpatches*S->file.ziosize*sizeof(Bytef *),"input raw pointers");
*/
AN.ziobufnum = (Bytef **)Malloc1(S->MaxFpatches*sizeof(Bytef *),"input raw pointers");
if ( AN.ziobuffers == 0 || AN.ziobufnum == 0 ) {
MLOCK(ErrorMessageLock);
MesCall("SetupAllInputGZIP");
MUNLOCK(ErrorMessageLock);
Terminate(-1);
}
for ( i = 0 ; i < S->MaxFpatches; i++ ) {
AN.ziobufnum[i] = AN.ziobuffers + i * (S->file.ziosize+128);
}
}
for ( i = 0; i < S->inNum; i++ ) {
#ifdef GZIPDEBUG
MLOCK(ErrorMessageLock);
MesPrint("%wPreparing z-stream %d with compression %d",i,S->fpincompressed[i]);
MUNLOCK(ErrorMessageLock);
#endif
if ( S->fpincompressed[i] ) {
zsp = &(S->zsparray[i]);
/*
1: Set the default fields:
*/
zsp->zalloc = Z_NULL;
zsp->zfree = Z_NULL;
zsp->opaque = Z_NULL;
/*
2: Set the output space:
*/
zsp->next_out = Z_NULL;
zsp->avail_out = 0;
zsp->total_out = 0;
/*
3: Set the input space temporarily:
*/
zsp->next_in = Z_NULL;
zsp->avail_in = 0;
zsp->total_in = 0;
/*
4: Initiate the inflation
*/
if ( inflateInit(zsp) != Z_OK ) {
MLOCK(ErrorMessageLock);
if ( zsp->msg ) MesPrint("%wError from inflateInit: %s",zsp->msg);
else MesPrint("%wError from inflateInit");
MesCall("SetupAllInputGZIP");
MUNLOCK(ErrorMessageLock);
Terminate(-1);
}
NumberOpened++;
}
}
return(NumberOpened);
}
/*
#] SetupAllInputGZIP :
#[ FillInputGZIP :
Routine is called when we need new input in the specified buffer.
This buffer is used for the output and we keep reading and uncompressing
input till either this buffer is full or the input stream is finished.
The return value is the number of bytes in the buffer.
*/
LONG FillInputGZIP(FILEHANDLE *f, POSITION *position, UBYTE *buffer, LONG buffersize, int numstream)
{
GETIDENTITY
int zerror;
LONG readsize, toread = 0;
SORTING *S = AT.SS;
z_streamp zsp;
POSITION pos;
if ( S->fpincompressed[numstream] ) {
zsp = &(S->zsparray[numstream]);
zsp->next_out = (Bytef *)buffer;
zsp->avail_out = buffersize;
zsp->total_out = 0;
if ( zsp->avail_in == 0 ) {
/*
First loading of the input
*/
if ( ISGEPOSINC(S->fPatchesStop[numstream],*position,f->ziosize) ) {
toread = f->ziosize;
}
else {
DIFPOS(pos,S->fPatchesStop[numstream],*position);
toread = (LONG)(BASEPOSITION(pos));
}
if ( toread > 0 ) {
#ifdef GZIPDEBUG
MLOCK(ErrorMessageLock);
MesPrint("%w-+Reading %l bytes in stream %d at position %10p; stop at %10p",toread,numstream,position,&(S->fPatchesStop[numstream]));
MUNLOCK(ErrorMessageLock);
#endif
#ifdef ALLLOCK
LOCK(f->pthreadslock);
#endif
SeekFile(f->handle,position,SEEK_SET);
readsize = ReadFile(f->handle,(UBYTE *)(AN.ziobufnum[numstream]),toread);
SeekFile(f->handle,position,SEEK_CUR);
#ifdef ALLLOCK
UNLOCK(f->pthreadslock);
#endif
#ifdef GZIPDEBUG
MLOCK(ErrorMessageLock);
{ char *s = AN.ziobufnum[numstream]+readsize;
MesPrint("%w read: %l +Last bytes read: %d %d %d %d %d in %s, newpos = %10p",readsize,s[-5],s[-4],s[-3],s[-2],s[-1],f->name,position);
}
MUNLOCK(ErrorMessageLock);
#endif
if ( readsize == 0 ) {
zsp->next_in = AN.ziobufnum[numstream];
zsp->avail_in = f->ziosize;
zsp->total_in = 0;
return(zsp->total_out);
}
if ( readsize < 0 ) {
MLOCK(ErrorMessageLock);
MesPrint("%wFillInputGZIP: Read error during compressed sort.");
MUNLOCK(ErrorMessageLock);
return(-1);
}
ADDPOS(f->filesize,readsize);
ADDPOS(f->POposition,readsize);
/*
Set the input
*/
zsp->next_in = AN.ziobufnum[numstream];
zsp->avail_in = readsize;
zsp->total_in = 0;
}
}
if ( toread > 0 || zsp->avail_in ) {
while ( ( zerror = inflate(zsp,Z_NO_FLUSH) ) == Z_OK ) {
if ( zsp->avail_out == 0 ) {
/*
Finish
*/
return((LONG)(zsp->total_out));
}
if ( zsp->avail_in == 0 ) {
if ( ISEQUALPOS(S->fPatchesStop[numstream],*position) ) {
/*
We finished this stream. Try to terminate.
*/
zerror = Z_STREAM_END;
break;
/*
if ( ( zerror = inflate(zsp,Z_SYNC_FLUSH) ) == Z_OK ) {
return((LONG)(zsp->total_out));
}
else
break;
*/
/*
#ifdef GZIPDEBUG
MLOCK(ErrorMessageLock);
MesPrint("%wClosing stream %d",numstream);
#endif
readsize = zsp->total_out;
#ifdef GZIPDEBUG
if ( readsize > 0 ) {
WORD *s = (WORD *)(buffer+zsp->total_out);
MesPrint("%w Last words: %d %d %d %d %d",s[-5],s[-4],s[-3],s[-2],s[-1]);
}
else {
MesPrint("%w No words");
}
MUNLOCK(ErrorMessageLock);
#endif
if ( ( zerror = inflateEnd(zsp) ) == Z_OK ) return(readsize);
break;
*/
}
/*
Read more input
*/
#ifdef GZIPDEBUG
if ( numstream == 0 ) {
MLOCK(ErrorMessageLock);
MesPrint("%wWant to read in stream 0 at position %10p",position);
MUNLOCK(ErrorMessageLock);
}
#endif
if ( ISGEPOSINC(S->fPatchesStop[numstream],*position,f->ziosize) ) {
toread = f->ziosize;
}
else {
DIFPOS(pos,S->fPatchesStop[numstream],*position);
toread = (LONG)(BASEPOSITION(pos));
}
#ifdef GZIPDEBUG
MLOCK(ErrorMessageLock);
MesPrint("%w--Reading %l bytes in stream %d at position %10p",toread,numstream,position);
MUNLOCK(ErrorMessageLock);
#endif
#ifdef ALLLOCK
LOCK(f->pthreadslock);
#endif
SeekFile(f->handle,position,SEEK_SET);
readsize = ReadFile(f->handle,(UBYTE *)(AN.ziobufnum[numstream]),toread);
SeekFile(f->handle,position,SEEK_CUR);
#ifdef ALLLOCK
UNLOCK(f->pthreadslock);
#endif
#ifdef GZIPDEBUG
MLOCK(ErrorMessageLock);
{ char *s = AN.ziobufnum[numstream]+readsize;
MesPrint("%w Last bytes read: %d %d %d %d %d",s[-5],s[-4],s[-3],s[-2],s[-1]);
}
MUNLOCK(ErrorMessageLock);
#endif
if ( readsize == 0 ) {
zsp->next_in = AN.ziobufnum[numstream];
zsp->avail_in = f->ziosize;
zsp->total_in = 0;
return(zsp->total_out);
}
if ( readsize < 0 ) {
MLOCK(ErrorMessageLock);
MesPrint("%wFillInputGZIP: Read error during compressed sort.");
MUNLOCK(ErrorMessageLock);
return(-1);
}
ADDPOS(f->filesize,readsize);
ADDPOS(f->POposition,readsize);
/*
Reset the input
*/
zsp->next_in = AN.ziobufnum[numstream];
zsp->avail_in = readsize;
zsp->total_in = 0;
}
else {
break;
}
}
}
else {
zerror = Z_STREAM_END;
zsp->total_out = 0;
}
#ifdef GZIPDEBUG
MLOCK(ErrorMessageLock);
MesPrint("%w zerror = %d in stream %d. At position %10p",zerror,numstream,position);
MUNLOCK(ErrorMessageLock);
#endif
if ( zerror == Z_STREAM_END ) {
/*
Reset the input
*/
zsp->next_in = Z_NULL;
zsp->avail_in = 0;
zsp->total_in = 0;
/*
Make the final call and finish
*/
#ifdef GZIPDEBUG
MLOCK(ErrorMessageLock);
MesPrint("%wClosing stream %d",numstream);
#endif
readsize = zsp->total_out;
#ifdef GZIPDEBUG
if ( readsize > 0 ) {
WORD *s = (WORD *)(buffer+zsp->total_out);
MesPrint("%w -Last words: %d %d %d %d %d",s[-5],s[-4],s[-3],s[-2],s[-1]);
}
else {
MesPrint("%w No words");
}
MUNLOCK(ErrorMessageLock);
#endif
if ( zsp->zalloc != Z_NULL ) {
zerror = inflateEnd(zsp);
zsp->zalloc = Z_NULL;
}
if ( zerror == Z_OK || zerror == Z_STREAM_END ) return(readsize);
}
MLOCK(ErrorMessageLock);
MesPrint("%wFillInputGZIP: Error in gzip handling of input. zerror = %d",zerror);
MUNLOCK(ErrorMessageLock);
return(-1);
}
else {
#ifdef GZIPDEBUG
MLOCK(ErrorMessageLock);
MesPrint("%w++Reading %l bytes at position %10p",buffersize,position);
MUNLOCK(ErrorMessageLock);
#endif
#ifdef ALLLOCK
LOCK(f->pthreadslock);
#endif
SeekFile(f->handle,position,SEEK_SET);
readsize = ReadFile(f->handle,buffer,buffersize);
SeekFile(f->handle,position,SEEK_CUR);
#ifdef ALLLOCK
UNLOCK(f->pthreadslock);
#endif
if ( readsize < 0 ) {
MLOCK(ErrorMessageLock);
MesPrint("%wFillInputGZIP: Read error during uncompressed sort.");
MesPrint("%w++Reading %l bytes at position %10p",buffersize,position);
MUNLOCK(ErrorMessageLock);
}
return(readsize);
}
}
/*
#] FillInputGZIP :
#[ ClearSortGZIP :
*/
void ClearSortGZIP(FILEHANDLE *f)
{
if ( f->ziobuffer ) {
M_free(f->ziobuffer,"output zbuffer");
M_free(f->zsp,"output zstream");
f->ziobuffer = 0;
}
}
/*
#] ClearSortGZIP :
*/
#endif
form-master/sources/comtool.c 0000664 0000000 0000000 00000043111 13565763364 0016637 0 ustar 00root root 0000000 0000000 /** @file comtool.c
*
* Utility routines for the compiler.
*/
/* #[ License : */
/*
* Copyright (C) 1984-2017 J.A.M. Vermaseren
* When using this file you are requested to refer to the publication
* J.A.M.Vermaseren "New features of FORM" math-ph/0010025
* This is considered a matter of courtesy as the development was paid
* for by FOM the Dutch physics granting agency and we would like to
* be able to track its scientific use to convince FOM of its value
* for the community.
*
* This file is part of FORM.
*
* FORM 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.
*
* FORM 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 FORM. If not, see .
*/
/* #] License : */
/*
#[ Includes :
*/
#include "form3.h"
/*
#] Includes :
#[ inicbufs :
*/
/**
* Creates a new compiler buffer and returns its ID number.
*
* @return The ID number for the new compiler buffer.
*/
int inicbufs(VOID)
{
int i, num = AC.cbufList.num;
CBUF *C = cbuf;
for ( i = 0; i < num; i++, C++ ) {
if ( C->Buffer == 0 ) break;
}
if ( i >= num ) C = (CBUF *)FromList(&AC.cbufList);
else num = i;
C->BufferSize = 2000;
C->Buffer = (WORD *)Malloc1(C->BufferSize*sizeof(WORD),"compiler buffer-1");
C->Pointer = C->Buffer;
C->Top = C->Buffer + C->BufferSize;
C->maxlhs = 10;
C->lhs = (WORD **)Malloc1(C->maxlhs*sizeof(WORD *),"compiler buffer-2");
C->numlhs = 0;
C->mnumlhs = 0;
C->maxrhs = 25;
C->rhs = (WORD **)Malloc1(C->maxrhs*(sizeof(WORD *)+2*sizeof(LONG)+2*sizeof(WORD)),"compiler buffer-3");
C->CanCommu = (LONG *)(C->rhs+C->maxrhs);
C->NumTerms = C->CanCommu+C->maxrhs;
C->numdum = (WORD *)(C->NumTerms+C->maxrhs);
C->dimension = C->numdum + C->maxrhs;
C->numrhs = 0;
C->mnumrhs = 0;
C->rhs[0] = C->rhs[1] = C->Pointer;
C->boomlijst = 0;
RedoTree(C,C->maxrhs);
ClearTree(num);
return(num);
}
/*
#] inicbufs :
#[ finishcbuf :
*/
/**
* Frees a compiler buffer.
*
* @param num The ID number for the buffer to be freed.
*/
void finishcbuf(WORD num)
{
CBUF *C = cbuf+num;
if ( C->Buffer ) M_free(C->Buffer,"compiler buffer-1");
if ( C->rhs ) M_free(C->rhs,"compiler buffer-3");
if ( C->lhs ) M_free(C->lhs,"compiler buffer-2");
if ( C->boomlijst ) M_free(C->boomlijst,"boomlijst");
C->Top = C->Pointer = C->Buffer = 0;
C->rhs = C->lhs = 0;
C->CanCommu = 0;
C->NumTerms = 0;
C->BufferSize = 0;
C->boomlijst = 0;
C->numlhs = C->numrhs = C->maxlhs = C->maxrhs = C->mnumlhs =
C->mnumrhs = C->numtree = C->rootnum = C->MaxTreeSize = 0;
}
/*
#] finishcbuf :
#[ clearcbuf :
*/
/**
* Clears contents in a compiler buffer.
*
* @param num The ID number for the buffer to be cleared.
*/
void clearcbuf(WORD num)
{
CBUF *C = cbuf+num;
if ( C->boomlijst ) M_free(C->boomlijst,"boomlijst");
C->Pointer = C->Buffer;
C->numrhs = C->numlhs = 0;
C->mnumlhs = 0;
C->boomlijst = 0;
C->mnumrhs = 0;
C->rhs[0] = C->rhs[1] = C->Pointer;
C->numtree = C->rootnum = C->MaxTreeSize = 0;
RedoTree(C,C->maxrhs);
ClearTree(num);
}
/*
#] clearcbuf :
#[ DoubleCbuffer :
*/
/**
* Doubles a compiler buffer.
*
* @param num The ID number for the buffer to be doubled.
* @param w The pointer to the end (exclusive) of the current buffer. The
* contents in the range of [cbuf[num].Buffer,w) will be kept.
*/
WORD *DoubleCbuffer(int num, WORD *w,int par)
{
CBUF *C = cbuf + num;
LONG newsize = C->BufferSize*2;
WORD *newbuffer = (WORD *)Malloc1(newsize*sizeof(WORD),"compiler buffer-4");
WORD *w1, *w2;
LONG offset, j, i;
DUMMYUSE(par)
/*
MLOCK(ErrorMessageLock);
MesPrint(" doubleCbuffer: par = %d",par);
MUNLOCK(ErrorMessageLock);
*/
w1 = C->Buffer; w2 = newbuffer;
i = w - w1;
j = i & 7;
while ( --j >= 0 ) *w2++ = *w1++;
i >>= 3;
while ( --i >= 0 ) {
*w2++ = *w1++; *w2++ = *w1++; *w2++ = *w1++; *w2++ = *w1++;
*w2++ = *w1++; *w2++ = *w1++; *w2++ = *w1++; *w2++ = *w1++;
}
offset = newbuffer - C->Buffer;
for ( i = 0; i <= C->numlhs; i++ ) C->lhs[i] += offset;
for ( i = 1; i <= C->numrhs; i++ ) C->rhs[i] += offset;
w1 = C->Buffer;
C->Pointer += offset;
C->Top = newbuffer + newsize;
C->BufferSize = newsize;
C->Buffer = newbuffer;
M_free(w1,"DoubleCbuffer");
return(w2);
}
/*
#] DoubleCbuffer :
#[ AddLHS :
*/
/**
* Adds an LHS to a compiler buffer and returns the pointer to a buffer for the
* new LHS.
*
* @param num The ID number for the buffer to get another LHS.
*/
WORD *AddLHS(int num)
{
CBUF *C = cbuf + num;
C->numlhs++;
if ( C->numlhs >= (C->maxlhs-2) ) {
WORD ***ppp = &(C->lhs); /* to avoid compiler warning */
if ( DoubleList((VOID ***)ppp,&(C->maxlhs),sizeof(WORD *),
"statement lists") ) Terminate(-1);
}
C->lhs[C->numlhs] = C->Pointer;
C->lhs[C->numlhs+1] = 0;
return(C->Pointer);
}
/*
#] AddLHS :
#[ AddRHS :
*/
/**
* Adds an RHS to a compiler buffer and returns the pointer to a buffer for the
* new RHS.
*
* @param num The ID number for the buffer to get another RHS.
* @param type If 0, the subexpression tree will be reallocated.
*/
WORD *AddRHS(int num, int type)
{
LONG fullsize, *lold, newsize;
int i;
WORD **old, *wold;
CBUF *C;
restart:;
C = cbuf + num;
if ( C->numrhs >= (C->maxrhs-2) ) {
if ( C->maxrhs == 0 ) newsize = 100;
else newsize = C->maxrhs * 2;
if ( newsize > MAXCOMBUFRHS ) newsize = MAXCOMBUFRHS;
if ( newsize == C->maxrhs ) {
if ( AC.tablefilling ) {
TABLES T = functions[AC.tablefilling].tabl;
/*
We add a compiler buffer, change a few settings and continue.
*/
if ( T->buffersfill >= T->bufferssize ) {
int new1 = 2*T->bufferssize;
WORD *nbufs = (WORD *)Malloc1(new1*sizeof(WORD),"Table compile buffers");
for ( i = 0; i < T->buffersfill; i++ )
nbufs[i] = T->buffers[i];
for ( ; i < new1; i++ ) nbufs[i] = 0;
M_free(T->buffers,"Table compile buffers");
T->buffers = nbufs;
T->bufferssize = new1;
}
T->buffers[T->buffersfill++] = T->bufnum = inicbufs();
AC.cbufnum = num = T->bufnum;
goto restart;
}
else {
MesPrint("@Compiler buffer overflow. Try to make modules smaller");
Terminate(-1);
}
}
old = C->rhs;
fullsize = newsize * (sizeof(WORD *) + 2*sizeof(LONG) + 2*sizeof(WORD));
C->rhs = (WORD **)Malloc1(fullsize,"subexpression lists");
for ( i = 0; i < C->maxrhs; i++ ) C->rhs[i] = old[i];
lold = C->CanCommu; C->CanCommu = (LONG *)(C->rhs+newsize);
for ( i = 0; i < C->maxrhs; i++ ) C->CanCommu[i] = lold[i];
lold = C->NumTerms; C->NumTerms = (LONG *)(C->rhs+2*newsize);
for ( i = 0; i < C->maxrhs; i++ ) C->NumTerms[i] = lold[i];
wold = C->numdum; C->numdum = (WORD *)(C->NumTerms+newsize);
for ( i = 0; i < C->maxrhs; i++ ) C->numdum[i] = wold[i];
wold = C->dimension; C->dimension = (WORD *)(C->numdum+newsize);
for ( i = 0; i < C->maxrhs; i++ ) C->dimension[i] = wold[i];
if ( old ) M_free(old,"subexpression lists");
C->maxrhs = newsize;
if ( type == 0 ) RedoTree(C,C->maxrhs);
}
C->numrhs++;
C->CanCommu[C->numrhs] = 0;
C->NumTerms[C->numrhs] = 0;
C->numdum[C->numrhs] = 0;
C->dimension[C->numrhs] = 0;
C->rhs[C->numrhs] = C->Pointer;
return(C->Pointer);
}
/*
#] AddRHS :
#[ AddNtoL :
*/
/**
* Adds an LHS with the given data to the current compiler buffer.
*
* @param n The length of the data.
* @param array The data to be added.
* @return 0 if succeeds.
*/
int AddNtoL(int n, WORD *array)
{
int i;
CBUF *C = cbuf+AC.cbufnum;
#ifdef COMPBUFDEBUG
MesPrint("LH: %a",n,array);
#endif
AddLHS(AC.cbufnum);
while ( C->Pointer+n >= C->Top ) DoubleCbuffer(AC.cbufnum,C->Pointer,1);
for ( i = 0; i < n; i++ ) *(C->Pointer)++ = *array++;
return(0);
}
/*
#] AddNtoL :
#[ AddNtoC :
Commentary: added the bufnum on 14-sep-2010 to make the whole a bit
more flexible (JV). Still to do with AddNtoL.
*/
/**
* Adds the given data to the last LHS/RHS in a compiler buffer.
*
* @param bufnum The ID number for the buffer where the data will be added.
* @param n The length of the data.
* @param array The data to be added.
* @return 0 if succeeds.
*/
int AddNtoC(int bufnum, int n, WORD *array,int par)
{
int i;
WORD *w;
CBUF *C = cbuf+bufnum;
#ifdef COMPBUFDEBUG
MesPrint("RH: %a",n,array);
#endif
while ( C->Pointer+n+1 >= C->Top ) DoubleCbuffer(bufnum,C->Pointer,50+par);
w = C->Pointer;
for ( i = 0; i < n; i++ ) *w++ = *array++;
C->Pointer = w;
return(0);
}
/*
#] AddNtoC :
#[ InsTree :
Routines for balanced tree searching and insertion.
Compared to Knuth we have a parent link. This minimizes the
number of compares. That is better for anything that is more
complicated than just single numbers.
There are no provisions for removing elements from the tree.
The routines are:
void RedoTree(size) Re-allocates the tree space. There will
be MaxTreeSize = size elements.
void ClearTree() Prunes the tree down to the root element.
int InsTree(int,int)Searches for the requested element. If not found it
will allocate a new element, balance the tree if
necessary and return the called number.
If it was in the tree, it returns the tree 'value'.
Commentary: added the bufnum on 14-sep-2010 to make the whole a bit
more flexible (JV).
*/
static COMPTREE comptreezero = {0,0,0,0,0,0};
int InsTree(int bufnum, int h)
{
CBUF *C = cbuf + bufnum;
COMPTREE *boomlijst = C->boomlijst, *q = boomlijst + C->rootnum, *p, *s;
WORD *v1, *v2, *v3;
int ip, iq, is;
if ( C->numtree + 1 >= C->MaxTreeSize ) {
if ( C->MaxTreeSize == 0 ) {
COMPTREE *root;
C->MaxTreeSize = 125;
C->boomlijst = (COMPTREE *)Malloc1((C->MaxTreeSize+1)*sizeof(COMPTREE),
"ClearInsTree");
root = C->boomlijst;
C->numtree = 0;
C->rootnum = 0;
root->left = -1;
root->right = -1;
root->parent = -1;
root->blnce = 0;
root->value = -1;
root->usage = 0;
for ( ip = 1; ip < C->MaxTreeSize; ip++ ) { C->boomlijst[ip] = comptreezero; }
}
else {
is = C->MaxTreeSize * 2;
s = (COMPTREE *)Malloc1((is+1)*sizeof(COMPTREE),"InsTree");
for ( ip = 0; ip < C->MaxTreeSize; ip++ ) { s[ip] = C->boomlijst[ip]; }
for ( ip = C->MaxTreeSize; ip <= is; ip++ ) { s[ip] = comptreezero; }
if ( C->boomlijst ) M_free(C->boomlijst,"InsTree");
C->boomlijst = s;
C->MaxTreeSize = is;
}
boomlijst = C->boomlijst;
q = boomlijst + C->rootnum;
}
if ( q->right == -1 ) { /* First element */
C->numtree++;
s = boomlijst+C->numtree;
q->right = C->numtree;
s->parent = C->rootnum;
s->left = s->right = -1;
s->blnce = 0;
s->value = h;
s->usage = 1;
return(h);
}
ip = q->right;
while ( ip >= 0 ) {
p = boomlijst + ip;
v1 = C->rhs[p->value]; v2 = v3 = C->rhs[h];
while ( *v3 ) v3 += *v3; /* find the 0 that indicates end-of-expr */
while ( *v1 == *v2 && v2 < v3 ) { v1++; v2++; }
if ( *v1 > *v2 ) {
iq = p->right;
if ( iq >= 0 ) { ip = iq; }
else {
C->numtree++;
is = C->numtree;
p->right = is;
s = boomlijst + is;
s->parent = ip; s->left = s->right = -1;
s->blnce = 0; s->value = h; s->usage = 1;
p->blnce++;
if ( p->blnce == 0 ) return(h);
goto balance;
}
}
else if ( *v1 < *v2 ) {
iq = p->left;
if ( iq >= 0 ) { ip = iq; }
else {
C->numtree++;
is = C->numtree;
s = boomlijst+is;
p->left = is;
s->parent = ip; s->left = s->right = -1;
s->blnce = 0; s->value = h; s->usage = 1;
p->blnce--;
if ( p->blnce == 0 ) return(h);
goto balance;
}
}
else {
p->usage++;
return(p->value);
}
}
MesPrint("We vallen uit de boom!");
Terminate(-1);
return(h);
balance:;
for (;;) {
p = boomlijst + ip;
iq = p->parent;
if ( iq == C->rootnum ) break;
q = boomlijst + iq;
if ( ip == q->left ) q->blnce--;
else q->blnce++;
if ( q->blnce == 0 ) break;
if ( q->blnce == -2 ) {
if ( p->blnce == -1 ) { /* single rotation */
q->left = p->right;
p->right = iq;
p->parent = q->parent;
q->parent = ip;
if ( boomlijst[p->parent].left == iq ) boomlijst[p->parent].left = ip;
else boomlijst[p->parent].right = ip;
if ( q->left >= 0 ) boomlijst[q->left].parent = iq;
q->blnce = p->blnce = 0;
}
else { /* double rotation */
s = boomlijst + is;
q->left = s->right;
p->right = s->left;
s->right = iq;
s->left = ip;
if ( p->right >= 0 ) boomlijst[p->right].parent = ip;
if ( q->left >= 0 ) boomlijst[q->left].parent = iq;
s->parent = q->parent;
q->parent = is;
p->parent = is;
if ( boomlijst[s->parent].left == iq )
boomlijst[s->parent].left = is;
else boomlijst[s->parent].right = is;
if ( s->blnce > 0 ) { q->blnce = s->blnce = 0; p->blnce = -1; }
else if ( s->blnce < 0 ) { p->blnce = s->blnce = 0; q->blnce = 1; }
else { p->blnce = s->blnce = q->blnce = 0; }
}
break;
}
else if ( q->blnce == 2 ) {
if ( p->blnce == 1 ) { /* single rotation */
q->right = p->left;
p->left = iq;
p->parent = q->parent;
q->parent = ip;
if ( boomlijst[p->parent].left == iq ) boomlijst[p->parent].left = ip;
else boomlijst[p->parent].right = ip;
if ( q->right >= 0 ) boomlijst[q->right].parent = iq;
q->blnce = p->blnce = 0;
}
else { /* double rotation */
s = boomlijst + is;
q->right = s->left;
p->left = s->right;
s->left = iq;
s->right = ip;
if ( p->left >= 0 ) boomlijst[p->left].parent = ip;
if ( q->right >= 0 ) boomlijst[q->right].parent = iq;
s->parent = q->parent;
q->parent = is;
p->parent = is;
if ( boomlijst[s->parent].left == iq ) boomlijst[s->parent].left = is;
else boomlijst[s->parent].right = is;
if ( s->blnce < 0 ) { q->blnce = s->blnce = 0; p->blnce = 1; }
else if ( s->blnce > 0 ) { p->blnce = s->blnce = 0; q->blnce = -1; }
else { p->blnce = s->blnce = q->blnce = 0; }
}
break;
}
is = ip; ip = iq;
}
return(h);
}
/*
#] InsTree :
#[ FindTree :
Routines for balanced tree searching.
Is like InsTree but without the insertions.
Returns -1 if the element is not in the tree.
The advantage of this routine over InsTree is that this routine
can be run in parallel.
*/
int FindTree(int bufnum, WORD *subexpr)
{
CBUF *C = cbuf + bufnum;
COMPTREE *boomlijst = C->boomlijst, *q = boomlijst + C->rootnum, *p;
WORD *v1, *v2, *v3;
int ip, iq;
ip = q->right;
while ( ip >= 0 ) {
p = boomlijst + ip;
v1 = C->rhs[p->value]; v2 = v3 = subexpr;
while ( *v3 ) v3 += *v3; /* find the 0 that indicates end-of-expr */
while ( *v1 == *v2 && v2 < v3 ) { v1++; v2++; }
if ( *v1 > *v2 ) {
iq = p->right;
if ( iq >= 0 ) { ip = iq; }
else { return(-1); }
}
else if ( *v1 < *v2 ) {
iq = p->left;
if ( iq >= 0 ) { ip = iq; }
else { return(-1); }
}
else {
p->usage++;
return(p->value);
}
}
return(-1);
}
/*
#] FindTree :
#[ RedoTree :
*/
void RedoTree(CBUF *C, int size)
{
COMPTREE *newboomlijst;
int i;
newboomlijst = (COMPTREE *)Malloc1((size+1)*sizeof(COMPTREE),"newboomlijst");
if ( C->boomlijst ) {
if ( C->MaxTreeSize > size ) C->MaxTreeSize = size;
for ( i = 0; i < C->MaxTreeSize; i++ ) newboomlijst[i] = C->boomlijst[i];
M_free(C->boomlijst,"boomlijst");
}
C->boomlijst = newboomlijst;
C->MaxTreeSize = size;
}
/*
#] RedoTree :
#[ ClearTree :
*/
void ClearTree(int i)
{
CBUF *C = cbuf + i;
COMPTREE *root = C->boomlijst;
if ( root ) {
C->numtree = 0;
C->rootnum = 0;
root->left = -1;
root->right = -1;
root->parent = -1;
root->blnce = 0;
root->value = -1;
root->usage = 0;
}
}
/*
#] ClearTree :
#[ IniFbuffer :
*/
/**
* Initialize a factorization cache buffer.
* We set the size of the rhs and boomlijst buffers immediately
* to their final values.
*/
int IniFbuffer(WORD bufnum)
{
CBUF *C = cbuf + bufnum;
COMPTREE *root;
int i;
LONG fullsize;
C->maxrhs = AM.fbuffersize;
C->MaxTreeSize = AM.fbuffersize;
/*
* Note that bufnum is a return value of inicbufs(). So C has been already
* initialized. (TU 20 Dec 2011)
*/
if ( C->boomlijst ) M_free(C->boomlijst, "IniFbuffer-tree");
if ( C->rhs ) M_free(C->rhs, "IniFbuffer-rhs");
C->boomlijst = (COMPTREE *)Malloc1((C->MaxTreeSize+1)*sizeof(COMPTREE),"IniFbuffer-tree");
root = C->boomlijst;
C->numtree = 0;
C->rootnum = 0;
root->left = -1;
root->right = -1;
root->parent = -1;
root->blnce = 0;
root->value = -1;
root->usage = 0;
for ( i = 1; i < C->MaxTreeSize; i++ ) { C->boomlijst[i] = comptreezero; }
fullsize = (C->maxrhs+1) * (sizeof(WORD *) + 2*sizeof(LONG) + 2*sizeof(WORD));
C->rhs = (WORD **)Malloc1(fullsize,"IniFbuffer-rhs");
C->CanCommu = (LONG *)(C->rhs+C->maxrhs);
C->NumTerms = (LONG *)(C->rhs+2*C->maxrhs);
C->numdum = (WORD *)(C->NumTerms+C->maxrhs);
C->dimension = (WORD *)(C->numdum+C->maxrhs);
return(0);
}
/*
#] IniFbuffer :
#[ numcommute :
Returns the number of non-commuting terms in the expression
*/
LONG numcommute(WORD *terms, LONG *numterms)
{
LONG num = 0;
WORD *t, *m;
*numterms = 0;
while ( *terms ) {
*numterms += 1;
t = terms + 1;
GETSTOP(terms,m);
while ( t < m ) {
if ( *t >= FUNCTION ) {
if ( functions[*t-FUNCTION].commute ) { num++; break; }
}
t += t[1];
}
terms = terms + *terms;
}
return(num);
}
/*
#] numcommute :
*/
form-master/sources/comtool.h 0000664 0000000 0000000 00000005204 13565763364 0016645 0 ustar 00root root 0000000 0000000 /** @file comtool.h
*
* Utility routines for the compiler.
*/
/* #[ License : */
/*
* Copyright (C) 1984-2017 J.A.M. Vermaseren
* When using this file you are requested to refer to the publication
* J.A.M.Vermaseren "New features of FORM" math-ph/0010025
* This is considered a matter of courtesy as the development was paid
* for by FOM the Dutch physics granting agency and we would like to
* be able to track its scientific use to convince FOM of its value
* for the community.
*
* This file is part of FORM.
*
* FORM 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.
*
* FORM 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 FORM. If not, see .
*/
/* #] License : */
#ifndef FORM_COMTOOL_H_
#define FORM_COMTOOL_H_
/*
#[ Includes :
*/
#include "form3.h"
/*
#] Includes :
#[ Inline functions :
*/
/**
* Skips white-spaces in the buffer. Here the white-spaces includes commas,
* which is treated as a space in FORM.
*
* @param[in,out] s The pointer to the buffer.
*/
static inline void SkipSpaces(UBYTE **s)
{
const char *p = (const char *)*s;
while ( *p == ' ' || *p == ',' || *p == '\t' ) p++;
*s = (UBYTE *)p;
}
/**
* Checks if the next word in the buffer is the given keyword, with ignoring
* case. If found, the pointer is moved such that the keyword is consumed in the
* buffer, and this function returns a non-zero value.
*
* @param[in,out] s The pointer to the buffer. Changed if the keyword found.
* @param opt The optional keyword.
* @return 1 if the keyword found, otherwise 0.
*/
static inline int ConsumeOption(UBYTE **s, const char *opt)
{
const char *p = (const char *)*s;
while ( *p && *opt && tolower(*p) == tolower(*opt) ) {
p++;
opt++;
}
/* Check if `opt` ended. */
if ( !*opt ) {
/* Check if `*p` is a word boundary. */
if ( !*p || !(FG.cTable[(unsigned char)*p] == 0 ||
FG.cTable[(unsigned char)*p] == 1 || *p == '_' ||
*p == '$') ) {
/* Consume the option. Skip the trailing spaces. */
*s = (UBYTE *)p;
SkipSpaces(s);
return(1);
}
}
return(0);
}
/*
#] Inline functions :
*/
#endif /* FORM_COMTOOL_H_ */
form-master/sources/declare.h 0000664 0000000 0000000 00000177753 13565763364 0016613 0 ustar 00root root 0000000 0000000 #ifndef __FDECLARE__
#define __FDECLARE__
/** @file declare.h
*
* Contains macros and function declarations.
*/
/* #[ License : */
/*
* Copyright (C) 1984-2017 J.A.M. Vermaseren
* When using this file you are requested to refer to the publication
* J.A.M.Vermaseren "New features of FORM" math-ph/0010025
* This is considered a matter of courtesy as the development was paid
* for by FOM the Dutch physics granting agency and we would like to
* be able to track its scientific use to convince FOM of its value
* for the community.
*
* This file is part of FORM.
*
* FORM 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.
*
* FORM 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 FORM. If not, see .
*/
/* #] License : */
/*
#[ Macro's :
*/
#define MaX(x,y) ((x) > (y) ? (x): (y))
#define MiN(x,y) ((x) < (y) ? (x): (y))
#define ABS(x) ( (x) < 0 ? -(x): (x) )
#define SGN(x) ( (x) > 0 ? 1 : (x) < 0 ? -1 : 0 )
#define REDLENG(x) ((((x)<0)?((x)+1):((x)-1))/2)
#define INCLENG(x) (((x)<0)?(((x)*2)-1):(((x)*2)+1))
#define GETCOEF(x,y) x += *x;y = x[-1];x -= ABS(y);y=REDLENG(y)
#define GETSTOP(x,y) y=x+(*x)-1;y -= ABS(*y)-1
#define StuffAdd(x,y) (((x)<0?-1:1)*(y)+((y)<0?-1:1)*(x))
#define EXCHN(t1,t2,n) { WORD a,i; for(i=0;inextchar[(stream)->isnextchar++]=c)
#ifdef WITHRETURN
#define AddLineFeed(s,n) { (s)[(n)++] = CARRIAGERETURN; (s)[(n)++] = LINEFEED; }
#else
#define AddLineFeed(s,n) { (s)[(n)++] = LINEFEED; }
#endif
#define TryRecover(x) Terminate(-1)
#define UngetChar(c) { pushbackchar = c; }
#define ParseNumber(x,s) {(x)=0;while(*(s)>='0'&&*(s)<='9')(x)=10*(x)+*(s)++ -'0';}
#define ParseSign(sgn,s) {(sgn)=0;while(*(s)=='-'||*(s)=='+'){\
if ( *(s)++ == '-' ) sgn ^= 1;}}
#define ParseSignedNumber(x,s) { int sgn; ParseSign(sgn,s)\
ParseNumber(x,s) if ( sgn ) x = -x; }
#define NCOPY(s,t,n) while ( --n >= 0 ) *s++ = *t++;
/*#define NCOPY(s,t,n) { memcpy(s,t,n*sizeof(WORD)); s+=n; t+=n; n = -1; }*/
#define NCOPYI(s,t,n) while ( --n >= 0 ) *s++ = *t++;
#define NCOPYB(s,t,n) while ( --n >= 0 ) *s++ = *t++;
#define NCOPYI32(s,t,n) while ( --n >= 0 ) *s++ = *t++;
#define WCOPY(s,t,n) { int nn=n; WORD *ss=(WORD *)s, *tt=(WORD *)t; while ( --nn >= 0 ) *ss++=*tt++; }
#define NeedNumber(x,s,err) { int sgn = 1; \
while ( *s == ' ' || *s == '\t' || *s == '-' || *s == '+' ) { \
if ( *s == '-' ) sgn = -sgn; s++; } \
if ( chartype[*s] != 1 ) goto err; \
ParseNumber(x,s) \
if ( sgn < 0 ) (x) = -(x); while ( *s == ' ' || *s == '\t' ) s++;\
}
#define SKIPBLANKS(s) { while ( *(s) == ' ' || *(s) == '\t' ) (s)++; }
#define FLUSHCONSOLE if ( AP.InOutBuf > 0 ) CharOut(LINEFEED)
#define SKIPBRA1(s) { int lev1=0; s++; while(*s) { if(*s=='[')lev1++; \
else if(*s==']'&&--lev1<0)break; s++;} }
#define SKIPBRA2(s) { int lev2=0; s++; while(*s) { if(*s=='{')lev2++; \
else if(*s=='}'&&--lev2<0)break; \
else if(*s=='[')SKIPBRA1(s) s++;} }
#define SKIPBRA3(s) { int lev3=0; s++; while(*s) { if(*s=='(')lev3++; \
else if(*s==')'&&--lev3<0)break; \
else if(*s=='{')SKIPBRA2(s) \
else if(*s=='[')SKIPBRA1(s) s++;} }
#define SKIPBRA4(s) { int lev4=0; s++; while(*s) { if(*s=='(')lev4++; \
else if(*s==')'&&--lev4<0)break; \
else if(*s=='[')SKIPBRA1(s) s++;} }
#define SKIPBRA5(s) { int lev5=0; s++; while(*s) { if(*s=='{')lev5++; \
else if(*s=='}'&&--lev5<0)break; \
else if(*s=='(')SKIPBRA4(s) \
else if(*s=='[')SKIPBRA1(s) s++;} }
/*
#define CYCLE1(a,i) {WORD iX,jX; iX=*a; for(jX=1;jXPointer>=c->Top) \
DoubleCbuffer(c-cbuf,c->Pointer,21); \
*(c->Pointer)++ = wx;
#define EXCHINOUT { FILEHANDLE *ffFi = AR.outfile; \
AR.outfile = AR.infile; AR.infile = ffFi; }
#define BACKINOUT { FILEHANDLE *ffFi = AR.outfile; POSITION posi; \
AR.outfile = AR.infile; AR.infile = ffFi; \
SetEndScratch(AR.infile,&posi); }
#define CopyArg(to,from) { if ( *from > 0 ) { int ica = *from; NCOPY(to,from,ica) } \
else if ( *from <= -FUNCTION ) *to++ = *from++; \
else { *to++ = *from++; *to++ = *from++; } }
#if ARGHEAD > 2
#define FILLARG(w) { int i = ARGHEAD-2; while ( --i >= 0 ) *w++ = 0; }
#define COPYARG(w,t) { int i = ARGHEAD-2; while ( --i >= 0 ) *w++ = *t++; }
#define ZEROARG(w) { int i; for ( i = 2; i < ARGHEAD; i++ ) w[i] = 0; }
#else
#define FILLARG(w)
#define COPYARG(w,t)
#define ZEROARG(w)
#endif
#if FUNHEAD > 2
#define FILLFUN(w) { *w++ = 0; FILLFUN3(w) }
#define COPYFUN(w,t) { *w++ = *t++; COPYFUN3(w,t) }
#else
#define FILLFUN(w)
#define COPYFUN(w,t)
#endif
#if FUNHEAD > 3
#define FILLFUN3(w) { int ie = FUNHEAD-3; while ( --ie >= 0 ) *w++ = 0; }
#define COPYFUN3(w,t) { int ie = FUNHEAD-3; while ( --ie >= 0 ) *w++ = *t++; }
#else
#define COPYFUN3(w,t)
#define FILLFUN3(w)
#endif
#if SUBEXPSIZE > 5
#define FILLSUB(w) { int ie = SUBEXPSIZE-5; while ( --ie >= 0 ) *w++ = 0; }
#define COPYSUB(w,ww) { int ie = SUBEXPSIZE-5; while ( --ie >= 0 ) *w++ = *ww++; }
#else
#define FILLSUB(w)
#define COPYSUB(w,ww)
#endif
#if EXPRHEAD > 4
#define FILLEXPR(w) { int ie = EXPRHEAD-4; while ( --ie >= 0 ) *w++ = 0; }
#else
#define FILLEXPR(w)
#endif
#define NEXTARG(x) if(*x>0) x += *x; else if(*x <= -FUNCTION)x++; else x += 2;
#define COPY1ARG(s1,t1) { int ica; if ( (ica=*t1) > 0 ) { NCOPY(s1,t1,ica) } \
else if(*t1<=-FUNCTION){*s1++=*t1++;} else{*s1++=*t1++;*s1++=*t1++;} }
/**
* Fills a buffer by zero in the range [begin,end).
*
* @param w The buffer.
* @param begin The index for the beginning of the range.
* @param end The index for the end of the range (exclusive).
*/
#define ZeroFillRange(w,begin,end) do { \
int tmp_i; \
for ( tmp_i = begin; tmp_i < end; tmp_i++ ) { (w)[tmp_i] = 0; } \
} while (0)
#define TABLESIZE(a,b) (((WORD)sizeof(a))/((WORD)sizeof(b)))
#define WORDDIF(x,y) (WORD)(x-y)
#define wsizeof(a) ((WORD)sizeof(a))
#define VARNAME(type,num) (AC.varnames->namebuffer+type[num].name)
#define DOLLARNAME(type,num) (AC.dollarnames->namebuffer+type[num].name)
#define EXPRNAME(num) (AC.exprnames->namebuffer+Expressions[num].name)
#define PREV(x) prevorder?prevorder:x
#define SETERROR(x) { Terminate(-1); return(-1); }
/* use this macro to avoid the unused parameter warning */
#define DUMMYUSE(x) (void)(x);
#ifdef _FILE_OFFSET_BITS
#if _FILE_OFFSET_BITS==64
/*:[19mar2004 mt]*/
#define ADDPOS(pp,x) (pp).p1 = ((pp).p1+(off_t)(x))
#define SETBASELENGTH(ss,x) (ss).p1 = (off_t)(x)
#define SETBASEPOSITION(pp,x) (pp).p1 = (off_t)(x)
#define ISEQUALPOSINC(pp1,pp2,x) ( (pp1).p1 == ((pp2).p1+(off_t)(x)) )
#define ISGEPOSINC(pp1,pp2,x) ( (pp1).p1 >= ((pp2).p1+(off_t)(x)) )
#define DIVPOS(pp,n) ( (pp).p1/(off_t)(n) )
#define MULPOS(pp,n) (pp).p1 *= (off_t)(n)
#else
#define ADDPOS(pp,x) (pp).p1 = ((pp).p1+(x))
#define SETBASELENGTH(ss,x) (ss).p1 = (x)
#define SETBASEPOSITION(pp,x) (pp).p1 = (x)
#define ISEQUALPOSINC(pp1,pp2,x) ( (pp1).p1 == ((pp2).p1+(LONG)(x)) )
#define ISGEPOSINC(pp1,pp2,x) ( (pp1).p1 >= ((pp2).p1+(LONG)(x)) )
#define DIVPOS(pp,n) ( (pp).p1/(n) )
#define MULPOS(pp,n) (pp).p1 *= (n)
#endif
#else
#define ADDPOS(pp,x) (pp).p1 = ((pp).p1+(LONG)(x))
#define SETBASELENGTH(ss,x) (ss).p1 = (LONG)(x)
#define SETBASEPOSITION(pp,x) (pp).p1 = (LONG)(x)
#define ISEQUALPOSINC(pp1,pp2,x) ( (pp1).p1 == ((pp2).p1+(LONG)(x)) )
#define ISGEPOSINC(pp1,pp2,x) ( (pp1).p1 >= ((pp2).p1+(LONG)(x)) )
#define DIVPOS(pp,n) ( (pp).p1/(LONG)(n) )
#define MULPOS(pp,n) (pp).p1 *= (LONG)(n)
#endif
#define DIFPOS(ss,pp1,pp2) (ss).p1 = ((pp1).p1-(pp2).p1)
#define DIFBASE(pp1,pp2) ((pp1).p1-(pp2).p1)
#define ADD2POS(pp1,pp2) (pp1).p1 += (pp2).p1
#define PUTZERO(pp) (pp).p1 = 0
#define BASEPOSITION(pp) ((pp).p1)
#define SETSTARTPOS(pp) (pp).p1 = -2
#define NOTSTARTPOS(pp) ( (pp).p1 > -2 )
#define ISMINPOS(pp) ( (pp).p1 == -1 )
#define ISEQUALPOS(pp1,pp2) ( (pp1).p1 == (pp2).p1 )
#define ISNOTEQUALPOS(pp1,pp2) ( (pp1).p1 != (pp2).p1 )
#define ISLESSPOS(pp1,pp2) ( (pp1).p1 < (pp2).p1 )
#define ISGEPOS(pp1,pp2) ( (pp1).p1 >= (pp2).p1 )
#define ISNOTZEROPOS(pp) ( (pp).p1 != 0 )
#define ISZEROPOS(pp) ( (pp).p1 == 0 )
#define ISPOSPOS(pp) ( (pp).p1 > 0 )
#define ISNEGPOS(pp) ( (pp).p1 < 0 )
extern VOID TELLFILE(int,POSITION *);
#define TOLONG(x) ((LONG)(x))
#define Add2Com(x) { WORD cod[2]; cod[0] = x; cod[1] = 2; AddNtoL(2,cod); }
#define Add3Com(x1,x2) { WORD cod[3]; cod[0] = x1; cod[1] = 3; cod[2] = x2; AddNtoL(3,cod); }
#define Add4Com(x1,x2,x3) { WORD cod[4]; cod[0] = x1; cod[1] = 4; \
cod[2] = x2; cod[3] = x3; AddNtoL(4,cod); }
#define Add5Com(x1,x2,x3,x4) { WORD cod[5]; cod[0] = x1; cod[1] = 5; \
cod[2] = x2; cod[3] = x3; cod[4] = x4; AddNtoL(5,cod); }
/*
The temporary variable ppp is to avoid a compiler warning about strict aliassing
*/
#define WantAddPointers(x) while((AT.pWorkPointer+(x))>AR.pWorkSize){WORD ***ppp=&AT.pWorkSpace;\
ExpandBuffer((void **)ppp,&AR.pWorkSize,(int)(sizeof(WORD *)));}
#define WantAddLongs(x) while((AT.lWorkPointer+(x))>AR.lWorkSize){LONG **ppp=&AT.lWorkSpace;\
ExpandBuffer((void **)ppp,&AR.lWorkSize,sizeof(LONG));}
#define WantAddPositions(x) while((AT.posWorkPointer+(x))>AR.posWorkSize){POSITION **ppp=&AT.posWorkSpace;\
ExpandBuffer((void **)ppp,&AR.posWorkSize,sizeof(POSITION));}
/* inline in form3.h (or config.h). */
#define FORM_INLINE inline
/*
Macro's for memory management. This can be done by routines, but that
would be slower. Inline routines could do this, but we don't want to
leave this to the friendliness of the compiler(s).
The routines can be found in the file tools.c
*/
#define MEMORYMACROS
#ifdef MEMORYMACROS
#define TermMalloc(x) ( (AT.TermMemTop <= 0 ) ? TermMallocAddMemory(BHEAD0), AT.TermMemHeap[--AT.TermMemTop]: AT.TermMemHeap[--AT.TermMemTop] )
#define NumberMalloc(x) ( (AT.NumberMemTop <= 0 ) ? NumberMallocAddMemory(BHEAD0), AT.NumberMemHeap[--AT.NumberMemTop]: AT.NumberMemHeap[--AT.NumberMemTop] )
#define CacheNumberMalloc(x) ( (AT.CacheNumberMemTop <= 0 ) ? CacheNumberMallocAddMemory(BHEAD0), AT.CacheNumberMemHeap[--AT.CacheNumberMemTop]: AT.CacheNumberMemHeap[--AT.CacheNumberMemTop] )
#define TermFree(TermMem,x) AT.TermMemHeap[AT.TermMemTop++] = (WORD *)(TermMem)
#define NumberFree(NumberMem,x) AT.NumberMemHeap[AT.NumberMemTop++] = (UWORD *)(NumberMem)
#define CacheNumberFree(NumberMem,x) AT.CacheNumberMemHeap[AT.CacheNumberMemTop++] = (UWORD *)(NumberMem)
#else
#define TermMalloc(x) TermMalloc2(BHEAD (char *)(x))
#define NumberMalloc(x) NumberMalloc2(BHEAD (char *)(x))
#define CacheNumberMalloc(x) CacheNumberMalloc2(BHEAD (char *)(x))
#define TermFree(x,y) TermFree2(BHEAD (WORD *)(x),(char *)(y))
#define NumberFree(x,y) NumberFree2(BHEAD (UWORD *)(x),(char *)(y))
#define CacheNumberFree(x,y) CacheNumberFree2(BHEAD (UWORD *)(x),(char *)(y))
#endif
/*
* Macros for checking nesting levels in the compiler, used as follows:
*
* AC.IfSumCheck[AC.IfLevel] = NestingChecksum();
* AC.IfLevel++;
*
* AC.IfLevel--;
* if ( AC.IfSumCheck[AC.IfLevel] != NestingChecksum() ) {
* MesNesting();
* }
*
* Note that NestingChecksum() also contains AC.IfLevel and so in this case
* using increment/decrement operators on it in the left-hand side may be
* confusing.
*/
#define NestingChecksum() (AC.IfLevel + AC.RepLevel + AC.arglevel + AC.insidelevel + AC.termlevel + AC.inexprlevel + AC.dolooplevel +AC.SwitchLevel)
#define MesNesting() MesPrint("&Illegal nesting of if, repeat, argument, inside, term, inexpression and do")
#define MarkPolyRatFunDirty(T) {if(*T&&AR.PolyFunType==2){WORD *TP,*TT;TT=T+*T;TT-=ABS(TT[-1]);\
TP=T+1;while(TP= AP.MaxPreAssignLevel ) { int i; \
LONG *ap = (LONG *)Malloc1(2*AP.MaxPreAssignLevel*sizeof(LONG *),"PreAssignStack"); \
for ( i = 0; i < AP.MaxPreAssignLevel; i++ ) ap[i] = AP.PreAssignStack[i]; \
M_free(AP.PreAssignStack,"PreAssignStack"); \
AP.MaxPreAssignLevel *= 2; AP.PreAssignStack = ap; \
} \
*AT.WorkPointer++ = AP.PreContinuation; AP.PreContinuation = 0; \
AP.PreAssignStack[AP.PreAssignLevel] = AC.iPointer - AC.iBuffer; }
#define POPPREASSIGNLEVEL if ( AP.PreAssignLevel > 0 ) { GETIDENTITY \
AC.iPointer = AC.iBuffer + AP.PreAssignStack[AP.PreAssignLevel--]; \
AP.PreContinuation = *--AT.WorkPointer; \
*AC.iPointer = 0; }
/*
MesPrint("P-level popped to %d with %d",AP.PreAssignLevel,(WORD)(AC.iPointer - AC.iBuffer));
#] Macro's :
#[ Inline functions :
*/
/*
* The following three functions give the unsigned absolute value of a signed
* integer even for the most negative integer. This is beyond the scope of
* the standard abs() function and its family, whose return-values are signed.
* In short, we should not use the unary minus operator with signed numbers
* unless we are sure that there are no integer overflows. Instead, we rely on
* two well-defined operations: (i) signed-to-unsigned conversion and
* (ii) unary minus of unsigned operands.
*
* See also:
* https://stackoverflow.com/a/4536188 (Unary minus and signed-to-unsigned conversion)
* https://stackoverflow.com/q/8026694 (C: unary minus operator behavior with unsigned operands)
* https://stackoverflow.com/q/1610947 (Why does stdlib.h's abs() family of functions return a signed value?)
* https://blog.regehr.org/archives/226 (A Guide to Undefined Behavior in C and C++, Part 2)
*/
static inline unsigned int IntAbs(int x)
{
if ( x >= 0 ) return x;
return(-((unsigned int)x));
}
static inline UWORD WordAbs(WORD x)
{
if ( x >= 0 ) return x;
return(-((UWORD)x));
}
static inline ULONG LongAbs(LONG x)
{
if ( x >= 0 ) return x;
return(-((ULONG)x));
}
/*
* The following functions provide portable unsigned-to-signed conversions
* (to avoid the implementation-defined behaviour), which is expected to be
* optimized to a no-op.
*
* See also:
* https://stackoverflow.com/a/13208789 (Efficient unsigned-to-signed cast avoiding implementation-defined behavior)
*/
static inline int UnsignedToInt(unsigned int x)
{
extern void Terminate(int);
if ( x <= INT_MAX ) return(x);
if ( x >= (unsigned int)INT_MIN ) return((int)(x - INT_MIN) + INT_MIN);
Terminate(1);
return(0);
}
static inline WORD UWordToWord(UWORD x)
{
extern void Terminate(int);
if ( x <= WORD_MAX_VALUE ) return(x);
if ( x >= (UWORD)WORD_MIN_VALUE ) return((WORD)(x - WORD_MIN_VALUE) + WORD_MIN_VALUE);
Terminate(1);
return(0);
}
static inline LONG ULongToLong(ULONG x)
{
extern void Terminate(int);
if ( x <= LONG_MAX_VALUE ) return(x);
if ( x >= (ULONG)LONG_MIN_VALUE ) return((LONG)(x - LONG_MIN_VALUE) + LONG_MIN_VALUE);
Terminate(1);
return(0);
}
/*
#] Inline functions :
#[ Thread objects :
*/
/**
* NOTE: We have replaced LOCK(ErrorMessageLock) and UNLOCK(ErrorMessageLock)
* by MLOCK(ErrorMessageLock) and MUNLOCK(ErrorMessageLock). They are used
* for the synchronised output in ParFORM.
* (TU 28 May 2011)
*/
#ifdef WITHPTHREADS
#define EXTERNLOCK(x) extern pthread_mutex_t x;
#define INILOCK(x) pthread_mutex_t x = PTHREAD_MUTEX_INITIALIZER;
#define EXTERNRWLOCK(x) extern pthread_rwlock_t x;
#define INIRWLOCK(x) pthread_rwlock_t x = PTHREAD_RWLOCK_INITIALIZER;
#ifdef DEBUGGINGLOCKS
#include
#define LOCK(x) while ( pthread_mutex_trylock(&(x)) == EBUSY ) {}
#define RWLOCKR(x) while ( pthread_rwlock_tryrdlock(&(x)) == EBUSY ) {}
#define RWLOCKW(x) while ( pthread_rwlock_trywrlock(&(x)) == EBUSY ) {}
#else
#define LOCK(x) pthread_mutex_lock(&(x))
#define RWLOCKR(x) pthread_rwlock_rdlock(&(x))
#define RWLOCKW(x) pthread_rwlock_wrlock(&(x))
#endif
#define UNLOCK(x) pthread_mutex_unlock(&(x))
#define UNRWLOCK(x) pthread_rwlock_unlock(&(x))
#define MLOCK(x) LOCK(x)
#define MUNLOCK(x) UNLOCK(x)
#define GETBIDENTITY
#define GETIDENTITY int identity = WhoAmI(); ALLPRIVATES *B = AB[identity];
#else
#define EXTERNLOCK(x)
#define INILOCK(x)
#define LOCK(x)
#define UNLOCK(x)
#define EXTERNRWLOCK(x)
#define INIRWLOCK(x)
#define RWLOCKR(x)
#define RWLOCKW(x)
#define UNRWLOCK(x)
#ifdef WITHMPI
#define MLOCK(x) do { if ( PF.me != MASTER ) PF_MLock(); } while (0)
#define MUNLOCK(x) do { if ( PF.me != MASTER ) PF_MUnlock(); } while (0)
#else
#define MLOCK(x)
#define MUNLOCK(x)
#endif
#define GETIDENTITY
#define GETBIDENTITY
#endif
/*
#] Thread objects :
#[ Declarations :
*/
#ifdef TERMMALLOCDEBUG
extern WORD **DebugHeap1, **DebugHeap2;
#endif
/**
* All functions (well, nearly all) are declared here.
*/
extern VOID StartVariables();
extern VOID setSignalHandlers(VOID);
extern UBYTE *CodeToLine(WORD,UBYTE *);
extern UBYTE *AddArrayIndex(WORD ,UBYTE *);
extern INDEXENTRY *FindInIndex(WORD,FILEDATA *,WORD,WORD);
extern INDEXENTRY *NextFileIndex(POSITION *);
extern WORD *PasteTerm(PHEAD WORD,WORD *,WORD *,WORD,WORD);
extern UBYTE *StrCopy(UBYTE *,UBYTE *);
extern UBYTE *WrtPower(UBYTE *,WORD);
extern WORD AccumGCD(PHEAD UWORD *,WORD *,UWORD *,WORD);
extern VOID AddArgs(PHEAD WORD *,WORD *,WORD *);
extern WORD AddCoef(PHEAD WORD **,WORD **);
extern WORD AddLong(UWORD *,WORD,UWORD *,WORD,UWORD *,WORD *);
extern WORD AddPLon(UWORD *,WORD,UWORD *,WORD,UWORD *,WORD *);
extern WORD AddPoly(PHEAD WORD **,WORD **);
extern WORD AddRat(PHEAD UWORD *,WORD,UWORD *,WORD,UWORD *,WORD *);
extern VOID AddToLine(UBYTE *);
extern WORD AddWild(PHEAD WORD,WORD,WORD);
extern WORD BigLong(UWORD *,WORD,UWORD *,WORD);
extern WORD BinomGen(PHEAD WORD *,WORD,WORD **,WORD,WORD,WORD,WORD,WORD,UWORD *,WORD);
extern WORD CheckWild(PHEAD WORD,WORD,WORD,WORD *);
extern WORD Chisholm(PHEAD WORD *,WORD);
extern WORD CleanExpr(WORD);
extern VOID CleanUp(WORD);
extern VOID ClearWild(PHEAD0);
extern WORD CompareFunctions(WORD *,WORD *);
extern WORD Commute(WORD *,WORD *);
extern WORD DetCommu(WORD *);
extern WORD DoesCommu(WORD *);
extern int CompArg(WORD *,WORD *);
extern WORD CompCoef(WORD *, WORD *);
extern WORD CompGroup(PHEAD WORD,WORD **,WORD *,WORD *,WORD);
extern WORD Compare1(WORD *,WORD *,WORD);
extern WORD CountDo(WORD *,WORD *);
extern WORD CountFun(WORD *,WORD *);
extern WORD DimensionSubterm(WORD *);
extern WORD DimensionTerm(WORD *);
extern WORD DimensionExpression(PHEAD WORD *);
extern WORD Deferred(PHEAD WORD *,WORD);
extern WORD DeleteStore(WORD);
extern WORD DetCurDum(PHEAD WORD *);
extern VOID DetVars(WORD *,WORD);
extern WORD Distribute(DISTRIBUTE *,WORD);
extern WORD DivLong(UWORD *,WORD,UWORD *,WORD,UWORD *,WORD *,UWORD *,WORD *);
extern WORD DivRat(PHEAD UWORD *,WORD,UWORD *,WORD,UWORD *,WORD *);
extern WORD Divvy(PHEAD UWORD *,WORD *,UWORD *,WORD);
extern WORD DoDelta(WORD *);
extern WORD DoDelta3(PHEAD WORD *,WORD);
extern WORD TestPartitions(WORD *, PARTI *);
extern WORD DoPartitions(PHEAD WORD *,WORD);
extern int CoCanonicalize(UBYTE *);
extern int DoCanonicalize(PHEAD WORD *, WORD *);
extern WORD GenTopologies(PHEAD WORD *,WORD);
extern WORD GenDiagrams(PHEAD WORD *,WORD);
extern int DoTopologyCanonicalize(PHEAD WORD *,WORD,WORD,WORD *);
extern int DoShattering(PHEAD WORD *,WORD *,WORD *,WORD);
extern WORD GenerateTopologies(PHEAD WORD,WORD,WORD,WORD);
extern WORD DoTableExpansion(WORD *,WORD);
extern WORD DoDistrib(PHEAD WORD *,WORD);
extern WORD DoShuffle(WORD *,WORD,WORD,WORD);
extern WORD DoPermutations(PHEAD WORD *,WORD);
extern int Shuffle(WORD *, WORD *, WORD *);
extern int FinishShuffle(WORD *);
extern WORD DoStuffle(WORD *,WORD,WORD,WORD);
extern int Stuffle(WORD *, WORD *, WORD *);
extern int FinishStuffle(WORD *);
extern WORD *StuffRootAdd(WORD *, WORD *, WORD *);
extern WORD TestUse(WORD *,WORD);
extern DBASE *FindTB(UBYTE *);
extern int CheckTableDeclarations(DBASE *);
extern WORD Apply(WORD *,WORD);
extern int ApplyExec(WORD *,int,WORD);
extern WORD ApplyReset(WORD);
extern WORD TableReset(VOID);
extern VOID ReWorkT(WORD *,WORD *,WORD);
extern WORD GetIfDollarNum(WORD *, WORD *);
extern int FindVar(WORD *,WORD *);
extern WORD DoIfStatement(PHEAD WORD *,WORD *);
extern WORD DoOnePow(PHEAD WORD *,WORD,WORD,WORD *,WORD *,WORD,WORD *);
extern void DoRevert(WORD *,WORD *);
extern WORD DoSumF1(PHEAD WORD *,WORD *,WORD,WORD);
extern WORD DoSumF2(PHEAD WORD *,WORD *,WORD,WORD);
extern WORD DoTheta(PHEAD WORD *);
extern LONG EndSort(PHEAD WORD *,int);
extern WORD EntVar(WORD,UBYTE *,WORD,WORD,WORD,WORD);
extern WORD EpfCon(PHEAD WORD *,WORD *,WORD,WORD);
extern WORD EpfFind(PHEAD WORD *,WORD *);
extern WORD EpfGen(WORD,WORD *,WORD *,WORD *,WORD);
extern WORD EqualArg(WORD *,WORD,WORD);
extern WORD Evaluate(UBYTE **);
extern int Factorial(PHEAD WORD,UWORD *,WORD *);
extern int Bernoulli(WORD,UWORD *,WORD *);
extern int FactorIn(PHEAD WORD *,WORD);
extern int FactorInExpr(PHEAD WORD *,WORD);
extern WORD FindAll(PHEAD WORD *,WORD *,WORD,WORD *);
extern WORD FindMulti(PHEAD WORD *,WORD *);
extern WORD FindOnce(PHEAD WORD *,WORD *);
extern WORD FindOnly(PHEAD WORD *,WORD *);
extern WORD FindRest(PHEAD WORD *,WORD *);
extern WORD FindSpecial(WORD *);
extern WORD FindrNumber(WORD,VARRENUM *);
extern VOID FiniLine(VOID);
extern WORD FiniTerm(PHEAD WORD *,WORD *,WORD *,WORD,WORD);
extern WORD FlushOut(POSITION *,FILEHANDLE *,int);
extern VOID FunLevel(PHEAD WORD *);
extern VOID AdjustRenumScratch(PHEAD0);
extern VOID GarbHand(VOID);
extern WORD GcdLong(PHEAD UWORD *,WORD,UWORD *,WORD,UWORD *,WORD *);
extern WORD LcmLong(PHEAD UWORD *,WORD,UWORD *,WORD,UWORD *,WORD *);
extern VOID GCD(UWORD *,WORD,UWORD *,WORD,UWORD *,WORD *);
extern ULONG GCD2(ULONG,ULONG);
extern WORD Generator(PHEAD WORD *,WORD);
extern WORD GetBinom(UWORD *,WORD *,WORD,WORD);
extern WORD GetFromStore(WORD *,POSITION *,RENUMBER,WORD *,WORD);
extern WORD GetLong(UBYTE *,UWORD *,WORD *);
extern WORD GetMoreTerms(WORD *);
extern WORD GetMoreFromMem(WORD *,WORD **);
extern WORD GetOneTerm(PHEAD WORD *,FILEHANDLE *,POSITION *,int);
extern RENUMBER GetTable(WORD,POSITION *,WORD);
extern WORD GetTerm(PHEAD WORD *);
extern WORD Glue(PHEAD WORD *,WORD *,WORD *,WORD);
extern WORD InFunction(PHEAD WORD *,WORD *);
extern VOID IniLine(WORD);
extern WORD IniVars(VOID);
extern VOID Initialize(VOID);
extern WORD InsertTerm(PHEAD WORD *,WORD,WORD,WORD *,WORD *,WORD);
extern VOID LongToLine(UWORD *,WORD);
extern WORD MakeDirty(WORD *,WORD *,WORD);
extern VOID MarkDirty(WORD *,WORD);
extern VOID PolyFunDirty(PHEAD WORD *);
extern VOID PolyFunClean(PHEAD WORD *);
extern WORD MakeModTable(VOID);
extern WORD MatchE(PHEAD WORD *,WORD *,WORD *,WORD);
extern int MatchCy(PHEAD WORD *,WORD *,WORD *,WORD);
extern int FunMatchCy(PHEAD WORD *,WORD *,WORD *,WORD);
extern int FunMatchSy(PHEAD WORD *,WORD *,WORD *,WORD);
extern int MatchArgument(PHEAD WORD *,WORD *);
extern WORD MatchFunction(PHEAD WORD *,WORD *,WORD *);
extern WORD MergePatches(WORD);
extern WORD MesCerr(char *, UBYTE *);
extern WORD MesComp(char *, UBYTE *, UBYTE *);
extern WORD Modulus(WORD *);
extern VOID MoveDummies(PHEAD WORD *,WORD);
extern WORD MulLong(UWORD *,WORD,UWORD *,WORD,UWORD *,WORD *);
extern WORD MulRat(PHEAD UWORD *,WORD,UWORD *,WORD,UWORD *,WORD *);
extern WORD Mully(PHEAD UWORD *,WORD *,UWORD *,WORD);
extern WORD MultDo(PHEAD WORD *,WORD *);
extern WORD NewSort(PHEAD0);
extern WORD ExtraSymbol(WORD,WORD,WORD,WORD *,WORD *);
extern WORD Normalize(PHEAD WORD *);
extern WORD BracketNormalize(PHEAD WORD *);
extern VOID DropCoefficient(PHEAD WORD *);
extern VOID DropSymbols(PHEAD WORD *);
extern int PutInside(PHEAD WORD *, WORD *);
extern WORD OpenTemp(VOID);
extern VOID Pack(UWORD *,WORD *,UWORD *,WORD );
extern LONG PasteFile(PHEAD WORD,WORD *,POSITION *,WORD **,RENUMBER,WORD *,WORD);
extern WORD Permute(PERM *,WORD);
extern WORD PermuteP(PERMP *,WORD);
extern WORD PolyFunMul(PHEAD WORD *);
extern WORD PopVariables(VOID);
extern WORD PrepPoly(PHEAD WORD *,WORD);
extern WORD Processor(VOID);
extern WORD Product(UWORD *,WORD *,WORD);
extern VOID PrtLong(UWORD *,WORD,UBYTE *);
extern VOID PrtTerms(VOID);
extern VOID PrintRunningTime(VOID);
extern LONG GetRunningTime(VOID);
extern WORD PutBracket(PHEAD WORD *);
extern LONG PutIn(FILEHANDLE *,POSITION *,WORD *,WORD **,int);
extern WORD PutInStore(INDEXENTRY *,WORD);
extern WORD PutOut(PHEAD WORD *,POSITION *,FILEHANDLE *,WORD);
extern UWORD Quotient(UWORD *,WORD *,WORD);
extern WORD RaisPow(PHEAD UWORD *,WORD *,UWORD);
extern VOID RaisPowCached (PHEAD WORD, WORD, UWORD **, WORD *);
extern WORD RaisPowMod (WORD, WORD, WORD);
extern int NormalModulus(UWORD *,WORD *);
extern int MakeInverses(VOID);
extern int GetModInverses(WORD,WORD,WORD *,WORD *);
extern int GetLongModInverses(PHEAD UWORD *, WORD, UWORD *, WORD, UWORD *, WORD *, UWORD *, WORD *);
extern VOID RatToLine(UWORD *,WORD);
extern WORD RatioFind(PHEAD WORD *,WORD *);
extern WORD RatioGen(PHEAD WORD *,WORD *,WORD,WORD);
extern WORD ReNumber(PHEAD WORD *);
extern WORD ReadSnum(UBYTE **);
extern WORD Remain10(UWORD *,WORD *);
extern WORD Remain4(UWORD *,WORD *);
extern WORD ResetScratch(VOID);
extern WORD ResolveSet(PHEAD WORD *,WORD *,WORD *);
extern WORD RevertScratch(VOID);
extern WORD ScanFunctions(PHEAD WORD *,WORD *,WORD);
extern VOID SeekScratch(FILEHANDLE *,POSITION *);
extern VOID SetEndScratch(FILEHANDLE *,POSITION *);
extern VOID SetEndHScratch(FILEHANDLE *,POSITION *);
extern WORD SetFileIndex(VOID);
extern WORD Sflush(FILEHANDLE *);
extern WORD Simplify(PHEAD UWORD *,WORD *,UWORD *,WORD *);
extern WORD SortWild(WORD *,WORD);
extern FILE *LocateBase(char **,char **);
extern LONG SplitMerge(PHEAD WORD **,LONG);
extern WORD StoreTerm(PHEAD WORD *);
extern VOID SubPLon(UWORD *,WORD,UWORD *,WORD,UWORD *,WORD *);
extern VOID Substitute(PHEAD WORD *,WORD *,WORD);
extern WORD SymFind(PHEAD WORD *,WORD *);
extern WORD SymGen(PHEAD WORD *,WORD *,WORD,WORD);
extern WORD Symmetrize(PHEAD WORD *,WORD *,WORD,WORD,WORD);
extern int FullSymmetrize(PHEAD WORD *,int);
extern WORD TakeModulus(UWORD *,WORD *,UWORD *,WORD,WORD);
extern WORD TakeNormalModulus(UWORD *,WORD *,UWORD *,WORD,WORD);
extern VOID TalToLine(UWORD);
extern WORD TenVec(PHEAD WORD *,WORD *,WORD,WORD);
extern WORD TenVecFind(PHEAD WORD *,WORD *);
extern WORD TermRenumber(WORD *,RENUMBER,WORD);
extern VOID TestDrop(VOID);
extern VOID PutInVflags(WORD);
extern WORD TestMatch(PHEAD WORD *,WORD *);
extern WORD TestSub(PHEAD WORD *,WORD);
extern LONG TimeCPU(WORD);
extern LONG TimeChildren(WORD);
extern LONG TimeWallClock(WORD);
extern LONG Timer(int);
extern int GetTimerInfo(LONG **,LONG **);
extern void WriteTimerInfo(LONG *,LONG *);
extern LONG GetWorkerTimes(VOID);
extern WORD ToStorage(EXPRESSIONS,POSITION *);
extern VOID TokenToLine(UBYTE *);
extern WORD Trace4(PHEAD WORD *,WORD *,WORD,WORD);
extern WORD Trace4Gen(PHEAD TRACES *,WORD);
extern WORD Trace4no(WORD,WORD *,TRACES *);
extern WORD TraceFind(PHEAD WORD *,WORD *);
extern WORD TraceN(PHEAD WORD *,WORD *,WORD,WORD);
extern WORD TraceNgen(PHEAD TRACES *,WORD);
extern WORD TraceNno(WORD,WORD *,TRACES *);
extern WORD Traces(PHEAD WORD *,WORD *,WORD,WORD);
extern WORD Trick(WORD *,TRACES *);
extern WORD TryDo(PHEAD WORD *,WORD *,WORD);
extern VOID UnPack(UWORD *,WORD,WORD *,WORD *);
extern WORD VarStore(UBYTE *,WORD,WORD,WORD);
extern WORD WildFill(PHEAD WORD *,WORD *,WORD *);
extern WORD WriteAll(VOID);
extern WORD WriteOne(UBYTE *,int,int,WORD);
extern VOID WriteArgument(WORD *);
extern WORD WriteExpression(WORD *,LONG);
extern WORD WriteInnerTerm(WORD *,WORD);
extern VOID WriteLists(VOID);
extern VOID WriteSetup(VOID);
extern VOID WriteStats(POSITION *,WORD);
extern WORD WriteSubTerm(WORD *,WORD);
extern WORD WriteTerm(WORD *,WORD *,WORD,WORD,WORD);
extern WORD execarg(PHEAD WORD *,WORD);
extern WORD execterm(PHEAD WORD *,WORD);
extern VOID SpecialCleanup(PHEAD0);
extern void SetMods();
extern void UnSetMods();
/*---------------------------------------------------------------------*/
extern WORD DoExecute(WORD,WORD);
extern VOID SetScratch(FILEHANDLE *,POSITION *);
extern VOID Warning(char *);
extern VOID HighWarning(char *);
extern int SpareTable(TABLES);
extern UBYTE *strDup1(UBYTE *,char *);
extern VOID *Malloc(LONG);
extern VOID *Malloc1(LONG,const char *);
extern int DoTail(int,UBYTE **);
extern int OpenInput(VOID);
extern int PutPreVar(UBYTE *,UBYTE *,UBYTE *,int);
extern VOID Error0(char *);
extern VOID Error1(char *,UBYTE *);
extern VOID Error2(char *,char *,UBYTE *);
extern UBYTE ReadFromStream(STREAM *);
extern UBYTE GetFromStream(STREAM *);
extern UBYTE LookInStream(STREAM *);
extern STREAM *OpenStream(UBYTE *,int,int,int);
extern int LocateFile(UBYTE **,int);
extern STREAM *CloseStream(STREAM *);
extern VOID PositionStream(STREAM *,LONG);
extern int ReverseStatements(STREAM *);
extern int ProcessOption(UBYTE *,UBYTE *,int);
extern int DoSetups(VOID);
extern VOID Terminate(int);
extern NAMENODE *GetNode(NAMETREE *,UBYTE *);
extern int AddName(NAMETREE *,UBYTE *,WORD,WORD,int *);
extern int GetName(NAMETREE *,UBYTE *,WORD *,int);
extern UBYTE *GetFunction(UBYTE *,WORD *);
extern UBYTE *GetNumber(UBYTE *,WORD *);
extern int GetLastExprName(UBYTE *,WORD *);
extern int GetAutoName(UBYTE *,WORD *);
extern int GetVar(UBYTE *,WORD *,WORD *,int,int);
extern int MakeDubious(NAMETREE *,UBYTE *,WORD *);
extern int GetOName(NAMETREE *,UBYTE *,WORD *,int);
extern VOID DumpTree(NAMETREE *);
extern VOID DumpNode(NAMETREE *,WORD,WORD);
extern VOID LinkTree(NAMETREE *,WORD,WORD);
extern VOID CopyTree(NAMETREE *,NAMETREE *,WORD,WORD);
extern int CompactifyTree(NAMETREE *,WORD);
extern NAMETREE *MakeNameTree(VOID);
extern VOID FreeNameTree(NAMETREE *);
extern int AddExpression(UBYTE *,int,int);
extern int AddSymbol(UBYTE *,int,int,int,int);
extern int AddDollar(UBYTE *,WORD,WORD *,LONG);
extern int ReplaceDollar(WORD,WORD,WORD *,LONG);
extern int DollarRaiseLow(UBYTE *,LONG);
extern int AddVector(UBYTE *,int,int);
extern int AddDubious(UBYTE *);
extern int AddIndex(UBYTE *,int,int);
extern UBYTE *DoDimension(UBYTE *,int *,int *);
extern int AddFunction(UBYTE *,int,int,int,int,int,int,int);
extern int CoCommuteInSet(UBYTE *);
extern int CoFunction(UBYTE *,int,int);
extern int TestName(UBYTE *);
extern int AddSet(UBYTE *,WORD);
extern int DoElements(UBYTE *,SETS,UBYTE *);
extern int DoTempSet(UBYTE *,UBYTE *);
extern int NameConflict(int,UBYTE *);
extern int OpenFile(char *);
extern int OpenAddFile(char *);
extern int ReOpenFile(char *);
extern int CreateFile(char *);
extern int CreateLogFile(char *);
extern VOID CloseFile(int);
extern int CopyFile(char *, char *);
extern int CreateHandle(VOID);
extern LONG ReadFile(int,UBYTE *,LONG);
extern LONG ReadPosFile(PHEAD FILEHANDLE *,UBYTE *,LONG,POSITION *);
extern LONG WriteFileToFile(int,UBYTE *,LONG);
extern VOID SeekFile(int,POSITION *,int);
extern LONG TellFile(int);
extern void FlushFile(int);
extern int GetPosFile(int,fpos_t *);
extern int SetPosFile(int,fpos_t *);
extern VOID SynchFile(int);
extern VOID TruncateFile(int);
extern int GetChannel(char *,int);
extern int GetAppendChannel(char *);
extern int CloseChannel(char *);
extern VOID inictable(VOID);
extern KEYWORD *findcommand(UBYTE *);
extern int inicbufs(VOID);
extern VOID StartFiles(VOID);
extern UBYTE *MakeDate(VOID);
extern VOID PreProcessor(VOID);
extern VOID *FromList(LIST *);
extern VOID *From0List(LIST *);
extern VOID *FromVarList(LIST *);
extern int DoubleList(VOID ***,int *,int,char *);
extern int DoubleLList(VOID ***,LONG *,int,char *);
extern void DoubleBuffer(void **,void **,int,char *);
extern void ExpandBuffer(void **,LONG *,int);
extern LONG iexp(LONG,int);
extern int IsLikeVector(WORD *);
extern int AreArgsEqual(WORD *,WORD *);
extern int CompareArgs(WORD *,WORD *);
extern UBYTE *SkipField(UBYTE *,int);
extern int StrCmp(UBYTE *,UBYTE *);
extern int StrICmp(UBYTE *,UBYTE *);
extern int StrHICmp(UBYTE *,UBYTE *);
extern int StrICont(UBYTE *,UBYTE *);
extern int CmpArray(WORD *,WORD *,WORD);
extern int ConWord(UBYTE *,UBYTE *);
extern int StrLen(UBYTE *);
extern UBYTE *GetPreVar(UBYTE *,int);
extern void ToGeneral(WORD *,WORD *,WORD);
extern WORD ToPolyFunGeneral(PHEAD WORD *);
extern int ToFast(WORD *,WORD *);
extern SETUPPARAMETERS *GetSetupPar(UBYTE *);
extern int RecalcSetups(VOID);
extern int AllocSetups(VOID);
extern SORTING *AllocSort(LONG,LONG,LONG,LONG,int,int,LONG);
extern VOID AllocSortFileName(SORTING *);
extern UBYTE *LoadInputFile(UBYTE *,int);
extern UBYTE GetInput(VOID);
extern VOID ClearPushback(VOID);
extern UBYTE GetChar(int);
extern VOID CharOut(UBYTE);
extern VOID UnsetAllowDelay(VOID);
extern VOID PopPreVars(int);
extern VOID IniModule(int);
extern VOID IniSpecialModule(int);
extern int ModuleInstruction(int *,int *);
extern int PreProInstruction(VOID);
extern int LoadInstruction(int);
extern int LoadStatement(int);
extern KEYWORD *FindKeyWord(UBYTE *,KEYWORD *,int);
extern KEYWORD *FindInKeyWord(UBYTE *,KEYWORD *,int);
extern int DoDefine(UBYTE *);
extern int DoRedefine(UBYTE *);
extern int TheDefine(UBYTE *,int);
extern int TheUndefine(UBYTE *);
extern int ClearMacro(UBYTE *);
extern int DoUndefine(UBYTE *);
extern int DoInclude(UBYTE *);
extern int DoReverseInclude(UBYTE *);
extern int Include(UBYTE *,int);
/*[14apr2004 mt]:*/
extern int DoExternal(UBYTE *);
extern int DoToExternal(UBYTE *);
extern int DoFromExternal(UBYTE *);
extern int DoPrompt(UBYTE *);
extern int DoSetExternal(UBYTE *);
/*[10may2006 mt]:*/
extern int DoSetExternalAttr(UBYTE *);
/*:[10may2006 mt]*/
extern int DoRmExternal(UBYTE *);
/*:[14apr2004 mt]*/
extern int DoFactDollar(UBYTE *);
extern WORD GetDollarNumber(UBYTE **,DOLLARS);
extern int DoSetRandom(UBYTE *);
extern int DoOptimize(UBYTE *);
extern int DoClearOptimize(UBYTE *);
extern int DoSkipExtraSymbols(UBYTE *);
extern int DoTimeOutAfter(UBYTE *);
extern int DoMessage(UBYTE *);
extern int DoPreOut(UBYTE *);
extern int DoPreAppend(UBYTE *);
extern int DoPreCreate(UBYTE *);
extern int DoPreAssign(UBYTE *);
extern int DoPreBreak(UBYTE *);
extern int DoPreDefault(UBYTE *);
extern int DoPreSwitch(UBYTE *);
extern int DoPreEndSwitch(UBYTE *);
extern int DoPreCase(UBYTE *);
extern int DoPreShow(UBYTE *);
extern int DoPreExchange(UBYTE *);
extern int DoSystem(UBYTE *);
extern int DoPipe(UBYTE *);
extern VOID StartPrepro(VOID);
extern int DoIfdef(UBYTE *,int);
extern int DoIfydef(UBYTE *);
extern int DoIfndef(UBYTE *);
extern int DoElse(UBYTE *);
extern int DoElseif(UBYTE *);
extern int DoEndif(UBYTE *);
extern int DoTerminate(UBYTE *);
extern int DoIf(UBYTE *);
extern int DoCall(UBYTE *);
extern int DoDebug(UBYTE *);
extern int DoDo(UBYTE *);
extern int DoBreakDo(UBYTE *);
extern int DoEnddo(UBYTE *);
extern int DoEndprocedure(UBYTE *);
extern int DoInside(UBYTE *);
extern int DoEndInside(UBYTE *);
extern int DoProcedure(UBYTE *);
extern int DoPrePrintTimes(UBYTE *);
extern int DoPreWrite(UBYTE *);
extern int DoPreClose(UBYTE *);
extern int DoPreRemove(UBYTE *);
extern int DoCommentChar(UBYTE *);
extern int DoPrcExtension(UBYTE *);
extern int DoPreReset(UBYTE *);
extern VOID WriteString(int,UBYTE *,int);
extern VOID WriteUnfinString(int,UBYTE *,int);
extern UBYTE *AddToString(UBYTE *,UBYTE *,int);
extern UBYTE *PreCalc(VOID);
extern UBYTE *PreEval(UBYTE *,LONG *);
extern VOID NumToStr(UBYTE *,LONG);
extern int PreCmp(int,int,UBYTE *,int,int,UBYTE *,int);
extern int PreEq(int,int,UBYTE *,int,int,UBYTE *,int);
extern UBYTE *pParseObject(UBYTE *,int *,LONG *);
extern UBYTE *PreIfEval(UBYTE *,int *);
extern int EvalPreIf(UBYTE *);
extern int PreLoad(PRELOAD *,UBYTE *,UBYTE *,int,char *);
extern int PreSkip(UBYTE *,UBYTE *,int);
extern UBYTE *EndOfToken(UBYTE *);
extern VOID SetSpecialMode(int,int);
extern VOID MakeGlobal(VOID);
extern int ExecModule(int);
extern int ExecStore(VOID);
extern VOID FullCleanUp(VOID);
extern int DoExecStatement(VOID);
extern int DoPipeStatement(VOID);
extern int DoPolyfun(UBYTE *);
extern int DoPolyratfun(UBYTE *);
extern int CompileStatement(UBYTE *);
extern UBYTE *ToToken(UBYTE *);
extern int GetDollar(UBYTE *);
extern int MesWork(VOID);
extern int MesPrint(const char *,...);
extern int MesCall(char *);
extern UBYTE *NumCopy(WORD,UBYTE *);
extern char *LongCopy(LONG,char *);
extern char *LongLongCopy(off_t *,char *);
extern VOID ReserveTempFiles(int);
extern VOID PrintTerm(WORD *,char *);
extern VOID PrintTermC(WORD *,char *);
extern VOID PrintSubTerm(WORD *,char *);
extern VOID PrintWords(WORD *,LONG);
extern void PrintSeq(WORD *,char *);
extern int ExpandTripleDots(int);
extern LONG ComPress(WORD **,LONG *);
extern VOID StageSort(FILEHANDLE *);
#define M_alloc(x) malloc((size_t)(x))
extern void M_free(VOID *,const char *);
extern void ClearWildcardNames(VOID);
extern int AddWildcardName(UBYTE *);
extern int GetWildcardName(UBYTE *);
extern void Globalize(int);
extern void ResetVariables(int);
extern void AddToPreTypes(int);
extern void MessPreNesting(int);
extern LONG GetStreamPosition(STREAM *);
extern WORD *DoubleCbuffer(int,WORD *,int);
extern WORD *AddLHS(int);
extern WORD *AddRHS(int,int);
extern int AddNtoL(int,WORD *);
extern int AddNtoC(int,int,WORD *,int);
extern VOID DoubleIfBuffers(VOID);
extern STREAM *CreateStream(UBYTE *);
extern int setonoff(UBYTE *,int *,int,int);
extern int DoPrint(UBYTE *,int);
extern int SetExpr(UBYTE *,int,int);
extern void AddToCom(int,WORD *);
extern int Add2ComStrings(int,WORD *,UBYTE *,UBYTE *);
extern int DoSymmetrize(UBYTE *,int);
extern int DoArgument(UBYTE *,int);
extern int ArgFactorize(PHEAD WORD *,WORD *);
extern WORD *TakeArgContent(PHEAD WORD *, WORD *);
extern WORD *MakeInteger(PHEAD WORD *,WORD *,WORD *);
extern WORD *MakeMod(PHEAD WORD *,WORD *,WORD *);
extern WORD FindArg(PHEAD WORD *);
extern WORD InsertArg(PHEAD WORD *,WORD *,int);
extern int CleanupArgCache(PHEAD WORD);
extern int ArgSymbolMerge(WORD *, WORD *);
extern int ArgDotproductMerge(WORD *, WORD *);
extern void SortWeights(LONG *,LONG *,WORD);
extern int DoBrackets(UBYTE *,int);
extern int DoPutInside(UBYTE *,int);
extern WORD *CountComp(UBYTE *,WORD *);
extern int CoAntiBracket(UBYTE *);
extern int CoAntiSymmetrize(UBYTE *);
extern int DoArgPlode(UBYTE *,int);
extern int CoArgExplode(UBYTE *);
extern int CoArgImplode(UBYTE *);
extern int CoArgument(UBYTE *);
extern int CoInside(UBYTE *);
extern int ExecInside(UBYTE *);
extern int CoInExpression(UBYTE *);
extern int CoInParallel(UBYTE *);
extern int CoNotInParallel(UBYTE *);
extern int DoInParallel(UBYTE *,int);
extern int CoEndInExpression(UBYTE *);
extern int CoBracket(UBYTE *);
extern int CoPutInside(UBYTE *);
extern int CoAntiPutInside(UBYTE *);
extern int CoMultiBracket(UBYTE *);
extern int CoCFunction(UBYTE *);
extern int CoCTensor(UBYTE *);
extern int CoCollect(UBYTE *);
extern int CoCompress(UBYTE *);
extern int CoContract(UBYTE *);
extern int CoCycleSymmetrize(UBYTE *);
extern int CoDelete(UBYTE *);
extern int CoTableBase(UBYTE *);
extern int CoApply(UBYTE *);
extern int CoDenominators(UBYTE *);
extern int CoDimension(UBYTE *);
extern int CoDiscard(UBYTE *);
extern int CoDisorder(UBYTE *);
extern int CoDrop(UBYTE *);
extern int CoDropCoefficient(UBYTE *);
extern int CoDropSymbols(UBYTE *);
extern int CoElse(UBYTE *);
extern int CoElseIf(UBYTE *);
extern int CoEndArgument(UBYTE *);
extern int CoEndInside(UBYTE *);
extern int CoEndIf(UBYTE *);
extern int CoEndRepeat(UBYTE *);
extern int CoEndTerm(UBYTE *);
extern int CoEndWhile(UBYTE *);
extern int CoExit(UBYTE *);
extern int CoFactArg(UBYTE *);
extern int CoFactDollar(UBYTE *);
extern int CoFactorize(UBYTE *);
extern int CoNFactorize(UBYTE *);
extern int CoUnFactorize(UBYTE *);
extern int CoNUnFactorize(UBYTE *);
extern int DoFactorize(UBYTE *,int);
extern int CoFill(UBYTE *);
extern int CoFillExpression(UBYTE *);
extern int CoFixIndex(UBYTE *);
extern int CoFormat(UBYTE *);
extern int CoGlobal(UBYTE *);
extern int CoGlobalFactorized(UBYTE *);
extern int CoGoTo(UBYTE *);
extern int CoId(UBYTE *);
extern int CoIdNew(UBYTE *);
extern int CoIdOld(UBYTE *);
extern int CoIf(UBYTE *);
extern int CoIfMatch(UBYTE *);
extern int CoIfNoMatch(UBYTE *);
extern int CoIndex(UBYTE *);
extern int CoInsideFirst(UBYTE *);
extern int CoKeep(UBYTE *);
extern int CoLabel(UBYTE *);
extern int CoLoad(UBYTE *);
extern int CoLocal(UBYTE *);
extern int CoLocalFactorized(UBYTE *);
extern int CoMany(UBYTE *);
extern int CoMerge(UBYTE *);
extern int CoStuffle(UBYTE *);
extern int CoMetric(UBYTE *);
extern int CoModOption(UBYTE *);
extern int CoModuleOption(UBYTE *);
extern int CoModulus(UBYTE *);
extern int CoMulti(UBYTE *);
extern int CoMultiply(UBYTE *);
extern int CoNFunction(UBYTE *);
extern int CoNPrint(UBYTE *);
extern int CoNTensor(UBYTE *);
extern int CoNWrite(UBYTE *);
extern int CoNoDrop(UBYTE *);
extern int CoNoSkip(UBYTE *);
extern int CoNormalize(UBYTE *);
extern int CoMakeInteger(UBYTE *);
extern int CoFlags(UBYTE *,int);
extern int CoOff(UBYTE *);
extern int CoOn(UBYTE *);
extern int CoOnce(UBYTE *);
extern int CoOnly(UBYTE *);
extern int CoOptimizeOption(UBYTE *);
extern int CoOptimize(UBYTE *);
extern int CoPolyFun(UBYTE *);
extern int CoPolyRatFun(UBYTE *);
extern int CoPrint(UBYTE *);
extern int CoPrintB(UBYTE *);
extern int CoProperCount(UBYTE *);
extern int CoUnitTrace(UBYTE *);
extern int CoRCycleSymmetrize(UBYTE *);
extern int CoRatio(UBYTE *);
extern int CoRedefine(UBYTE *);
extern int CoRenumber(UBYTE *);
extern int CoRepeat(UBYTE *);
extern int CoSave(UBYTE *);
extern int CoSelect(UBYTE *);
extern int CoSet(UBYTE *);
extern int CoSetExitFlag(UBYTE *);
extern int CoSkip(UBYTE *);
extern int CoProcessBucket(UBYTE *);
extern int CoPushHide(UBYTE *);
extern int CoPopHide(UBYTE *);
extern int CoHide(UBYTE *);
extern int CoIntoHide(UBYTE *);
extern int CoNoHide(UBYTE *);
extern int CoUnHide(UBYTE *);
extern int CoNoUnHide(UBYTE *);
extern int CoSort(UBYTE *);
extern int CoSplitArg(UBYTE *);
extern int CoSplitFirstArg(UBYTE *);
extern int CoSplitLastArg(UBYTE *);
extern int CoSum(UBYTE *);
extern int CoSymbol(UBYTE *);
extern int CoSymmetrize(UBYTE *);
extern int DoTable(UBYTE *,int);
extern int CoTable(UBYTE *);
extern int CoTerm(UBYTE *);
extern int CoNTable(UBYTE *);
extern int CoCTable(UBYTE *);
extern void EmptyTable(TABLES);
extern int CoToTensor(UBYTE *);
extern int CoToVector(UBYTE *);
extern int CoTrace4(UBYTE *);
extern int CoTraceN(UBYTE *);
extern int CoChisholm(UBYTE *);
extern int CoTransform(UBYTE *);
extern int CoClearTable(UBYTE *);
extern int DoChain(UBYTE *,int);
extern int CoChainin(UBYTE *);
extern int CoChainout(UBYTE *);
extern int CoTryReplace(UBYTE *);
extern int CoVector(UBYTE *);
extern int CoWhile(UBYTE *);
extern int CoWrite(UBYTE *);
extern int CoAuto(UBYTE *);
extern int CoSwitch(UBYTE *);
extern int CoCase(UBYTE *);
extern int CoBreak(UBYTE *);
extern int CoDefault(UBYTE *);
extern int CoEndSwitch(UBYTE *);
extern int CoTBaddto(UBYTE *);
extern int CoTBaudit(UBYTE *);
extern int CoTBcleanup(UBYTE *);
extern int CoTBcreate(UBYTE *);
extern int CoTBenter(UBYTE *);
extern int CoTBhelp(UBYTE *);
extern int CoTBload(UBYTE *);
extern int CoTBoff(UBYTE *);
extern int CoTBon(UBYTE *);
extern int CoTBopen(UBYTE *);
extern int CoTBreplace(UBYTE *);
extern int CoTBuse(UBYTE *);
extern int CoTestUse(UBYTE *);
extern int CoThreadBucket(UBYTE *);
extern int AddComString(int,WORD *,UBYTE *,int);
extern int CompileAlgebra(UBYTE *,int,WORD *);
extern int IsIdStatement(UBYTE *);
extern UBYTE *IsRHS(UBYTE *,UBYTE);
extern int ParenthesesTest(UBYTE *);
extern int tokenize(UBYTE *,WORD);
extern void WriteTokens(SBYTE *);
extern int simp1token(SBYTE *);
extern int simpwtoken(SBYTE *);
extern int simp2token(SBYTE *);
extern int simp3atoken(SBYTE *,int);
extern int simp3btoken(SBYTE *,int);
extern int simp4token(SBYTE *);
extern int simp5token(SBYTE *,int);
extern int simp6token(SBYTE *,int);
extern UBYTE *SkipAName(UBYTE *);
extern int TestTables(VOID);
extern int GetLabel(UBYTE *);
extern int CoIdExpression(UBYTE *,int);
extern int CoAssign(UBYTE *);
extern int DoExpr(UBYTE *,int,int);
extern int CompileSubExpressions(SBYTE *);
extern int CodeGenerator(SBYTE *);
extern int CompleteTerm(WORD *,UWORD *,UWORD *,WORD,WORD,int);
extern int CodeFactors(SBYTE *s);
extern WORD GenerateFactors(WORD,WORD);
extern int InsTree(int,int);
extern int FindTree(int,WORD *);
extern void RedoTree(CBUF *,int);
extern void ClearTree(int);
extern int CatchDollar(int);
extern int AssignDollar(PHEAD WORD *,WORD);
extern UBYTE *WriteDollarToBuffer(WORD,WORD);
extern UBYTE *WriteDollarFactorToBuffer(WORD,WORD,WORD);
extern void AddToDollarBuffer(UBYTE *);
extern int PutTermInDollar(WORD *,WORD);
extern void TermAssign(WORD *);
extern void WildDollars(PHEAD WORD *);
extern LONG numcommute(WORD *,LONG *);
extern int FullRenumber(PHEAD WORD *,WORD);
extern int Lus(WORD *,WORD,WORD,WORD,WORD,WORD);
extern int FindLus(int,int,int);
extern int CoReplaceLoop(UBYTE *);
extern int CoFindLoop(UBYTE *);
extern int DoFindLoop(UBYTE *,int);
extern int CoFunPowers(UBYTE *);
extern int SortTheList(int *,int);
extern int MatchIsPossible(WORD *,WORD *);
extern int StudyPattern(WORD *);
extern WORD DolToTensor(PHEAD WORD);
extern WORD DolToFunction(PHEAD WORD);
extern WORD DolToVector(PHEAD WORD);
extern WORD DolToNumber(PHEAD WORD);
extern WORD DolToSymbol(PHEAD WORD);
extern WORD DolToIndex(PHEAD WORD);
extern LONG DolToLong(PHEAD WORD);
extern int DollarFactorize(PHEAD WORD);
extern int CoPrintTable(UBYTE *);
extern int CoDeallocateTable(UBYTE *);
extern void CleanDollarFactors(DOLLARS);
extern WORD *TakeDollarContent(PHEAD WORD *,WORD **);
extern WORD *MakeDollarInteger(PHEAD WORD *,WORD **);
extern WORD *MakeDollarMod(PHEAD WORD *,WORD **);
extern int GetDolNum(PHEAD WORD *, WORD *);
extern void AddPotModdollar(WORD);
extern int Optimize(WORD, int);
extern int ClearOptimize(VOID);
extern int LoadOpti(WORD);
extern int PutObject(WORD *,int);
extern void CleanOptiBuffer(VOID);
extern int PrintOptima(WORD);
extern int FindScratchName(VOID);
extern WORD MaxPowerOpti(LONG);
extern WORD HuntNumFactor(LONG,WORD *,int);
extern WORD HuntFactor(LONG,WORD *,int);
extern void HuntPairs(LONG,WORD);
extern void HuntBrackets(LONG);
extern int AddToOpti(WORD *,int);
extern LONG TestNewSca(LONG,WORD *,WORD *);
extern void NormOpti(WORD *);
extern void SortOpti(LONG);
extern void SplitOpti(WORD **,LONG);
extern void CombiOpti(VOID);
extern int TakeLongRoot(UWORD *,WORD *,WORD);
extern int TakeRatRoot(UWORD *,WORD *,WORD);
extern int MakeRational(WORD ,WORD , WORD *, WORD *);
extern int MakeLongRational(PHEAD UWORD *,WORD ,UWORD *,WORD ,UWORD *,WORD *);
extern void HuntPowers(LONG,WORD);
extern void HuntNumBrackets(LONG);
extern void ClearTableTree(TABLES);
extern int InsTableTree(TABLES,WORD *);
extern void RedoTableTree(TABLES,int);
extern int FindTableTree(TABLES,WORD *,int);
extern void finishcbuf(WORD);
extern void clearcbuf(WORD);
extern void CleanUpSort(int);
extern FILEHANDLE *AllocFileHandle(WORD,char *);
extern VOID DeAllocFileHandle(FILEHANDLE *);
extern VOID LowerSortLevel(VOID);
extern WORD *PolyRatFunSpecial(PHEAD WORD *, WORD *);
extern VOID SimpleSplitMergeRec(WORD *,WORD,WORD *);
extern VOID SimpleSplitMerge(WORD *,WORD);
extern WORD BinarySearch(WORD *,WORD,WORD);
extern int InsideDollar(PHEAD WORD *,WORD);
extern DOLLARS DolToTerms(PHEAD WORD);
extern WORD EvalDoLoopArg(PHEAD WORD *,WORD);
extern int SetExprCases(int,int,int);
extern int TestSelect(WORD *,WORD *);
extern VOID SubsInAll(PHEAD0);
extern VOID TransferBuffer(int,int,int);
extern int TakeIDfunction(PHEAD WORD *);
extern int MakeSetupAllocs(VOID);
extern int TryFileSetups(VOID);
extern void ExchangeExpressions(int,int);
extern void ExchangeDollars(int,int);
extern int GetFirstBracket(WORD *,int);
extern int GetFirstTerm(WORD *,int);
extern int GetContent(WORD *,int);
extern int CleanupTerm(WORD *);
extern WORD ContentMerge(PHEAD WORD *,WORD *);
extern UBYTE *PreIfDollarEval(UBYTE *,int *);
extern LONG TermsInDollar(WORD);
extern LONG SizeOfDollar(WORD);
extern LONG TermsInExpression(WORD);
extern LONG SizeOfExpression(WORD);
extern WORD *TranslateExpression(UBYTE *);
extern int IsSetMember(WORD *,WORD);
extern int IsMultipleOf(WORD *,WORD *);
extern int TwoExprCompare(WORD *,WORD *,int);
extern void UpdatePositions(VOID);
extern void M_check(VOID);
extern void M_print(VOID);
extern void M_check1(VOID);
extern void PrintTime(UBYTE *);
extern POSITION *FindBracket(WORD,WORD *);
extern VOID PutBracketInIndex(PHEAD WORD *,POSITION *);
extern void ClearBracketIndex(WORD);
extern VOID OpenBracketIndex(WORD);
extern int DoNoParallel(UBYTE *);
extern int DoParallel(UBYTE *);
extern int DoModSum(UBYTE *);
extern int DoModMax(UBYTE *);
extern int DoModMin(UBYTE *);
extern int DoModLocal(UBYTE *);
extern UBYTE *DoModDollar(UBYTE *,int);
extern int DoProcessBucket(UBYTE *);
extern int DoinParallel(UBYTE *);
extern int DonotinParallel(UBYTE *);
extern int FlipTable(FUNCTIONS,int);
extern int ChainIn(PHEAD WORD *,WORD);
extern int ChainOut(PHEAD WORD *,WORD);
extern int ArgumentImplode(PHEAD WORD *,WORD *);
extern int ArgumentExplode(PHEAD WORD *,WORD *);
extern int DenToFunction(WORD *,WORD);
extern WORD HowMany(PHEAD WORD *,WORD *);
extern VOID RemoveDollars(VOID);
extern LONG CountTerms1(PHEAD0);
extern LONG TermsInBracket(PHEAD WORD *,WORD);
extern int Crash(VOID);
extern char *str_dup(char *);
extern void convertblock(INDEXBLOCK *,INDEXBLOCK *,int);
extern void convertnamesblock(NAMESBLOCK *,NAMESBLOCK *,int);
extern void convertiniinfo(INIINFO *,INIINFO *,int);
extern int ReadIndex(DBASE *);
extern int WriteIndexBlock(DBASE *,MLONG);
extern int WriteNamesBlock(DBASE *,MLONG);
extern int WriteIndex(DBASE *);
extern int WriteIniInfo(DBASE *);
extern int ReadIniInfo(DBASE *);
extern int AddToIndex(DBASE *,MLONG);
extern DBASE *GetDbase(char *);
extern DBASE *OpenDbase(char *);
extern char *ReadObject(DBASE *,MLONG,char *);
extern char *ReadijObject(DBASE *,MLONG,MLONG,char *);
extern int ExistsObject(DBASE *,MLONG,char *);
extern int DeleteObject(DBASE *,MLONG,char *);
extern int WriteObject(DBASE *,MLONG,char *,char *,MLONG);
extern MLONG AddObject(DBASE *,MLONG,char *,char *);
extern int Cleanup(DBASE *);
extern DBASE *NewDbase(char *,MLONG);
extern void FreeTableBase(DBASE *);
extern int ComposeTableNames(DBASE *);
extern int PutTableNames(DBASE *);
extern MLONG AddTableName(DBASE *,char *,TABLES);
extern MLONG GetTableName(DBASE *,char *);
extern MLONG FindTableNumber(DBASE *,char *);
extern int TryEnvironment(VOID);
#ifdef WITHZLIB
extern int SetupOutputGZIP(FILEHANDLE *);
extern int PutOutputGZIP(FILEHANDLE *);
extern int FlushOutputGZIP(FILEHANDLE *);
extern int SetupAllInputGZIP(SORTING *);
extern LONG FillInputGZIP(FILEHANDLE *,POSITION *,UBYTE *,LONG,int);
extern void ClearSortGZIP(FILEHANDLE *f);
#endif
#ifdef WITHPTHREADS
extern VOID BeginIdentities(VOID);
extern int WhoAmI(VOID);
extern int StartAllThreads(int);
extern void StartHandleLock(VOID);
extern VOID TerminateAllThreads(VOID);
extern int GetAvailableThread(VOID);
extern int ConditionalGetAvailableThread(VOID);
extern int BalanceRunThread(PHEAD int,WORD *,WORD);
extern void WakeupThread(int,int);
extern int MasterWait(VOID);
extern int InParallelProcessor(VOID);
extern int ThreadsProcessor(EXPRESSIONS,WORD,WORD);
extern int MasterMerge(VOID);
extern int PutToMaster(PHEAD WORD *);
extern void SetWorkerFiles(VOID);
extern int MakeThreadBuckets(int,int);
extern int SendOneBucket(int);
extern int LoadOneThread(int,int,THREADBUCKET *,int);
extern void *RunSortBot(void *);
extern void MasterWaitAllSortBots(VOID);
extern int SortBotMerge(PHEAD0);
extern int SortBotOut(PHEAD WORD *);
extern void DefineSortBotTree(VOID);
extern int SortBotMasterMerge(VOID);
extern int SortBotWait(int);
extern void StartIdentity(VOID);
extern void FinishIdentity(void *);
extern int SetIdentity(int *);
extern ALLPRIVATES *InitializeOneThread(int);
extern void FinalizeOneThread(int);
extern void ClearAllThreads(VOID);
extern void *RunThread(void *);
extern void IAmAvailable(int);
extern int ThreadWait(int);
extern int ThreadClaimedBlock(int);
extern int GetThread(int);
extern int UpdateOneThread(int);
extern void MasterWaitAll(VOID);
extern void MasterWaitAllBlocks(VOID);
extern int MasterWaitThread(int);
extern void WakeupMasterFromThread(int,int);
extern int LoadReadjusted(VOID);
extern int IniSortBlocks(int);
extern int TreatIndexEntry(PHEAD LONG);
extern WORD GetTerm2(PHEAD WORD *);
extern void SetHideFiles(VOID);
#endif
extern int CopyExpression(FILEHANDLE *,FILEHANDLE *);
extern int set_in(UBYTE, set_of_char);
extern one_byte set_set(UBYTE, set_of_char);
extern one_byte set_del(UBYTE, set_of_char);
extern one_byte set_sub (set_of_char, set_of_char, set_of_char);
extern int DoPreAddSeparator(UBYTE *);
extern int DoPreRmSeparator(UBYTE *);
/*See the file extcmd.c*/
extern int openExternalChannel(UBYTE *,int,UBYTE *,UBYTE *);
extern int initPresetExternalChannels(UBYTE *, int);
extern int closeExternalChannel(int);
extern int selectExternalChannel(int);
extern int getCurrentExternalChannel(VOID);
extern VOID closeAllExternalChannels(VOID);
typedef int (*WRITEBUFTOEXTCHANNEL)(char *,size_t);
typedef int (*GETCFROMEXTCHANNEL)(VOID);
typedef int (*SETTERMINATORFOREXTERNALCHANNEL)(char *);
typedef int (*SETKILLMODEFOREXTERNALCHANNEL)(int,int);
typedef LONG (*WRITEFILE)(int,UBYTE *,LONG);
typedef WORD (*GETTERM)(PHEAD WORD *);
#define CompareTerms ((COMPARE)AR.CompareRoutine)
#define FiniShuffle AN.SHvar.finishuf
#define DoShtuffle ((DO_UFFLE)AN.SHvar.do_uffle)
extern UBYTE *defineChannel(UBYTE*, HANDLERS*);
extern int writeToChannel(int,UBYTE *,HANDLERS*);
#ifdef WITHEXTERNALCHANNEL
extern LONG WriteToExternalChannel(int,UBYTE *,LONG);
#endif
extern int writeBufToExtChannelOk(char *,size_t);
extern int getcFromExtChannelOk(VOID);
extern int setKillModeForExternalChannelOk(int,int);
extern int setTerminatorForExternalChannelOk(char *);
extern int getcFromExtChannelFailure(VOID);
extern int setKillModeForExternalChannelFailure(int,int);
extern int setTerminatorForExternalChannelFailure(char *);
extern int writeBufToExtChannelFailure(char *,size_t);
extern int ReleaseTB(VOID);
extern int SymbolNormalize(WORD *);
extern int TestFunFlag(PHEAD WORD *);
extern WORD CompareSymbols(WORD *,WORD *,WORD);
extern WORD CompareHSymbols(WORD *,WORD *,WORD);
extern WORD NextPrime(PHEAD WORD);
extern UWORD wranf(PHEAD0);
extern UWORD iranf(PHEAD UWORD);
extern void iniwranf(PHEAD0);
extern UBYTE *PreRandom(UBYTE *);
extern WORD *PolyNormPoly (PHEAD WORD);
extern WORD *EvaluateGcd(PHEAD WORD *);
extern int TreatPolyRatFun(PHEAD WORD *);
extern WORD ReadSaveHeader(VOID);
extern WORD ReadSaveIndex(FILEINDEX *);
extern WORD ReadSaveExpression(UBYTE *,UBYTE *,LONG *,LONG *);
extern UBYTE *ReadSaveTerm32(UBYTE *,UBYTE *,UBYTE **,UBYTE *,UBYTE *,int);
extern WORD ReadSaveVariables(UBYTE *,UBYTE *,LONG *,LONG *,INDEXENTRY *,LONG *);
extern WORD WriteStoreHeader(WORD);
extern void InitRecovery(VOID);
extern int CheckRecoveryFile(VOID);
extern void DeleteRecoveryFile(VOID);
extern char *RecoveryFilename(VOID);
extern int DoRecovery(int *);
extern void DoCheckpoint(int);
extern VOID NumberMallocAddMemory(PHEAD0);
extern VOID CacheNumberMallocAddMemory(PHEAD0);
extern VOID TermMallocAddMemory(PHEAD0);
#ifndef MEMORYMACROS
extern WORD *TermMalloc2(PHEAD char *text);
extern VOID TermFree2(PHEAD WORD *term,char *text);
extern UWORD *NumberMalloc2(PHEAD char *text);
extern UWORD *CacheNumberMalloc2(PHEAD char *text);
extern VOID NumberFree2(PHEAD UWORD *NumberMem,char *text);
extern VOID CacheNumberFree2(PHEAD UWORD *NumberMem,char *text);
#endif
extern void ExprStatus(EXPRESSIONS);
extern VOID iniTools(VOID);
extern int TestTerm(WORD *);
extern WORD RunTransform(PHEAD WORD *term, WORD *params);
extern WORD RunEncode(PHEAD WORD *fun, WORD *args, WORD *info);
extern WORD RunDecode(PHEAD WORD *fun, WORD *args, WORD *info);
extern WORD RunReplace(PHEAD WORD *fun, WORD *args, WORD *info);
extern WORD RunImplode(WORD *fun, WORD *args);
extern WORD RunExplode(PHEAD WORD *fun, WORD *args);
extern int TestArgNum(int n, int totarg, WORD *args);
extern WORD PutArgInScratch(WORD *arg,UWORD *scrat);
extern UBYTE *ReadRange(UBYTE *s, WORD *out, int par);
extern int FindRange(PHEAD WORD *,WORD *,WORD *,WORD);
extern WORD RunPermute(PHEAD WORD *fun, WORD *args, WORD *info);
extern WORD RunReverse(PHEAD WORD *fun, WORD *args);
extern WORD RunCycle(PHEAD WORD *fun, WORD *args, WORD *info);
extern WORD RunAddArg(PHEAD WORD *fun, WORD *args);
extern WORD RunMulArg(PHEAD WORD *fun, WORD *args);
extern WORD RunIsLyndon(PHEAD WORD *fun, WORD *args, int par);
extern WORD RunToLyndon(PHEAD WORD *fun, WORD *args, int par);
extern WORD RunDropArg(PHEAD WORD *fun, WORD *args);
extern WORD RunSelectArg(PHEAD WORD *fun, WORD *args);
extern WORD RunDedup(PHEAD WORD *fun, WORD *args);
extern int NormPolyTerm(PHEAD WORD *);
extern WORD ComparePoly(WORD *, WORD *, WORD);
extern int ConvertToPoly(PHEAD WORD *, WORD *,WORD *,WORD);
extern int LocalConvertToPoly(PHEAD WORD *, WORD *, WORD,WORD);
extern int ConvertFromPoly(PHEAD WORD *, WORD *, WORD, WORD, WORD, WORD);
extern WORD FindSubterm(WORD *);
extern WORD FindLocalSubterm(PHEAD WORD *, WORD);
extern void PrintSubtermList(int,int);
extern void PrintExtraSymbol(int,WORD *,int);
extern WORD FindSubexpression(WORD *);
extern void UpdateMaxSize(VOID);
extern int CoToPolynomial(UBYTE *);
extern int CoFromPolynomial(UBYTE *);
extern int CoArgToExtraSymbol(UBYTE *);
extern int CoExtraSymbols(UBYTE *);
extern UBYTE *GetDoParam(UBYTE *, WORD **, int);
extern WORD *GetIfDollarFactor(UBYTE **, WORD *);
extern int CoDo(UBYTE *);
extern int CoEndDo(UBYTE *);
extern int ExtraSymFun(PHEAD WORD *,WORD);
extern int PruneExtraSymbols(WORD);
extern int IniFbuffer(WORD);
extern void IniFbufs(VOID);
extern int GCDfunction(PHEAD WORD *,WORD);
extern WORD *GCDfunction3(PHEAD WORD *,WORD *);
extern WORD *GCDfunction4(PHEAD WORD *,WORD *);
extern int ReadPolyRatFun(PHEAD WORD *);
extern int FromPolyRatFun(PHEAD WORD *, WORD **, WORD **);
extern void PRFnormalize(PHEAD WORD *);
extern WORD *PRFadd(PHEAD WORD *, WORD *);
extern WORD *PolyDiv(PHEAD WORD *,WORD *,char *);
extern WORD *PolyGCD(PHEAD WORD *,WORD *);
extern WORD *PolyAdd(PHEAD WORD *,WORD *);
extern void GCDclean(PHEAD WORD *, WORD *);
extern int RatFunNormalize(PHEAD WORD *);
extern WORD *TakeSymbolContent(PHEAD WORD *,WORD *);
extern int GCDterms(PHEAD WORD *,WORD *,WORD *);
extern WORD *PutExtraSymbols(PHEAD WORD *,WORD,int *);
extern WORD *TakeExtraSymbols(PHEAD WORD *,WORD);
extern WORD *MultiplyWithTerm(PHEAD WORD *, WORD *,WORD);
extern WORD *TakeContent(PHEAD WORD *, WORD *);
extern int MergeSymbolLists(PHEAD WORD *, WORD *, int);
extern int MergeDotproductLists(PHEAD WORD *, WORD *, int);
extern WORD *CreateExpression(PHEAD WORD);
extern int DIVfunction(PHEAD WORD *,WORD,int);
extern WORD *MULfunc(PHEAD WORD *, WORD *);
extern WORD *ConvertArgument(PHEAD WORD *,int *);
extern int ExpandRat(PHEAD WORD *);
extern int InvPoly(PHEAD WORD *,WORD,WORD);
extern WORD TestDoLoop(PHEAD WORD *,WORD);
extern WORD TestEndDoLoop(PHEAD WORD *,WORD);
extern WORD *poly_gcd(PHEAD WORD *, WORD *, WORD);
extern WORD *poly_div(PHEAD WORD *, WORD *, WORD);
extern WORD *poly_rem(PHEAD WORD *, WORD *, WORD);
extern WORD *poly_inverse(PHEAD WORD *, WORD *);
extern WORD *poly_mul(PHEAD WORD *, WORD *);
extern WORD *poly_ratfun_add(PHEAD WORD *, WORD *);
extern int poly_ratfun_normalize(PHEAD WORD *);
extern int poly_factorize_argument(PHEAD WORD *, WORD *);
extern WORD *poly_factorize_dollar(PHEAD WORD *);
extern int poly_factorize_expression(EXPRESSIONS);
extern int poly_unfactorize_expression(EXPRESSIONS);
extern void poly_free_poly_vars(PHEAD const char *);
extern VOID optimize_print_code (int);
#ifdef WITHPTHREADS
extern void find_Horner_MCTS_expand_tree();
extern void find_Horner_MCTS_expand_tree_threaded();
extern void optimize_expression_given_Horner();
extern void optimize_expression_given_Horner_threaded();
#endif
extern int DoPreAdd(UBYTE *s);
extern int DoPreUseDictionary(UBYTE *s);
extern int DoPreCloseDictionary(UBYTE *s);
extern int DoPreOpenDictionary(UBYTE *s);
extern void RemoveDictionary(DICTIONARY *dict);
extern void UnSetDictionary(VOID);
extern int SetDictionaryOptions(UBYTE *options);
extern int SelectDictionary(UBYTE *name,UBYTE *options);
extern int AddToDictionary(DICTIONARY *dict,UBYTE *left,UBYTE *right);
extern int AddDictionary(UBYTE *name);
extern int FindDictionary(UBYTE *name);
extern int IsExponentSign(VOID);
extern int IsMultiplySign(VOID);
extern VOID TransformRational(UWORD *a, WORD na);
extern void WriteDictionary(DICTIONARY *);
extern void ShrinkDictionary(DICTIONARY *);
extern void MultiplyToLine(VOID);
extern UBYTE *FindSymbol(WORD num);
extern UBYTE *FindVector(WORD num);
extern UBYTE *FindIndex(WORD num);
extern UBYTE *FindFunction(WORD num);
extern UBYTE *FindFunWithArgs(WORD *t);
extern UBYTE *FindExtraSymbol(WORD num);
extern LONG DictToBytes(DICTIONARY *dict,UBYTE *buf);
extern DICTIONARY *DictFromBytes(UBYTE *buf);
extern int CoCreateSpectator(UBYTE *inp);
extern int CoToSpectator(UBYTE *inp);
extern int CoRemoveSpectator(UBYTE *inp);
extern int CoEmptySpectator(UBYTE *inp);
extern int CoCopySpectator(UBYTE *inp);
extern int PutInSpectator(WORD *,WORD);
extern void ClearSpectators(WORD);
extern WORD GetFromSpectator(WORD *,WORD);
extern void FlushSpectators(VOID);
extern WORD *PreGCD(PHEAD WORD *, WORD *,int);
extern WORD *FindCommonVariables(PHEAD int,int);
extern VOID AddToSymbolList(PHEAD WORD);
extern int AddToListPoly(PHEAD0);
extern int InvPoly(PHEAD WORD *,WORD,WORD);
extern int ReadFromScratch(FILEHANDLE *,POSITION *,UBYTE *,POSITION *);
extern int AddToScratch(FILEHANDLE *,POSITION *,UBYTE *,POSITION *,int);
extern int DoPreAppendPath(UBYTE *);
extern int DoPrePrependPath(UBYTE *);
extern int DoSwitch(PHEAD WORD *, WORD *);
extern int DoEndSwitch(PHEAD WORD *, WORD *);
extern SWITCHTABLE *FindCase(WORD, WORD);
extern VOID SwitchSplitMergeRec(SWITCHTABLE *, WORD, SWITCHTABLE *);
extern VOID SwitchSplitMerge(SWITCHTABLE *, WORD);
extern int DoubleSwitchBuffers(VOID);
/*
#] Declarations :
*/
#endif
form-master/sources/diagrams.c 0000664 0000000 0000000 00000054222 13565763364 0016757 0 ustar 00root root 0000000 0000000 /** @file diagrams.c
*
* Contains the wrapper routines for diagram manipulations.
*/
/* #[ License : */
/*
* Copyright (C) 1984-2017 J.A.M. Vermaseren
* When using this file you are requested to refer to the publication
* J.A.M.Vermaseren "New features of FORM" math-ph/0010025
* This is considered a matter of courtesy as the development was paid
* for by FOM the Dutch physics granting agency and we would like to
* be able to track its scientific use to convince FOM of its value
* for the community.
*
* This file is part of FORM.
*
* FORM 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.
*
* FORM 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 FORM. If not, see .
*/
/* #] License : */
/*
#[ Includes : diagrams.c
*/
#include "form3.h"
static WORD one = 1;
/*
#] Includes :
#[ CoCanonicalize :
Syntax:
Canonicalize,mainoption,...
With the main options currently:
Canonicalize,topology,vertexfunction,edgefunction,OutDollar,extraoptions;
Canonicalize,polynomial,InDollar,set_or_setname_or_dollar,OutDollar,extraoptions;
The vertex function needs to have the format (assume it is called vx):
vx(p1,p2,-p3) or vx(-p1,p2,p3,-p4) etc.
The external lines have a vertex with only one line.
All momenta that form connections should be unique.
In principle the - signs are not relevant for the topology, but they
may exist already in the remaining part of the diagram. They are also
part of the canonical form.
The edge function can be used in different ways, depending on the options.
The extraoption(s) should be nonnegative integers or $variables that evaluate
into nonnegative integers (integers less than 2^31).
The Indollar variable contains the polynomial to be canonicalized.
The OutDollar variable should be the name of a $-variable (as in $out) which
will be filled with a replace_ function. The canonicalization can then
be executed in the whole term with the Multiply $out; command.
*/
int CoCanonicalize(UBYTE *s)
{
WORD args[10], *a, num;
UBYTE *t, c;
args[0] = TYPECANONICALIZE;
a = args+2;
while ( *s == ',' || *s == '\t' || *s == ' ' ) s++;
t = s; while ( FG.cTable[*s] == 0 ) s++;
c = *s; *s = 0;
if ( StrICmp(t,(UBYTE *)("topology")) == 0 ) {
*s = c; *a++ = 0;
while ( *s == ',' || *s == '\t' || *s == ' ' ) s++;
s = GetFunction(s,a);
while ( *s == ',' || *s == '\t' || *s == ' ' ) s++;
s = GetFunction(s,a+1);
if ( *a == 0 || a[1] == 0 ) return(1);
a += 2;
}
else if ( StrICmp(t,(UBYTE *)("polynomial")) == 0 ) {
*s = c; *a++ = 1;
while ( *s == ',' || *s == '\t' || *s == ' ' ) s++;
/*
Now get the name of the input $-variable
*/
if ( *s != '$' ) {
MesPrint("&Canonicalize statement needs a $-variable for its input.");
return(1);
}
s++; t = s; while ( FG.cTable[*s] < 2 ) s++;
c = *s; *s = 0;
if ( GetName(AC.dollarnames,t,&num,NOAUTO) == CDOLLAR ) *a++ = num;
else { *a++ = AddDollar(t,DOLINDEX,&one,1); }
*s = c;
/*
Now the set
*/
if ( *s == '{' ) {
t = s+1; SKIPBRA2(s)
c = *s; *s = 0;
*a++ = DoTempSet(t,s);
*s++ = c;
}
else if ( FG.cTable[*s] == 0 || *s == '[' ) {
t = s;
if ( ( s = SkipAName(s) ) == 0 ) {
MesPrint("&Illegal name for set in Canonicalize statement: %s",t);
return(1);
}
c = *s; *s = 0;
if ( GetName(AC.varnames,t,a,WITHAUTO) == CSET ) {
if ( Sets[*a].type != CSYMBOL ) {
MesPrint("&In Canonicalize: %s is not a set of symbols.",t);
return(1);
}
}
else {
MesPrint("&In Canonicalize: %s is not a set.",t);
return(1);
}
*s = c; a++;
}
else if ( *s == '$' ) {
s++; t = s; while ( FG.cTable[*s] < 2 ) s++;
c = *s; *s = 0;
if ( GetName(AC.dollarnames,t,&num,NOAUTO) == CDOLLAR ) *a++ = -num-2;
else {
MesPrint("&In Canonicalize: %s is undefined.",t-1);
return(1);
}
*s = c;
}
else {
MesPrint("&In Canonicalize: Illegal third(=set) argument.");
return(1);
}
}
else {
MesPrint("&Unrecognized option in Canonicalize statement: %s",t);
return(1);
}
/*
Now get the name of the output $-variable
*/
while ( *s == ',' || *s == '\t' || *s == ' ' ) s++;
if ( *s != '$' ) {
MesPrint("&Canonicalize statement needs a $-variable for its output.");
return(1);
}
s++; t = s; while ( FG.cTable[*s] < 2 ) s++;
c = *s; *s = 0;
if ( GetName(AC.dollarnames,t,&num,NOAUTO) == CDOLLAR ) *a++ = num;
else { *a++ = AddDollar(t,DOLINDEX,&one,1); }
*s = c;
/*
Now the options. At the moment we just do one of them.
(the first extra option is relevant to determine the use of the edge function)
In the future we may have to be more flexible.
*/
a[0] = 0; /* default value */
while ( *s == ',' || *s == '\t' || *s == ' ' ) s++;
if ( *s != 0 ) {
s = GetNumber(s,a);
if ( *a == -1 ) return(1);
while ( *s == ',' || *s == '\t' || *s == ' ' ) s++;
a++;
}
/*
Now complete the args string and put it in the compiler buffer
*/
args[1] = a-args;
AddNtoL(args[1],args);
return(0);
}
/*
#] CoCanonicalize :
#[ DoCanonicalize :
Does the canonicalization. The output term overwrites the input term.
*/
int DoCanonicalize(PHEAD WORD *term, WORD *params)
{
WORD args[10];
int i;
/*
First check whether we need to expand dollars;
*/
for ( i = 0; i < params[1]; i++ ) args[i] = params[i];
if ( args[2] == 0 ) { /* topology */
for ( i = 3; i < 5; i++ ) {
if ( args[i] < 0 ) { /* This is a dollar */
args[i] = DolToFunction(BHEAD -args[i]-2);
if ( args[i] == 0 ) {
MLOCK(ErrorMessageLock);
MesPrint("Value of $-variable in Canonicalize statement should be a function.");
MUNLOCK(ErrorMessageLock);
Terminate(-1);
}
}
}
for ( i = 6; i < args[1]; i++ ) { /* Extra options */
if ( args[i] < 0 ) { /* This is a dollar */
args[i] = DolToNumber(BHEAD -args[i]-2);
if ( args[i] < 0 ) {
MLOCK(ErrorMessageLock);
MesPrint("Value of $-variable in Canonicalize statement should be a nonnegative number < %l.",(LONG)MAXPOSITIVE);
MUNLOCK(ErrorMessageLock);
Terminate(-1);
}
}
}
switch ( args[6] ) {
case 1: {/* pass the vertex and edge functions. */
WORD *tstop, *t, *tedge, *te;
tstop = term + *term; tstop -= ABS(tstop[-1]);
t = term+1;
tedge = AT.WorkPointer; te = tedge+1;
while ( t < tstop ) {
if ( *t != args[3] && *t != args[4] ) { t += t[1]; continue; }
for ( i = 0; i < t[1]; i++ ) te[i] = t[i];
te += t[1]; t += t[1];
}
*te++ = 1; *te++ = 1; *te++ = 3;
tedge[0] = te-tedge;
AT.WorkPointer = te;
/*
DoVertexCanonicalize(BHEAD term,tedge,args[3],args[4],args[5],args[6]);
*/
AT.WorkPointer = tedge;
} break;
case 2: {/* pass the edge functions only */
WORD *tstop, *t, *tedge, *te;
tstop = term + *term; tstop -= ABS(tstop[-1]);
t = term+1;
tedge = AT.WorkPointer; te = tedge+1;
while ( t < tstop ) {
if ( *t != args[4] ) { t += t[1]; continue; }
for ( i = 0; i < t[1]; i++ ) te[i] = t[i];
te += t[1]; t += t[1];
}
*te++ = 1; *te++ = 1; *te++ = 3;
tedge[0] = te-tedge;
AT.WorkPointer = te;
/*
DoEdgeCanonicalize(BHEAD term,tedge,args[5]);
*/
AT.WorkPointer = tedge;
} break;
default: {
DoTopologyCanonicalize(BHEAD term,args[3],args[4],args+5);
} break;
}
/*
Call here the topology canonicalization
We will have the arguments:
args[3]: The function used as vertex function
args[4]: The function used as edge function
args[5]: The number of the dollar to be used for the output
args[6]: Potentially other options (like saying how to use args[4]).
term: The term in which the topology resides.
*/
}
else if ( args[2] == 1 ) { /* polynomial */
WORD *symlist, nsymlist;
for ( i = 6; i < args[1]; i++ ) { /* Extra options */
if ( args[i] < 0 ) { /* This is a dollar */
args[i] = DolToNumber(BHEAD -args[i]-2);
if ( args[i] < 0 ) {
MLOCK(ErrorMessageLock);
MesPrint("Value of $-variable in Canonicalize statement should be a nonnegative number < %l.",(LONG)MAXPOSITIVE);
MUNLOCK(ErrorMessageLock);
Terminate(-1);
}
}
}
/*
Now we sort out the set. We create a pointer to the list of set
elements, and we determine the number of elements in the set.
*/
symlist = AT.WorkPointer;
if ( args[4] < -1 ) { /* Dollar that should expand into a list of symbols */
DOLLARS d = Dollars - args[4] - 2;
WORD *ds, *insym;
if ( d->type != DOLWILDARGS ) {
NoWildArg:
MLOCK(ErrorMessageLock);
MesPrint("Value of $-variable in Canonicalize statement should be a argument wildcard of symbol arguments.");
MUNLOCK(ErrorMessageLock);
Terminate(-1);
}
insym = symlist; ds = d->where+1;
while ( *ds ) {
if ( *ds != -SYMBOL ) goto NoWildArg;
*insym++ = ds[1];
ds += 2;
}
nsymlist = insym-symlist;
}
else { /*if ( args[4] >= 0 ) */
WORD *ss, *sy, n;
ss = (WORD *)(AC.SetElementList.lijst)+Sets[args[4]].first;
nsymlist = n = Sets[args[4]].last-Sets[args[4]].first;
sy = symlist = AT.WorkPointer;
NCOPY(sy,ss,n);
}
AT.WorkPointer = symlist+nsymlist;
/*
Call here the polynomial canonicalization
We will have the arguments:
args[3]: The number of the dollar to be used for the input
symlist: an array of symbols
nsymlist: the number of symbols in symlist
args[5]: The number of the dollar to be used for the output
args[6]: Potentially other options.
*/
/*
DoPolynomialCanonicalize(BHEAD args[3],symlist,nsymlist,args[5],args[6]);
*/
AT.WorkPointer = symlist;
}
return(0);
}
/*
#] DoCanonicalize :
#[ GenTopologies :
This function has the syntax
topologies_(nloops, Number of loops
nlegs, Number of legs
setvertexsizes, A set which tells which vertices are allowed like {3,4}.
set_extmomenta, The name of a set with the external momenta
set_intmomenta The name of a set with the internal momenta
[,options])
The output will be using the built in functions vertex_ and edge_.
The test for whether this function can be evaluated is in TestSub (inside
file proces.c) (search for the string TOPOLOGIES).
This passes the code -15 in AN.TeInFun to Generator, which then calls
the GenTopologies routine.
*/
WORD GenTopologies(PHEAD WORD *term,WORD level)
{
WORD *t1, *tt1, *tstop, *t, *tt;
WORD *oldworkpointer = AT.WorkPointer;
WORD option1 = 0, option2 = 0, setoption = -1;
WORD retval;
/*
We have to go through the testing procedure again, because there could
be more than one topologies_ function and not all have to be expandable.
*/
tstop = term+*term; tstop -= ABS(tstop[-1]);
tt = term+1;
while ( tt < tstop ) {
t = tt; tt = t+t[1];
if ( *t != TOPOLOGIES ) continue;
tt = t + t[1]; t1 = t + FUNHEAD;
if ( t1+10 > tt || *t1 != -SNUMBER || t1[1] < 0 || /* loops */
t1[2] != -SNUMBER || ( t1[3] < 0 && t1[3] != -2 ) ||/* legs */
t1[4] != -SETSET || Sets[t1[5]].type != CNUMBER || /* set vertices */
t1[6] != -SETSET || Sets[t1[7]].type != CVECTOR || /* outvectors */
t1[8] != -SETSET || Sets[t1[9]].type != CVECTOR ) continue;
tt1 = t1 + 10;
if ( tt1+2 <= tt && tt1[0] == -SETSET ) {
if ( Sets[t1[5]].last-Sets[t1[5]].first !=
Sets[tt1[1]].last-Sets[tt1[1]].first ) continue;
setoption = tt1[1]; tt1 += 2;
}
if ( tt1+2 <= tt && tt1[0] == -SNUMBER ) { option1 = tt1[1]; tt1 += 2; }
if ( tt1+2 <= tt && tt1[0] == -SNUMBER ) { option2 = tt1[1]; tt1 += 2; }
AT.setinterntopo = t1[9];
AT.setexterntopo = t1[7];
AT.TopologiesTerm = term;
AT.TopologiesStart = t;
AT.TopologiesLevel = level;
AT.TopologiesOptions[0] = option1;
AT.TopologiesOptions[1] = option2;
retval = GenerateTopologies(BHEAD t1[1],t1[3],t1[5],setoption);
AT.WorkPointer = oldworkpointer;
return(retval);
}
MLOCK(ErrorMessageLock);
MesPrint("Internal error: topologies_ function not encountered.");
MUNLOCK(ErrorMessageLock);
return(-1);
}
/*
#] GenTopologies :
#[ GenDiagrams :
*/
WORD GenDiagrams(PHEAD WORD *term,WORD level)
{
#ifdef WITHPTHREADS
DUMMYUSE(B)
#endif
DUMMYUSE(term)
DUMMYUSE(level)
return(0);
}
/*
#] GenDiagrams :
#[ DoTopologyCanonicalize :
term: The term
vert: the vertex function
edge: the edge function
args[0]: the number of the output dollar
args[1]: options
return value should be zero if all is correct.
The external lines connect to an 'external vertex' which has only one line.
The vertices are of a type: vertex_(p1,p2,-p3)*vertex_(p3,p4,p5) etc.
The edges indicate noninteger powers of the lines:
edge_(p1,2) means 1/p1.p1^(2*ep)
*/
int DoTopologyCanonicalize(PHEAD WORD *term,WORD vert,WORD edge,WORD *args)
{
int nvert = 0, nvert2, i, ii, jj, flipnames = 0, nparts, level, num;
WORD *tstop, *t, *tt, *tend, *td;
WORD *oldworkpointer = AT.WorkPointer;
WORD *termcopy = TermMalloc("TopologyCanonize1");
WORD *vet= TermMalloc("TopologyCanonize2");
WORD *partition, *environ, *connect, *pparts, *p;
/*
WORD *pparts;
*/
WORD momenta[150],flips[50],nmomenta = 0, nflips = 0;
/*
Step one: the vertices should get a number. We copy the term for this.
We need a high number for the vertex function to make sure
that it comes after the edge function in the sorting.
*/
if ( args[0] < args[1] ) { flipnames = 1; }
tend = term + *term; tend -= ABS(tend[-1]); t = term+1; tt = termcopy+1;
while ( t < tend ) {
if ( *t == vert ) {
for ( i = FUNHEAD; i < t[1]; i += 2 ) {
if ( t[i] == -VECTOR || ( t[i] == -INDEX && t[i+1] < 0 ) ) {
momenta[nmomenta++] = -VECTOR;
momenta[nmomenta++] = t[i+1];
}
else if ( t[i] == -MINVECTOR ) {
momenta[nmomenta++] = -MINVECTOR;
momenta[nmomenta++] = t[i+1];
}
else goto notgoodvertex;
momenta[nmomenta++] = nvert;
}
ii = FUNHEAD; i = t[1]-FUNHEAD;
NCOPY(tt,t,ii)
if ( flipnames ) tt[-FUNHEAD] = edge;
tt[-FUNHEAD+1] += 2;
*tt++ = -CNUMBER; *tt++ = nvert++;
}
else if ( *t == edge && flipnames ) {
i = t[1] - 1; *tt++ = vert; t++;
}
else {
notgoodvertex:
i = t[1];
}
NCOPY(tt,t,i)
}
while ( t < tend ) *tt++ = *t++;
termcopy[0] = tt - termcopy;
if ( flipnames ) EXCH(edge,vert)
nvert2 = nvert*nvert;
/*
Sort the momenta. Keep the sign order.
*/
for ( i = 0; i < nmomenta-3; i+=3 ) {
jj = i;
while ( jj >= 0 && momenta[jj+4] > momenta[jj+1] ) {
EXCH(momenta[jj+5],momenta[jj+2])
EXCH(momenta[jj+4],momenta[jj+1])
EXCH(momenta[jj+3],momenta[jj])
jj -= 3;
}
}
/*
Step two: make now the edge functions in the proper notation.
*/
t = vet+1;
for ( i = 0; i < nmomenta; i += 6 ) {
if ( momenta[i] == -VECTOR && momenta[i+3] == -MINVECTOR
&& momenta[i+1] == momenta[i+4] ) {
}
else if ( momenta[i] == -MINVECTOR && momenta[i+3] == -VECTOR
&& momenta[i+1] == momenta[i+4] ) {
flips[nflips++] = momenta[i+1];
DUMMYUSE(flips[nflips-1]);
}
else { /* something wrong with the momenta */
MLOCK(ErrorMessageLock);
MesPrint("No momentum conservation or wrong momenta in Canonicalize statement");
MUNLOCK(ErrorMessageLock);
Terminate(-1);
}
*t++ = EDGE; *t++ = FUNHEAD+10; FILLFUN(t)
*t++ = -SNUMBER; *t++ = momenta[i+2];
*t++ = -SNUMBER; *t++ = momenta[i+5];
*t++ = -VECTOR; *t++ = momenta[i+1];
*t++ = -SNUMBER; *t++ = 0; /* provisional power/color, multiple of ep */
*t++ = -SNUMBER; *t++ = 0; /* provisional power/color, integer */
}
tend = t;
*t++ = 1; *t++ = 1; *t++ = 3; vet[0] = t-vet; *t = 0;
/*
Now the powers of the denominators
*/
tstop = termcopy+*termcopy; tstop -= ABS(tstop[-1]); td = termcopy+1;
while ( td < tstop ) {
if ( *td == edge && td[1] == FUNHEAD+4 ) {
if ( td[FUNHEAD+2] == -SNUMBER && ( td[FUNHEAD] == -VECTOR || td[FUNHEAD] == -INDEX
|| td[FUNHEAD] == -MINVECTOR ) ) {}
else {
MLOCK(ErrorMessageLock);
MesPrint("Illegal argument in edge function in Canonicalize statement");
MUNLOCK(ErrorMessageLock);
Terminate(-1);
}
tt = vet+1;
while ( tt < tend ) {
if ( tt[FUNHEAD+5] == td[FUNHEAD+1] ) { tt[FUNHEAD+7] = td[FUNHEAD+3]; break; }
tt += tt[1];
}
}
else if ( *td == DOTPRODUCT ) break;
td += td[1];
}
if ( td < tstop ) {
tt = vet+1;
while ( tt < tend ) {
/*
tt[FUNHEAD+5] is a vector. We look for dotproducts with twice tt[FUNHEAD+5]
*/
for ( i = 2; i < td[1]; i += 3 ) {
if ( td[i] == tt[FUNHEAD+5] && td[i+1] == tt[FUNHEAD+5] ) {
tt[FUNHEAD+9] = td[i+2];
break;
}
}
tt += tt[1];
}
}
Normalize(BHEAD vet);
/*
Now we have a term `vet' in the proper notation and we can start.
To keep track of the shattering we use an array of 2*nvert.
each entry is Number,marker
When the marker is zero, the vertices are in the same partition.
For the environment we need a matrix that is nvert x nvert
At the same time we keep the connectivity matrix, because that will
save much time later.
The partitions are stored in a matrix as well. This allows them to be
treated as a stack. The entries are separated by 0 if they belong to
the same part, and by a 1 when they belong to different parts.
*/
partition = AT.WorkPointer; AT.WorkPointer += 2*nvert2;
for ( i = 0; i < nvert; i++ ) { partition[2*i] = i; partition[2*i+1] = 0; }
partition[2*i-1] = -1; /* end of the first part which is currently all vertices */
nparts = 1;
connect = AT.WorkPointer; AT.WorkPointer += nvert2;
for ( i = 0; i < nvert2; i++ ) connect[i] = 0;
tstop = vet+*vet; tstop -= ABS(tstop[-1]); t = vet+1;
while ( t < tstop ) {
if ( *t == EDGE ) {
connect[t[FUNHEAD+1]*nvert+t[FUNHEAD+3]]++;
connect[t[FUNHEAD+3]*nvert+t[FUNHEAD+1]]++;
}
t += t[1];
}
for ( i = 0; i < nvert; i++ ) {
MesPrint("connectivity: %d -- %a",i,nvert,connect+i*nvert);
}
/*
Create the environment matrix and sort it.
*/
environ = AT.WorkPointer; AT.WorkPointer += nvert2;
/*
And now the refinement process starts.
*/
WantAddPointers(nvert+1);
for ( i = 0; i < nvert2; i++ ) environ[i] = 0;
level = 0;
pparts = partition;
while ( nparts < nvert ) {
nparts = DoShattering(BHEAD connect,environ,pparts,nvert);
if ( nparts < nvert ) { /* raise level and make a copy and split a part */
p = pparts + 2*nvert;
level++;
for ( i = 0; i < 2*nvert; i++ ) p[i] = pparts[i];
for ( ii = 0; ii < 2*nvert; ii += 2 ) {
if ( p[ii+1] == 0 ) { /* found a part with more than one */
num = 2; i = ii+2;
while ( p[i+1] == 0 ) { num++; i += 2; }
p[ii+1] = -1; pparts = p;
break;
}
}
}
MesPrint("partition: %d -- %a",nparts,2*nvert,pparts);
}
/*
Just for now
*/
PutTermInDollar(vet,args[0]);
TermFree(vet,"TopologyCanonize2");
TermFree(termcopy,"TopologyCanonize1");
AT.WorkPointer = oldworkpointer;
return(0);
}
/*
#] DoTopologyCanonicalize :
#[ DoShattering :
*/
int DoShattering(PHEAD WORD *connect, WORD *environ, WORD *partitions, WORD nvert)
{
int nparts, i, j, ii, jj, iii, jjj, newmarker;
WORD **p = AT.pWorkSpace + AT.pWorkPointer, *part, *endpart;
WORD *poin1, *poin2;
#ifdef SHATBUG
MesPrint("Entering DoShattering. partitions = %a",2*nvert,partitions);
#endif
restart:
/*
Determine the number of parts
p will be an array with pointers to the parts.
We made space for this array in the calling routine and because this
routine is not calling any other routines we do not need to raise
the pointer in this stack (AT.pWorkPointer).
*/
nparts = 0; newmarker = 0;
part = partitions; endpart = part + 2*nvert;
p[0] = part;
while ( part < endpart ) {
if ( part[1] != 0 ) { p[++nparts] = part+2; }
part += 2;
}
for ( i = 0; i < nparts; i++ )
AT.WorkPointer[i] = (p[i+1]-p[i])/2;
#ifdef SHATBUG
MesPrint("DoShattering: calculated the pointers");
MesPrint("DoShattering: sizes: %a",nparts,AT.WorkPointer);
MesPrint("DoShattering: p[0]: %a, p[1]: %a",6,p[0],6,p[1]);
#endif
for ( i = 0; i < nparts; i++ ) {
if ( AT.WorkPointer[i] > 1 ) {
for ( j = 0; j < nparts; j++ ) {
/*
Shatter part i wrt part j.
if there is action, go to restart.
*/
for ( ii = 0; ii < AT.WorkPointer[i]; ii++ ) {
for ( jj = 0; jj < AT.WorkPointer[j]; jj++ ) {
environ[ii*AT.WorkPointer[j]+jj] += connect[p[i][2*ii]*nvert+p[j][2*jj]];
}
}
#ifdef SHATBUG
for ( ii = 0; ii < AT.WorkPointer[i]; ii++ ) {
MesPrint("Environ(%d,%d): %a",i,j,AT.WorkPointer[j],environ+ii*AT.WorkPointer[j]);
}
#endif
/*
Sort the rows internally, then sort the rows wrt each other
and finally place new markers. If a new marker, we restart.
Don't forget to clean up the environ array.
*/
for ( ii = 0; ii < nvert; ii++ ) {
poin1 = environ+ii*AT.WorkPointer[j];
for ( jj = 0; jj < AT.WorkPointer[j]-1; jj++ ) {
jjj = jj;
while ( jjj >= 0 && poin1[jjj+1] > poin1[jjj] ) {
EXCH(poin1[jjj+1],poin1[jjj])
jjj--;
}
}
}
#ifdef SHATBUG
for ( ii = 0; ii < AT.WorkPointer[i]; ii++ ) {
MesPrint("environ(%d,%d): %a",i,j,AT.WorkPointer[j],environ+ii*AT.WorkPointer[j]);
}
#endif
for ( ii = 0; ii < AT.WorkPointer[i]-1; ii++ ) {
poin2 = environ+ii*AT.WorkPointer[j]; poin1 = poin2+AT.WorkPointer[j];
iii = ii;
while ( iii >= 0 && ( CmpArray(poin1,poin2,AT.WorkPointer[j]) < 0 ) ) {
EXCHN(poin2,poin1,AT.WorkPointer[j])
EXCH(p[i][2*iii+2],p[i][2*iii])
iii--; poin1 = poin2; poin2 = poin1-AT.WorkPointer[j];
}
}
#ifdef SHATBUG
for ( ii = 0; ii < AT.WorkPointer[i]; ii++ ) {
MesPrint("environ(%d,%d): %a",i,j,AT.WorkPointer[j],environ+ii*AT.WorkPointer[j]);
}
MesPrint("partitions = %a",2*nvert,partitions);
#endif
for ( ii = 0; ii < AT.WorkPointer[i]-1; ii++ ) {
poin2 = environ+ii*AT.WorkPointer[j]; poin1 = poin2+AT.WorkPointer[j];
if ( CmpArray(poin1,poin2,AT.WorkPointer[j]) == 0 ) continue;
p[i][2*ii+1] = -1; nparts++; newmarker++;
}
#ifdef SHATBUG
MesPrint("partitions = %a",2*nvert,partitions);
#endif
/*
Clear environ. This is probably faster than just clearing the whole array.
Maybe in the future a test could be done on nvert to decide how to clear.
*/
for ( ii = 0; ii < AT.WorkPointer[i]; ii++ ) {
for ( jj = 0; jj < AT.WorkPointer[j]; jj++ ) {
environ[ii*AT.WorkPointer[j]+jj] = 0;
}
}
if ( newmarker ) { goto restart; }
}
}
}
return(nparts);
}
/*
#] DoShattering :
*/
form-master/sources/dict.c 0000664 0000000 0000000 00000073634 13565763364 0016123 0 ustar 00root root 0000000 0000000 /** @file dict.c
*
* Contains the code pertaining to dictionaries
* Commands are:
* #opendictionary name
* #closedictionary
* #selectdictionary name
* There can be several dictionaries, but only one can be active.
* Defining elements is done with
* #add object: "replacement"
* Replacements are strings when a dictionary is for output translation.
* Objects can be
* 1: a number (rational)
* 2: a variable
* 3: * ^
* 4: a function with arguments
*/
/* #[ License : */
/*
* Copyright (C) 1984-2017 J.A.M. Vermaseren
* When using this file you are requested to refer to the publication
* J.A.M.Vermaseren "New features of FORM" math-ph/0010025
* This is considered a matter of courtesy as the development was paid
* for by FOM the Dutch physics granting agency and we would like to
* be able to track its scientific use to convince FOM of its value
* for the community.
*
* This file is part of FORM.
*
* FORM 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.
*
* FORM 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 FORM. If not, see .
*/
/* #] License : */
/*
#[ Includes : ratio.c
Data setup:
AO.Dictionaries Array of pointers to DICTIONARY
AO.NumDictionaries
AO.SizeDictionaries
AO.CurrentDictionary
AO.CurDictNumbers
AO.CurDictVariables
AO.CurDictSpecials
AP.OpenDictionary
*/
#include "form3.h"
/*
#] Includes :
#[ TransformRational:
Tries to transform the rational number a according to the rules of
the current dictionary. Whatever cannot be translated goes to the
regular output.
Options for AO.CurDictNumbers are:
DICT_ALLNUMBERS, DICT_RATIONALONLY, DICT_INTEGERONLY, DICT_NONUMBERS
*/
VOID TransformRational(UWORD *a, WORD na)
{
DICTIONARY *dict;
WORD i, j, nb, i1, i2; UWORD *b;
if ( AO.CurrentDictionary <= 0 ) goto NoAction;
dict = AO.Dictionaries[AO.CurrentDictionary-1];
if ( na < 0 ) na = -na;
switch ( AO.CurDictNumbers ) {
case DICT_NONUMBERS:
goto NoAction;
case DICT_INTEGERONLY:
if ( a[na] != 1 ) goto NoAction;
if ( na > 1 ) {
for ( i = 1; i < na; i++ ) {
if ( a[na+i] != 0 ) goto NoAction;
}
}
Numeratoronly:;
for ( i = dict->numelements-1; i >= 0; i-- ) {
if ( dict->elements[i]->type == DICT_INTEGERNUMBER ) {
if ( dict->elements[i]->size == na ) {
for ( j = 0; j < na; j++ ) {
if ( (UWORD)(dict->elements[i]->lhs[j]) != a[j] ) break;
}
if ( j == na ) { /* Got it */
TokenToLine((UBYTE *)(dict->elements[i]->rhs));
return;
}
}
}
}
goto NotFound;
case DICT_RATIONALONLY:
nb = 2*na;
for ( i = dict->numelements-1; i >= 0; i-- ) {
if ( dict->elements[i]->type == DICT_RATIONALNUMBER ) {
if ( dict->elements[i]->size == nb+2 ) {
for ( j = 0; j < nb; j++ ) {
if ( (UWORD)(dict->elements[i]->lhs[j+1]) != a[j] ) break;
}
if ( j == nb ) { /* Got it */
TokenToLine((UBYTE *)(dict->elements[i]->rhs));
return;
}
}
}
}
goto NotFound;
case DICT_ALLNUMBERS:
/*
First fish for rationals
*/
nb = 2*na;
for ( i = dict->numelements-1; i >= 0; i-- ) {
if ( dict->elements[i]->type == DICT_RATIONALNUMBER ) {
if ( dict->elements[i]->size == nb+2 ) {
for ( j = 0; j < nb; j++ ) {
if ( (UWORD)(dict->elements[i]->lhs[j+1]) != a[j] ) break;
}
if ( j == nb ) { /* Got it */
TokenToLine((UBYTE *)(dict->elements[i]->rhs));
return;
}
}
}
}
/*
Now look for element[j1]/element[j2]
*/
nb = na; b = a+na;
while ( b[nb-1] == 0 ) nb--;
if ( nb == 1 && b[0] == 1 ) goto Numeratoronly;
while ( a[na-1] == 0 ) na--;
for ( i1 = dict->numelements-1; i1 >= 0; i1-- ) {
if ( dict->elements[i1]->type == DICT_INTEGERNUMBER ) {
if ( dict->elements[i1]->size == na ) {
for ( j = 0; j < na; j++ ) {
if ( (UWORD)(dict->elements[i1]->lhs[j]) != a[j] ) break;
}
if ( j == na ) break;
}
}
}
for ( i2 = dict->numelements-1; i2 >= 0; i2-- ) {
if ( dict->elements[i2]->type == DICT_INTEGERNUMBER ) {
if ( dict->elements[i2]->size == nb ) {
for ( j = 0; j < nb; j++ ) {
if ( (UWORD)(dict->elements[i2]->lhs[j]) != b[j] ) break;
}
if ( j == nb ) break;
}
}
}
if ( i1 < 0 ) {
if ( i2 < 0 ) goto NotFound;
else { /* number/replacement[i2] */
LongToLine(a,na);
if ( na > 1 || ( AO.DoubleFlag & 4 ) == 4 ) {
if ( AC.OutputMode == FORTRANMODE || AC.OutputMode == PFORTRANMODE
|| AC.OutputMode == CMODE ) {
if ( ( AO.DoubleFlag & 2 ) == 2 ) { AddToLine((UBYTE *)".Q0/"); }
else if ( ( AO.DoubleFlag & 1 ) == 1 ) { AddToLine((UBYTE *)".D0/"); }
else { AddToLine((UBYTE *)"/"); }
}
}
else AddToLine((UBYTE *)("/"));
TokenToLine((UBYTE *)(dict->elements[i2]->rhs));
}
}
else if ( i2 < 0 ) { /* replacement[i1]/number */
TokenToLine((UBYTE *)(dict->elements[i1]->rhs));
AddToLine((UBYTE *)("/"));
LongToLine((UWORD *)(b),nb);
if ( nb > 1 || ( AO.DoubleFlag & 4 ) == 4 ) {
if ( AC.OutputMode == FORTRANMODE || AC.OutputMode == PFORTRANMODE
|| AC.OutputMode == CMODE ) {
if ( ( AO.DoubleFlag & 2 ) == 2 ) { AddToLine((UBYTE *)".Q0"); }
else if ( ( AO.DoubleFlag & 1 ) == 1 ) { AddToLine((UBYTE *)".D0"); }
}
}
}
else { /* replacement[i1]/replacement[i2] */
TokenToLine((UBYTE *)(dict->elements[i1]->rhs));
AddToLine((UBYTE *)("/"));
TokenToLine((UBYTE *)(dict->elements[i2]->rhs));
}
break;
default:
MesPrint("Illegal code in TransformRational: %d",AO.CurDictNumbers);
Terminate(-1);
}
return;
NotFound:
if ( na != 1 || a[1] != 1 ) {
if ( AO.CurDictNumberWarning ) {
MesPrint(">>>>>>>>Could not translate coefficient with dictionary %s<<<<<<<<<<<<",dict->name);
} }
NoAction:
RatToLine(a,na);
return;
}
/*
#] TransformRational:
#[ IsMultiplySign:
*/
int IsMultiplySign(VOID)
{
DICTIONARY *dict;
int i;
if ( AO.CurrentDictionary <= 0 ) return(0);
dict = AO.Dictionaries[AO.CurrentDictionary-1];
if ( dict->characters == 0 ) return(0);
for ( i = dict->numelements-1; i >= 0; i-- ) {
if ( ( dict->elements[i]->type == DICT_SPECIALCHARACTER )
&& ( dict->elements[i]->lhs[0] == (WORD)('*') ) ) return(i+1);
}
return(0);
}
/*
#] IsMultiplySign:
#[ IsExponentSign:
*/
int IsExponentSign(VOID)
{
DICTIONARY *dict;
int i;
if ( AO.CurrentDictionary <= 0 ) return(0);
dict = AO.Dictionaries[AO.CurrentDictionary-1];
if ( dict->characters == 0 ) return(0);
for ( i = dict->numelements-1; i >= 0; i-- ) {
if ( ( dict->elements[i]->type == DICT_SPECIALCHARACTER )
&& ( dict->elements[i]->lhs[0] == (WORD)('^') ) ) return(i+1);
}
return(0);
}
/*
#] IsExponentSign:
#[ FindSymbol :
*/
UBYTE *FindSymbol(WORD num)
{
if ( AO.CurrentDictionary > 0 ) {
DICTIONARY *dict = AO.Dictionaries[AO.CurrentDictionary-1];
int i;
if ( dict->variables > 0 && AO.CurDictVariables == DICT_DOVARIABLES ) {
for ( i = dict->numelements-1; i >= 0; i-- ) {
if ( dict->elements[i]->type == DICT_SYMBOL &&
dict->elements[i]->lhs[0] == num )
return((UBYTE *)(dict->elements[i]->rhs));
}
}
}
return(VARNAME(symbols,num));
}
/*
#] FindSymbol :
#[ FindVector :
*/
UBYTE *FindVector(WORD num)
{
if ( AO.CurrentDictionary > 0 ) {
DICTIONARY *dict = AO.Dictionaries[AO.CurrentDictionary-1];
int i;
if ( dict->variables > 0 && AO.CurDictVariables == DICT_DOVARIABLES ) {
for ( i = dict->numelements-1; i >= 0; i-- ) {
if ( dict->elements[i]->type == DICT_VECTOR &&
dict->elements[i]->lhs[0] == num )
return((UBYTE *)(dict->elements[i]->rhs));
}
}
}
num -= AM.OffsetVector;
return(VARNAME(vectors,num));
}
/*
#] FindVector :
#[ FindIndex :
*/
UBYTE *FindIndex(WORD num)
{
if ( AO.CurrentDictionary > 0 ) {
DICTIONARY *dict = AO.Dictionaries[AO.CurrentDictionary-1];
int i;
if ( dict->variables > 0 && AO.CurDictVariables == DICT_DOVARIABLES ) {
for ( i = dict->numelements-1; i >= 0; i-- ) {
if ( dict->elements[i]->type == DICT_INDEX &&
dict->elements[i]->lhs[0] == num )
return((UBYTE *)(dict->elements[i]->rhs));
}
}
}
num -= AM.OffsetIndex;
return(VARNAME(indices,num));
}
/*
#] FindIndex :
#[ FindFunction :
*/
UBYTE *FindFunction(WORD num)
{
if ( AO.CurrentDictionary > 0 ) {
DICTIONARY *dict = AO.Dictionaries[AO.CurrentDictionary-1];
int i;
if ( dict->variables > 0 && AO.CurDictVariables == DICT_DOVARIABLES ) {
for ( i = dict->numelements-1; i >= 0; i-- ) {
if ( dict->elements[i]->type == DICT_FUNCTION &&
dict->elements[i]->lhs[0] == num )
return((UBYTE *)(dict->elements[i]->rhs));
}
}
}
num -= FUNCTION;
return(VARNAME(functions,num));
}
/*
#] FindFunction :
#[ FindFunWithArgs :
*/
UBYTE *FindFunWithArgs(WORD *t)
{
if ( AO.CurrentDictionary > 0 ) {
DICTIONARY *dict = AO.Dictionaries[AO.CurrentDictionary-1];
int i, j;
if ( dict->funwith > 0
&& AO.CurDictFunWithArgs == DICT_DOFUNWITHARGS ) {
for ( i = dict->numelements-1; i >= 0; i-- ) {
if ( dict->elements[i]->type == DICT_FUNCTION_WITH_ARGUMENTS &&
(WORD)(dict->elements[i]->lhs[0]) == t[0] &&
(WORD)(dict->elements[i]->lhs[1]) == t[1] ) {
for ( j = 2; j < t[1]; j++ ) {
if ( (WORD)(dict->elements[i]->lhs[j]) != t[j] ) break;
}
if ( j >= t[1] ) return((UBYTE *)(dict->elements[i]->rhs));
}
}
}
}
return(0);
}
/*
#] FindFunWithArgs :
#[ FindExtraSymbol :
The extra symbol is constructed in the WorkSpace. This way we do not
have to worry about Malloc and freeing the object later.
The input value num is already the number of the extra symbol.
We do NOT need num = MAXVARIABLES-num;
*/
UBYTE *FindExtraSymbol(WORD num)
{
GETIDENTITY;
UBYTE *out = (UBYTE *)(AT.WorkPointer);
*out = 0;
if ( AO.CurrentDictionary > 0 ) {
DICTIONARY *dict = AO.Dictionaries[AO.CurrentDictionary-1];
int i;
if ( dict->ranges > 0 && AO.CurDictVariables == DICT_DOVARIABLES ) {
for ( i = dict->numelements-1; i >= 0; i-- ) {
if ( dict->elements[i]->type == DICT_RANGE
&& num >= dict->elements[i]->lhs[0]
&& num <= dict->elements[i]->lhs[1] ) {
/*
Now we have to translate the rhs
%# gives the number
%@ gives the number as its position in the range
*/
UBYTE *r = (UBYTE *)(dict->elements[i]->rhs);
while ( *r ) {
if ( *r == (UBYTE)'%' && ( r[1] == (UBYTE)'#'
|| r[1] == (UBYTE)'@' ) ) {
if ( r[1] == (UBYTE)'#' ) {
out = NumCopy(num,out);
}
else {
out = NumCopy(num-dict->elements[i]->lhs[0]+1,out);
}
r += 2;
}
else {
*out++ = *r++;
}
}
*out = 0;
return((UBYTE *)(AT.WorkPointer));
}
}
}
}
out = StrCopy((UBYTE *)AC.extrasym,out);
if ( AC.extrasymbols == 0 ) {
out = NumCopy(num,out);
out = StrCopy((UBYTE *)"_",out);
}
else if ( AC.extrasymbols == 1 ) {
out = AddArrayIndex(num,out);
}
return((UBYTE *)(AT.WorkPointer));
}
/*
#] FindExtraSymbol :
#[ FindDictionary :
*/
int FindDictionary(UBYTE *name)
{
int i;
for ( i = 0; i < AO.NumDictionaries; i++ ) {
if ( StrCmp(AO.Dictionaries[i]->name,name) == 0 )
return(i+1);
}
return(0);
}
/*
#] FindDictionary :
#[ AddDictionary :
*/
int AddDictionary(UBYTE *name)
{
DICTIONARY *dict;
/*
First make space for the pointer in the list.
*/
if ( AO.NumDictionaries >= AO.SizeDictionaries-1 ) {
DICTIONARY **d;
int i;
if ( AO.SizeDictionaries <= 0 ) AO.SizeDictionaries = 10;
else AO.SizeDictionaries = 2*AO.SizeDictionaries;
d = (DICTIONARY **)Malloc1(AO.SizeDictionaries*sizeof(DICTIONARY *),"Dictionaries");
for ( i = 0; i < AO.NumDictionaries; i++ ) d[i] = AO.Dictionaries[i];
if ( AO.Dictionaries != 0 ) M_free(AO.Dictionaries,"Dictionaries");
AO.Dictionaries = d;
}
/*
Now create an empty dictionary.
*/
dict = (DICTIONARY *)Malloc1(sizeof(DICTIONARY),"Dictionary");
AO.Dictionaries[AO.NumDictionaries++] = dict;
dict->elements = 0;
dict->name = strDup1(name,"DictionaryName");
dict->sizeelements = 0;
dict->numelements = 0;
dict->numbers = 0;
dict->variables = 0;
dict->characters = 0;
dict->funwith = 0;
dict->gnumelements = 0;
dict->ranges = 0;
return(AO.NumDictionaries);
}
/*
#] AddDictionary :
#[ AddToDictionary :
To be called from #add left:right
*/
int AddToDictionary(DICTIONARY *dict,UBYTE *left,UBYTE *right)
{
GETIDENTITY
CBUF *C = cbuf+AC.cbufnum;
WORD *w = AT.WorkPointer;
WORD *OldWork = AT.WorkPointer;
WORD *s, oldnumrhs = C->numrhs, oldnumlhs = C->numlhs;
WORD *ow, *ww, *mm, oldEside, *where = 0, type, number, range[3];
LONG oldcpointer;
int error = 0, sizelhs, sizerhs, i, retcode;
UBYTE *r;
DICTIONARY_ELEMENT *new;
WORD power = (WORD)('^'), times = (WORD)('*');
if ( ( left[0] == '^' && left[1] == 0 )
|| ( left[0] == '*' && left[1] == '*' && left[2] == 0 ) ) {
type = DICT_SPECIALCHARACTER;
number = 1;
where = &power;
goto TestDouble;
}
else if ( left[0] == '*' && left[1] == 0 ) {
type = DICT_SPECIALCHARACTER;
number = 1;
where = ×
goto TestDouble;
}
else if ( left[0] == '(' ) { /* range of extra symbols */
WORD x1 = 0, x2 = 0;
r = left+1;
while ( FG.cTable[*r] == 1 ) x1 = 10*x1 + *r++ - '0';
if ( *r == ',' ) {
r++;
while ( FG.cTable[*r] == 1 ) x2 = 10*x2 + *r++ - '0';
}
else x2 = x1;
number = 2;
if ( *r != ')' ) {
MesPrint("&Illegal range specification in LHS of %#add instruction.");
return(1);
}
type = DICT_RANGE;
if ( x1 <= 0 || x2 <= 0 || x1 > x2 ) {
MesPrint("&Illegal range in LHS of %#add instruction.");
return(1);
}
range[0] = x1;
range[1] = x2;
range[2] = 0;
where = range;
goto TestDouble;
}
/*
Translate the left part. Determine type.
We follow the code in CoIdExpression and then veto what we do not like.
Just make sure to pop what needs to be popped in the compiler buffer.
*/
AC.ProtoType = w;
*w++ = SUBEXPRESSION;
*w++ = SUBEXPSIZE;
*w++ = C->numrhs+1;
*w++ = 1;
*w++ = AC.cbufnum;
FILLSUB(w)
AC.WildC = w;
AC.NwildC = 0;
AT.WorkPointer = s = w + 4*AM.MaxWildcards + 8;
/*
Now read the LHS
*/
oldcpointer = AddLHS(AC.cbufnum) - C->Buffer;
if ( ( retcode = CompileAlgebra(left,LHSIDE,AC.ProtoType) ) < 0 ) { error = 1; }
else AC.ProtoType[2] = retcode;
AT.WorkPointer = s;
if ( AC.NwildC && SortWild(w,AC.NwildC) ) error = 1;
OldWork[1] = AC.WildC-OldWork;
w = AC.WildC;
AT.WorkPointer = w;
s = C->rhs[C->numrhs];
/*
We have the expression in the compiler buffers.
The main level is at lhs[numlhs]
The partial lhs (including ProtoType) is in OldWork (in WorkSpace)
We need to load the result at w after the prototype
Because these sort routines don't use the WorkSpace
there should not be a conflict
*/
if ( !error && *s == 0 ) {
IllLeft:MesPrint("&Illegal LHS in dictionary");
AC.lhdollarflag = 0;
return(1);
}
if ( !error && *(s+*s) != 0 ) {
MesPrint("&LHS in dictionary should be one term only");
return(1);
}
if ( error == 0 ) {
if ( NewSort(BHEAD0) || NewSort(BHEAD0) ) {
if ( !error ) error = 1;
return(error);
}
AN.RepPoint = AT.RepCount + 1;
ow = (WORD *)(((UBYTE *)(AT.WorkPointer)) + AM.MaxTer);
mm = s; ww = ow; i = *mm;
while ( --i >= 0 ) *ww++ = *mm++; AT.WorkPointer = ww;
AC.lhdollarflag = 0; oldEside = AR.Eside; AR.Eside = LHSIDE;
AR.Cnumlhs = C->numlhs;
if ( Generator(BHEAD ow,C->numlhs) ) {
AR.Eside = oldEside;
LowerSortLevel(); LowerSortLevel(); goto IllLeft;
}
AR.Eside = oldEside;
AT.WorkPointer = w;
if ( EndSort(BHEAD w,0) < 0 ) { LowerSortLevel(); goto IllLeft; }
if ( *w == 0 || *(w+*w) != 0 ) {
MesPrint("&LHS must be one term");
AC.lhdollarflag = 0;
return(1);
}
LowerSortLevel();
}
AT.WorkPointer = w + *w;
AC.DumNum = 0;
/*
Everything is now after OldWork. We can pop the compilerbuffer.
Next test for illegal things like a coefficient
At this point we have:
w = the term of the LHS
*/
C->Pointer = C->Buffer + oldcpointer;
C->numrhs = oldnumrhs;
C->numlhs = oldnumlhs;
AC.lhdollarflag = 0;
/*
Test for undesirables.
1: wildcards
2: sign
3: more than one term
4: composite terms
*/
if ( AC.ProtoType[1] != SUBEXPSIZE ) {
MesPrint("& Currently no wildcards allowed in dictionaries.");
return(1);
}
if ( w[w[0]-1] < 0 ) {
MesPrint("& Currently no sign allowed in dictionaries.");
return(1);
}
if ( w[w[0]] != 0 ) {
MesPrint("& More than one term in dictionary element.");
return(1);
}
if ( w[0] == w[w[0]-1]+1 ) { /* Only coefficient */
WORD *numer, *denom;
WORD nsize, dsize;
nsize = dsize = (w[w[0]-1]-1)/2;
numer = w+1;
denom = numer+nsize;
while ( numer[nsize-1] == 0 ) nsize--;
while ( denom[dsize-1] == 0 ) dsize--;
if ( dsize == 1 && denom[0] == 1 ) {
type = DICT_INTEGERNUMBER;
number = nsize;
where = numer;
}
else {
type = DICT_RATIONALNUMBER;
number = w[0];
where = w;
}
}
else {
s = w + w[0]-1;
if ( s[0] != 3 || s[-1] != 1 || s[-2] != 1 ) {
Compositeness:;
MesPrint("& Currently no composite objects allowed in dictionaries.");
return(1);
}
if ( w[0] != w[2]+4 ) goto Compositeness;
s = w+1;
switch ( *s ) {
case SYMBOL:
if ( s[1] != 4 || s[3] != 1 ) goto Compositeness;
type = DICT_SYMBOL;
number = 1;
where = s+2;
break;
case INDEX:
if ( s[1] != 3 ) goto Compositeness;
if ( s[2] < 0 ) type = DICT_VECTOR;
else type = DICT_INDEX;
number = 1;
where = s+2;
break;
default:
if ( *s < FUNCTION ) {
MesPrint("& Illegal object in dictionary.");
return(1);
}
if ( s[1] == FUNHEAD ) {
type = DICT_FUNCTION;
number = 1;
where = s;
break;
}
else {
type = DICT_FUNCTION_WITH_ARGUMENTS;
number = s[1];
where = s;
}
break;
}
}
TestDouble:;
/*
Create a new element
*/
if ( dict->numelements >= dict->sizeelements ) {
DICTIONARY_ELEMENT **d;
if ( dict->sizeelements <= 0 ) dict->sizeelements = 10;
else dict->sizeelements *= 2;
d = (DICTIONARY_ELEMENT **)Malloc1(
sizeof(DICTIONARY_ELEMENT *)*dict->sizeelements,"Dictionary elements");
for ( i = 0; i < dict->numelements; i++ )
d[i] = dict->elements[i];
if ( dict->elements ) M_free(dict->elements,"Dictionary elements");
dict->elements = d;
}
sizelhs = number+1;
sizerhs = 1; r = right; while ( *r++ ) sizerhs++;
sizerhs = (sizerhs+sizeof(WORD)-1)/sizeof(WORD)+1;
new = (DICTIONARY_ELEMENT *)Malloc1(sizeof(DICTIONARY_ELEMENT)
+sizeof(WORD)*(sizelhs+sizerhs),"Dictionary element");
new->lhs = (WORD *)(new+1);
new->rhs = new->lhs+sizelhs;
new->type = type;
new->size = number;
for ( i = 0; i < number; i++ ) new->lhs[i] = where[i];
new->lhs[i] = 0;
r = (UBYTE *)(new->rhs);
while ( *right ) {
if ( *right == '\\' && ( right[1] == '`' || right[1] == '\'' ) ) right++;
*r++ = *right++;
}
*r = 0;
dict->elements[dict->numelements++] = new;
switch ( type ) {
case DICT_INTEGERNUMBER:
case DICT_RATIONALNUMBER:
dict->numbers++; break;
case DICT_SYMBOL:
case DICT_VECTOR:
case DICT_INDEX:
case DICT_FUNCTION:
dict->variables++; break;
case DICT_FUNCTION_WITH_ARGUMENTS:
dict->funwith++; break;
case DICT_SPECIALCHARACTER:
dict->characters++; break;
case DICT_RANGE:
dict->ranges++; break;
}
AT.WorkPointer = OldWork;
return(0);
}
/*
#] AddToDictionary :
#[ UseDictionary :
*/
int UseDictionary(UBYTE *name,UBYTE *options)
{
int i;
for ( i = 0; i < AO.NumDictionaries; i++ ) {
if ( StrCmp(AO.Dictionaries[i]->name,name) == 0 ) {
AO.CurrentDictionary = i+1;
if ( SetDictionaryOptions(options) < 0 ) {
AO.CurrentDictionary = 0;
return(-1);
}
else { /* Now test whether what is requested is really there? */
return(0);
}
}
}
MesPrint("@There is no dictionary with the name %s",name);
exit(-1);
}
/*
#] UseDictionary :
#[ SetDictionaryOptions :
*/
int SetDictionaryOptions(UBYTE *options)
{
UBYTE *opt, *s, c;
int retval = 0;
s = options;
AO.CurDictNumbers = DICT_ALLNUMBERS;
AO.CurDictVariables = DICT_DOVARIABLES;
AO.CurDictSpecials = DICT_DOSPECIALS;
AO.CurDictFunWithArgs = DICT_DOFUNWITHARGS;
AO.CurDictNumberWarning = 0;
AO.CurDictNotInFunctions= 0;
AO.CurDictInDollars = DICT_NOTINDOLLARS;
while ( *s ) {
opt = s;
while ( *s && *s != ',' && *s != ' ' ) s++;
c = *s; *s = 0;
if ( opt[0] == '$' && opt[1] == 0 ) {
AO.CurDictInDollars = DICT_INDOLLARS;
}
else if ( StrICmp(opt,(UBYTE *)"nonumbers") == 0 ) {
AO.CurDictNumbers = DICT_NONUMBERS;
}
else if ( StrICmp(opt,(UBYTE *)"integersonly") == 0 ) {
AO.CurDictNumbers = DICT_INTEGERONLY;
}
else if ( StrICmp(opt,(UBYTE *)"rationalsonly") == 0 ) {
AO.CurDictNumbers = DICT_RATIONALONLY;
}
else if ( StrICmp(opt,(UBYTE *)"allnumbers") == 0 ) {
AO.CurDictNumbers = DICT_ALLNUMBERS;
}
else if ( StrICmp(opt,(UBYTE *)"novariables") == 0 ) {
AO.CurDictVariables = DICT_NOVARIABLES;
}
else if ( StrICmp(opt,(UBYTE *)"numbersonly") == 0 ) {
AO.CurDictNumbers = DICT_ALLNUMBERS;
AO.CurDictVariables = DICT_NOVARIABLES;
AO.CurDictSpecials = DICT_NOSPECIALS;
AO.CurDictFunWithArgs = DICT_NOFUNWITHARGS;
}
else if ( StrICmp(opt,(UBYTE *)"variablesonly") == 0 ) {
AO.CurDictNumbers = DICT_NONUMBERS;
AO.CurDictVariables = DICT_DOVARIABLES;
AO.CurDictSpecials = DICT_NOSPECIALS;
AO.CurDictFunWithArgs = DICT_NOFUNWITHARGS;
}
else if ( StrICmp(opt,(UBYTE *)"nospecials") == 0 ) {
AO.CurDictSpecials = DICT_NOSPECIALS;
}
else if ( StrICmp(opt,(UBYTE *)"specialsonly") == 0 ) {
AO.CurDictNumbers = DICT_NONUMBERS;
AO.CurDictVariables = DICT_NOVARIABLES;
AO.CurDictSpecials = DICT_DOSPECIALS;
AO.CurDictFunWithArgs = DICT_NOFUNWITHARGS;
}
else if ( StrICmp(opt,(UBYTE *)"nofunwithargs") == 0 ) {
AO.CurDictFunWithArgs = DICT_NOFUNWITHARGS;
}
else if ( StrICmp(opt,(UBYTE *)"funwithargsonly") == 0 ) {
AO.CurDictNumbers = DICT_NONUMBERS;
AO.CurDictVariables = DICT_NOVARIABLES;
AO.CurDictSpecials = DICT_NOSPECIALS;
AO.CurDictFunWithArgs = DICT_DOFUNWITHARGS;
}
else if ( StrICmp(opt,(UBYTE *)"warnings") == 0
|| StrICmp(opt,(UBYTE *)"warning") == 0 ) {
AO.CurDictNumberWarning = 1;
}
else if ( StrICmp(opt,(UBYTE *)"nowarnings") == 0
|| StrICmp(opt,(UBYTE *)"nowarning") == 0 ) {
AO.CurDictNumberWarning = 0;
}
else if ( StrICmp(opt,(UBYTE *)"infunctions") == 0 ) {
AO.CurDictNotInFunctions= 0;
}
else if ( StrICmp(opt,(UBYTE *)"notinfunctions") == 0 ) {
AO.CurDictNotInFunctions= 1;
}
else {
MesPrint("@ Unrecognized option in %#SetDictionary: %s",opt);
retval = -1;
}
*s = c;
if ( c == ',' ) s++;
}
return(retval);
}
/*
#] SetDictionaryOptions :
#[ UnSetDictionary :
*/
void UnSetDictionary(VOID)
{
AO.CurrentDictionary = 0;
AO.CurDictNumbers = -1;
AO.CurDictVariables = -1;
AO.CurDictSpecials = -1;
AO.CurDictFunWithArgs = -1;
AO.CurDictFunWithArgs = -1;
AO.CurDictNumberWarning = -1;
AO.CurDictNotInFunctions= -1;
}
/*
#] UnSetDictionary :
#[ RemoveDictionary :
Mostly needed for .clear
*/
void RemoveDictionary(DICTIONARY *dict)
{
int i;
if ( dict == 0 ) return;
for ( i = 0; i < AO.NumDictionaries; i++ ) {
if ( AO.Dictionaries[i] == dict ) {
for (i++; i < AO.NumDictionaries; i++ ) {
AO.Dictionaries[i-1] = AO.Dictionaries[i];
}
AO.NumDictionaries--;
goto removeit;
}
}
MesPrint("@ Dictionary not found in RemoveDictionary");
exit(-1);
removeit:;
for ( i = 0; i < dict->numelements; i++ )
M_free(dict->elements[i],"Dictionary element");
for ( i = 0; i < dict->numelements; i++ ) dict->elements[i] = 0;
if ( dict->elements ) M_free(dict->elements,"Dictionary elements");
if ( dict->name ) {
M_free(dict->name,"DictionaryName");
dict->name = 0;
}
dict->sizeelements = 0;
dict->numelements = 0;
dict->numbers = 0;
dict->variables = 0;
dict->characters = 0;
dict->funwith = 0;
dict->gnumelements = 0;
dict->ranges = 0;
}
/*
#] RemoveDictionary :
#[ ShrinkDictionary :
To be called after a .store to restore the dictionary to the state
it had at the last .global
We do not make the elements array shorter.
*/
void ShrinkDictionary(DICTIONARY *dict)
{
while ( dict->numelements > dict->gnumelements ) {
dict->numelements--;
M_free(dict->elements[dict->numelements],"Dictionary element");
dict->elements[dict->numelements] = 0;
}
}
/*
#] ShrinkDictionary :
#[ DoPreOpenDictionary :
*/
int DoPreOpenDictionary(UBYTE *s)
{
UBYTE *name;
int dict;
if ( AP.PreSwitchModes[AP.PreSwitchLevel] != EXECUTINGPRESWITCH ) return(0);
if ( AP.PreIfStack[AP.PreIfLevel] != EXECUTINGIF ) return(0);
while ( *s == ' ' ) s++;
name = s; s = SkipAName(s);
if ( *s != 0 && *s != ';' ) {
MesPrint("@proper syntax is #opendictionary name");
return(-1);
}
*s = 0;
if ( AP.OpenDictionary > 0 ) {
MesPrint("@you cannot nest #opendictionary instructions");
MesPrint("@dictionary %s is open already",
AO.Dictionaries[AP.OpenDictionary-1]->name);
return(-1);
}
if ( AO.CurrentDictionary > 0 ) {
MesPrint("@before opening a dictionary you have to first close the selected dictionary");
return(-1);
}
/*
Do we have this dictionary already?
*/
dict = FindDictionary(name);
if ( dict == 0 ) dict = AddDictionary(name);
AP.OpenDictionary = dict;
return(0);
}
/*
#] DoPreOpenDictionary :
#[ DoPreCloseDictionary :
*/
int DoPreCloseDictionary(UBYTE *s)
{
if ( AP.PreSwitchModes[AP.PreSwitchLevel] != EXECUTINGPRESWITCH ) return(0);
if ( AP.PreIfStack[AP.PreIfLevel] != EXECUTINGIF ) return(0);
while ( *s == ' ' ) s++;
if ( AP.OpenDictionary == 0 && AO.CurrentDictionary == 0 ) {
MesPrint("@you have neither an open, nor a selected dictionary");
return(-1);
}
AP.OpenDictionary = 0;
AO.CurrentDictionary = 0;
AO.CurDictNotInFunctions = 0;
return(0);
}
/*
#] DoPreCloseDictionary :
#[ DoPreUseDictionary :
*/
int DoPreUseDictionary(UBYTE *s)
{
UBYTE *options, c, *ss, *sss, *name;
if ( AP.PreSwitchModes[AP.PreSwitchLevel] != EXECUTINGPRESWITCH ) return(0);
if ( AP.PreIfStack[AP.PreIfLevel] != EXECUTINGIF ) return(0);
while ( *s == ' ' ) s++;
if ( AP.OpenDictionary > 0 ) {
MesPrint("@before selecting a dictionary you have to first close the open dictionary");
return(-1);
}
name = s; s = SkipAName(s);
ss = s; while ( *s && *s != '(' ) s++;
c = *ss; *ss = 0;
if ( c == 0 ) {
options = ss;
}
else {
options = s+1; SKIPBRA3(s)
if ( *s != ')' ) {
MesPrint("@Irregular end of %#UseDictionary instruction");
return(-1);
}
sss = s;
s++; while ( *s == ' ' || *s == '\t' || *s == ';' ) s++;
*sss = 0;
if ( *s ) {
MesPrint("@Irregular end of %#UseDictionary instruction");
return(-1);
}
}
return(UseDictionary(name,options));
}
/*
#] DoPreUseDictionary :
#[ DoPreAdd :
Syntax:
#add left :right
#add left : "right"
Adds to the currently open dictionary
*/
int DoPreAdd(UBYTE *s)
{
UBYTE *left, *right;
if ( AP.PreSwitchModes[AP.PreSwitchLevel] != EXECUTINGPRESWITCH ) return(0);
if ( AP.PreIfStack[AP.PreIfLevel] != EXECUTINGIF ) return(0);
while ( *s == ' ' ) s++;
if ( AP.OpenDictionary == 0 ) {
MesPrint("@there is no open dictionary to add to");
return(-1);
}
/*
Scan to the : and mark the left and right parts.
*/
left = s;
while ( *s && *s != ':' ) {
if ( *s == '[' ) { SKIPBRA1(s) s++; }
else if ( *s == '{' ) { SKIPBRA2(s) s++; }
else if ( *s == '(' ) { SKIPBRA3(s) s++; }
else if ( *s == ']' || *s == '}' || *s == ')' ) {
MesPrint("@unmatched brackets in #add instruction");
return(-1);
}
else s++;
}
if ( *s == 0 ) {
MesPrint("@Missing : in #add instruction");
return(-1);
}
*s++ = 0;
right = s;
while ( *s == ' ' || *s == '\t' ) s++;
if ( *s == '"' && s[1] ) {
right = s+1;
s = s+2;
while ( *s ) s++;
while ( s[-1] != '"' ) s--;
if ( s <= right ) {
MesPrint("@Irregular use of double quotes in #add instruction");
return(-1);
}
s[-1] = 0;
}
return(AddToDictionary(AO.Dictionaries[AP.OpenDictionary-1],left,right));
}
/*
#] DoPreAdd :
#[ DictToBytes :
*/
LONG DictToBytes(DICTIONARY *dict,UBYTE *buf)
{
int numelements = dict->numelements, sizeelement, i, j, x;
UBYTE *s1, *s2 = buf;
DICTIONARY_ELEMENT *e;
/*
First copy the struct
*/
s1 = (UBYTE *)dict; j = sizeof(DICTIONARY);
NCOPY(s2,s1,j)
/*
Now the elements. Put a size indicator in front of each of them.
*/
for ( i = 0; i < numelements; i++ ) {
e = dict->elements[i];
sizeelement = sizeof(DICTIONARY_ELEMENT)+(e->size+1)*sizeof(WORD);
s1 = (UBYTE *)e->rhs; x = 0;
while ( *s1 ) { s1++; x++; }
x /= sizeof(WORD);
sizeelement += (x+1) * sizeof(WORD);
s1 = (UBYTE *)(&sizeelement); j = sizeof(WORD); NCOPY(s2,s1,j)
s1 = (UBYTE *)e; j = sizeof(DICTIONARY_ELEMENT); NCOPY(s2,s1,j)
s1 = (UBYTE *)e->lhs; j = (e->size+1)*(sizeof(WORD)); NCOPY(s2,s1,j)
s1 = (UBYTE *)e->rhs; j = (x+1)*(sizeof(WORD)); NCOPY(s2,s1,j)
}
return(s2-buf);
}
/*
#] DictToBytes :
#[ DictFromBytes :
*/
DICTIONARY *DictFromBytes(UBYTE *buf)
{
DICTIONARY *dict = Malloc1(sizeof(DICTIONARY),"Dictionary");
UBYTE *s1, *s2;
int i, j, sizeelement;
DICTIONARY_ELEMENT *e;
/*
First read the dictionary itself
*/
s1 = buf;
s2 = (UBYTE *)dict; j = sizeof(DICTIONARY); NCOPY(s2,s1,j)
/*
Allocate the elements array:
*/
dict->elements = (DICTIONARY_ELEMENT **)Malloc1(
sizeof(DICTIONARY_ELEMENT *)*dict->sizeelements,"dictionary elements");
for ( i = 0; i < dict->numelements; i++ ) {
s2 = (UBYTE *)(&sizeelement); j = sizeof(WORD); NCOPY(s2,s1,j)
e = (DICTIONARY_ELEMENT *)Malloc1(sizeelement*sizeof(UBYTE),"dictionary element");
dict->elements[i] = e;
j = sizeelement; s2 = (UBYTE *)e; NCOPY(s2,s1,j)
e->lhs = (WORD *)(e+1);
e->rhs = e->lhs + e->size+1;
}
return(dict);
}
/*
#] DictFromBytes :
*/
form-master/sources/dollar.c 0000664 0000000 0000000 00000317342 13565763364 0016452 0 ustar 00root root 0000000 0000000 /** @file dollar.c
*
* The routines that deal with the dollar variables.
* The name administration is to be found in the file names.c
*/
/* #[ License : */
/*
* Copyright (C) 1984-2017 J.A.M. Vermaseren
* When using this file you are requested to refer to the publication
* J.A.M.Vermaseren "New features of FORM" math-ph/0010025
* This is considered a matter of courtesy as the development was paid
* for by FOM the Dutch physics granting agency and we would like to
* be able to track its scientific use to convince FOM of its value
* for the community.
*
* This file is part of FORM.
*
* FORM 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.
*
* FORM 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 FORM. If not, see .
*/
/* #] License : */
/*
#[ Includes :
*/
#include "form3.h"
/* EXTERNLOCK(dummylock) */
static UBYTE underscore[2] = {'_',0};
/*
#] Includes :
#[ CatchDollar :
Works out a dollar expression during compile type.
Steals it from the buffer and puts it in an assignment.
At the moment we should keep this inside the small buffer.
Later with more sort buffers we can do this better.
Par == 0 : regular assignment
par == -1: after error. Just make zero for now.
*/
int CatchDollar(int par)
{
GETIDENTITY
CBUF *C = cbuf + AC.cbufnum;
int error = 0, numterms = 0, numdollar, resetmods = 0;
LONG newsize, retval;
WORD *w, *t, n, nsize, *oldwork = AT.WorkPointer, *dbuffer;
WORD oldncmod = AN.ncmod;
DOLLARS d;
if ( AN.ncmod && ( ( AC.modmode & ALSODOLLARS ) == 0 ) ) AN.ncmod = 0;
if ( AN.ncmod && AN.cmod == 0 ) { SetMods(); resetmods = 1; }
numdollar = C->lhs[C->numlhs][2];
d = Dollars+numdollar;
if ( par == -1 ) {
d->type = DOLUNDEFINED;
cbuf[AM.dbufnum].CanCommu[numdollar] = 0;
cbuf[AM.dbufnum].NumTerms[numdollar] = 0;
if ( d->where && d->where != &(AM.dollarzero) ) M_free(d->where,"$-buffer old");
d->size = 0; d->where = &(AM.dollarzero);
cbuf[AM.dbufnum].rhs[numdollar] = d->where;
AN.ncmod = oldncmod;
if ( resetmods ) UnSetMods();
return(0);
}
#ifdef WITHMPI
/*
* The problem here is that only the master can make an assignment
* like #$a=g; where g is an expression: only the master has an access to
* the expression. So, in cases where the RHS contains expression names,
* only the master invokes Generator() and then broadcasts the result to
* the all slaves.
* Broadcasting must be performed immediately; one cannot postpone it
* to the end of the module because the dollar variable is visible
* in the current module. For the same reason, this should be done
* regardless of on/off parallel status.
* If the RHS does not contain any expression names, it can be processed
* in each slave.
*/
if ( PF.me == MASTER || !AC.RhsExprInModuleFlag ) {
#endif
EXCHINOUT
if ( NewSort(BHEAD0) ) { if ( !error ) error = 1; goto onerror; }
if ( NewSort(BHEAD0) ) {
LowerSortLevel();
if ( !error ) error = 1;
goto onerror;
}
AN.RepPoint = AT.RepCount + 1;
w = C->rhs[C->lhs[C->numlhs][5]];
while ( *w ) {
n = *w; t = oldwork;
NCOPY(t,w,n)
AT.WorkPointer = t;
AR.Cnumlhs = C->numlhs;
if ( Generator(BHEAD oldwork,C->numlhs) ) { error = 1; break; }
}
AT.WorkPointer = oldwork;
AN.tryterm = 0; /* for now */
dbuffer = 0;
if ( ( retval = EndSort(BHEAD (WORD *)((VOID *)(&dbuffer)),2) ) < 0 ) { error = 1; }
LowerSortLevel();
if ( retval <= 1 || dbuffer == 0 ) {
d->type = DOLZERO;
if ( d->where && d->where != &(AM.dollarzero) ) M_free(d->where,"$-buffer old");
d->size = 0; d->where = &(AM.dollarzero);
cbuf[AM.dbufnum].CanCommu[numdollar] = 0;
cbuf[AM.dbufnum].NumTerms[numdollar] = 0;
goto docopy2;
}
w = dbuffer;
if ( error == 0 )
while ( *w ) { w += *w; numterms++; }
else
goto onerror;
newsize = (w-dbuffer)+1;
#ifdef WITHMPI
}
if ( AC.RhsExprInModuleFlag )
/* PF_BroadcastPreDollar allocates dbuffer for slaves! */
if ( (error = PF_BroadcastPreDollar(&dbuffer, &newsize, &numterms)) != 0 )
goto onerror;
#endif
if ( newsize < MINALLOC ) newsize = MINALLOC;
newsize = ((newsize+7)/8)*8;
if ( numterms == 0 ) {
d->type = DOLZERO;
goto docopy;
}
else if ( numterms == 1 ) {
t = dbuffer;
n = *t;
nsize = t[n-1];
if ( nsize < 0 ) { nsize = -nsize; }
if ( nsize == (n-1) ) { /* numerical */
nsize = (nsize-1)/2;
w = t + 1 + nsize;
if ( *w != 1 ) goto doterms;
w++; while ( w < ( t + n - 1 ) ) { if ( *w ) break; w++; }
if ( w < ( t + n - 1 ) ) goto doterms;
d->type = DOLNUMBER;
goto docopy;
}
else if ( n == 7 && t[6] == 3 && t[5] == 1 && t[4] == 1
&& t[1] == INDEX && t[2] == 3 ) {
d->type = DOLINDEX;
d->index = t[3];
goto docopy;
}
else goto doterms;
}
else {
doterms:;
d->type = DOLTERMS;
cbuf[AM.dbufnum].CanCommu[numdollar] = numcommute(dbuffer,
&(cbuf[AM.dbufnum].NumTerms[numdollar]));
docopy:;
if ( d->where && d->where != &(AM.dollarzero) ) M_free(d->where,"$-buffer old");
d->size = newsize; d->where = dbuffer;
docopy2:;
cbuf[AM.dbufnum].rhs[numdollar] = d->where;
}
if ( C->Pointer > C->rhs[C->numrhs] ) C->Pointer = C->rhs[C->numrhs];
C->numlhs--; C->numrhs--;
onerror:
#ifdef WITHMPI
if ( PF.me == MASTER || !AC.RhsExprInModuleFlag )
#endif
BACKINOUT
AN.ncmod = oldncmod;
if ( resetmods ) UnSetMods();
return(error);
}
/*
#] CatchDollar :
#[ AssignDollar :
To be called from Generator. Assigns an expression to a $ variable.
This one is slightly different from CatchDollar.
We have no easy buffer this time.
We will have to hack our way using what we normally use for functions.
Note that in the threaded case we trust the user. That means that
we are not going to recheck whether there is a maximum, minimum or sum.
If the user says it is like that, we treat it like that.
We only check that in this centralized version MODLOCAL isn't used.
In a later stage dtype could be used for actually checking MODMAX
and MODMIN cases.
*/
int AssignDollar(PHEAD WORD *term, WORD level)
{
GETBIDENTITY
CBUF *C = cbuf+AM.rbufnum;
int numterms = 0, numdollar = C->lhs[level][2];
LONG newsize;
DOLLARS d = Dollars + numdollar;
WORD *w, *t, n, nsize, *rh = cbuf[C->lhs[level][7]].rhs[C->lhs[level][5]];
WORD *ss, *ww;
WORD olddefer, oldcompress, oldncmod = AN.ncmod;
#ifdef WITHPTHREADS
int nummodopt, dtype = -1, dw;
WORD numvalue;
if ( AN.ncmod && ( ( AC.modmode & ALSODOLLARS ) == 0 ) ) AN.ncmod = 0;
if ( AS.MultiThreaded && ( AC.mparallelflag == PARALLELFLAG ) ) {
/*
Here we come only when the module runs with more than one thread.
This must be a variable with a special module option.
For the multi-threaded version we only allow MODSUM, MODMAX and MODMIN.
*/
for ( nummodopt = 0; nummodopt < NumModOptdollars; nummodopt++ ) {
if ( numdollar == ModOptdollars[nummodopt].number ) break;
}
if ( nummodopt >= NumModOptdollars ) {
MLOCK(ErrorMessageLock);
MesPrint("Illegal attempt to change $-variable in multi-threaded module %l",AC.CModule);
MUNLOCK(ErrorMessageLock);
Terminate(-1);
}
dtype = ModOptdollars[nummodopt].type;
if ( dtype == MODLOCAL ) {
d = ModOptdollars[nummodopt].dstruct+AT.identity;
}
}
#endif
DUMMYUSE(term);
w = rh;
/*
First some shortcuts
*/
if ( *w == 0 ) {
/*
#[ Thread version : Zero case
*/
#ifdef WITHPTHREADS
if ( dtype > 0 ) {
/* LOCK(d->pthreadslockwrite); */
LOCK(d->pthreadslockread);
NewValIsZero:;
switch ( d->type ) {
case DOLZERO: goto NoChangeZero;
case DOLNUMBER:
case DOLTERMS:
if ( ( dw = d->where[0] ) > 0 && d->where[dw] != 0 ) {
break; /* was not a single number. Trust the user */
}
if ( dtype == MODMAX && d->where[dw-1] >= 0 ) goto NoChangeZero;
if ( dtype == MODMIN && d->where[dw-1] <= 0 ) goto NoChangeZero;
break;
default:
numvalue = DolToNumber(BHEAD numdollar);
if ( AN.ErrorInDollar != 0 ) break;
if ( dtype == MODMAX && numvalue >= 0 ) goto NoChangeZero;
if ( dtype == MODMIN && numvalue <= 0 ) goto NoChangeZero;
break;
}
d->type = DOLZERO;
d->where[0] = 0;
cbuf[AM.dbufnum].CanCommu[numdollar] = 0;
cbuf[AM.dbufnum].NumTerms[numdollar] = 0;
NoChangeZero:;
CleanDollarFactors(d);
/* UNLOCK(d->pthreadslockwrite); */
UNLOCK(d->pthreadslockread);
AN.ncmod = oldncmod;
return(0);
}
#endif
/*
#] Thread version :
*/
d->type = DOLZERO;
d->where[0] = 0;
cbuf[AM.dbufnum].CanCommu[numdollar] = 0;
cbuf[AM.dbufnum].NumTerms[numdollar] = 0;
CleanDollarFactors(d);
AN.ncmod = oldncmod;
return(0);
}
else if ( *w == 4 && w[4] == 0 && w[2] == 1 ) {
/*
#[ Thread version : New value is 'single precision'
*/
#ifdef WITHPTHREADS
if ( dtype > 0 ) {
/* LOCK(d->pthreadslockwrite); */
LOCK(d->pthreadslockread);
if ( d->size < MINALLOC ) {
WORD oldsize, *oldwhere, i;
oldsize = d->size; oldwhere = d->where;
d->size = MINALLOC;
d->where = (WORD *)Malloc1(d->size*sizeof(WORD),"dollar contents");
cbuf[AM.dbufnum].rhs[numdollar] = d->where;
if ( oldsize > 0 ) {
for ( i = 0; i < oldsize; i++ ) d->where[i] = oldwhere[i];
}
else d->where[0] = 0;
if ( oldwhere && oldwhere != &(AM.dollarzero) ) M_free(oldwhere,"dollar contents");
}
switch ( d->type ) {
case DOLZERO:
HandleDolZero:;
if ( dtype == MODMAX && w[3] <= 0 ) goto NoChangeOne;
if ( dtype == MODMIN && w[3] >= 0 ) goto NoChangeOne;
break;
case DOLNUMBER:
case DOLTERMS:
if ( ( dw = d->where[0] ) > 0 && d->where[dw] != 0 ) {
break; /* was not a single number. Trust the user */
}
if ( dtype == MODMAX && CompCoef(d->where,w) >= 0 ) goto NoChangeOne;
if ( dtype == MODMIN && CompCoef(d->where,w) <= 0 ) goto NoChangeOne;
break;
default:
{
/*
Note that we convert the type for the next time around.
*/
WORD extraterm[4];
numvalue = DolToNumber(BHEAD numdollar);
if ( AN.ErrorInDollar != 0 ) break;
if ( numvalue == 0 ) {
d->type = DOLZERO;
d->where[0] = 0;
cbuf[AM.dbufnum].CanCommu[numdollar] = 0;
cbuf[AM.dbufnum].NumTerms[numdollar] = 0;
goto HandleDolZero;
}
d->where[0] = extraterm[0] = 4;
d->where[1] = extraterm[1] = ABS(numvalue);
d->where[2] = extraterm[2] = 1;
d->where[3] = extraterm[3] = numvalue > 0 ? 3 : -3;
d->where[4] = 0;
d->type = DOLNUMBER;
if ( dtype == MODMAX && CompCoef(extraterm,w) >= 0 ) goto NoChangeOne;
if ( dtype == MODMIN && CompCoef(extraterm,w) <= 0 ) goto NoChangeOne;
break;
}
}
d->where[0] = w[0];
d->where[1] = w[1];
d->where[2] = w[2];
d->where[3] = w[3];
d->where[4] = 0;
d->type = DOLNUMBER;
cbuf[AM.dbufnum].CanCommu[numdollar] = 0;
cbuf[AM.dbufnum].NumTerms[numdollar] = 1;
NoChangeOne:;
CleanDollarFactors(d);
/* UNLOCK(d->pthreadslockwrite); */
UNLOCK(d->pthreadslockread);
AN.ncmod = oldncmod;
return(0);
}
#endif
/*
#] Thread version :
*/
if ( d->size < MINALLOC ) {
if ( d->where && d->where != &(AM.dollarzero) ) M_free(d->where,"dollar contents");
d->size = MINALLOC;
d->where = (WORD *)Malloc1(d->size*sizeof(WORD),"dollar contents");
cbuf[AM.dbufnum].rhs[numdollar] = d->where;
}
d->where[0] = w[0];
d->where[1] = w[1];
d->where[2] = w[2];
d->where[3] = w[3];
d->where[4] = 0;
d->type = DOLNUMBER;
cbuf[AM.dbufnum].CanCommu[numdollar] = 0;
cbuf[AM.dbufnum].NumTerms[numdollar] = 1;
CleanDollarFactors(d);
AN.ncmod = oldncmod;
return(0);
}
/*
Now the real evaluation.
In the case of threads and MODSUM this requires an immediate lock.
Otherwise the lock could be placed later.
*/
#ifdef WITHPTHREADS
if ( dtype == MODSUM ) {
/* LOCK(d->pthreadslockwrite); */
LOCK(d->pthreadslockread);
}
#endif
CleanDollarFactors(d);
/*
The following case cannot occur. We treated it already
if ( *w == 0 ) {
ss = 0; numterms = 0; newsize = 0;
olddefer = AR.DeferFlag; AR.DeferFlag = 0;
oldcompress = AR.NoCompress; AR.NoCompress = 1;
}
else
*/
{
/*
New value is an expression that has to be evaluated first
This is all generic. It won't foliate due to the sort level
*/
if ( NewSort(BHEAD0) ) {
AN.ncmod = oldncmod;
return(1);
}
olddefer = AR.DeferFlag; AR.DeferFlag = 0;
oldcompress = AR.NoCompress; AR.NoCompress = 1;
while ( *w ) {
n = *w; t = ww = AT.WorkPointer;
NCOPY(t,w,n);
AT.WorkPointer = t;
if ( Generator(BHEAD ww,AR.Cnumlhs) ) {
AT.WorkPointer = ww;
LowerSortLevel();
AR.DeferFlag = olddefer;
AN.ncmod = oldncmod;
return(1);
}
AT.WorkPointer = ww;
}
AN.tryterm = 0; /* for now */
if ( ( newsize = EndSort(BHEAD (WORD *)((VOID *)(&ss)),2) ) < 0 ) {
AN.ncmod = oldncmod;
return(1);
}
numterms = 0; t = ss; while ( *t ) { numterms++; t += *t; }
}
#ifdef WITHPTHREADS
if ( dtype != MODSUM ) {
/* LOCK(d->pthreadslockwrite); */
LOCK(d->pthreadslockread);
}
#endif
if ( numterms == 0 ) {
/*
the new value evaluates to zero
*/
#ifdef WITHPTHREADS
if ( dtype == MODMAX || dtype == MODMIN ) {
if ( ss ) { M_free(ss,"Sort of $"); ss = 0; }
AR.DeferFlag = olddefer; AR.NoCompress = oldcompress;
goto NewValIsZero;
}
else
#endif
{
if ( d->where && d->where != &(AM.dollarzero) ) M_free(d->where,"dollar contents");
d->where = &(AM.dollarzero);
d->size = 0;
cbuf[AM.dbufnum].rhs[numdollar] = 0;
cbuf[AM.dbufnum].CanCommu[numdollar] = 0;
cbuf[AM.dbufnum].NumTerms[numdollar] = 0;
d->type = DOLZERO;
}
if ( ss ) { M_free(ss,"Sort of $"); ss = 0; }
}
else {
/*
#[ Thread version :
*/
#ifdef WITHPTHREADS
if ( dtype == MODMAX || dtype == MODMIN ) {
if ( numterms == 1 && ( *ss-1 == ABS(ss[*ss-1]) ) ) { /* is number */
switch ( d->type ) {
case DOLZERO:
HandleDolZero1:;
if ( dtype == MODMAX && ss[*ss-1] > 0 ) break;
if ( dtype == MODMIN && ss[*ss-1] < 0 ) break;
if ( ss ) { M_free(ss,"Sort of $"); ss = 0; }
AR.DeferFlag = olddefer; AR.NoCompress = oldcompress;
goto NoChange;
case DOLTERMS:
case DOLNUMBER:
if ( ( dw = d->where[0] ) > 0 && d->where[dw] != 0 ) break;
if ( dtype == MODMAX && CompCoef(ss,d->where) > 0 ) break;
if ( dtype == MODMIN && CompCoef(ss,d->where) < 0 ) break;
if ( ss ) { M_free(ss,"Sort of $"); ss = 0; }
AR.DeferFlag = olddefer; AR.NoCompress = oldcompress;
goto NoChange;
default: {
WORD extraterm[4];
numvalue = DolToNumber(BHEAD numdollar);
if ( AN.ErrorInDollar != 0 ) break;
if ( numvalue == 0 ) {
d->type = DOLZERO;
d->where[0] = 0;
cbuf[AM.dbufnum].CanCommu[numdollar] = 0;
cbuf[AM.dbufnum].NumTerms[numdollar] = 0;
goto HandleDolZero1;
}
d->where[0] = extraterm[0] = 4;
d->where[1] = extraterm[1] = ABS(numvalue);
d->where[2] = extraterm[2] = 1;
d->where[3] = extraterm[3] = numvalue > 0 ? 3 : -3;
d->where[4] = 0;
d->type = DOLNUMBER;
if ( dtype == MODMAX && CompCoef(ss,extraterm) > 0 ) break;
if ( dtype == MODMIN && CompCoef(ss,extraterm) < 0 ) break;
if ( ss ) { M_free(ss,"Sort of $"); ss = 0; }
AR.DeferFlag = olddefer; AR.NoCompress = oldcompress;
goto NoChange;
}
}
}
else {
if ( ss ) { M_free(ss,"Sort of $"); ss = 0; }
AR.DeferFlag = olddefer; AR.NoCompress = oldcompress;
goto NoChange;
}
}
#endif
/*
#] Thread version :
*/
d->type = DOLTERMS;
if ( d->where && d->where != &(AM.dollarzero) ) { M_free(d->where,"dollar contents"); d->where = 0; }
d->size = newsize + 1;
d->where = ss;
cbuf[AM.dbufnum].rhs[numdollar] = w = d->where;
}
AR.DeferFlag = olddefer; AR.NoCompress = oldcompress;
/*
Now find the special cases
*/
if ( numterms == 0 ) {
d->type = DOLZERO;
}
else if ( numterms == 1 ) {
t = d->where;
n = *t;
nsize = t[n-1];
if ( nsize < 0 ) { nsize = -nsize; }
if ( nsize == (n-1) ) {
nsize = (nsize-1)/2;
w = t + 1 + nsize;
if ( *w == 1 ) {
w++; while ( w < ( t + n - 1 ) ) { if ( *w ) break; w++; }
if ( w >= ( t + n - 1 ) ) d->type = DOLNUMBER;
}
}
else if ( n == 7 && t[6] == 3 && t[5] == 1 && t[4] == 1
&& t[1] == INDEX && t[2] == 3 ) {
d->type = DOLINDEX;
d->index = t[3];
}
}
if ( d->type == DOLTERMS ) {
cbuf[AM.dbufnum].CanCommu[numdollar] = numcommute(d->where,
&(cbuf[AM.dbufnum].NumTerms[numdollar]));
}
else {
cbuf[AM.dbufnum].CanCommu[numdollar] = 0;
cbuf[AM.dbufnum].NumTerms[numdollar] = 1;
}
#ifdef WITHPTHREADS
NoChange:;
/* UNLOCK(d->pthreadslockwrite); */
UNLOCK(d->pthreadslockread);
#endif
AN.ncmod = oldncmod;
return(0);
}
/*
#] AssignDollar :
#[ WriteDollarToBuffer :
Takes the numbered dollar expression and writes it to output.
We catch however the output in a buffer and return its address.
This routine is needed when we need a text representation of
a dollar expression like for the construction `$name' in the preprocessor.
If par==0 we leave the current printing mode.
If par==1 we insist on normal mode
*/
UBYTE *WriteDollarToBuffer(WORD numdollar, WORD par)
{
DOLLARS d = Dollars+numdollar;
UBYTE *s, *oldcurbufwrt = AO.CurBufWrt;
WORD *t, lbrac = 0, first = 0, arg[2], oldOutputMode = AC.OutputMode;
WORD oldinfbrack = AO.InFbrack;
int error = 0;
int dict = AO.CurrentDictionary;
AO.DollarOutSizeBuffer = 32;
AO.DollarOutBuffer = (UBYTE *)Malloc1(AO.DollarOutSizeBuffer,"DollarOutBuffer");
AO.DollarInOutBuffer = 1;
AO.PrintType = 1;
AO.InFbrack = 0;
s = AO.DollarOutBuffer;
*s = 0;
if ( par > 0 && AO.CurDictInDollars == 0 ) {
AC.OutputMode = NORMALFORMAT;
AO.CurrentDictionary = 0;
}
else {
AO.CurBufWrt = (UBYTE *)underscore;
}
AO.OutInBuffer = 1;
switch ( d->type ) {
case DOLARGUMENT:
WriteArgument(d->where);
break;
case DOLSUBTERM:
WriteSubTerm(d->where,1);
break;
case DOLNUMBER:
case DOLTERMS:
t = d->where;
while ( *t ) {
if ( WriteTerm(t,&lbrac,first,PRINTON,0) ) {
error = 1; break;
}
t += *t;
}
break;
case DOLWILDARGS:
t = d->where+1;
while ( *t ) {
WriteArgument(t);
NEXTARG(t)
if ( *t ) TokenToLine((UBYTE *)(","));
}
break;
case DOLINDEX:
arg[0] = -INDEX; arg[1] = d->index;
WriteArgument(arg);
break;
case DOLZERO:
*s++ = '0'; *s = 0;
AO.DollarInOutBuffer = 1;
break;
case DOLUNDEFINED:
*s = 0;
AO.DollarInOutBuffer = 1;
break;
}
AC.OutputMode = oldOutputMode;
AO.OutInBuffer = 0;
AO.InFbrack = oldinfbrack;
AO.CurBufWrt = oldcurbufwrt;
AO.CurrentDictionary = dict;
if ( error ) {
MLOCK(ErrorMessageLock);
MesPrint("&Illegal dollar object for writing");
MUNLOCK(ErrorMessageLock);
M_free(AO.DollarOutBuffer,"DollarOutBuffer");
AO.DollarOutBuffer = 0;
AO.DollarOutSizeBuffer = 0;
return(0);
}
return(AO.DollarOutBuffer);
}
/*
#] WriteDollarToBuffer :
#[ WriteDollarFactorToBuffer :
Takes the numbered dollar expression and writes it to output.
We catch however the output in a buffer and return its address.
This routine is needed when we need a text representation of
a dollar expression like for the construction `$name' in the preprocessor.
If par==0 we leave the current printing mode.
If par==1 we insist on normal mode
*/
UBYTE *WriteDollarFactorToBuffer(WORD numdollar, WORD numfac, WORD par)
{
DOLLARS d = Dollars+numdollar;
UBYTE *s, *oldcurbufwrt = AO.CurBufWrt;
WORD *t, lbrac = 0, first = 0, n[5], oldOutputMode = AC.OutputMode;
WORD oldinfbrack = AO.InFbrack;
int error = 0;
int dict = AO.CurrentDictionary;
if ( numfac > d->nfactors || numfac < 0 ) {
MLOCK(ErrorMessageLock);
MesPrint("&Illegal factor number for this dollar variable: %d",numfac);
MesPrint("&There are %d factors",d->nfactors);
MUNLOCK(ErrorMessageLock);
return(0);
}
AO.DollarOutSizeBuffer = 32;
AO.DollarOutBuffer = (UBYTE *)Malloc1(AO.DollarOutSizeBuffer,"DollarOutBuffer");
AO.DollarInOutBuffer = 1;
AO.PrintType = 1;
AO.InFbrack = 0;
s = AO.DollarOutBuffer;
*s = 0;
if ( par > 0 ) {
AC.OutputMode = NORMALFORMAT;
AO.CurrentDictionary = 0;
}
else {
AO.CurBufWrt = (UBYTE *)underscore;
}
AO.OutInBuffer = 1;
if ( numfac == 0 ) { /* write the number d->nfactors */
n[0] = 4; n[1] = d->nfactors; n[2] = 1; n[3] = 3; n[4] = 0; t = n;
}
else if ( numfac == 1 && d->factors == 0 ) { /* Here d->factors is zero and d->where is fine */
t = d->where;
}
else if ( d->factors[numfac-1].where == 0 ) { /* write the value */
if ( d->factors[numfac-1].value < 0 ) {
n[0] = 4; n[1] = -d->factors[numfac-1].value; n[2] = 1; n[3] = -3; n[4] = 0; t = n;
}
else {
n[0] = 4; n[1] = d->factors[numfac-1].value; n[2] = 1; n[3] = 3; n[4] = 0; t = n;
}
}
else { t = d->factors[numfac-1].where; }
while ( *t ) {
if ( WriteTerm(t,&lbrac,first,PRINTON,0) ) {
error = 1; break;
}
t += *t;
}
AC.OutputMode = oldOutputMode;
AO.OutInBuffer = 0;
AO.InFbrack = oldinfbrack;
AO.CurBufWrt = oldcurbufwrt;
AO.CurrentDictionary = dict;
if ( error ) {
MLOCK(ErrorMessageLock);
MesPrint("&Illegal dollar object for writing");
MUNLOCK(ErrorMessageLock);
M_free(AO.DollarOutBuffer,"DollarOutBuffer");
AO.DollarOutBuffer = 0;
AO.DollarOutSizeBuffer = 0;
return(0);
}
return(AO.DollarOutBuffer);
}
/*
#] WriteDollarFactorToBuffer :
#[ AddToDollarBuffer :
*/
void AddToDollarBuffer(UBYTE *s)
{
int i;
UBYTE *t = s, *u, *newdob;
LONG j;
while ( *t ) { t++; }
i = t - s;
while ( i + AO.DollarInOutBuffer >= AO.DollarOutSizeBuffer ) {
j = AO.DollarInOutBuffer;
AO.DollarOutSizeBuffer *= 2;
t = AO.DollarOutBuffer;
newdob = (UBYTE *)Malloc1(AO.DollarOutSizeBuffer,"DollarOutBuffer");
u = newdob;
while ( --j >= 0 ) *u++ = *t++;
M_free(AO.DollarOutBuffer,"DollarOutBuffer");
AO.DollarOutBuffer = newdob;
}
t = AO.DollarOutBuffer + AO.DollarInOutBuffer-1;
while ( t == AO.DollarOutBuffer && ( *s == '+' || *s == ' ' ) ) s++;
i = 0;
if ( AO.CurrentDictionary == 0 ) {
while ( *s ) {
if ( *s == ' ' ) { s++; continue; }
*t++ = *s++; i++;
}
}
else {
while ( *s ) { *t++ = *s++; i++; }
}
*t = 0;
AO.DollarInOutBuffer += i;
}
/*
#] AddToDollarBuffer :
#[ TermAssign :
This routine is called from a piece of code in Normalize that has been
commented out.
*/
void TermAssign(WORD *term)
{
DOLLARS d;
WORD *t, *tstop, *astop, *w, *m;
WORD i, newsize;
for (;;) {
astop = term + *term;
tstop = astop - ABS(astop[-1]);
t = term + 1;
while ( t < tstop ) {
if ( *t == AM.termfunnum && t[1] == FUNHEAD+2
&& t[FUNHEAD] == -DOLLAREXPRESSION ) {
d = Dollars + t[FUNHEAD+1];
newsize = *term - FUNHEAD - 1;
if ( newsize < MINALLOC ) newsize = MINALLOC;
newsize = ((newsize+7)/8)*8;
if ( d->size > 2*newsize && d->size > 1000 ) {
if ( d->where && d->where != &(AM.dollarzero) ) M_free(d->where,"dollar contents");
d->size = 0;
d->where = &(AM.dollarzero);
}
if ( d->size < newsize ) {
if ( d->where && d->where != &(AM.dollarzero) ) M_free(d->where,"dollar contents");
d->size = newsize;
d->where = (WORD *)Malloc1(newsize*sizeof(WORD),"dollar contents");
}
cbuf[AM.dbufnum].rhs[t[FUNHEAD+1]] = w = d->where;
m = term;
while ( m < t ) *w++ = *m++;
m += t[1];
while ( m < tstop ) {
if ( *m == AM.termfunnum && m[1] == FUNHEAD+2
&& m[FUNHEAD] == -DOLLAREXPRESSION ) { m += m[1]; }
else {
i = m[1];
while ( --i >= 0 ) *w++ = *m++;
}
}
while ( m < astop ) *w++ = *m++;
*(d->where) = w - d->where;
*w = 0;
d->type = DOLTERMS;
w = t; m = t + t[1];
while ( m < astop ) *w++ = *m++;
*term = w - term;
break;
}
t += t[1];
}
if ( t >= tstop ) return;
}
}
/*
#] TermAssign :
#[ PutTermInDollar :
We assume here that the dollar is local.
*/
int PutTermInDollar(WORD *term, WORD numdollar)
{
DOLLARS d = Dollars+numdollar;
WORD i;
if ( term == 0 || *term == 0 ) {
d->type = DOLZERO;
return(0);
}
if ( d->size < *term || d->size > 2*term[0] || d->where == 0 ) {
if ( d->size > 0 && d->where ) {
M_free(d->where,"dollar contents");
}
d->where = Malloc1((term[0]+1)*sizeof(WORD),"dollar contents");
d->size = term[0]+1;
}
d->type = DOLTERMS;
for ( i = 0; i < term[0]; i++ ) d->where[i] = term[i];
d->where[i] = 0;
return(0);
}
/*
#] PutTermInDollar :
#[ WildDollars :
Note that we cannot upload wildcards into dollar variables when WITHPTHREADS.
LONG alloccounter = 0;
*/
void WildDollars(PHEAD WORD *term)
{
GETBIDENTITY
DOLLARS d;
WORD *m, *t, *w, *ww, *orig = 0, *wildvalue, *wildstop;
int numdollar;
LONG weneed, i;
struct DoLlArS;
#ifdef WITHPTHREADS
int dtype = -1;
#endif
/* alloccounter++; */
if ( term == 0 ) {
m = wildvalue = AN.WildValue;
wildstop = AN.WildStop;
}
else {
ww = term + *term; ww -= ABS(ww[-1]); w = term+1;
while ( w < ww && *w != SUBEXPRESSION ) w += w[1];
if ( w >= ww ) return;
wildstop = w + w[1];
w += SUBEXPSIZE;
wildvalue = m = w;
}
while ( m < wildstop ) {
if ( *m != LOADDOLLAR ) { m += m[1]; continue; }
t = m - 4;
while ( *t == LOADDOLLAR || *t == FROMSET || *t == SETTONUM ) t -= 4;
if ( t < wildvalue ) {
MLOCK(ErrorMessageLock);
MesPrint("&Serious bug in wildcard prototype. Found in WildDollars");
MUNLOCK(ErrorMessageLock);
Terminate(-1);
}
numdollar = m[2];
d = Dollars + numdollar;
#ifdef WITHPTHREADS
{
int nummodopt;
dtype = -1;
if ( AS.MultiThreaded && ( AC.mparallelflag == PARALLELFLAG ) ) {
for ( nummodopt = 0; nummodopt < NumModOptdollars; nummodopt++ ) {
if ( numdollar == ModOptdollars[nummodopt].number ) break;
}
if ( nummodopt < NumModOptdollars ) {
dtype = ModOptdollars[nummodopt].type;
if ( dtype == MODLOCAL ) {
d = ModOptdollars[nummodopt].dstruct+AT.identity;
}
else {
MLOCK(ErrorMessageLock);
MesPrint("&Illegal attempt to use $-variable %s in module %l",
DOLLARNAME(Dollars,numdollar),AC.CModule);
MUNLOCK(ErrorMessageLock);
Terminate(-1);
}
}
}
}
#endif
/*
The value of this wildcard goes into our $-variable
First compute the space we need.
*/
switch ( *t ) {
case SYMTONUM:
weneed = 5;
break;
case SYMTOSYM:
weneed = 9;
break;
case SYMTOSUB:
case VECTOSUB:
case INDTOSUB:
orig = cbuf[AT.ebufnum].rhs[t[3]];
w = orig; while ( *w ) w += *w;
weneed = w - orig + 1;
break;
case VECTOMIN:
case VECTOVEC:
case INDTOIND:
weneed = 8;
break;
case FUNTOFUN:
weneed = FUNHEAD+5;
break;
case ARGTOARG:
orig = cbuf[AT.ebufnum].rhs[t[3]];
if ( *orig > 0 ) weneed = *orig+2;
else {
w = orig+1; while ( *w ) { NEXTARG(w) }
weneed = w - orig + 1;
}
break;
default:
weneed = MINALLOC;
break;
}
if ( weneed < MINALLOC ) weneed = MINALLOC;
weneed = ((weneed+7)/8)*8;
if ( d->size > 2*weneed && d->size > 1000 ) {
if ( d->where && d->where != &(AM.dollarzero) ) M_free(d->where,"dollarspace");
d->where = &(AM.dollarzero);
d->size = 0;
}
if ( d->size < weneed ) {
if ( d->where && d->where != &(AM.dollarzero) ) M_free(d->where,"dollarspace");
d->where = (WORD *)Malloc1(weneed*sizeof(WORD),"dollarspace");
d->size = weneed;
}
/*
It is not clear what the following code does for TFORM
if ( dtype != MODLOCAL ) {
*/
cbuf[AM.dbufnum].CanCommu[numdollar] = 0;
cbuf[AM.dbufnum].NumTerms[numdollar] = 1;
/* cbuf[AM.dbufnum].rhs[numdollar] = d->where; */
cbuf[AM.dbufnum].rhs[numdollar] = (WORD *)(1);
/*
}
Now load up the value of the wildcard in compiler buffer format
*/
w = d->where;
d->type = DOLTERMS;
switch ( *t ) {
case SYMTONUM:
d->where[0] = 4; d->where[2] = 1;
if ( t[3] >= 0 ) { d->where[1] = t[3]; d->where[3] = 3; }
else { d->where[1] = -t[3]; d->where[3] = -3; }
if ( t[3] == 0 ) { d->type = DOLZERO; d->where[0] = 0; }
else { d->type = DOLNUMBER; d->where[4] = 0; }
break;
case SYMTOSYM:
*w++ = 8;
*w++ = SYMBOL;
*w++ = 4;
*w++ = t[3];
*w++ = 1;
*w++ = 1;
*w++ = 1;
*w++ = 3;
*w = 0;
break;
case SYMTOSUB:
case VECTOSUB:
case INDTOSUB:
while ( *orig ) {
i = *orig; while ( --i >= 0 ) *w++ = *orig++;
}
*w = 0;
/*
And then we have to fix up CanCommu
*/
break;
case VECTOMIN:
*w++ = 7; *w++ = INDEX; *w++ = 3; *w++ = t[3];
*w++ = 1; *w++ = 1; *w++ = -3; *w = 0;
break;
case VECTOVEC:
*w++ = 7; *w++ = INDEX; *w++ = 3; *w++ = t[3];
*w++ = 1; *w++ = 1; *w++ = 3; *w = 0;
break;
case INDTOIND:
d->type = DOLINDEX; d->index = t[3]; *w = 0;
break;
case FUNTOFUN:
*w++ = FUNHEAD+4; *w++ = t[3]; *w++ = FUNHEAD;
FILLFUN(w)
*w++ = 1; *w++ = 1; *w++ = 3; *w = 0;
break;
case ARGTOARG:
if ( *orig > 0 ) ww = orig + *orig + 1;
else {
ww = orig+1; while ( *ww ) { NEXTARG(ww) }
}
while ( orig < ww ) *w++ = *orig++;
*w = 0;
d->type = DOLWILDARGS;
break;
default:
d->type = DOLUNDEFINED;
break;
}
m += m[1];
}
}
/*
#] WildDollars :
#[ DolToTensor : with LOCK
*/
WORD DolToTensor(PHEAD WORD numdollar)
{
GETBIDENTITY
DOLLARS d = Dollars + numdollar;
WORD retval;
#ifdef WITHPTHREADS
int nummodopt, dtype = -1;
if ( AS.MultiThreaded && ( AC.mparallelflag == PARALLELFLAG ) ) {
for ( nummodopt = 0; nummodopt < NumModOptdollars; nummodopt++ ) {
if ( numdollar == ModOptdollars[nummodopt].number ) break;
}
if ( nummodopt < NumModOptdollars ) {
dtype = ModOptdollars[nummodopt].type;
if ( dtype == MODLOCAL ) {
d = ModOptdollars[nummodopt].dstruct+AT.identity;
}
else {
LOCK(d->pthreadslockread);
}
}
}
#endif
AN.ErrorInDollar = 0;
if ( d->type == DOLTERMS && d->where[0] == FUNHEAD+4 &&
d->where[FUNHEAD+4] == 0 && d->where[FUNHEAD+3] == 3 &&
d->where[FUNHEAD+2] == 1 && d->where[FUNHEAD+1] == 1 &&
d->where[1] >= FUNCTION && d->where[1] < FUNCTION+WILDOFFSET
&& functions[d->where[1]-FUNCTION].spec >= TENSORFUNCTION ) {
retval = d->where[1];
}
else if ( d->type == DOLARGUMENT &&
d->where[0] <= -FUNCTION && d->where[0] > -FUNCTION-WILDOFFSET
&& functions[-d->where[0]-FUNCTION].spec >= TENSORFUNCTION ) {
retval = -d->where[0];
}
else if ( d->type == DOLWILDARGS && d->where[0] == 0
&& d->where[1] <= -FUNCTION && d->where[1] > -FUNCTION-WILDOFFSET
&& d->where[2] == 0
&& functions[-d->where[1]-FUNCTION].spec >= TENSORFUNCTION ) {
retval = -d->where[1];
}
else if ( d->type == DOLSUBTERM &&
d->where[0] >= FUNCTION && d->where[0] < FUNCTION+WILDOFFSET
&& functions[d->where[0]-FUNCTION].spec >= TENSORFUNCTION ) {
retval = d->where[0];
}
else {
AN.ErrorInDollar = 1;
retval = 0;
}
#ifdef WITHPTHREADS
if ( dtype > 0 && dtype != MODLOCAL ) { UNLOCK(d->pthreadslockread); }
#endif
return(retval);
}
/*
#] DolToTensor :
#[ DolToFunction : with LOCK
*/
WORD DolToFunction(PHEAD WORD numdollar)
{
GETBIDENTITY
DOLLARS d = Dollars + numdollar;
WORD retval;
#ifdef WITHPTHREADS
int nummodopt, dtype = -1;
if ( AS.MultiThreaded && ( AC.mparallelflag == PARALLELFLAG ) ) {
for ( nummodopt = 0; nummodopt < NumModOptdollars; nummodopt++ ) {
if ( numdollar == ModOptdollars[nummodopt].number ) break;
}
if ( nummodopt < NumModOptdollars ) {
dtype = ModOptdollars[nummodopt].type;
if ( dtype == MODLOCAL ) {
d = ModOptdollars[nummodopt].dstruct+AT.identity;
}
else {
LOCK(d->pthreadslockread);
}
}
}
#endif
AN.ErrorInDollar = 0;
if ( d->type == DOLTERMS && d->where[0] == FUNHEAD+4 &&
d->where[FUNHEAD+4] == 0 && d->where[FUNHEAD+3] == 3 &&
d->where[FUNHEAD+2] == 1 && d->where[FUNHEAD+1] == 1 &&
d->where[1] >= FUNCTION && d->where[1] < FUNCTION+WILDOFFSET ) {
retval = d->where[1];
}
else if ( d->type == DOLARGUMENT &&
d->where[0] <= -FUNCTION && d->where[0] > -FUNCTION-WILDOFFSET ) {
retval = -d->where[0];
}
else if ( d->type == DOLWILDARGS && d->where[0] == 0
&& d->where[1] <= -FUNCTION && d->where[1] > -FUNCTION-WILDOFFSET
&& d->where[2] == 0 ) {
retval = -d->where[1];
}
else if ( d->type == DOLSUBTERM &&
d->where[0] >= FUNCTION && d->where[0] < FUNCTION+WILDOFFSET ) {
retval = d->where[0];
}
else {
AN.ErrorInDollar = 1;
retval = 0;
}
#ifdef WITHPTHREADS
if ( dtype > 0 && dtype != MODLOCAL ) { UNLOCK(d->pthreadslockread); }
#endif
return(retval);
}
/*
#] DolToFunction :
#[ DolToVector : with LOCK
*/
WORD DolToVector(PHEAD WORD numdollar)
{
GETBIDENTITY
DOLLARS d = Dollars + numdollar;
WORD retval;
#ifdef WITHPTHREADS
int nummodopt, dtype = -1;
if ( AS.MultiThreaded && ( AC.mparallelflag == PARALLELFLAG ) ) {
for ( nummodopt = 0; nummodopt < NumModOptdollars; nummodopt++ ) {
if ( numdollar == ModOptdollars[nummodopt].number ) break;
}
if ( nummodopt < NumModOptdollars ) {
dtype = ModOptdollars[nummodopt].type;
if ( dtype == MODLOCAL ) {
d = ModOptdollars[nummodopt].dstruct+AT.identity;
}
else {
LOCK(d->pthreadslockread);
}
}
}
#endif
AN.ErrorInDollar = 0;
if ( d->type == DOLINDEX && d->index < 0 ) {
retval = d->index;
}
else if ( d->type == DOLARGUMENT && ( d->where[0] == -VECTOR
|| d->where[0] == -MINVECTOR ) ) {
retval = d->where[1];
}
else if ( d->type == DOLSUBTERM && d->where[0] == INDEX
&& d->where[1] == 3 && d->where[2] < 0 ) {
retval = d->where[2];
}
else if ( d->type == DOLTERMS && d->where[0] == 7 &&
d->where[7] == 0 && d->where[6] == 3 &&
d->where[5] == 1 && d->where[4] == 1 &&
d->where[1] >= INDEX && d->where[3] < 0 ) {
retval = d->where[3];
}
else if ( d->type == DOLWILDARGS && d->where[0] == 0
&& ( d->where[1] == -VECTOR || d->where[1] == -MINVECTOR )
&& d->where[3] == 0 ) {
retval = d->where[2];
}
else if ( d->type == DOLWILDARGS && d->where[0] == 1
&& d->where[1] < 0 ) {
retval = d->where[1];
}
else {
AN.ErrorInDollar = 1;
retval = 0;
}
#ifdef WITHPTHREADS
if ( dtype > 0 && dtype != MODLOCAL ) { UNLOCK(d->pthreadslockread); }
#endif
return(retval);
}
/*
#] DolToVector :
#[ DolToNumber :
*/
WORD DolToNumber(PHEAD WORD numdollar)
{
GETBIDENTITY
DOLLARS d = Dollars + numdollar;
#ifdef WITHPTHREADS
int nummodopt, dtype = -1;
if ( AS.MultiThreaded && ( AC.mparallelflag == PARALLELFLAG ) ) {
for ( nummodopt = 0; nummodopt < NumModOptdollars; nummodopt++ ) {
if ( numdollar == ModOptdollars[nummodopt].number ) break;
}
if ( nummodopt < NumModOptdollars ) {
dtype = ModOptdollars[nummodopt].type;
if ( dtype == MODLOCAL ) {
d = ModOptdollars[nummodopt].dstruct+AT.identity;
}
}
}
#endif
AN.ErrorInDollar = 0;
if ( ( d->type == DOLTERMS || d->type == DOLNUMBER )
&& d->where[0] == 4 &&
d->where[4] == 0 && ( d->where[3] == 3 || d->where[3] == -3 )
&& d->where[2] == 1 && ( d->where[1] & TOPBITONLY ) == 0 ) {
if ( d->where[3] > 0 ) return(d->where[1]);
else return(-d->where[1]);
}
else if ( d->type == DOLARGUMENT && d->where[0] == -SNUMBER ) {
return(d->where[1]);
}
else if ( d->type == DOLARGUMENT && d->where[0] == -INDEX
&& d->where[1] >= 0 && d->where[1] < AM.OffsetIndex ) {
return(d->where[1]);
}
else if ( d->type == DOLZERO ) return(0);
else if ( d->type == DOLWILDARGS && d->where[0] == 0
&& d->where[1] == -SNUMBER && d->where[3] == 0 ) {
return(d->where[2]);
}
else if ( d->type == DOLINDEX && d->index >= 0 && d->index < AM.OffsetIndex ) {
return(d->index);
}
else if ( d->type == DOLWILDARGS && d->where[0] == 1
&& d->where[1] >= 0 && d->where[1] < AM.OffsetIndex ) {
return(d->where[1]);
}
else if ( d->type == DOLWILDARGS && d->where[0] == 0
&& d->where[1] == -INDEX && d->where[3] == 0 && d->where[2] >= 0
&& d->where[2] < AM.OffsetIndex ) {
return(d->where[2]);
}
AN.ErrorInDollar = 1;
return(0);
}
/*
#] DolToNumber :
#[ DolToSymbol : with LOCK
*/
WORD DolToSymbol(PHEAD WORD numdollar)
{
GETBIDENTITY
DOLLARS d = Dollars + numdollar;
WORD retval;
#ifdef WITHPTHREADS
int nummodopt, dtype = -1;
if ( AS.MultiThreaded && ( AC.mparallelflag == PARALLELFLAG ) ) {
for ( nummodopt = 0; nummodopt < NumModOptdollars; nummodopt++ ) {
if ( numdollar == ModOptdollars[nummodopt].number ) break;
}
if ( nummodopt < NumModOptdollars ) {
dtype = ModOptdollars[nummodopt].type;
if ( dtype == MODLOCAL ) {
d = ModOptdollars[nummodopt].dstruct+AT.identity;
}
else {
LOCK(d->pthreadslockread);
}
}
}
#endif
AN.ErrorInDollar = 0;
if ( d->type == DOLTERMS && d->where[0] == 8 &&
d->where[8] == 0 && d->where[7] == 3 && d->where[6] == 1
&& d->where[5] == 1 && d->where[4] == 1 && d->where[1] == SYMBOL ) {
retval = d->where[3];
}
else if ( d->type == DOLARGUMENT && d->where[0] == -SYMBOL ) {
retval = d->where[1];
}
else if ( d->type == DOLSUBTERM && d->where[0] == SYMBOL
&& d->where[1] == 4 && d->where[3] == 1 ) {
retval = d->where[2];
}
else if ( d->type == DOLWILDARGS && d->where[0] == 0
&& d->where[1] == -SYMBOL && d->where[3] == 0 ) {
retval = d->where[2];
}
else {
AN.ErrorInDollar = 1;
retval = -1;
}
#ifdef WITHPTHREADS
if ( dtype > 0 && dtype != MODLOCAL ) { UNLOCK(d->pthreadslockread); }
#endif
return(retval);
}
/*
#] DolToSymbol :
#[ DolToIndex : with LOCK
*/
WORD DolToIndex(PHEAD WORD numdollar)
{
GETBIDENTITY
DOLLARS d = Dollars + numdollar;
WORD retval;
#ifdef WITHPTHREADS
int nummodopt, dtype = -1;
if ( AS.MultiThreaded && ( AC.mparallelflag == PARALLELFLAG ) ) {
for ( nummodopt = 0; nummodopt < NumModOptdollars; nummodopt++ ) {
if ( numdollar == ModOptdollars[nummodopt].number ) break;
}
if ( nummodopt < NumModOptdollars ) {
dtype = ModOptdollars[nummodopt].type;
if ( dtype == MODLOCAL ) {
d = ModOptdollars[nummodopt].dstruct+AT.identity;
}
else {
LOCK(d->pthreadslockread);
}
}
}
#endif
AN.ErrorInDollar = 0;
if ( d->type == DOLTERMS && d->where[0] == 7 &&
d->where[7] == 0 && d->where[6] == 3 && d->where[5] == 1
&& d->where[4] == 1 && d->where[1] == INDEX && d->where[3] >= 0 ) {
retval = d->where[3];
}
else if ( d->type == DOLARGUMENT && d->where[0] == -SNUMBER
&& d->where[1] >= 0 && d->where[1] < AM.OffsetIndex ) {
retval = d->where[1];
}
else if ( d->type == DOLARGUMENT && d->where[0] == -INDEX
&& d->where[1] >= 0 ) {
retval = d->where[1];
}
else if ( d->type == DOLZERO ) return(0);
else if ( d->type == DOLWILDARGS && d->where[0] == 0
&& d->where[1] == -SNUMBER && d->where[3] == 0 && d->where[2] >= 0
&& d->where[2] < AM.OffsetIndex ) {
retval = d->where[2];
}
else if ( d->type == DOLINDEX && d->index >= 0 ) {
retval = d->index;
}
else if ( d->type == DOLNUMBER && d->where[0] == 4 && d->where[2] == 1
&& d->where[3] == 3 && d->where[4] == 0 && d->where[1] < AM.OffsetIndex ) {
retval = d->where[1];
}
else if ( d->type == DOLWILDARGS && d->where[0] == 1
&& d->where[1] >= 0 ) {
retval = d->where[1];
}
else if ( d->type == DOLSUBTERM && d->where[0] == INDEX
&& d->where[1] == 3 && d->where[2] >= 0 ) {
retval = d->where[2];
}
else if ( d->type == DOLWILDARGS && d->where[0] == 0
&& d->where[1] == -INDEX && d->where[3] == 0 && d->where[2] >= 0 ) {
retval = d->where[2];
}
else {
AN.ErrorInDollar = 1;
retval = 0;
}
#ifdef WITHPTHREADS
if ( dtype > 0 && dtype != MODLOCAL ) { UNLOCK(d->pthreadslockread); }
#endif
return(retval);
}
/*
#] DolToIndex :
#[ DolToTerms :
Returns a struct of type DOLLARS which contains a copy of the
original dollar variable, provided it can be expressed in terms of
an expression (type = DOLTERMS). Otherwise it returns zero.
The dollar is expressed in terms in the buffer "where"
*/
DOLLARS DolToTerms(PHEAD WORD numdollar)
{
GETBIDENTITY
LONG size;
DOLLARS d = Dollars + numdollar, newd;
WORD *t, *w, i;
#ifdef WITHPTHREADS
int nummodopt, dtype = -1;
if ( AS.MultiThreaded && ( AC.mparallelflag == PARALLELFLAG ) ) {
for ( nummodopt = 0; nummodopt < NumModOptdollars; nummodopt++ ) {
if ( numdollar == ModOptdollars[nummodopt].number ) break;
}
if ( nummodopt < NumModOptdollars ) {
dtype = ModOptdollars[nummodopt].type;
if ( dtype == MODLOCAL ) {
d = ModOptdollars[nummodopt].dstruct+AT.identity;
}
}
}
#endif
AN.ErrorInDollar = 0;
switch ( d->type ) {
case DOLARGUMENT:
t = d->where;
if ( t[0] < 0 ) {
ShortArgument:
w = AT.WorkPointer;
if ( t[0] <= -FUNCTION ) {
*w++ = FUNHEAD+4; *w++ = -t[0];
*w++ = FUNHEAD; FILLFUN(w)
*w++ = 1; *w++ = 1; *w++ = 3;
}
else if ( t[0] == -SYMBOL ) {
*w++ = 8; *w++ = SYMBOL; *w++ = 4; *w++ = t[1];
*w++ = 1; *w++ = 1; *w++ = 1; *w++ = 3;
}
else if ( t[0] == -VECTOR || t[0] == -INDEX ) {
*w++ = 7; *w++ = INDEX; *w++ = 3; *w++ = t[1];
*w++ = 1; *w++ = 1; *w++ = 3;
}
else if ( t[0] == -MINVECTOR ) {
*w++ = 7; *w++ = INDEX; *w++ = 3; *w++ = t[1];
*w++ = 1; *w++ = 1; *w++ = -3;
}
else if ( t[0] == -SNUMBER ) {
*w++ = 4;
if ( t[1] < 0 ) {
*w++ = -t[1]; *w++ = 1; *w++ = -3;
}
else {
*w++ = t[1]; *w++ = 1; *w++ = 3;
}
}
*w = 0; size = w - AT.WorkPointer;
w = AT.WorkPointer;
break;
}
/* fall through */
case DOLNUMBER:
case DOLTERMS:
t = d->where;
while ( *t ) t += *t;
size = t - d->where;
w = d->where;
break;
case DOLSUBTERM:
w = AT.WorkPointer;
size = d->where[1];
*w++ = size+4; t = d->where; NCOPY(w,t,size)
*w++ = 1; *w++ = 1; *w++ = 3;
w = AT.WorkPointer; size = d->where[1]+4;
break;
case DOLINDEX:
w = AT.WorkPointer;
*w++ = 7; *w++ = INDEX; *w++ = 3; *w++ = d->index;
*w++ = 1; *w++ = 1; *w++ = 3; *w = 0;
w = AT.WorkPointer; size = 7;
break;
case DOLWILDARGS:
/*
In some cases we can make a copy
*/
t = d->where+1;
if ( *t == 0 ) return(0);
NEXTARG(t);
if ( *t ) { /* More than one argument in here */
MLOCK(ErrorMessageLock);
MesPrint("Trying to convert a $ with an argument field into an expression");
MUNLOCK(ErrorMessageLock);
Terminate(-1);
}
/*
Now we have a single argument
*/
t = d->where+1;
if ( *t < 0 ) goto ShortArgument;
size = *t - ARGHEAD;
w = t + ARGHEAD;
break;
case DOLUNDEFINED:
MLOCK(ErrorMessageLock);
MesPrint("Trying to use an undefined $ in an expression");
MUNLOCK(ErrorMessageLock);
Terminate(-1);
/* fall through */
case DOLZERO:
if ( d->where ) { d->where[0] = 0; }
else d->where = &(AM.dollarzero);
size = 0;
w = d->where;
break;
default:
return(0);
}
newd = (DOLLARS)Malloc1(sizeof(struct DoLlArS)+(size+1)*sizeof(WORD),
"Copy of dollar variable");
t = (WORD *)(newd+1);
newd->where = t;
newd->name = d->name;
newd->node = d->node;
newd->type = DOLTERMS;
newd->size = size;
newd->numdummies = d->numdummies;
#ifdef WITHPTHREADS
newd->pthreadslockread = dummylock;
newd->pthreadslockwrite = dummylock;
#endif
size++;
NCOPY(t,w,size);
newd->nfactors = d->nfactors;
if ( d->nfactors > 1 ) {
newd->factors = (FACDOLLAR *)Malloc1(d->nfactors*sizeof(FACDOLLAR),"Dollar factors");
for ( i = 0; i < d->nfactors; i++ ) {
newd->factors[i].where = 0;
newd->factors[i].size = 0;
newd->factors[i].type = DOLUNDEFINED;
newd->factors[i].value = d->factors[i].value;
}
}
else { newd->factors = 0; }
return(newd);
}
/*
#] DolToTerms :
#[ DolToLong :
*/
LONG DolToLong(PHEAD WORD numdollar)
{
GETBIDENTITY
DOLLARS d = Dollars + numdollar;
LONG x;
#ifdef WITHPTHREADS
int nummodopt, dtype = -1;
if ( AS.MultiThreaded && ( AC.mparallelflag == PARALLELFLAG ) ) {
for ( nummodopt = 0; nummodopt < NumModOptdollars; nummodopt++ ) {
if ( numdollar == ModOptdollars[nummodopt].number ) break;
}
if ( nummodopt < NumModOptdollars ) {
dtype = ModOptdollars[nummodopt].type;
if ( dtype == MODLOCAL ) {
d = ModOptdollars[nummodopt].dstruct+AT.identity;
}
}
}
#endif
AN.ErrorInDollar = 0;
if ( ( d->type == DOLTERMS || d->type == DOLNUMBER )
&& d->where[0] == 4 &&
d->where[4] == 0 && ( d->where[3] == 3 || d->where[3] == -3 )
&& d->where[2] == 1 && ( d->where[1] & TOPBITONLY ) == 0 ) {
x = d->where[1];
if ( d->where[3] > 0 ) return(x);
else return(-x);
}
else if ( ( d->type == DOLTERMS || d->type == DOLNUMBER )
&& d->where[0] == 6 &&
d->where[6] == 0 && ( d->where[5] == 5 || d->where[5] == -5 )
&& d->where[3] == 1 && d->where[4] == 1 && ( d->where[2] & TOPBITONLY ) == 0 ) {
x = d->where[1] + ( (LONG)(d->where[2]) << BITSINWORD );
if ( d->where[5] > 0 ) return(x);
else return(-x);
}
else if ( d->type == DOLARGUMENT && d->where[0] == -SNUMBER ) {
x = d->where[1];
return(x);
}
else if ( d->type == DOLARGUMENT && d->where[0] == -INDEX
&& d->where[1] >= 0 && d->where[1] < AM.OffsetIndex ) {
x = d->where[1];
return(x);
}
else if ( d->type == DOLZERO ) return(0);
else if ( d->type == DOLWILDARGS && d->where[0] == 0
&& d->where[1] == -SNUMBER && d->where[3] == 0 ) {
x = d->where[2];
return(x);
}
else if ( d->type == DOLINDEX && d->index >= 0 && d->index < AM.OffsetIndex ) {
x = d->index;
return(x);
}
else if ( d->type == DOLWILDARGS && d->where[0] == 1
&& d->where[1] >= 0 && d->where[1] < AM.OffsetIndex ) {
x = d->where[1];
return(x);
}
else if ( d->type == DOLWILDARGS && d->where[0] == 0
&& d->where[1] == -INDEX && d->where[3] == 0 && d->where[2] >= 0
&& d->where[2] < AM.OffsetIndex ) {
x = d->where[2];
return(x);
}
AN.ErrorInDollar = 1;
return(0);
}
/*
#] DolToLong :
#[ ExecInside :
*/
int ExecInside(UBYTE *s)
{
GETIDENTITY
UBYTE *t, c;
WORD *w, number;
int error = 0;
w = AT.WorkPointer;
if ( AC.insidelevel >= MAXNEST ) {
MLOCK(ErrorMessageLock);
MesPrint("@Nesting of inside statements more than %d levels",(WORD)MAXNEST);
MUNLOCK(ErrorMessageLock);
return(-1);
}
AC.insidesumcheck[AC.insidelevel] = NestingChecksum();
AC.insidestack[AC.insidelevel] = cbuf[AC.cbufnum].Pointer
- cbuf[AC.cbufnum].Buffer + 2;
AC.insidelevel++;
*w++ = TYPEINSIDE;
w++; w++;
for(;;) { /* Look for a (comma separated) list of dollar variables */
while ( *s == ',' ) s++;
if ( *s == 0 ) break;
if ( *s == '$' ) {
s++; t = s;
if ( FG.cTable[*s] != 0 ) {
MLOCK(ErrorMessageLock);
MesPrint("Illegal name for $ variable: %s",s-1);
MUNLOCK(ErrorMessageLock);
goto skipdol;
}
while ( FG.cTable[*s] == 0 || FG.cTable[*s] == 1 ) s++;
c = *s; *s = 0;
if ( ( number = GetDollar(t) ) < 0 ) {
number = AddDollar(t,0,0,0);
}
*s = c;
*w++ = number;
AddPotModdollar(number);
}
else {
MLOCK(ErrorMessageLock);
MesPrint("&Illegal object in Inside statement");
MUNLOCK(ErrorMessageLock);
skipdol: error = 1;
while ( *s && *s != ',' && s[1] != '$' ) s++;
if ( *s == 0 ) break;
}
}
AT.WorkPointer[1] = w - AT.WorkPointer;
AddNtoL(AT.WorkPointer[1],AT.WorkPointer);
return(error);
}
/*
#] ExecInside :
#[ InsideDollar :
Execution part of Inside $a;
We have to take the variables one by one and then
convert them into proper terms and call Generator for the proper levels.
The conversion copies the whole dollar into a new buffer, making us
insensitive to redefinitions of $a inside the Inside.
In the end we sort and redefine $a.
*/
int InsideDollar(PHEAD WORD *ll, WORD level)
{
GETBIDENTITY
int numvar = (int)(ll[1]-3), j, error = 0;
WORD numdol, *oldcterm, *oldwork = AT.WorkPointer, olddefer, *r, *m;
WORD oldnumlhs, *dbuffer;
DOLLARS d, newd;
oldcterm = AN.cTerm; AN.cTerm = 0;
oldnumlhs = AR.Cnumlhs; AR.Cnumlhs = ll[2];
ll += 3;
olddefer = AR.DeferFlag;
AR.DeferFlag = 0;
while ( --numvar >= 0 ) {
numdol = *ll++;
d = Dollars + numdol;
{
#ifdef WITHPTHREADS
int nummodopt, dtype = -1;
if ( AS.MultiThreaded && ( AC.mparallelflag == PARALLELFLAG ) ) {
for ( nummodopt = 0; nummodopt < NumModOptdollars; nummodopt++ ) {
if ( numdol == ModOptdollars[nummodopt].number ) break;
}
if ( nummodopt < NumModOptdollars ) {
dtype = ModOptdollars[nummodopt].type;
if ( dtype == MODLOCAL ) {
d = ModOptdollars[nummodopt].dstruct+AT.identity;
}
else {
/* LOCK(d->pthreadslockwrite); */
LOCK(d->pthreadslockread);
}
}
}
#endif
newd = DolToTerms(BHEAD numdol);
if ( newd == 0 || newd->where[0] == 0 ) continue;
r = newd->where;
NewSort(BHEAD0);
while ( *r ) { /* Sum over the terms */
m = AT.WorkPointer;
j = *r;
while ( --j >= 0 ) *m++ = *r++;
AT.WorkPointer = m;
/*
What to do with dummy indices?
*/
if ( Generator(BHEAD oldwork,level) ) {
LowerSortLevel();
error = -1; goto idcall;
}
AT.WorkPointer = oldwork;
}
AN.tryterm = 0; /* for now */
if ( EndSort(BHEAD (WORD *)((VOID *)(&dbuffer)),2) < 0 ) { error = 1; break; }
if ( d->where && d->where != &(AM.dollarzero) ) M_free(d->where,"old buffer of dollar");
d->where = dbuffer;
if ( dbuffer == 0 || *dbuffer == 0 ) {
d->type = DOLZERO;
if ( dbuffer ) M_free(dbuffer,"buffer of dollar");
d->where = &(AM.dollarzero); d->size = 0;
}
else {
d->type = DOLTERMS;
r = d->where; while ( *r ) r += *r;
d->size = (r-d->where)+1;
}
/* cbuf[AM.dbufnum].rhs[numdol] = d->where; */
cbuf[AM.dbufnum].rhs[numdol] = (WORD *)(1);
/*
Now we have a little cleaning up to do
*/
#ifdef WITHPTHREADS
if ( dtype > 0 && dtype != MODLOCAL ) {
/* UNLOCK(d->pthreadslockwrite); */
UNLOCK(d->pthreadslockread);
}
#endif
if ( newd->factors ) M_free(newd->factors,"Dollar factors");
M_free(newd,"Copy of dollar variable");
}
}
idcall:;
AR.Cnumlhs = oldnumlhs;
AR.DeferFlag = olddefer;
AN.cTerm = oldcterm;
AT.WorkPointer = oldwork;
return(error);
}
/*
#] InsideDollar :
#[ ExchangeDollars :
*/
void ExchangeDollars(int num1, int num2)
{
DOLLARS d1, d2;
WORD node1, node2;
LONG nam;
d1 = Dollars + num1; node1 = d1->node;
d2 = Dollars + num2; node2 = d2->node;
nam = d1->name; d1->name = d2->name; d2->name = nam;
d1->node = node2; d2->node = node1;
AC.dollarnames->namenode[node1].number = num2;
AC.dollarnames->namenode[node2].number = num1;
}
/*
#] ExchangeDollars :
#[ TermsInDollar :
*/
LONG TermsInDollar(WORD num)
{
GETIDENTITY
DOLLARS d = Dollars + num;
WORD *t;
LONG n;
#ifdef WITHPTHREADS
int nummodopt, dtype = -1;
if ( AS.MultiThreaded && ( AC.mparallelflag == PARALLELFLAG ) ) {
for ( nummodopt = 0; nummodopt < NumModOptdollars; nummodopt++ ) {
if ( num == ModOptdollars[nummodopt].number ) break;
}
if ( nummodopt < NumModOptdollars ) {
dtype = ModOptdollars[nummodopt].type;
if ( dtype == MODLOCAL ) {
d = ModOptdollars[nummodopt].dstruct+AT.identity;
}
else {
LOCK(d->pthreadslockread);
}
}
}
#endif
if ( d->type == DOLTERMS ) {
n = 0;
t = d->where;
while ( *t ) { t += *t; n++; }
}
else if ( d->type == DOLWILDARGS ) {
n = 0;
if ( d->where[0] == 0 ) {
t = d->where+1;
while ( *t != 0 ) { NEXTARG(t); n++; }
}
else if ( d->where[0] == 1 ) n = 1;
}
else if ( d->type == DOLZERO ) n = 0;
else n = 1;
#ifdef WITHPTHREADS
if ( dtype > 0 && dtype != MODLOCAL ) { UNLOCK(d->pthreadslockread); }
#endif
return(n);
}
/*
#] TermsInDollar :
#[ SizeOfDollar :
*/
LONG SizeOfDollar(WORD num)
{
GETIDENTITY
DOLLARS d = Dollars + num;
WORD *t;
LONG n;
#ifdef WITHPTHREADS
int nummodopt, dtype = -1;
if ( AS.MultiThreaded && ( AC.mparallelflag == PARALLELFLAG ) ) {
for ( nummodopt = 0; nummodopt < NumModOptdollars; nummodopt++ ) {
if ( num == ModOptdollars[nummodopt].number ) break;
}
if ( nummodopt < NumModOptdollars ) {
dtype = ModOptdollars[nummodopt].type;
if ( dtype == MODLOCAL ) {
d = ModOptdollars[nummodopt].dstruct+AT.identity;
}
else {
LOCK(d->pthreadslockread);
}
}
}
#endif
if ( d->type == DOLTERMS ) {
t = d->where;
while ( *t ) t += *t;
t++;
n = (LONG)(t - d->where);
}
else if ( d->type == DOLWILDARGS ) {
n = 0;
if ( d->where[0] == 0 ) {
t = d->where+1;
while ( *t != 0 ) { NEXTARG(t); n++; }
t++;
n = (LONG)(t - d->where);
}
else if ( d->where[0] == 1 ) n = 1;
}
else if ( d->type == DOLZERO ) n = 0;
else n = 1;
#ifdef WITHPTHREADS
if ( dtype > 0 && dtype != MODLOCAL ) { UNLOCK(d->pthreadslockread); }
#endif
return(n);
}
/*
#] SizeOfDollar :
#[ PreIfDollarEval :
Routine is invoked in #if etc after $( is encountered.
$(expr1 operator expr2) makes compares between expressions,
$(expr1 operator _keyword) makes compares between expressions,
interpreted as expressions. We are here mainly looking at $variables.
First we look for the operator:
>, <, ==, >=, <=, != : < means that it comes before.
_keywords can be:
_set(setname) (does the expr belong to the set (only with == or !=))
_productof(expr)
*/
UBYTE *PreIfDollarEval(UBYTE *s, int *value)
{
GETIDENTITY
UBYTE *s1,*s2,*s3,*s4,*s5,*t,c,c1,c2,c3;
int oprtr, type;
WORD *buf1 = 0, *buf2 = 0, numset, *oldwork = AT.WorkPointer;
EXCHINOUT
/*
Find the three composing objects (epxression, operator, expression or keyw
*/
while ( *s == ' ' || *s == '\t' || *s == '\n' || *s == '\r' ) s++;
s1 = t = s;
while ( *t != '=' && *t != '!' && *t != '>' && *t != '<' ) {
if ( *t == '[' ) { SKIPBRA1(t) }
else if ( *t == '{' ) { SKIPBRA2(t) }
else if ( *t == '(' ) { SKIPBRA3(t) }
else if ( *t == ']' || *t == '}' || *t == ')' ) {
MLOCK(ErrorMessageLock);
MesPrint("@Improper bracketting in #if");
MUNLOCK(ErrorMessageLock);
goto onerror;
}
t++;
}
s2 = t;
while ( *t == '=' || *t == '!' || *t == '>' || *t == '<' ) t++;
s3 = t;
while ( *t && *t != ')' ) {
if ( *t == '[' ) { SKIPBRA1(t) }
else if ( *t == '{' ) { SKIPBRA2(t) }
else if ( *t == '(' ) { SKIPBRA3(t) }
else if ( *t == ']' || *t == '}' ) {
MLOCK(ErrorMessageLock);
MesPrint("@Improper brackets in #if");
MUNLOCK(ErrorMessageLock);
goto onerror;
}
t++;
}
if ( *t == 0 ) {
MLOCK(ErrorMessageLock);
MesPrint("@Missing ) to match $( in #if");
MUNLOCK(ErrorMessageLock);
goto onerror;
}
s4 = t; c2 = *s4; *s4 = 0;
if ( s2+2 < s3 || s2 == s3 ) {
IllOp:;
MLOCK(ErrorMessageLock);
MesPrint("@Illegal operator in $( option of #if");
MUNLOCK(ErrorMessageLock);
goto onerror;
}
if ( s2+1 == s3 ) {
if ( *s2 == '=' ) oprtr = EQUAL;
else if ( *s2 == '>' ) oprtr = GREATER;
else if ( *s2 == '<' ) oprtr = LESS;
else goto IllOp;
}
else if ( *s2 == '!' && s2[1] == '=' ) oprtr = NOTEQUAL;
else if ( *s2 == '=' && s2[1] == '=' ) oprtr = EQUAL;
else if ( *s2 == '<' && s2[1] == '=' ) oprtr = LESSEQUAL;
else if ( *s2 == '>' && s2[1] == '=' ) oprtr = GREATEREQUAL;
else goto IllOp;
c1 = *s2; *s2 = 0;
/*
The two expressions are now zero terminated
Look for the special keywords
*/
while ( *s3 == ' ' || *s3 == '\t' || *s3 == '\n' || *s3 == '\r' ) s3++;
t = s3;
while ( chartype[*t] == 0 ) t++;
if ( *t == '_' ) {
t++; c = *t; *t = 0;
if ( StrICmp(s3,(UBYTE *)"set_") == 0 ) {
if ( oprtr != EQUAL && oprtr != NOTEQUAL ) {
ImpOp:;
MLOCK(ErrorMessageLock);
MesPrint("@Improper operator for special keyword in $( ) option");
MUNLOCK(ErrorMessageLock);
goto onerror;
}
type = 1;
}
else if ( StrICmp(s3,(UBYTE *)"multipleof_") == 0 ) {
if ( oprtr != EQUAL && oprtr != NOTEQUAL ) goto ImpOp;
type = 2;
}
/*
else if ( StrICmp(s3,(UBYTE *)"productof_") == 0 ) {
if ( oprtr != EQUAL && oprtr != NOTEQUAL ) goto ImpOp;
type = 3;
}
*/
else type = 0;
}
else { type = 0; c = *t; }
if ( type > 0 ) {
*t++ = c; s3 = t; s5 = s4-1;
while ( *s5 != ')' ) {
if ( *s5 == ' ' || *s5 == '\t' || *s5 == '\n' || *s5 == '\r' ) s5--;
else {
MLOCK(ErrorMessageLock);
MesPrint("@Improper use of special keyword in $( ) option");
MUNLOCK(ErrorMessageLock);
goto onerror;
}
}
c3 = *s5; *s5 = 0;
}
else { c3 = c2; s5 = s4; }
/*
Expand the first expression.
*/
if ( ( buf1 = TranslateExpression(s1) ) == 0 ) {
AT.WorkPointer = oldwork;
goto onerror;
}
if ( type == 1 ) { /* determine the set */
if ( *s3 == '{' ) {
t = s3+1;
SKIPBRA2(s3)
numset = DoTempSet(t,s3);
s3++;
if ( numset < 0 ) {
noset:;
MLOCK(ErrorMessageLock);
MesPrint("@Argument of set_ is not a valid set");
MUNLOCK(ErrorMessageLock);
goto onerror;
}
}
else {
t = s3;
while ( FG.cTable[*s3] == 0 || FG.cTable[*s3] == 1
|| *s3 == '_' ) s3++;
c = *s3; *s3 = 0;
if ( GetName(AC.varnames,t,&numset,NOAUTO) != CSET ) {
*s3 = c; goto noset;
}
*s3 = c;
}
while ( *s3 == ' ' || *s3 == '\t' || *s3 == '\n' || *s3 == '\r' ) s3++;
if ( s3 != s5 ) goto noset;
*value = IsSetMember(buf1,numset);
if ( oprtr == NOTEQUAL ) *value ^= 1;
}
else {
if ( ( buf2 = TranslateExpression(s3) ) == 0 ) goto onerror;
}
if ( type == 0 ) {
*value = TwoExprCompare(buf1,buf2,oprtr);
}
else if ( type == 2 ) {
*value = IsMultipleOf(buf1,buf2);
if ( oprtr == NOTEQUAL ) *value ^= 1;
}
/*
else if ( type == 3 ) {
*value = IsProductOf(buf1,buf2);
if ( oprtr == NOTEQUAL ) *value ^= 1;
}
*/
if ( buf1 ) M_free(buf1,"Buffer in $()");
if ( buf2 ) M_free(buf2,"Buffer in $()");
*s5 = c3; *s4++ = c2; *s2 = c1;
AT.WorkPointer = oldwork;
BACKINOUT
return(s4);
onerror:
if ( buf1 ) M_free(buf1,"Buffer in $()");
if ( buf2 ) M_free(buf2,"Buffer in $()");
AT.WorkPointer = oldwork;
BACKINOUT
return(0);
}
/*
#] PreIfDollarEval :
#[ TranslateExpression :
*/
WORD *TranslateExpression(UBYTE *s)
{
GETIDENTITY
CBUF *C = cbuf+AC.cbufnum;
WORD oldnumrhs = C->numrhs;
LONG oldcpointer = C->Pointer - C->Buffer;
WORD *w = AT.WorkPointer;
WORD retcode, oldEside;
WORD *outbuffer;
*w++ = SUBEXPSIZE + 4;
AC.ProtoType = w;
*w++ = SUBEXPRESSION;
*w++ = SUBEXPSIZE;
*w++ = C->numrhs+1;
*w++ = 1;
*w++ = AC.cbufnum;
FILLSUB(w)
*w++ = 1; *w++ = 1; *w++ = 3; *w++ = 0;
AT.WorkPointer = w;
if ( ( retcode = CompileAlgebra(s,RHSIDE,AC.ProtoType) ) < 0 ) {
MLOCK(ErrorMessageLock);
MesPrint("@Error translating first expression in $( ) option");
MUNLOCK(ErrorMessageLock);
return(0);
}
else { AC.ProtoType[2] = retcode; }
/*
Evaluate this expression
*/
if ( NewSort(BHEAD0) || NewSort(BHEAD0) ) { return(0); }
AN.RepPoint = AT.RepCount + 1;
oldEside = AR.Eside; AR.Eside = RHSIDE;
AR.Cnumlhs = C->numlhs;
if ( Generator(BHEAD AC.ProtoType-1,C->numlhs) ) {
AR.Eside = oldEside;
LowerSortLevel(); LowerSortLevel(); return(0);
}
AR.Eside = oldEside;
AT.WorkPointer = w;
AN.tryterm = 0; /* for now */
if ( EndSort(BHEAD (WORD *)((VOID *)(&outbuffer)),2) < 0 ) { LowerSortLevel(); return(0); }
LowerSortLevel();
C->Pointer = C->Buffer + oldcpointer;
C->numrhs = oldnumrhs;
AT.WorkPointer = AC.ProtoType - 1;
return(outbuffer);
}
/*
#] TranslateExpression :
#[ IsSetMember :
Checks whether the expression in the buffer can be seen as an element
of the given set.
For the special sets: if more than one term: no match!!!
*/
int IsSetMember(WORD *buffer, WORD numset)
{
WORD *t = buffer, *tt, num, csize, num1;
WORD bufterm[4];
int i, j, type;
if ( numset < AM.NumFixedSets ) {
if ( t[*t] != 0 ) return(0); /* More than one term */
if ( *t == 0 ) {
if ( numset == POS0_ || numset == NEG0_ || numset == EVEN_
|| numset == Z_ || numset == Q_ ) return(1);
else return(0);
}
if ( numset == SYMBOL_ ) {
if ( *t == 8 && t[1] == SYMBOL && t[7] == 3 && t[6] == 1
&& t[5] == 1 && t[4] == 1 ) return(1);
else return(0);
}
if ( numset == INDEX_ ) {
if ( *t == 7 && t[1] == INDEX && t[6] == 3 && t[5] == 1
&& t[4] == 1 && t[3] > 0 ) return(1);
if ( *t == 4 && t[3] == 3 && t[2] == 1 && t[1] < AM.OffsetIndex)
return(1);
return(0);
}
if ( numset == FIXED_ ) {
if ( *t == 7 && t[1] == INDEX && t[6] == 3 && t[5] == 1
&& t[4] == 1 && t[3] > 0 && t[3] < AM.OffsetIndex ) return(1);
if ( *t == 4 && t[3] == 3 && t[2] == 1 && t[1] < AM.OffsetIndex)
return(1);
return(0);
}
if ( numset == DUMMYINDEX_ ) {
if ( *t == 7 && t[1] == INDEX && t[6] == 3 && t[5] == 1
&& t[4] == 1 && t[3] >= AM.IndDum && t[3] < AM.IndDum+MAXDUMMIES ) return(1);
if ( *t == 4 && t[3] == 3 && t[2] == 1
&& t[1] >= AM.IndDum && t[1] < AM.IndDum+MAXDUMMIES ) return(1);
return(0);
}
if ( numset == VECTOR_ ) {
if ( *t == 7 && t[1] == INDEX && t[6] == 3 && t[5] == 1
&& t[4] == 1 && t[3] < (AM.OffsetVector+WILDOFFSET) && t[3] >= AM.OffsetVector ) return(1);
return(0);
}
tt = t + *t - 1;
if ( ABS(tt[0]) != *t-1 ) return(0);
if ( numset == Q_ ) return(1);
if ( numset == POS_ || numset == POS0_ ) return(tt[0]>0);
else if ( numset == NEG_ || numset == NEG0_ ) return(tt[0]<0);
i = (ABS(tt[0])-1)/2;
tt -= i;
if ( tt[0] != 1 ) return(0);
for ( j = 1; j < i; j++ ) { if ( tt[j] != 0 ) return(0); }
if ( numset == Z_ ) return(1);
if ( numset == ODD_ ) return(t[1]&1);
if ( numset == EVEN_ ) return(1-(t[1]&1));
return(0);
}
if ( t[*t] != 0 ) return(0); /* More than one term */
type = Sets[numset].type;
switch ( type ) {
case CSYMBOL:
if ( t[0] == 8 && t[1] == SYMBOL && t[7] == 3 && t[6] == 1
&& t[5] == 1 && t[4] == 1 ) {
num = t[3];
}
else if ( t[0] == 4 && t[2] == 1 && t[1] <= MAXPOWER ) {
num = t[1];
if ( t[3] < 0 ) num = -num;
num += 2*MAXPOWER;
}
else return(0);
break;
case CVECTOR:
if ( t[0] == 7 && t[1] == INDEX && t[6] == 3 && t[5] == 1
&& t[4] == 1 && t[3] < 0 ) {
num = t[3];
}
else return(0);
break;
case CINDEX:
if ( t[0] == 7 && t[1] == INDEX && t[6] == 3 && t[5] == 1
&& t[4] == 1 && t[3] > 0 ) {
num = t[3];
}
else if ( t[0] == 4 && t[3] == 3 && t[2] == 1 && t[1] < AM.OffsetIndex ) {
num = t[1];
}
else return(0);
break;
case CFUNCTION:
if ( t[0] == 4+FUNHEAD && t[3+FUNHEAD] == 3 && t[2+FUNHEAD] == 1
&& t[1+FUNHEAD] == 1 && t[1] >= FUNCTION ) {
num = t[1];
}
else return(0);
break;
case CNUMBER:
if ( t[0] == 4 && t[2] == 1 && t[1] <= AM.OffsetIndex && t[3] == 3 ) {
num = t[1];
}
else return(0);
break;
case CRANGE:
csize = t[t[0]-1];
csize = ABS(csize);
if ( csize != t[0]-1 ) return(0);
if ( Sets[numset].first < 3*MAXPOWER ) {
num1 = num = Sets[numset].first;
if ( num >= MAXPOWER ) num -= 2*MAXPOWER;
if ( num == 0 ) {
if ( num1 < MAXPOWER ) {
if ( t[t[0]-1] >= 0 ) return(0);
}
else if ( t[t[0]-1] > 0 ) return(0);
}
else {
bufterm[0] = 4; bufterm[1] = ABS(num);
bufterm[2] = 1;
if ( num < 0 ) bufterm[3] = -3;
else bufterm[3] = 3;
num = CompCoef(t,bufterm);
if ( num1 < MAXPOWER ) {
if ( num >= 0 ) return(0);
}
else if ( num > 0 ) return(0);
}
}
if ( Sets[numset].last > -3*MAXPOWER ) {
num1 = num = Sets[numset].last;
if ( num <= -MAXPOWER ) num += 2*MAXPOWER;
if ( num == 0 ) {
if ( num1 > -MAXPOWER ) {
if ( t[t[0]-1] <= 0 ) return(0);
}
else if ( t[t[0]-1] < 0 ) return(0);
}
else {
bufterm[0] = 4; bufterm[1] = ABS(num);
bufterm[2] = 1;
if ( num < 0 ) bufterm[3] = -3;
else bufterm[3] = 3;
num = CompCoef(t,bufterm);
if ( num1 > -MAXPOWER ) {
if ( num <= 0 ) return(0);
}
else if ( num < 0 ) return(0);
}
}
return(1);
break;
default: return(0);
}
t = SetElements + Sets[numset].first;
tt = SetElements + Sets[numset].last;
do {
if ( num == *t ) return(1);
t++;
} while ( t < tt );
return(0);
}
/*
#] IsSetMember :
#[ IsProductOf :
Checks whether the expression in buf1 is a single term multiple of
the expression in buf2.
int IsProductOf(WORD *buf1, WORD *buf2)
{
return(0);
}
#] IsProductOf :
#[ IsMultipleOf :
Checks whether the expression in buf1 is a numerical multiple of
the expression in buf2.
*/
int IsMultipleOf(WORD *buf1, WORD *buf2)
{
GETIDENTITY
LONG num1, num2;
WORD *t1, *t2, *m1, *m2, *r1, *r2, nc1, nc2, ni1, ni2;
UWORD *IfScrat1, *IfScrat2;
int i, j;
if ( *buf1 == 0 && *buf2 == 0 ) return(1);
/*
First count terms
*/
t1 = buf1; t2 = buf2; num1 = 0; num2 = 0;
while ( *t1 ) { t1 += *t1; num1++; }
while ( *t2 ) { t2 += *t2; num2++; }
if ( num1 != num2 ) return(0);
/*
Test similarity of terms. Difference up to a number.
*/
t1 = buf1; t2 = buf2;
while ( *t1 ) {
m1 = t1+1; m2 = t2+1; t1 += *t1; t2 += *t2;
r1 = t1 - ABS(t1[-1]); r2 = t2 - ABS(t2[-1]);
if ( r1-m1 != r2-m2 ) return(0);
while ( m1 < r1 ) {
if ( *m1 != *m2 ) return(0);
m1++; m2++;
}
}
/*
Now we have to test the constant factor
*/
IfScrat1 = (UWORD *)(TermMalloc("IsMultipleOf")); IfScrat2 = (UWORD *)(TermMalloc("IsMultipleOf"));
t1 = buf1; t2 = buf2;
t1 += *t1; t2 += *t2;
if ( *t1 == 0 && *t2 == 0 ) return(1);
r1 = t1 - ABS(t1[-1]); r2 = t2 - ABS(t2[-1]);
nc1 = REDLENG(t1[-1]); nc2 = REDLENG(t2[-1]);
if ( DivRat(BHEAD (UWORD *)r1,nc1,(UWORD *)r2,nc2,IfScrat1,&ni1) ) {
MLOCK(ErrorMessageLock);
MesPrint("@Called from MultipleOf in $( )");
MUNLOCK(ErrorMessageLock);
TermFree(IfScrat1,"IsMultipleOf"); TermFree(IfScrat2,"IsMultipleOf");
Terminate(-1);
}
while ( *t1 ) {
t1 += *t1; t2 += *t2;
r1 = t1 - ABS(t1[-1]); r2 = t2 - ABS(t2[-1]);
nc1 = REDLENG(t1[-1]); nc2 = REDLENG(t2[-1]);
if ( DivRat(BHEAD (UWORD *)r1,nc1,(UWORD *)r2,nc2,IfScrat2,&ni2) ) {
MLOCK(ErrorMessageLock);
MesPrint("@Called from MultipleOf in $( )");
MUNLOCK(ErrorMessageLock);
TermFree(IfScrat1,"IsMultipleOf"); TermFree(IfScrat2,"IsMultipleOf");
Terminate(-1);
}
if ( ni1 != ni2 ) return(0);
i = 2*ABS(ni1);
for ( j = 0; j < i; j++ ) {
if ( IfScrat1[j] != IfScrat2[j] ) {
TermFree(IfScrat1,"IsMultipleOf"); TermFree(IfScrat2,"IsMultipleOf");
return(0);
}
}
}
TermFree(IfScrat1,"IsMultipleOf"); TermFree(IfScrat2,"IsMultipleOf");
return(1);
}
/*
#] IsMultipleOf :
#[ TwoExprCompare :
Compares the expressions in buf1 and buf2 according to oprtr
*/
int TwoExprCompare(WORD *buf1, WORD *buf2, int oprtr)
{
GETIDENTITY
WORD *t1, *t2, cond;
t1 = buf1; t2 = buf2;
while ( *t1 && *t2 ) {
cond = CompareTerms(t1,t2,1);
if ( cond != 0 ) {
if ( cond > 0 ) { /* t1 comes first */
switch ( oprtr ) { /* t1 is less */
case EQUAL: return(0);
case NOTEQUAL: return(1);
case GREATEREQUAL: return(0);
case GREATER: return(0);
case LESS: return(1);
case LESSEQUAL: return(1);
}
}
else {
switch ( oprtr ) {
case EQUAL: return(0);
case NOTEQUAL: return(1);
case GREATEREQUAL: return(1);
case GREATER: return(1);
case LESS: return(0);
case LESSEQUAL: return(0);
}
}
}
t1 += *t1; t2 += *t2;
}
if ( *t1 == *t2 ) { /* They are equal */
switch ( oprtr ) {
case EQUAL: return(1);
case NOTEQUAL: return(0);
case GREATEREQUAL: return(1);
case GREATER: return(0);
case LESS: return(0);
case LESSEQUAL: return(1);
}
}
else if ( *t1 ) { /* t1 is greater */
switch ( oprtr ) {
case EQUAL: return(0);
case NOTEQUAL: return(1);
case GREATEREQUAL: return(1);
case GREATER: return(1);
case LESS: return(0);
case LESSEQUAL: return(0);
}
}
else {
switch ( oprtr ) { /* t1 is less */
case EQUAL: return(0);
case NOTEQUAL: return(1);
case GREATEREQUAL: return(0);
case GREATER: return(0);
case LESS: return(1);
case LESSEQUAL: return(1);
}
}
MLOCK(ErrorMessageLock);
MesPrint("@Internal problems with operator in $( )");
MUNLOCK(ErrorMessageLock);
Terminate(-1);
return(0);
}
/*
#] TwoExprCompare :
#[ DollarRaiseLow :
Raises or lowers the numerical value of a dollar variable
Not to be used in parallel.
*/
static UWORD *dscrat = 0;
static WORD ndscrat;
int DollarRaiseLow(UBYTE *name, LONG value)
{
GETIDENTITY
int num;
DOLLARS d;
int sgn = 1;
WORD lnum[4], nnum, *t1, *t2, i;
UBYTE *s, c;
s = name; while ( *s ) s++;
if ( s[-1] == '-' && s[-2] == '-' && s > name+2 ) s -= 2;
else if ( s[-1] == '+' && s[-2] == '+' && s > name+2 ) s -= 2;
c = *s; *s = 0;
num = GetDollar(name);
*s = c;
d = Dollars + num;
if ( value < 0 ) { value = -value; sgn = -1; }
if ( d->type == DOLZERO ) {
if ( d->where ) M_free(d->where,"DollarRaiseLow");
d->size = MINALLOC;
d->where = (WORD *)Malloc1(d->size*sizeof(WORD),"DollarRaiseLow");
if ( ( value & AWORDMASK ) != 0 ) {
d->where[0] = 6; d->where[1] = value >> BITSINWORD;
d->where[2] = (WORD)value; d->where[3] = 1; d->where[4] = 0;
d->where[5] = 5*sgn; d->where[6] = 0;
d->type = DOLTERMS;
}
else {
d->where[0] = 4; d->where[1] = (WORD)value; d->where[2] = 1;
d->where[3] = 3*sgn; d->where[4] = 0;
d->type = DOLNUMBER;
}
}
else if ( d->type == DOLNUMBER || ( d->type == DOLTERMS
&& d->where[d->where[0]] == 0
&& d->where[0] == ABS(d->where[d->where[0]-1])+1 ) ) {
if ( ( value & AWORDMASK ) != 0 ) {
lnum[0] = value >> BITSINWORD;
lnum[1] = (WORD)value; lnum[2] = 1; lnum[3] = 0;
nnum = 2*sgn;
}
else {
lnum[0] = (WORD)value; lnum[1] = 1; nnum = sgn;
}
i = d->where[d->where[0]-1];
i = REDLENG(i);
if ( dscrat == 0 ) {
dscrat = (UWORD *)Malloc1((AM.MaxTal+2)*sizeof(UWORD),"DollarRaiseLow");
}
if ( AddRat(BHEAD (UWORD *)(d->where+1),i,
(UWORD *)lnum,nnum,dscrat,&ndscrat) ) {
MLOCK(ErrorMessageLock);
MesCall("DollarRaiseLow");
MUNLOCK(ErrorMessageLock);
Terminate(-1);
}
ndscrat = INCLENG(ndscrat);
i = ABS(ndscrat);
if ( i == 0 ) {
M_free(d->where,"DollarRaiseLow");
d->where = 0;
d->type = DOLZERO;
d->size = 0;
return(0);
}
if ( i+2 > d->size ) {
M_free(d->where,"DollarRaiseLow");
d->size = i+2;
if ( d->size < MINALLOC ) d->size = MINALLOC;
d->size = ((d->size+7)/8)*8;
d->where = (WORD *)Malloc1(d->size*sizeof(WORD),"DollarRaiseLow");
}
t1 = d->where; *t1++ = i+1; t2 = (WORD *)dscrat;
while ( --i > 0 ) *t1++ = *t2++;
*t1++ = ndscrat; *t1 = 0;
d->type = DOLTERMS;
}
return(0);
}
/*
#] DollarRaiseLow :
#[ EvalDoLoopArg :
*/
/**
* Evaluates one argument of a do loop. Such an argument is constructed
* from SNUMBERs DOLLAREXPRESSIONs and possibly DOLLAREXPR2s which indicate
* factors of the preceeding dollar. Hence we have
* SNUMBER,num
* DOLLAREXPRESSION,numdollar
* DOLLAREXPRESSION,numdollar,DOLLAREXPR2,numfactor
* DOLLAREXPRESSION,numdollar,DOLLAREXPR2,numfactor,DOLLAREXPR2,numfactor
* etc.
* Because we have a do-loop at every stage we should have a number.
* The notation in DOLLAREXPR2 is that >= 0 is number of yet another dollar
* and < 0 is -n-1 with n the array element or zero.
* The return value is the (short) number.
* The routine works its way through the list in a recursive manner.
*/
WORD EvalDoLoopArg(PHEAD WORD *arg, WORD par)
{
WORD num, type, *td;
DOLLARS d;
if ( *arg == SNUMBER ) return(arg[1]);
if ( *arg == DOLLAREXPR2 && arg[1] < 0 ) return(-arg[1]-1);
d = Dollars + arg[1];
#ifdef WITHPTHREADS
{
int nummodopt, dtype = -1;
if ( AS.MultiThreaded && ( AC.mparallelflag == PARALLELFLAG ) ) {
for ( nummodopt = 0; nummodopt < NumModOptdollars; nummodopt++ ) {
if ( arg[1] == ModOptdollars[nummodopt].number ) break;
}
if ( nummodopt < NumModOptdollars ) {
dtype = ModOptdollars[nummodopt].type;
if ( dtype == MODLOCAL ) {
d = ModOptdollars[nummodopt].dstruct+AT.identity;
}
}
}
}
#endif
if ( *arg == DOLLAREXPRESSION ) {
if ( arg[2] != DOLLAREXPR2 ) { /* end of chain */
endofchain:
type = d->type;
if ( type == DOLZERO ) {}
else if ( type == DOLNUMBER ) {
td = d->where;
if ( ( td[0] != 4 ) || ( (td[1]&SPECMASK) != 0 ) || ( td[2] != 1 ) ) {
MLOCK(ErrorMessageLock);
if ( par == -1 ) {
MesPrint("$-variable is not a short number in print statement");
}
else {
MesPrint("$-variable is not a short number in do loop");
}
MUNLOCK(ErrorMessageLock);
Terminate(-1);
}
return( td[3] > 0 ? td[1]: -td[1] );
}
else {
MLOCK(ErrorMessageLock);
if ( par == -1 ) {
MesPrint("$-variable is not a number in print statement");
}
else {
MesPrint("$-variable is not a number in do loop");
}
MUNLOCK(ErrorMessageLock);
Terminate(-1);
}
return(0);
}
num = EvalDoLoopArg(BHEAD arg+2,par);
}
else if ( *arg == DOLLAREXPR2 ) {
if ( arg[1] < 0 ) { num = -arg[1]-1; }
else if ( arg[2] != DOLLAREXPR2 && par == -1 ) {
goto endofchain;
}
else { num = EvalDoLoopArg(BHEAD arg+2,par); }
}
else {
MLOCK(ErrorMessageLock);
if ( par == -1 ) {
MesPrint("Invalid $-variable in print statement");
}
else {
MesPrint("Invalid $-variable in do loop");
}
MUNLOCK(ErrorMessageLock);
Terminate(-1);
return(0);
}
if ( num == 0 ) return(d->nfactors);
if ( num > d->nfactors || num < 1 ) {
MLOCK(ErrorMessageLock);
if ( par == -1 ) {
MesPrint("Not a valid factor number for $-variable in print statement");
}
else {
MesPrint("Not a valid factor number for $-variable in do loop");
}
MUNLOCK(ErrorMessageLock);
Terminate(-1);
return(0);
}
if ( d->factors[num].type == DOLNUMBER )
return(d->factors[num].value);
else { /* If correct, type can only be DOLNUMBER or DOLTERMS */
MLOCK(ErrorMessageLock);
if ( par == -1 ) {
MesPrint("$-variable in print statement is not a number");
}
else {
MesPrint("$-variable in do loop is not a number");
}
MUNLOCK(ErrorMessageLock);
Terminate(-1);
return(0);
}
}
/*
#] EvalDoLoopArg :
#[ TestDoLoop :
*/
WORD TestDoLoop(PHEAD WORD *lhsbuf, WORD level)
{
GETBIDENTITY
WORD start,finish,incr;
WORD *h;
DOLLARS d;
h = lhsbuf + 4; /* address of the start value */
start = EvalDoLoopArg(BHEAD h,0);
while ( ( *h == DOLLAREXPRESSION || *h == DOLLAREXPR2 )
&& ( h[2] == DOLLAREXPR2 ) ) h += 2;
h += 2;
finish = EvalDoLoopArg(BHEAD h,0);
while ( ( *h == DOLLAREXPRESSION || *h == DOLLAREXPR2 )
&& ( h[2] == DOLLAREXPR2 ) ) h += 2;
h += 2;
incr = EvalDoLoopArg(BHEAD h,0);
if ( ( finish == start ) || ( finish > start && incr > 0 )
|| ( finish < start && incr < 0 ) ) {}
else { level = lhsbuf[3]; } /* skips the loop */
/*
Put start in the dollar variable indicated by lhsbuf[2]
*/
d = Dollars + lhsbuf[2];
#ifdef WITHPTHREADS
{
int nummodopt, dtype = -1;
if ( AS.MultiThreaded && ( AC.mparallelflag == PARALLELFLAG ) ) {
for ( nummodopt = 0; nummodopt < NumModOptdollars; nummodopt++ ) {
if ( lhsbuf[2] == ModOptdollars[nummodopt].number ) break;
}
if ( nummodopt < NumModOptdollars ) {
dtype = ModOptdollars[nummodopt].type;
if ( dtype == MODLOCAL ) {
d = ModOptdollars[nummodopt].dstruct+AT.identity;
}
}
}
}
#endif
if ( d->size < MINALLOC ) {
if ( d->where && d->where != &(AM.dollarzero) ) M_free(d->where,"dollar contents");
d->size = MINALLOC;
d->where = (WORD *)Malloc1(d->size*sizeof(WORD),"dollar contents");
}
if ( start > 0 ) {
d->where[0] = 4;
d->where[1] = start;
d->where[2] = 1;
d->where[3] = 3;
d->where[4] = 0;
d->type = DOLNUMBER;
}
else if ( start < 0 ) {
d->where[0] = 4;
d->where[1] = -start;
d->where[2] = 1;
d->where[3] = -3;
d->where[4] = 0;
d->type = DOLNUMBER;
}
else
d->type = DOLZERO;
if ( d == Dollars + lhsbuf[2] ) {
cbuf[AM.dbufnum].CanCommu[lhsbuf[2]] = 0;
cbuf[AM.dbufnum].NumTerms[lhsbuf[2]] = 1;
cbuf[AM.dbufnum].rhs[lhsbuf[2]] = d->where;
}
return(level);
}
/*
#] TestDoLoop :
#[ TestEndDoLoop :
*/
WORD TestEndDoLoop(PHEAD WORD *lhsbuf, WORD level)
{
GETBIDENTITY
WORD start,finish,incr,value;
WORD *h;
DOLLARS d;
h = lhsbuf + 4; /* address of the start value */
start = EvalDoLoopArg(BHEAD h,0);
while ( ( *h == DOLLAREXPRESSION || *h == DOLLAREXPR2 )
&& ( h[2] == DOLLAREXPR2 ) ) h += 2;
h += 2;
finish = EvalDoLoopArg(BHEAD h,0);
while ( ( *h == DOLLAREXPRESSION || *h == DOLLAREXPR2 )
&& ( h[2] == DOLLAREXPR2 ) ) h += 2;
h += 2;
incr = EvalDoLoopArg(BHEAD h,0);
if ( ( finish == start ) || ( finish > start && incr > 0 )
|| ( finish < start && incr < 0 ) ) {}
else { level = lhsbuf[3]; } /* skips the loop */
/*
Put start in the dollar variable indicated by lhsbuf[2]
*/
d = Dollars + lhsbuf[2];
#ifdef WITHPTHREADS
{
int nummodopt, dtype = -1;
if ( AS.MultiThreaded && ( AC.mparallelflag == PARALLELFLAG ) ) {
for ( nummodopt = 0; nummodopt < NumModOptdollars; nummodopt++ ) {
if ( lhsbuf[2] == ModOptdollars[nummodopt].number ) break;
}
if ( nummodopt < NumModOptdollars ) {
dtype = ModOptdollars[nummodopt].type;
if ( dtype == MODLOCAL ) {
d = ModOptdollars[nummodopt].dstruct+AT.identity;
}
}
}
}
#endif
/*
Get the value
*/
if ( d->type == DOLZERO ) {
value = 0;
}
else if ( ( d->type == DOLNUMBER || d->type == DOLTERMS )
&& ( d->where[4] == 0 ) && ( d->where[0] == 4 )
&& ( d->where[1] > 0 ) && ( d->where[2] == 1 ) ) {
value = ( d->where[3] < 0 ) ? -d->where[1]: d->where[1];
}
else {
MLOCK(ErrorMessageLock);
MesPrint("Wrong type of object in do loop parameter");
MUNLOCK(ErrorMessageLock);
Terminate(-1);
return(level);
}
value += incr;
if ( ( finish > start && value <= finish ) ||
( finish < start && value >= finish ) ||
( finish == start && value == finish ) ) {}
else level = lhsbuf[3];
if ( d->size < MINALLOC ) {
if ( d->where && d->where != &(AM.dollarzero) ) M_free(d->where,"dollar contents");
d->size = MINALLOC;
d->where = (WORD *)Malloc1(d->size*sizeof(WORD),"dollar contents");
}
if ( value > 0 ) {
d->where[0] = 4;
d->where[1] = value;
d->where[2] = 1;
d->where[3] = 3;
d->where[4] = 0;
d->type = DOLNUMBER;
}
else if ( start < 0 ) {
d->where[0] = 4;
d->where[1] = -value;
d->where[2] = 1;
d->where[3] = -3;
d->where[4] = 0;
d->type = DOLNUMBER;
}
else
d->type = DOLZERO;
if ( d == Dollars + lhsbuf[2] ) {
cbuf[AM.dbufnum].CanCommu[lhsbuf[2]] = 0;
cbuf[AM.dbufnum].NumTerms[lhsbuf[2]] = 1;
cbuf[AM.dbufnum].rhs[lhsbuf[2]] = d->where;
}
return(level);
}
/*
#] TestEndDoLoop :
#[ DollarFactorize :
*/
/**
* Factors a dollar expression.
* Notation: d->nfactors becomes nonzero.
* if the number of factors is one, we leave d->factors zero.
* Otherwise factors is an array of pointers to the factors.
* These are pointers of the type FACDOLLAR.
* fd->where pointer to contents in term notation
* fd->size size of the buffer fd->where points to
* fd->type DOLNUMBER or DOLTERMS
* fd->value value if type is DOLNUMBER and it fits in a WORD.
*/
/* #define STEP2 */
#define STEP2
int DollarFactorize(PHEAD WORD numdollar)
{
GETBIDENTITY
DOLLARS d = Dollars + numdollar;
CBUF *C, *CC;
WORD *oldworkpointer;
WORD *buf1, *t, *term, *buf1content, *buf2, *termextra;
WORD *buf3, *argextra;
#ifdef STEP2
WORD *tstop, pow, *r;
#endif
int i, j, jj, action = 0, sign = 1;
LONG insize, ii;
WORD startebuf = cbuf[AT.ebufnum].numrhs;
WORD nfactors, factorsincontent, extrafactor = 0;
WORD oldsorttype = AR.SortType;
#ifdef WITHPTHREADS
int nummodopt, dtype;
dtype = -1;
if ( AS.MultiThreaded ) {
for ( nummodopt = 0; nummodopt < NumModOptdollars; nummodopt++ ) {
if ( numdollar == ModOptdollars[nummodopt].number ) break;
}
if ( nummodopt < NumModOptdollars ) {
dtype = ModOptdollars[nummodopt].type;
if ( dtype == MODLOCAL ) {
d = ModOptdollars[nummodopt].dstruct+AT.identity;
}
else {
LOCK(d->pthreadslockread);
}
}
}
#endif
CleanDollarFactors(d);
#ifdef WITHPTHREADS
if ( dtype > 0 && dtype != MODLOCAL ) { UNLOCK(d->pthreadslockread); }
#endif
if ( d->type != DOLTERMS ) { /* only one term */
if ( d->type != DOLZERO ) d->nfactors = 1;
return(0);
}
if ( d->where[d->where[0]] == 0 ) { /* only one term. easy */
}
/*
Here should come the code for the factorization
We copied the routine ArgFactorize in argument.c and changed the
memory management completely. For the actual factorization it
calls WORD *DoFactorizeDollar(PHEAD WORD *expr) which allocates
space for the answer. Notation:
term,...,term,0,term,...,term,0,term,...,term,0,0
#[ Step 1: sort the terms properly and/or make copy --> buf1,insize
*/
term = d->where;
AR.SortType = SORTHIGHFIRST;
if ( oldsorttype != AR.SortType ) {
NewSort(BHEAD0);
while ( *term ) {
t = term + *term;
if ( AN.ncmod != 0 ) {
if ( AN.ncmod != 1 || ( (WORD)AN.cmod[0] < 0 ) ) {
AR.SortType = oldsorttype;
MLOCK(ErrorMessageLock);
MesPrint("Factorization modulus a number, greater than a WORD not implemented.");
MUNLOCK(ErrorMessageLock);
Terminate(-1);
}
if ( Modulus(term) ) {
AR.SortType = oldsorttype;
MLOCK(ErrorMessageLock);
MesCall("DollarFactorize");
MUNLOCK(ErrorMessageLock);
Terminate(-1);
}
if ( !*term) { term = t; continue; }
}
StoreTerm(BHEAD term);
term = t;
}
AN.tryterm = 0; /* for now */
EndSort(BHEAD (WORD *)((void *)(&buf1)),2);
t = buf1; while ( *t ) t += *t;
insize = t - buf1;
}
else {
t = term; while ( *t ) t += *t;
ii = insize = t - term;
buf1 = (WORD *)Malloc1((insize+1)*sizeof(WORD),"DollarFactorize-1");
t = buf1;
NCOPY(t,term,ii);
*t++ = 0;
}
/*
#] Step 1:
#[ Step 2: take out the 'content'.
*/
#ifdef STEP2
buf1content = TermMalloc("DollarContent");
AN.tryterm = -1;
if ( ( buf2 = TakeContent(BHEAD buf1,buf1content) ) == 0 ) {
AN.tryterm = 0;
TermFree(buf1content,"DollarContent");
M_free(buf1,"DollarFactorize-1");
AR.SortType = oldsorttype;
MLOCK(ErrorMessageLock);
MesCall("DollarFactorize");
MUNLOCK(ErrorMessageLock);
Terminate(-1);
return(1);
}
else if ( ( buf1content[0] == 4 ) && ( buf1content[1] == 1 ) &&
( buf1content[2] == 1 ) && ( buf1content[3] == 3 ) ) { /* Nothing happened */
AN.tryterm = 0;
if ( buf2 != buf1 ) {
M_free(buf2,"DollarFactorize-2");
buf2 = buf1;
}
factorsincontent = 0;
}
else {
/*
The way we took out objects is rather brutish. We have to normalize
*/
AN.tryterm = 0;
if ( buf2 != buf1 ) M_free(buf1,"DollarFactorize-1");
buf1 = buf2;
t = buf1; while ( *t ) t += *t;
insize = t - buf1;
/*
Now analyse how many factors there are in the content
*/
factorsincontent = 0;
term = buf1content;
tstop = term + *term;
if ( tstop[-1] < 0 ) factorsincontent++;
if ( ABS(tstop[-1]) == 3 && tstop[-2] == 1 && tstop[-3] == 1 ) {
tstop -= ABS(tstop[-1]);
}
else {
factorsincontent++;
tstop -= ABS(tstop[-1]);
}
term++;
while ( term < tstop ) {
switch ( *term ) {
case SYMBOL:
t = term+2; i = (term[1]-2)/2;
while ( i > 0 ) {
factorsincontent += ABS(t[1]);
i--; t += 2;
}
break;
case DOTPRODUCT:
t = term+2; i = (term[1]-2)/3;
while ( i > 0 ) {
factorsincontent += ABS(t[2]);
i--; t += 3;
}
break;
case VECTOR:
case DELTA:
factorsincontent += (term[1]-2)/2;
break;
case INDEX:
factorsincontent += term[1]-2;
break;
default:
if ( *term >= FUNCTION ) factorsincontent++;
break;
}
term += term[1];
}
}
#else
factorsincontent = 0;
buf1content = 0;
#endif
/*
#] Step 2: take out the 'content'.
#[ Step 3: ConvertToPoly
if there are objects that are not SYMBOLs,
invoke ConvertToPoly
We keep the original in buf1 in case there are no factors
*/
t = buf1;
while ( *t ) {
if ( ( t[1] != SYMBOL ) && ( *t != (ABS(t[*t-1])+1) ) ) {
action = 1; break;
}
t += *t;
}
if ( DetCommu(buf1) > 1 ) {
MesPrint("Cannot factorize a $-expression with more than one noncommuting object");
AR.SortType = oldsorttype;
M_free(buf1,"DollarFactorize-2");
if ( buf1content ) TermFree(buf1content,"DollarContent");
MesCall("DollarFactorize");
Terminate(-1);
return(-1);
}
if ( action ) {
t = buf1;
termextra = AT.WorkPointer;
NewSort(BHEAD0);
NewSort(BHEAD0);
while ( *t ) {
if ( LocalConvertToPoly(BHEAD t,termextra,startebuf,0) < 0 ) {
getout:
AR.SortType = oldsorttype;
M_free(buf1,"DollarFactorize-2");
if ( buf1content ) TermFree(buf1content,"DollarContent");
MesCall("DollarFactorize");
Terminate(-1);
return(-1);
}
StoreTerm(BHEAD termextra);
t += *t;
}
AN.tryterm = 0; /* for now */
if ( EndSort(BHEAD (WORD *)((void *)(&buf2)),2) < 0 ) { goto getout; }
LowerSortLevel();
t = buf2; while ( *t > 0 ) t += *t;
}
else {
buf2 = buf1;
}
/*
#] Step 3: ConvertToPoly
#[ Step 4: Now the hard work.
*/
if ( ( buf3 = poly_factorize_dollar(BHEAD buf2) ) == 0 ) {
MesCall("DollarFactorize");
AR.SortType = oldsorttype;
if ( buf2 != buf1 && buf2 ) M_free(buf2,"DollarFactorize-3");
M_free(buf1,"DollarFactorize-3");
if ( buf1content ) TermFree(buf1content,"DollarContent");
Terminate(-1);
return(-1);
}
if ( buf2 != buf1 && buf2 ) {
M_free(buf2,"DollarFactorize-3");
buf2 = 0;
}
term = buf3;
AR.SortType = oldsorttype;
/*
Count the factors and strip a factor -1
*/
nfactors = 0;
while ( *term ) {
#ifdef STEP2
if ( *term == 4 && term[4] == 0 && term[3] == -3 && term[2] == 1
&& term[1] == 1 ) {
WORD *tt1, *tt2, *ttstop;
sign = -sign;
tt1 = term; tt2 = term + *term + 1;
ttstop = tt2;
while ( *ttstop ) {
while ( *ttstop ) ttstop += *ttstop;
ttstop++;
}
while ( tt2 < ttstop ) *tt1++ = *tt2++;
*tt1 = 0;
factorsincontent++;
extrafactor++;
}
else
#endif
{
term += *term;
while ( *term ) { term += *term; }
nfactors++; term++;
}
}
/*
We have now:
buf1: the original before ConvertToPoly for if only one factor
buf3: the factored expression with nfactors factors
#] Step 4:
#[ Step 5: ConvertFromPoly
If ConvertToPoly was used, use now ConvertFromPoly
Be careful: there should be more than one factor now.
*/
#ifdef WITHPTHREADS
if ( dtype > 0 && dtype != MODLOCAL ) { LOCK(d->pthreadslockread); }
#endif
if ( nfactors == 1 && extrafactor == 0 ) { /* we can use the buf1 contents */
if ( factorsincontent == 0 ) {
d->nfactors = 1;
#ifdef WITHPTHREADS
if ( dtype > 0 && dtype != MODLOCAL ) { UNLOCK(d->pthreadslockread); }
#endif
/*
We used here (before 3-sep-2015) the original and did not make
provisions for having a factors struct, figuring that all info
is identical to the full dollar. This makes things too
complicated at later stages.
*/
d->factors = (FACDOLLAR *)Malloc1(sizeof(FACDOLLAR),"factors in dollar");
term = buf1; while ( *term ) term += *term;
d->factors[0].size = i = term - buf1;
d->factors[0].where = t = (WORD *)Malloc1(sizeof(WORD)*(i+1),"DollarFactorize-5");
term = buf1; NCOPY(t,term,i); *t = 0;
AR.SortType = oldsorttype;
M_free(buf3,"DollarFactorize-4");
if ( buf2 != buf1 && buf2 ) M_free(buf2,"DollarFactorize-4");
M_free(buf1,"DollarFactorize-4");
if ( buf1content ) TermFree(buf1content,"DollarContent");
return(0);
}
else {
d->factors = (FACDOLLAR *)Malloc1(sizeof(FACDOLLAR)*(nfactors+factorsincontent),"factors in dollar");
term = buf1; while ( *term ) term += *term;
d->factors[0].size = i = term - buf1;
d->factors[0].where = t = (WORD *)Malloc1(sizeof(WORD)*(i+1),"DollarFactorize-5");
term = buf1; NCOPY(t,term,i); *t = 0;
M_free(buf3,"DollarFactorize-4");
buf3 = 0;
if ( buf2 != buf1 && buf2 ) {
M_free(buf2,"DollarFactorize-4");
buf2 = 0;
}
}
}
else if ( action ) {
C = cbuf+AC.cbufnum;
CC = cbuf+AT.ebufnum;
oldworkpointer = AT.WorkPointer;
d->factors = (FACDOLLAR *)Malloc1(sizeof(FACDOLLAR)*(nfactors+factorsincontent),"factors in dollar");
term = buf3;
for ( i = 0; i < nfactors; i++ ) {
argextra = AT.WorkPointer;
NewSort(BHEAD0);
NewSort(BHEAD0);
while ( *term ) {
if ( ConvertFromPoly(BHEAD term,argextra,numxsymbol,CC->numrhs-startebuf+numxsymbol
,startebuf-numxsymbol,1) <= 0 ) {
LowerSortLevel();
getout2: AR.SortType = oldsorttype;
M_free(d->factors,"factors in dollar");
d->factors = 0;
#ifdef WITHPTHREADS
if ( dtype > 0 && dtype != MODLOCAL ) { UNLOCK(d->pthreadslockread); }
#endif
M_free(buf3,"DollarFactorize-4");
if ( buf2 != buf1 && buf2 ) M_free(buf2,"DollarFactorize-4");
M_free(buf1,"DollarFactorize-4");
if ( buf1content ) TermFree(buf1content,"DollarContent");
return(-3);
}
AT.WorkPointer = argextra + *argextra;
/*
ConvertFromPoly leaves terms with subexpressions. Hence:
*/
if ( Generator(BHEAD argextra,C->numlhs+1) ) {
goto getout2;
}
term += *term;
}
term++;
AT.WorkPointer = oldworkpointer;
AN.tryterm = 0; /* for now */
EndSort(BHEAD (WORD *)((void *)(&(d->factors[i].where))),2);
LowerSortLevel();
d->factors[i].type = DOLTERMS;
t = d->factors[i].where;
while ( *t ) t += *t;
d->factors[i].size = t - d->factors[i].where;
}
CC->numrhs = startebuf;
}
else {
C = cbuf+AC.cbufnum;
oldworkpointer = AT.WorkPointer;
d->factors = (FACDOLLAR *)Malloc1(sizeof(FACDOLLAR)*(nfactors+factorsincontent),"factors in dollar");
term = buf3;
for ( i = 0; i < nfactors; i++ ) {
NewSort(BHEAD0);
while ( *term ) {
argextra = oldworkpointer;
j = *term;
NCOPY(argextra,term,j)
AT.WorkPointer = argextra;
if ( Generator(BHEAD oldworkpointer,C->numlhs+1) ) {
goto getout2;
}
}
term++;
AT.WorkPointer = oldworkpointer;
AN.tryterm = 0; /* for now */
EndSort(BHEAD (WORD *)((void *)(&(d->factors[i].where))),2);
d->factors[i].type = DOLTERMS;
t = d->factors[i].where;
while ( *t ) t += *t;
d->factors[i].size = t - d->factors[i].where;
}
}
d->nfactors = nfactors + factorsincontent;
/*
#] Step 5: ConvertFromPoly
#[ Step 6: The factors of the content
*/
if ( buf3 ) M_free(buf3,"DollarFactorize-5");
if ( buf2 != buf1 && buf2 ) M_free(buf2,"DollarFactorize-5");
M_free(buf1,"DollarFactorize-5");
j = nfactors;
#ifdef STEP2
term = buf1content;
tstop = term + *term;
if ( tstop[-1] < 0 ) { tstop[-1] = -tstop[-1]; sign = -sign; }
tstop -= tstop[-1];
term++;
while ( term < tstop ) {
switch ( *term ) {
case SYMBOL:
t = term+2; i = (term[1]-2)/2;
while ( i > 0 ) {
if ( t[1] < 0 ) { t[1] = -t[1]; pow = -1; }
else { pow = 1; }
for ( jj = 0; jj < t[1]; jj++ ) {
r = d->factors[j].where = (WORD *)Malloc1(9*sizeof(WORD),"factor");
r[0] = 8; r[1] = SYMBOL; r[2] = 4; r[3] = *t; r[4] = pow;
r[5] = 1; r[6] = 1; r[7] = 3; r[8] = 0;
d->factors[j].type = DOLTERMS;
d->factors[j].size = 8;
j++;
}
i--; t += 2;
}
break;
case DOTPRODUCT:
t = term+2; i = (term[1]-2)/3;
while ( i > 0 ) {
if ( t[2] < 0 ) { t[2] = -t[2]; pow = -1; }
else { pow = 1; }
for ( jj = 0; jj < t[2]; jj++ ) {
r = d->factors[j].where = (WORD *)Malloc1(10*sizeof(WORD),"factor");
r[0] = 9; r[1] = DOTPRODUCT; r[2] = 5; r[3] = t[0]; r[4] = t[1];
r[5] = pow; r[6] = 1; r[7] = 1; r[8] = 3; r[9] = 0;
d->factors[j].type = DOLTERMS;
d->factors[j].size = 9;
j++;
}
i--; t += 3;
}
break;
case VECTOR:
case DELTA:
t = term+2; i = (term[1]-2)/2;
while ( i > 0 ) {
for ( jj = 0; jj < t[1]; jj++ ) {
r = d->factors[j].where = (WORD *)Malloc1(9*sizeof(WORD),"factor");
r[0] = 8; r[1] = *term; r[2] = 4; r[3] = *t; r[4] = t[1];
r[5] = 1; r[6] = 1; r[7] = 3; r[8] = 0;
d->factors[j].type = DOLTERMS;
d->factors[j].size = 8;
j++;
}
i--; t += 2;
}
break;
case INDEX:
t = term+2; i = term[1]-2;
while ( i > 0 ) {
for ( jj = 0; jj < t[1]; jj++ ) {
r = d->factors[j].where = (WORD *)Malloc1(8*sizeof(WORD),"factor");
r[0] = 7; r[1] = *term; r[2] = 3; r[3] = *t;
r[4] = 1; r[5] = 1; r[6] = 3; r[7] = 0;
d->factors[j].type = DOLTERMS;
d->factors[j].size = 7;
j++;
}
i--; t++;
}
break;
default:
if ( *term >= FUNCTION ) {
r = d->factors[j].where = (WORD *)Malloc1((term[1]+5)*sizeof(WORD),"factor");
*r++ = d->factors[j].size = term[1]+4;
for ( jj = 0; jj < t[1]; jj++ ) *r++ = term[jj];
*r++ = 1; *r++ = 1; *r++ = 3; *r = 0;
j++;
}
break;
}
term += term[1];
}
#endif
/*
#] Step 6:
#[ Step 7: Numerical factors
*/
#ifdef STEP2
term = buf1content;
tstop = term + *term;
if ( tstop[-1] == 3 && tstop[-2] == 1 && tstop[-3] == 1 ) {}
else if ( tstop[-1] == 3 && tstop[-2] == 1 && (UWORD)(tstop[-3]) <= MAXPOSITIVE ) {
d->factors[j].where = 0;
d->factors[j].size = 0;
d->factors[j].type = DOLNUMBER;
d->factors[j].value = sign*tstop[-3];
sign = 1;
j++;
}
else {
d->factors[j].where = r = (WORD *)Malloc1((tstop[-1]+2)*sizeof(WORD),"numfactor");
d->factors[j].size = tstop[-1]+1;
d->factors[j].type = DOLTERMS;
d->factors[j].value = 0;
i = tstop[-1];
t = tstop - i;
*r++ = tstop[-1]+1;
NCOPY(r,t,i);
*r = 0;
if ( sign < 0 ) {
r = d->factors[j].where;
while ( *r ) {
r += *r; r[-1] = -r[-1];
}
sign = 1;
}
j++;
}
#endif
if ( sign < 0 ) { /* Note that this guy should come first */
for ( jj = j; jj > 0; jj-- ) {
d->factors[jj] = d->factors[jj-1];
}
d->factors[0].where = 0;
d->factors[0].size = 0;
d->factors[0].type = DOLNUMBER;
d->factors[0].value = -1;
j++;
}
d->nfactors = j;
if ( buf1content ) TermFree(buf1content,"DollarContent");
/*
#] Step 7:
#[ Step 8: Sorting the factors
There are d->nfactors factors. Look which ones have a 'where'
Sort them by bubble sort
*/
if ( d->nfactors > 1 ) {
WORD ***fac, j1, j2, k, ret, *s1, *s2, *s3;
LONG **facsize, x;
facsize = (LONG **)Malloc1((sizeof(WORD **)+sizeof(LONG *))*d->nfactors,"SortDollarFactors");
fac = (WORD ***)(facsize+d->nfactors);
k = 0;
for ( j = 0; j < d->nfactors; j++ ) {
if ( d->factors[j].where ) {
fac[k] = &(d->factors[j].where);
facsize[k] = &(d->factors[j].size);
k++;
}
}
if ( k > 1 ) {
for ( j = 1; j < k; j++ ) { /* bubble sort */
j1 = j; j2 = j1-1;
nextj1:;
s1 = *(fac[j1]); s2 = *(fac[j2]);
while ( *s1 && *s2 ) {
if ( ( ret = CompareTerms(s2, s1, (WORD)2) ) == 0 ) {
s1 += *s1; s2 += *s2;
}
else if ( ret > 0 ) goto nextj;
else {
exch:
s3 = *(fac[j1]); *(fac[j1]) = *(fac[j2]); *(fac[j2]) = s3;
x = *(facsize[j1]); *(facsize[j1]) = *(facsize[j2]); *(facsize[j2]) = x;
j1--; j2--;
if ( j1 > 0 ) goto nextj1;
goto nextj;
}
}
if ( *s1 ) goto nextj;
if ( *s2 ) goto exch;
nextj:;
}
}
M_free(facsize,"SortDollarFactors");
}
/*
#] Step 8:
*/
#ifdef WITHPTHREADS
if ( dtype > 0 && dtype != MODLOCAL ) { UNLOCK(d->pthreadslockread); }
#endif
return(0);
}
/*
#] DollarFactorize :
#[ CleanDollarFactors :
*/
void CleanDollarFactors(DOLLARS d)
{
int i;
if ( d->nfactors > 1 ) {
for ( i = 0; i < d->nfactors; i++ ) {
if ( d->factors[i].where )
M_free(d->factors[i].where,"dollar factors");
}
}
if ( d->factors ) {
M_free(d->factors,"dollar factors");
d->factors = 0;
}
d->nfactors = 0;
}
/*
#] CleanDollarFactors :
#[ TakeDollarContent :
*/
WORD *TakeDollarContent(PHEAD WORD *dollarbuffer, WORD **factor)
{
WORD *remain, *t;
int pow;
/*
We force the sign of the first term to be positive.
*/
t = dollarbuffer; pow = 1;
t += *t;
if ( t[-1] < 0 ) {
pow = 0;
t[-1] = -t[-1];
while ( *t ) {
t += *t; t[-1] = -t[-1];
}
}
/*
Now the GCD of the numerators and the LCM of the denominators:
*/
if ( AN.cmod != 0 ) {
if ( ( *factor = MakeDollarMod(BHEAD dollarbuffer,&remain) ) == 0 ) {
Terminate(-1);
}
if ( pow == 0 ) {
(*factor)[**factor-1] = -(*factor)[**factor-1];
(*factor)[**factor-1] += AN.cmod[0];
}
}
else {
if ( ( *factor = MakeDollarInteger(BHEAD dollarbuffer,&remain) ) == 0 ) {
Terminate(-1);
}
if ( pow == 0 ) {
(*factor)[**factor-1] = -(*factor)[**factor-1];
}
}
return(remain);
}
/*
#] TakeDollarContent :
#[ MakeDollarInteger :
*/
/**
* For normalizing everything to integers we have to
* determine for all elements of this argument the LCM of
* the denominators and the GCD of the numerators.
* The input argument is in bufin.
* The number that comes out is the return value.
* The normalized argument is in bufout.
*/
WORD *MakeDollarInteger(PHEAD WORD *bufin,WORD **bufout)
{
GETBIDENTITY
UWORD *GCDbuffer, *GCDbuffer2, *LCMbuffer, *LCMb, *LCMc;
WORD *r, *r1, *r2, *r3, *rnext, i, k, j, *oldworkpointer, *factor;
WORD kGCD, kLCM, kGCD2, kkLCM, jLCM, jGCD;
CBUF *C = cbuf+AC.cbufnum;
GCDbuffer = NumberMalloc("MakeDollarInteger");
GCDbuffer2 = NumberMalloc("MakeDollarInteger");
LCMbuffer = NumberMalloc("MakeDollarInteger");
LCMb = NumberMalloc("MakeDollarInteger");
LCMc = NumberMalloc("MakeDollarInteger");
r = bufin;
/*
First take the first term to load up the LCM and the GCD
*/
r2 = r + *r;
j = r2[-1];
r3 = r2 - ABS(j);
k = REDLENG(j);
if ( k < 0 ) k = -k;
while ( ( k > 1 ) && ( r3[k-1] == 0 ) ) k--;
for ( kGCD = 0; kGCD < k; kGCD++ ) GCDbuffer[kGCD] = r3[kGCD];
k = REDLENG(j);
if ( k < 0 ) k = -k;
r3 += k;
while ( ( k > 1 ) && ( r3[k-1] == 0 ) ) k--;
for ( kLCM = 0; kLCM < k; kLCM++ ) LCMbuffer[kLCM] = r3[kLCM];
r1 = r2;
/*
Now go through the rest of the terms in this argument.
*/
while ( *r1 ) {
r2 = r1 + *r1;
j = r2[-1];
r3 = r2 - ABS(j);
k = REDLENG(j);
if ( k < 0 ) k = -k;
while ( ( k > 1 ) && ( r3[k-1] == 0 ) ) k--;
if ( ( ( GCDbuffer[0] == 1 ) && ( kGCD == 1 ) ) ) {
/*
GCD is already 1
*/
}
else if ( ( ( k != 1 ) || ( r3[0] != 1 ) ) ) {
if ( GcdLong(BHEAD GCDbuffer,kGCD,(UWORD *)r3,k,GCDbuffer2,&kGCD2) ) {
goto MakeDollarIntegerErr;
}
kGCD = kGCD2;
for ( i = 0; i < kGCD; i++ ) GCDbuffer[i] = GCDbuffer2[i];
}
else {
kGCD = 1; GCDbuffer[0] = 1;
}
k = REDLENG(j);
if ( k < 0 ) k = -k;
r3 += k;
while ( ( k > 1 ) && ( r3[k-1] == 0 ) ) k--;
if ( ( ( LCMbuffer[0] == 1 ) && ( kLCM == 1 ) ) ) {
for ( kLCM = 0; kLCM < k; kLCM++ )
LCMbuffer[kLCM] = r3[kLCM];
}
else if ( ( k != 1 ) || ( r3[0] != 1 ) ) {
if ( GcdLong(BHEAD LCMbuffer,kLCM,(UWORD *)r3,k,LCMb,&kkLCM) ) {
goto MakeDollarIntegerErr;
}
DivLong((UWORD *)r3,k,LCMb,kkLCM,LCMb,&kkLCM,LCMc,&jLCM);
MulLong(LCMbuffer,kLCM,LCMb,kkLCM,LCMc,&jLCM);
for ( kLCM = 0; kLCM < jLCM; kLCM++ )
LCMbuffer[kLCM] = LCMc[kLCM];
}
else {} /* LCM doesn't change */
r1 = r2;
}
/*
Now put the factor together: GCD/LCM
*/
r3 = (WORD *)(GCDbuffer);
if ( kGCD == kLCM ) {
for ( jGCD = 0; jGCD < kGCD; jGCD++ )
r3[jGCD+kGCD] = LCMbuffer[jGCD];
k = kGCD;
}
else if ( kGCD > kLCM ) {
for ( jGCD = 0; jGCD < kLCM; jGCD++ )
r3[jGCD+kGCD] = LCMbuffer[jGCD];
for ( jGCD = kLCM; jGCD < kGCD; jGCD++ )
r3[jGCD+kGCD] = 0;
k = kGCD;
}
else {
for ( jGCD = kGCD; jGCD < kLCM; jGCD++ )
r3[jGCD] = 0;
for ( jGCD = 0; jGCD < kLCM; jGCD++ )
r3[jGCD+kLCM] = LCMbuffer[jGCD];
k = kLCM;
}
j = 2*k+1;
/*
Now we have to write this to factor
*/
factor = r1 = (WORD *)Malloc1((j+2)*sizeof(WORD),"MakeDollarInteger");
*r1++ = j+1; r2 = r3;
for ( i = 0; i < k; i++ ) { *r1++ = *r2++; *r1++ = *r2++; }
*r1++ = j;
*r1 = 0;
/*
Next we have to take the factor out from the argument.
This cannot be done in location, because the denominator stuff can make
coefficients longer.
We do this via a sort because the things may be jumbled any way and we
do not know in advance how much space we need.
*/
NewSort(BHEAD0);
r = bufin;
oldworkpointer = AT.WorkPointer;
while ( *r ) {
rnext = r + *r;
j = ABS(rnext[-1]);
r3 = rnext - j;
r2 = oldworkpointer;
while ( r < r3 ) *r2++ = *r++;
j = (j-1)/2; /* reduced length. Remember, k is the other red length */
if ( DivRat(BHEAD (UWORD *)r3,j,GCDbuffer,k,(UWORD *)r2,&i) ) {
goto MakeDollarIntegerErr;
}
i = 2*i+1;
r2 = r2 + i;
if ( rnext[-1] < 0 ) r2[-1] = -i;
else r2[-1] = i;
*oldworkpointer = r2-oldworkpointer;
AT.WorkPointer = r2;
if ( Generator(BHEAD oldworkpointer,C->numlhs) ) {
goto MakeDollarIntegerErr;
}
r = rnext;
}
AT.WorkPointer = oldworkpointer;
AN.tryterm = 0; /* for now */
EndSort(BHEAD (WORD *)bufout,2);
/*
Cleanup
*/
NumberFree(LCMc,"MakeDollarInteger");
NumberFree(LCMb,"MakeDollarInteger");
NumberFree(LCMbuffer,"MakeDollarInteger");
NumberFree(GCDbuffer2,"MakeDollarInteger");
NumberFree(GCDbuffer,"MakeDollarInteger");
return(factor);
MakeDollarIntegerErr:
NumberFree(LCMc,"MakeDollarInteger");
NumberFree(LCMb,"MakeDollarInteger");
NumberFree(LCMbuffer,"MakeDollarInteger");
NumberFree(GCDbuffer2,"MakeDollarInteger");
NumberFree(GCDbuffer,"MakeDollarInteger");
MesCall("MakeDollarInteger");
Terminate(-1);
return(0);
}
/*
#] MakeDollarInteger :
#[ MakeDollarMod :
*/
/**
* Similar to MakeDollarInteger but now with modulus arithmetic using only
* a one WORD 'prime'. We make the coefficient of the first term in the
* argument equal to one.
* Already the coefficients are taken modulus AN.cmod and AN.ncmod == 1
*/
WORD *MakeDollarMod(PHEAD WORD *buffer, WORD **bufout)
{
GETBIDENTITY
WORD *r, *r1, x, xx, ix, ip;
WORD *factor, *oldworkpointer;
int i;
CBUF *C = cbuf+AC.cbufnum;
r = buffer;
x = r[*r-3];
if ( r[*r-1] < 0 ) x += AN.cmod[0];
if ( GetModInverses(x,(WORD)(AN.cmod[0]),&ix,&ip) ) {
Terminate(-1);
}
factor = (WORD *)Malloc1(5*sizeof(WORD),"MakeDollarMod");
factor[0] = 4; factor[1] = x; factor[2] = 1; factor[3] = 3; factor[4] = 0;
/*
Now we have to multiply all coefficients by ix.
This does not make things longer, but we should keep to the conventions
of MakeDollarInteger.
*/
NewSort(BHEAD0);
r = buffer;
oldworkpointer = AT.WorkPointer;
while ( *r ) {
r1 = oldworkpointer; i = *r;
NCOPY(r1,r,i);
xx = r1[-3]; if ( r1[-1] < 0 ) xx += AN.cmod[0];
r1[-1] = (WORD)((((LONG)xx)*ix) % AN.cmod[0]);
*r1 = 0; AT.WorkPointer = r1;
if ( Generator(BHEAD oldworkpointer,C->numlhs) ) {
Terminate(-1);
}
}
AT.WorkPointer = oldworkpointer;
AN.tryterm = 0; /* for now */
EndSort(BHEAD (WORD *)bufout,2);
return(factor);
}
/*
#] MakeDollarMod :
#[ GetDolNum :
Evaluates a chain of DOLLAREXPR2 into a number
*/
int GetDolNum(PHEAD WORD *t, WORD *tstop)
{
DOLLARS d;
WORD num, *w;
if ( t+3 < tstop && t[3] == DOLLAREXPR2 ) {
d = Dollars + t[2];
#ifdef WITHPTHREADS
{
int nummodopt, dtype;
dtype = -1;
if ( AS.MultiThreaded && ( AC.mparallelflag == PARALLELFLAG ) ) {
for ( nummodopt = 0; nummodopt < NumModOptdollars; nummodopt++ ) {
if ( t[2] == ModOptdollars[nummodopt].number ) break;
}
if ( nummodopt < NumModOptdollars ) {
dtype = ModOptdollars[nummodopt].type;
if ( dtype == MODLOCAL ) {
d = ModOptdollars[nummodopt].dstruct+AT.identity;
}
else {
MLOCK(ErrorMessageLock);
MesPrint("&Illegal attempt to use $-variable %s in module %l",
DOLLARNAME(Dollars,t[2]),AC.CModule);
MUNLOCK(ErrorMessageLock);
Terminate(-1);
}
}
}
}
#endif
if ( d->factors == 0 ) {
MLOCK(ErrorMessageLock);
MesPrint("Attempt to use a factor of an unfactored $-variable");
MUNLOCK(ErrorMessageLock);
Terminate(-1);
}
num = GetDolNum(BHEAD t+t[1],tstop);
if ( num == 0 ) return(d->nfactors);
if ( num > d->nfactors ) {
MLOCK(ErrorMessageLock);
MesPrint("Attempt to use an nonexisting factor %d of a $-variable",num);
MUNLOCK(ErrorMessageLock);
Terminate(-1);
}
w = d->factors[num-1].where;
if ( w == 0 ) return(d->factors[num-1].value);
if ( w[0] == 4 && w[4] == 0 && w[3] == 3 && w[2] == 1 && w[1] > 0
&& w[1] < MAXPOSITIVE ) return(w[1]);
else {
MLOCK(ErrorMessageLock);
MesPrint("Illegal type of factor number of a $-variable");
MUNLOCK(ErrorMessageLock);
Terminate(-1);
}
}
else if ( t[2] < 0 ) {
return(-t[2]-1);
}
else {
d = Dollars + t[2];
#ifdef WITHPTHREADS
{
int nummodopt, dtype;
dtype = -1;
if ( AS.MultiThreaded && ( AC.mparallelflag == PARALLELFLAG ) ) {
for ( nummodopt = 0; nummodopt < NumModOptdollars; nummodopt++ ) {
if ( t[2] == ModOptdollars[nummodopt].number ) break;
}
if ( nummodopt < NumModOptdollars ) {
dtype = ModOptdollars[nummodopt].type;
if ( dtype == MODLOCAL ) {
d = ModOptdollars[nummodopt].dstruct+AT.identity;
}
else {
MLOCK(ErrorMessageLock);
MesPrint("&Illegal attempt to use $-variable %s in module %l",
DOLLARNAME(Dollars,t[2]),AC.CModule);
MUNLOCK(ErrorMessageLock);
Terminate(-1);
}
}
}
}
#endif
if ( d->type == DOLZERO ) return(0);
if ( d->type == DOLTERMS || d->type == DOLNUMBER ) {
if ( d->where[0] == 4 && d->where[4] == 0 && d->where[3] == 3
&& d->where[2] == 1 && d->where[1] > 0
&& d->where[1] < MAXPOSITIVE ) return(d->where[1]);
MLOCK(ErrorMessageLock);
MesPrint("Attempt to use an nonexisting factor of a $-variable");
MUNLOCK(ErrorMessageLock);
Terminate(-1);
}
MLOCK(ErrorMessageLock);
MesPrint("Illegal type of factor number of a $-variable");
MUNLOCK(ErrorMessageLock);
Terminate(-1);
}
return(0);
}
/*
#] GetDolNum :
#[ AddPotModdollar :
*/
/**
* Adds a $-variable specified by \a numdollar to the list of potentially
* modified $-variables unless it has already been included in the list.
*
* @param numdollar The index of the $-variable to be added.
*/
void AddPotModdollar(WORD numdollar)
{
int i, n = NumPotModdollars;
for ( i = 0; i < n; i++ ) {
if ( numdollar == PotModdollars[i] ) break;
}
if ( i >= n ) {
*(WORD *)FromList(&AC.PotModDolList) = numdollar;
}
}
/*
#] AddPotModdollar :
*/
form-master/sources/execute.c 0000664 0000000 0000000 00000211440 13565763364 0016627 0 ustar 00root root 0000000 0000000 /** @file execute.c
*
* The routines that start the execution phase of a module.
* It also contains the routines for placing the bracket subterm.
*/
/* #[ License : */
/*
* Copyright (C) 1984-2017 J.A.M. Vermaseren
* When using this file you are requested to refer to the publication
* J.A.M.Vermaseren "New features of FORM" math-ph/0010025
* This is considered a matter of courtesy as the development was paid
* for by FOM the Dutch physics granting agency and we would like to
* be able to track its scientific use to convince FOM of its value
* for the community.
*
* This file is part of FORM.
*
* FORM 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.
*
* FORM 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 FORM. If not, see .
*/
/* #] License : */
/*
#[ Includes : execute.c
*/
#include "form3.h"
/*
#] Includes :
#[ DoExecute :
#[ CleanExpr :
par == 1 after .store or .clear
par == 0 after .sort
*/
WORD CleanExpr(WORD par)
{
GETIDENTITY
WORD j, n, i;
POSITION length;
EXPRESSIONS e_in, e_out, e;
int numhid = 0;
NAMENODE *node;
n = NumExpressions;
j = 0;
e_in = e_out = Expressions;
if ( n > 0 ) { do {
e_in->vflags &= ~( TOBEFACTORED | TOBEUNFACTORED );
if ( par ) {
if ( e_in->renumlists ) {
if ( e_in->renumlists != AN.dummyrenumlist )
M_free(e_in->renumlists,"Renumber-lists");
e_in->renumlists = 0;
}
if ( e_in->renum ) {
M_free(e_in->renum,"Renumber"); e_in->renum = 0;
}
}
if ( e_in->status == HIDDENLEXPRESSION
|| e_in->status == HIDDENGEXPRESSION ) numhid++;
switch ( e_in->status ) {
case SPECTATOREXPRESSION:
case LOCALEXPRESSION:
case HIDDENLEXPRESSION:
if ( par ) {
AC.exprnames->namenode[e_in->node].type = CDELETE;
AC.DidClean = 1;
if ( e_in->status != HIDDENLEXPRESSION )
ClearBracketIndex(e_in-Expressions);
break;
}
/* fall through */
case GLOBALEXPRESSION:
case HIDDENGEXPRESSION:
if ( par ) {
#ifdef WITHMPI
/*
* Broadcast the global expression from the master to the all workers.
*/
if ( PF_BroadcastExpr(e_in, e_in->status == HIDDENGEXPRESSION ? AR.hidefile : AR.outfile) ) return -1;
if ( PF.me == MASTER ) {
#endif
e = e_in;
i = n-1;
while ( --i >= 0 ) {
e++;
if ( e_in->status == HIDDENGEXPRESSION ) {
if ( e->status == HIDDENGEXPRESSION
|| e->status == HIDDENLEXPRESSION ) break;
}
else {
if ( e->status == GLOBALEXPRESSION
|| e->status == LOCALEXPRESSION ) break;
}
}
#ifdef WITHMPI
}
else {
/*
* On the slaves, the broadcast expression is sitting at the end of the file.
*/
e = e_in;
i = -1;
}
#endif
if ( i >= 0 ) {
DIFPOS(length,e->onfile,e_in->onfile);
}
else {
FILEHANDLE *f = e_in->status == HIDDENGEXPRESSION ? AR.hidefile : AR.outfile;
if ( f->handle < 0 ) {
SETBASELENGTH(length,TOLONG(f->POfull)
- TOLONG(f->PObuffer)
- BASEPOSITION(e_in->onfile));
}
else {
SeekFile(f->handle,&(f->filesize),SEEK_SET);
DIFPOS(length,f->filesize,e_in->onfile);
}
}
if ( ToStorage(e_in,&length) ) {
return(MesCall("CleanExpr"));
}
e_in->status = STOREDEXPRESSION;
if ( e_in->status != HIDDENGEXPRESSION )
ClearBracketIndex(e_in-Expressions);
}
/* fall through */
case SKIPLEXPRESSION:
case DROPLEXPRESSION:
case DROPHLEXPRESSION:
case DROPGEXPRESSION:
case DROPHGEXPRESSION:
case STOREDEXPRESSION:
case DROPSPECTATOREXPRESSION:
if ( e_out != e_in ) {
node = AC.exprnames->namenode + e_in->node;
node->number = e_out - Expressions;
e_out->onfile = e_in->onfile;
e_out->size = e_in->size;
e_out->printflag = 0;
if ( par ) e_out->status = STOREDEXPRESSION;
else e_out->status = e_in->status;
e_out->name = e_in->name;
e_out->node = e_in->node;
e_out->renum = e_in->renum;
e_out->renumlists = e_in->renumlists;
e_out->counter = e_in->counter;
e_out->hidelevel = e_in->hidelevel;
e_out->inmem = e_in->inmem;
e_out->bracketinfo = e_in->bracketinfo;
e_out->newbracketinfo = e_in->newbracketinfo;
e_out->numdummies = e_in->numdummies;
e_out->numfactors = e_in->numfactors;
e_out->vflags = e_in->vflags;
e_out->sizeprototype = e_in->sizeprototype;
}
#ifdef PARALLELCODE
e_out->partodo = 0;
#endif
e_out++;
j++;
break;
case DROPPEDEXPRESSION:
break;
default:
AC.exprnames->namenode[e_in->node].type = CDELETE;
AC.DidClean = 1;
break;
}
e_in++;
} while ( --n > 0 ); }
UpdateMaxSize();
NumExpressions = j;
if ( numhid == 0 && AR.hidefile->PObuffer ) {
if ( AR.hidefile->handle >= 0 ) {
CloseFile(AR.hidefile->handle);
remove(AR.hidefile->name);
AR.hidefile->handle = -1;
}
AR.hidefile->POfull =
AR.hidefile->POfill = AR.hidefile->PObuffer;
PUTZERO(AR.hidefile->POposition);
}
FlushSpectators();
return(0);
}
/*
#] CleanExpr :
#[ PopVariables :
Pops the local variables from the tables.
The Expressions are reprocessed and their tables are compactified.
*/
WORD PopVariables()
{
GETIDENTITY
WORD i, j, retval;
UBYTE *s;
retval = CleanExpr(1);
ResetVariables(1);
if ( AC.DidClean ) CompactifyTree(AC.exprnames,EXPRNAMES);
AC.CodesFlag = AM.gCodesFlag;
AC.NamesFlag = AM.gNamesFlag;
AC.StatsFlag = AM.gStatsFlag;
AC.OldFactArgFlag = AM.gOldFactArgFlag;
AC.TokensWriteFlag = AM.gTokensWriteFlag;
AC.extrasymbols = AM.gextrasymbols;
if ( AC.extrasym ) { M_free(AC.extrasym,"extrasym"); AC.extrasym = 0; }
i = 1; s = AM.gextrasym; while ( *s ) { s++; i++; }
AC.extrasym = (UBYTE *)Malloc1(i*sizeof(UBYTE),"extrasym");
for ( j = 0; j < i; j++ ) AC.extrasym[j] = AM.gextrasym[j];
AO.NoSpacesInNumbers = AM.gNoSpacesInNumbers;
AO.IndentSpace = AM.gIndentSpace;
AC.lUnitTrace = AM.gUnitTrace;
AC.lDefDim = AM.gDefDim;
AC.lDefDim4 = AM.gDefDim4;
if ( AC.halfmod ) {
if ( AC.ncmod == AM.gncmod && AC.modmode == AM.gmodmode ) {
j = ABS(AC.ncmod);
while ( --j >= 0 ) {
if ( AC.cmod[j] != AM.gcmod[j] ) break;
}
if ( j >= 0 ) {
M_free(AC.halfmod,"halfmod");
AC.halfmod = 0; AC.nhalfmod = 0;
}
}
else {
M_free(AC.halfmod,"halfmod");
AC.halfmod = 0; AC.nhalfmod = 0;
}
}
if ( AC.modinverses ) {
if ( AC.ncmod == AM.gncmod && AC.modmode == AM.gmodmode ) {
j = ABS(AC.ncmod);
while ( --j >= 0 ) {
if ( AC.cmod[j] != AM.gcmod[j] ) break;
}
if ( j >= 0 ) {
M_free(AC.modinverses,"modinverses");
AC.modinverses = 0;
}
}
else {
M_free(AC.modinverses,"modinverses");
AC.modinverses = 0;
}
}
AN.ncmod = AC.ncmod = AM.gncmod;
AC.npowmod = AM.gnpowmod;
AC.modmode = AM.gmodmode;
if ( ( ( AC.modmode & INVERSETABLE ) != 0 ) && ( AC.modinverses == 0 ) )
MakeInverses();
AC.funpowers = AM.gfunpowers;
AC.lPolyFun = AM.gPolyFun;
AC.lPolyFunInv = AM.gPolyFunInv;
AC.lPolyFunType = AM.gPolyFunType;
AC.lPolyFunExp = AM.gPolyFunExp;
AR.PolyFunVar = AC.lPolyFunVar = AM.gPolyFunVar;
AC.lPolyFunPow = AM.gPolyFunPow;
AC.parallelflag = AM.gparallelflag;
AC.ProcessBucketSize = AC.mProcessBucketSize = AM.gProcessBucketSize;
AC.properorderflag = AM.gproperorderflag;
AC.ThreadBucketSize = AM.gThreadBucketSize;
AC.ThreadStats = AM.gThreadStats;
AC.FinalStats = AM.gFinalStats;
AC.OldGCDflag = AM.gOldGCDflag;
AC.WTimeStatsFlag = AM.gWTimeStatsFlag;
AC.ThreadsFlag = AM.gThreadsFlag;
AC.ThreadBalancing = AM.gThreadBalancing;
AC.ThreadSortFileSynch = AM.gThreadSortFileSynch;
AC.ProcessStats = AM.gProcessStats;
AC.OldParallelStats = AM.gOldParallelStats;
AC.IsFortran90 = AM.gIsFortran90;
AC.SizeCommuteInSet = AM.gSizeCommuteInSet;
PruneExtraSymbols(AM.gnumextrasym);
if ( AC.Fortran90Kind ) {
M_free(AC.Fortran90Kind,"Fortran90 Kind");
AC.Fortran90Kind = 0;
}
if ( AM.gFortran90Kind ) {
AC.Fortran90Kind = strDup1(AM.gFortran90Kind,"Fortran90 Kind");
}
if ( AC.ThreadsFlag && AM.totalnumberofthreads > 1 ) AS.MultiThreaded = 1;
{
UWORD *p, *m;
p = AM.gcmod;
m = AC.cmod;
j = ABS(AC.ncmod);
NCOPY(m,p,j);
p = AM.gpowmod;
m = AC.powmod;
j = AC.npowmod;
NCOPY(m,p,j);
if ( AC.DirtPow ) {
if ( MakeModTable() ) {
MesPrint("===No printing in powers of generator");
}
AC.DirtPow = 0;
}
}
{
WORD *p, *m;
p = AM.gUniTrace;
m = AC.lUniTrace;
j = 4;
NCOPY(m,p,j);
}
AC.Cnumpows = AM.gCnumpows;
AC.OutputMode = AM.gOutputMode;
AC.OutputSpaces = AM.gOutputSpaces;
AC.OutNumberType = AM.gOutNumberType;
AR.SortType = AC.SortType = AM.gSortType;
AC.ShortStatsMax = AM.gShortStatsMax;
/*
Now we have to clean up the commutation properties
*/
for ( i = 0; i < NumFunctions; i++ ) functions[i].flags &= ~COULDCOMMUTE;
if ( AC.CommuteInSet ) {
WORD *g, *gg;
g = AC.CommuteInSet;
while ( *g ) {
gg = g+1; g += *g;
while ( gg < g ) {
if ( *gg <= GAMMASEVEN && *gg >= GAMMA ) {
functions[GAMMA-FUNCTION].flags |= COULDCOMMUTE;
functions[GAMMAI-FUNCTION].flags |= COULDCOMMUTE;
functions[GAMMAFIVE-FUNCTION].flags |= COULDCOMMUTE;
functions[GAMMASIX-FUNCTION].flags |= COULDCOMMUTE;
functions[GAMMASEVEN-FUNCTION].flags |= COULDCOMMUTE;
}
else {
functions[*gg-FUNCTION].flags |= COULDCOMMUTE;
}
}
}
}
/*
Clean up the dictionaries.
*/
for ( i = AO.NumDictionaries-1; i >= AO.gNumDictionaries; i-- ) {
RemoveDictionary(AO.Dictionaries[i]);
M_free(AO.Dictionaries[i],"Dictionary");
}
for( ; i >= 0; i-- ) {
ShrinkDictionary(AO.Dictionaries[i]);
}
AO.NumDictionaries = AO.gNumDictionaries;
return(retval);
}
/*
#] PopVariables :
#[ MakeGlobal :
*/
VOID MakeGlobal()
{
WORD i, j, *pp, *mm;
UWORD *p, *m;
UBYTE *s;
Globalize(0);
AM.gCodesFlag = AC.CodesFlag;
AM.gNamesFlag = AC.NamesFlag;
AM.gStatsFlag = AC.StatsFlag;
AM.gOldFactArgFlag = AC.OldFactArgFlag;
AM.gextrasymbols = AC.extrasymbols;
if ( AM.gextrasym ) { M_free(AM.gextrasym,"extrasym"); AM.gextrasym = 0; }
i = 1; s = AC.extrasym; while ( *s ) { s++; i++; }
AM.gextrasym = (UBYTE *)Malloc1(i*sizeof(UBYTE),"extrasym");
for ( j = 0; j < i; j++ ) AM.gextrasym[j] = AC.extrasym[j];
AM.gTokensWriteFlag= AC.TokensWriteFlag;
AM.gNoSpacesInNumbers = AO.NoSpacesInNumbers;
AM.gIndentSpace = AO.IndentSpace;
AM.gUnitTrace = AC.lUnitTrace;
AM.gDefDim = AC.lDefDim;
AM.gDefDim4 = AC.lDefDim4;
AM.gncmod = AC.ncmod;
AM.gnpowmod = AC.npowmod;
AM.gmodmode = AC.modmode;
AM.gCnumpows = AC.Cnumpows;
AM.gOutputMode = AC.OutputMode;
AM.gOutputSpaces = AC.OutputSpaces;
AM.gOutNumberType = AC.OutNumberType;
AM.gfunpowers = AC.funpowers;
AM.gPolyFun = AC.lPolyFun;
AM.gPolyFunInv = AC.lPolyFunInv;
AM.gPolyFunType = AC.lPolyFunType;
AM.gPolyFunExp = AC.lPolyFunExp;
AM.gPolyFunVar = AC.lPolyFunVar;
AM.gPolyFunPow = AC.lPolyFunPow;
AM.gparallelflag = AC.parallelflag;
AM.gProcessBucketSize = AC.ProcessBucketSize;
AM.gproperorderflag = AC.properorderflag;
AM.gThreadBucketSize = AC.ThreadBucketSize;
AM.gThreadStats = AC.ThreadStats;
AM.gFinalStats = AC.FinalStats;
AM.gOldGCDflag = AC.OldGCDflag;
AM.gWTimeStatsFlag = AC.WTimeStatsFlag;
AM.gThreadsFlag = AC.ThreadsFlag;
AM.gThreadBalancing = AC.ThreadBalancing;
AM.gThreadSortFileSynch = AC.ThreadSortFileSynch;
AM.gProcessStats = AC.ProcessStats;
AM.gOldParallelStats = AC.OldParallelStats;
AM.gIsFortran90 = AC.IsFortran90;
AM.gSizeCommuteInSet = AC.SizeCommuteInSet;
AM.gnumextrasym = (cbuf+AM.sbufnum)->numrhs;
if ( AM.gFortran90Kind ) {
M_free(AM.gFortran90Kind,"Fortran 90 Kind");
AM.gFortran90Kind = 0;
}
if ( AC.Fortran90Kind ) {
AM.gFortran90Kind = strDup1(AC.Fortran90Kind,"Fortran 90 Kind");
}
p = AM.gcmod;
m = AC.cmod;
i = ABS(AC.ncmod);
NCOPY(p,m,i);
p = AM.gpowmod;
m = AC.powmod;
i = AC.npowmod;
NCOPY(p,m,i);
pp = AM.gUniTrace;
mm = AC.lUniTrace;
i = 4;
NCOPY(pp,mm,i);
AM.gSortType = AC.SortType;
AM.gShortStatsMax = AC.ShortStatsMax;
if ( AO.CurrentDictionary > 0 || AP.OpenDictionary > 0 ) {
Warning("You cannot have an open or selected dictionary at a .global. Dictionary closed.");
AP.OpenDictionary = 0;
AO.CurrentDictionary = 0;
}
AO.gNumDictionaries = AO.NumDictionaries;
for ( i = 0; i < AO.NumDictionaries; i++ ) {
AO.Dictionaries[i]->gnumelements = AO.Dictionaries[i]->numelements;
}
if ( AM.NumSpectatorFiles > 0 ) {
for ( i = 0; i < AM.SizeForSpectatorFiles; i++ ) {
if ( AM.SpectatorFiles[i].name != 0 )
AM.SpectatorFiles[i].flags |= GLOBALSPECTATORFLAG;
}
}
}
/*
#] MakeGlobal :
#[ TestDrop :
*/
VOID TestDrop()
{
EXPRESSIONS e;
WORD j;
for ( j = 0, e = Expressions; j < NumExpressions; j++, e++ ) {
switch ( e->status ) {
case SKIPLEXPRESSION:
e->status = LOCALEXPRESSION;
break;
case UNHIDELEXPRESSION:
e->status = LOCALEXPRESSION;
ClearBracketIndex(j);
e->bracketinfo = e->newbracketinfo; e->newbracketinfo = 0;
break;
case HIDELEXPRESSION:
e->status = HIDDENLEXPRESSION;
break;
case SKIPGEXPRESSION:
e->status = GLOBALEXPRESSION;
break;
case UNHIDEGEXPRESSION:
e->status = GLOBALEXPRESSION;
ClearBracketIndex(j);
e->bracketinfo = e->newbracketinfo; e->newbracketinfo = 0;
break;
case HIDEGEXPRESSION:
e->status = HIDDENGEXPRESSION;
break;
case DROPLEXPRESSION:
case DROPGEXPRESSION:
case DROPHLEXPRESSION:
case DROPHGEXPRESSION:
case DROPSPECTATOREXPRESSION:
e->status = DROPPEDEXPRESSION;
ClearBracketIndex(j);
e->bracketinfo = e->newbracketinfo; e->newbracketinfo = 0;
if ( e->replace >= 0 ) {
Expressions[e->replace].replace = REGULAREXPRESSION;
AC.exprnames->namenode[e->node].number = e->replace;
e->replace = REGULAREXPRESSION;
}
else {
AC.exprnames->namenode[e->node].type = CDELETE;
AC.DidClean = 1;
}
break;
case LOCALEXPRESSION:
case GLOBALEXPRESSION:
ClearBracketIndex(j);
e->bracketinfo = e->newbracketinfo; e->newbracketinfo = 0;
break;
case HIDDENLEXPRESSION:
case HIDDENGEXPRESSION:
break;
case INTOHIDELEXPRESSION:
ClearBracketIndex(j);
e->bracketinfo = e->newbracketinfo; e->newbracketinfo = 0;
e->status = HIDDENLEXPRESSION;
break;
case INTOHIDEGEXPRESSION:
ClearBracketIndex(j);
e->bracketinfo = e->newbracketinfo; e->newbracketinfo = 0;
e->status = HIDDENGEXPRESSION;
break;
default:
ClearBracketIndex(j);
e->bracketinfo = 0;
break;
}
if ( e->replace == NEWLYDEFINEDEXPRESSION ) e->replace = REGULAREXPRESSION;
}
}
/*
#] TestDrop :
#[ PutInVflags :
*/
void PutInVflags(WORD nexpr)
{
EXPRESSIONS e = Expressions + nexpr;
POSITION *old;
WORD *oldw;
int i;
restart:;
if ( AS.OldOnFile == 0 ) {
AS.NumOldOnFile = 20;
AS.OldOnFile = (POSITION *)Malloc1(AS.NumOldOnFile*sizeof(POSITION),"file pointers");
}
else if ( nexpr >= AS.NumOldOnFile ) {
old = AS.OldOnFile;
AS.OldOnFile = (POSITION *)Malloc1(2*AS.NumOldOnFile*sizeof(POSITION),"file pointers");
for ( i = 0; i < AS.NumOldOnFile; i++ ) AS.OldOnFile[i] = old[i];
AS.NumOldOnFile = 2*AS.NumOldOnFile;
M_free(old,"process file pointers");
}
if ( AS.OldNumFactors == 0 ) {
AS.NumOldNumFactors = 20;
AS.OldNumFactors = (WORD *)Malloc1(AS.NumOldNumFactors*sizeof(WORD),"numfactors pointers");
AS.Oldvflags = (WORD *)Malloc1(AS.NumOldNumFactors*sizeof(WORD),"vflags pointers");
}
else if ( nexpr >= AS.NumOldNumFactors ) {
oldw = AS.OldNumFactors;
AS.OldNumFactors = (WORD *)Malloc1(2*AS.NumOldNumFactors*sizeof(WORD),"numfactors pointers");
for ( i = 0; i < AS.NumOldNumFactors; i++ ) AS.OldNumFactors[i] = oldw[i];
M_free(oldw,"numfactors pointers");
oldw = AS.Oldvflags;
AS.Oldvflags = (WORD *)Malloc1(2*AS.NumOldNumFactors*sizeof(WORD),"vflags pointers");
for ( i = 0; i < AS.NumOldNumFactors; i++ ) AS.Oldvflags[i] = oldw[i];
AS.NumOldNumFactors = 2*AS.NumOldNumFactors;
M_free(oldw,"vflags pointers");
}
/*
The next is needed when we Load a .sav file with lots of expressions.
*/
if ( nexpr >= AS.NumOldOnFile || nexpr >= AS.NumOldNumFactors ) goto restart;
AS.OldOnFile[nexpr] = e->onfile;
AS.OldNumFactors[nexpr] = e->numfactors;
AS.Oldvflags[nexpr] = e->vflags;
}
/*
#] PutInVflags :
#[ DoExecute :
*/
WORD DoExecute(WORD par, WORD skip)
{
GETIDENTITY
WORD RetCode = 0;
int i, oldmultithreaded = AS.MultiThreaded;
#ifdef PARALLELCODE
int j;
#endif
SpecialCleanup(BHEAD0);
if ( skip ) goto skipexec;
if ( AC.IfLevel > 0 ) {
MesPrint(" %d endif statement(s) missing",AC.IfLevel);
RetCode = 1;
}
if ( AC.WhileLevel > 0 ) {
MesPrint(" %d endwhile statement(s) missing",AC.WhileLevel);
RetCode = 1;
}
if ( AC.arglevel > 0 ) {
MesPrint(" %d endargument statement(s) missing",AC.arglevel);
RetCode = 1;
}
if ( AC.termlevel > 0 ) {
MesPrint(" %d endterm statement(s) missing",AC.termlevel);
RetCode = 1;
}
if ( AC.insidelevel > 0 ) {
MesPrint(" %d endinside statement(s) missing",AC.insidelevel);
RetCode = 1;
}
if ( AC.inexprlevel > 0 ) {
MesPrint(" %d endinexpression statement(s) missing",AC.inexprlevel);
RetCode = 1;
}
if ( AC.NumLabels > 0 ) {
for ( i = 0; i < AC.NumLabels; i++ ) {
if ( AC.Labels[i] < 0 ) {
MesPrint(" -->Label %s missing",AC.LabelNames[i]);
RetCode = 1;
}
}
}
if ( AC.SwitchLevel > 0 ) {
MesPrint(" %d endswitch statement(s) missing",AC.SwitchLevel);
RetCode = 1;
}
if ( AC.dolooplevel > 0 ) {
MesPrint(" %d enddo statement(s) missing",AC.dolooplevel);
RetCode = 1;
}
if ( AP.OpenDictionary > 0 ) {
MesPrint(" Dictionary %s has not been closed.",
AO.Dictionaries[AP.OpenDictionary-1]->name);
AP.OpenDictionary = 0;
RetCode = 1;
}
if ( RetCode ) return(RetCode);
AR.Cnumlhs = cbuf[AM.rbufnum].numlhs;
if ( ( AS.ExecMode = par ) == GLOBALMODULE ) AS.ExecMode = 0;
#ifdef PARALLELCODE
/*
Now check whether we have either the regular parallel flag or the
mparallel flag set.
Next check whether any of the expressions has partodo set.
If any of the above we need to check what the dollar status is.
*/
AC.partodoflag = -1;
if ( NumPotModdollars >= 0 ) {
for ( i = 0; i < NumExpressions; i++ ) {
if ( Expressions[i].partodo ) { AC.partodoflag = 1; break; }
}
}
#ifdef WITHMPI
if ( AC.partodoflag > 0 && PF.numtasks < 3 ) {
AC.partodoflag = 0;
}
#endif
if ( AC.partodoflag > 0 || ( NumPotModdollars > 0 && AC.mparallelflag == PARALLELFLAG ) ) {
if ( NumPotModdollars > NumModOptdollars ) {
AC.mparallelflag |= NOPARALLEL_DOLLAR;
#ifdef WITHPTHREADS
AS.MultiThreaded = 0;
#endif
AC.partodoflag = 0;
}
else {
for ( i = 0; i < NumPotModdollars; i++ ) {
for ( j = 0; j < NumModOptdollars; j++ )
if ( PotModdollars[i] == ModOptdollars[j].number ) break;
if ( j >= NumModOptdollars ) {
AC.mparallelflag |= NOPARALLEL_DOLLAR;
#ifdef WITHPTHREADS
AS.MultiThreaded = 0;
#endif
AC.partodoflag = 0;
break;
}
switch ( ModOptdollars[j].type ) {
case MODSUM:
case MODMAX:
case MODMIN:
case MODLOCAL:
break;
default:
AC.mparallelflag |= NOPARALLEL_DOLLAR;
AS.MultiThreaded = 0;
AC.partodoflag = 0;
break;
}
}
}
}
else if ( ( AC.mparallelflag & NOPARALLEL_USER ) != 0 ) {
#ifdef WITHPTHREADS
AS.MultiThreaded = 0;
#endif
AC.partodoflag = 0;
}
if ( AC.partodoflag == 0 ) {
for ( i = 0; i < NumExpressions; i++ ) {
Expressions[i].partodo = 0;
}
}
else if ( AC.partodoflag == -1 ) {
AC.partodoflag = 0;
}
#endif
#ifdef WITHMPI
/*
* Check RHS expressions.
*/
if ( AC.RhsExprInModuleFlag && (AC.mparallelflag == PARALLELFLAG || AC.partodoflag) ) {
if (PF.rhsInParallel) {
PF.mkSlaveInfile=1;
if(PF.me != MASTER){
PF.slavebuf.PObuffer=(WORD *)Malloc1(AM.ScratSize*sizeof(WORD),"PF inbuf");
PF.slavebuf.POsize=AM.ScratSize*sizeof(WORD);
PF.slavebuf.POfull = PF.slavebuf.POfill = PF.slavebuf.PObuffer;
PF.slavebuf.POstop= PF.slavebuf.PObuffer+AM.ScratSize;
PUTZERO(PF.slavebuf.POposition);
}/*if(PF.me != MASTER)*/
}
else {
AC.mparallelflag |= NOPARALLEL_RHS;
AC.partodoflag = 0;
for ( i = 0; i < NumExpressions; i++ ) {
Expressions[i].partodo = 0;
}
}
}
/*
* Set $-variables with MODSUM to zero on the slaves.
*/
if ( (AC.mparallelflag == PARALLELFLAG || AC.partodoflag) && PF.me != MASTER ) {
for ( i = 0; i < NumModOptdollars; i++ ) {
if ( ModOptdollars[i].type == MODSUM ) {
DOLLARS d = Dollars + ModOptdollars[i].number;
d->type = DOLZERO;
if ( d->where && d->where != &AM.dollarzero ) M_free(d->where, "old content of dollar");
d->where = &AM.dollarzero;
d->size = 0;
CleanDollarFactors(d);
}
}
}
#endif
AR.SortType = AC.SortType;
#ifdef WITHMPI
if ( PF.me == MASTER )
#endif
{
if ( AC.SetupFlag ) WriteSetup();
if ( AC.NamesFlag || AC.CodesFlag ) WriteLists();
}
if ( par == GLOBALMODULE ) MakeGlobal();
if ( RevertScratch() ) return(-1);
if ( AC.ncmod ) SetMods();
/*
Warn if the module has to run in sequential mode due to some problems.
*/
#ifdef WITHMPI
if ( PF.me == MASTER )
#endif
{
if ( !AC.ThreadsFlag || AC.mparallelflag & NOPARALLEL_USER ) {
/* The user switched off the parallel execution explicitly. */
}
else if ( AC.mparallelflag & NOPARALLEL_DOLLAR ) {
if ( AC.WarnFlag >= 2 ) { /* HighWarning */
int i, j, k, n;
UBYTE *s, *s1;
s = strDup1((UBYTE *)"","NOPARALLEL_DOLLAR s");
n = 0;
j = NumPotModdollars;
for ( i = 0; i < j; i++ ) {
for ( k = 0; k < NumModOptdollars; k++ )
if ( ModOptdollars[k].number == PotModdollars[i] ) break;
if ( k >= NumModOptdollars ) {
/* global $-variable */
if ( n > 0 )
s = AddToString(s,(UBYTE *)", ",0);
s = AddToString(s,(UBYTE *)"$",0);
s = AddToString(s,DOLLARNAME(Dollars,PotModdollars[i]),0);
n++;
}
}
s1 = strDup1((UBYTE *)"This module is forced to run in sequential mode due to $-variable","NOPARALLEL_DOLLAR s1");
if ( n != 1 )
s1 = AddToString(s1,(UBYTE *)"s",0);
s1 = AddToString(s1,(UBYTE *)": ",0);
s1 = AddToString(s1,s,0);
HighWarning((char *)s1);
M_free(s,"NOPARALLEL_DOLLAR s");
M_free(s1,"NOPARALLEL_DOLLAR s1");
}
}
else if ( AC.mparallelflag & NOPARALLEL_RHS ) {
HighWarning("This module is forced to run in sequential mode due to RHS expression names");
}
else if ( AC.mparallelflag & NOPARALLEL_CONVPOLY ) {
HighWarning("This module is forced to run in sequential mode due to conversion to extra symbols");
}
else if ( AC.mparallelflag & NOPARALLEL_SPECTATOR ) {
HighWarning("This module is forced to run in sequential mode due to tospectator/copyspectator");
}
else if ( AC.mparallelflag & NOPARALLEL_TBLDOLLAR ) {
HighWarning("This module is forced to run in sequential mode due to $-variable assignments in tables");
}
else if ( AC.mparallelflag & NOPARALLEL_NPROC ) {
HighWarning("This module is forced to run in sequential mode because there is only one processor");
}
}
/*
Now the actual execution
*/
#ifdef WITHMPI
/*
* Turn on AS.printflag to print runtime errors occurring on slaves.
*/
AS.printflag = 1;
#endif
if ( AP.preError == 0 && ( Processor() || WriteAll() ) ) RetCode = -1;
#ifdef WITHMPI
AS.printflag = 0;
#endif
/*
That was it. Next is cleanup.
*/
if ( AC.ncmod ) UnSetMods();
AS.MultiThreaded = oldmultithreaded;
TableReset();
/*[28sep2005 mt]:*/
#ifdef WITHMPI
/* Combine and then broadcast modified dollar variables. */
if ( NumPotModdollars > 0 ) {
RetCode = PF_CollectModifiedDollars();
if ( RetCode ) return RetCode;
RetCode = PF_BroadcastModifiedDollars();
if ( RetCode ) return RetCode;
}
/* Broadcast redefined preprocessor variables. */
if ( AC.numpfirstnum > 0 ) {
RetCode = PF_BroadcastRedefinedPreVars();
if ( RetCode ) return RetCode;
}
/* Broadcast the list of objects converted to symbols in AM.sbufnum. */
if ( AC.topolynomialflag & TOPOLYNOMIALFLAG ) {
RetCode = PF_BroadcastCBuf(AM.sbufnum);
if ( RetCode ) return RetCode;
}
/*
* Broadcast AR.expflags, which may be used on the slaves in the next module
* via ZERO_ or UNCHANGED_. It also broadcasts several flags of each expression.
*/
RetCode = PF_BroadcastExpFlags();
if ( RetCode ) return RetCode;
/*
* Clean the hide file on the slaves, which was used for RHS expressions
* broadcast from the master at the beginning of the module.
*/
if ( PF.me != MASTER && AR.hidefile->PObuffer ) {
if ( AR.hidefile->handle >= 0 ) {
CloseFile(AR.hidefile->handle);
AR.hidefile->handle = -1;
remove(AR.hidefile->name);
}
AR.hidefile->POfull = AR.hidefile->POfill = AR.hidefile->PObuffer;
PUTZERO(AR.hidefile->POposition);
}
#endif
#ifdef WITHPTHREADS
for ( j = 0; j < NumModOptdollars; j++ ) {
if ( ModOptdollars[j].dstruct ) {
/*
First clean up dollar values.
*/
for ( i = 0; i < AM.totalnumberofthreads; i++ ) {
if ( ModOptdollars[j].dstruct[i].size > 0 ) {
CleanDollarFactors(&(ModOptdollars[j].dstruct[i]));
M_free(ModOptdollars[j].dstruct[i].where,"Local dollar value");
}
}
/*
Now clean up the whole array.
*/
M_free(ModOptdollars[j].dstruct,"Local DOLLARS");
ModOptdollars[j].dstruct = 0;
}
}
#endif
/*:[28sep2005 mt]*/
/*
@@@@@@@@@@@@@@@
Now follows the code to invalidate caches for all objects in the
PotModdollars. There are NumPotModdollars of them and PotModdollars
is an array of WORD.
*/
/*
Cleanup:
*/
#ifdef JV_IS_WRONG
/*
Giving back this memory gives way too much activity with Malloc1
Better to keep it and just put the number of used objects to zero (JV)
If you put the lijst equal to NULL, please also make maxnum = 0
*/
if ( ModOptdollars ) M_free(ModOptdollars, "ModOptdollars pointer");
if ( PotModdollars ) M_free(PotModdollars, "PotModdollars pointer");
/* ModOptdollars changed to AC.ModOptDolList.lijst because AIX C compiler complained. MF 30/07/2003. */
AC.ModOptDolList.lijst = NULL;
/* PotModdollars changed to AC.PotModDolList.lijst because AIX C compiler complained. MF 30/07/2003. */
AC.PotModDolList.lijst = NULL;
#endif
NumPotModdollars = 0;
NumModOptdollars = 0;
skipexec:
/*
Clean up the switch information.
We keep the switch array and heap.
*/
if ( AC.SwitchInArray > 0 ) {
for ( i = 0; i < AC.SwitchInArray; i++ ) {
SWITCH *sw = AC.SwitchArray + i;
if ( sw->table ) M_free(sw->table,"Switch table");
sw->table = 0;
sw->defaultcase.ncase = 0;
sw->defaultcase.value = 0;
sw->defaultcase.compbuffer = 0;
sw->endswitch.ncase = 0;
sw->endswitch.value = 0;
sw->endswitch.compbuffer = 0;
sw->typetable = 0;
sw->maxcase = 0;
sw->mincase = 0;
sw->numcases = 0;
sw->tablesize = 0;
sw->caseoffset = 0;
sw->iflevel = 0;
sw->whilelevel = 0;
sw->nestingsum = 0;
}
AC.SwitchInArray = 0;
AC.SwitchLevel = 0;
}
#ifdef PARALLELCODE
AC.numpfirstnum = 0;
#endif
AC.DidClean = 0;
AC.PolyRatFunChanged = 0;
TestDrop();
if ( par == STOREMODULE || par == CLEARMODULE ) {
ClearOptimize();
if ( par == STOREMODULE && PopVariables() ) RetCode = -1;
if ( AR.infile->handle >= 0 ) {
CloseFile(AR.infile->handle);
remove(AR.infile->name);
AR.infile->handle = -1;
}
AR.infile->POfill = AR.infile->PObuffer;
PUTZERO(AR.infile->POposition);
AR.infile->POfull = AR.infile->PObuffer;
if ( AR.outfile->handle >= 0 ) {
CloseFile(AR.outfile->handle);
remove(AR.outfile->name);
AR.outfile->handle = -1;
}
AR.outfile->POfull =
AR.outfile->POfill = AR.outfile->PObuffer;
PUTZERO(AR.outfile->POposition);
if ( AR.hidefile->handle >= 0 ) {
CloseFile(AR.hidefile->handle);
remove(AR.hidefile->name);
AR.hidefile->handle = -1;
}
AR.hidefile->POfull =
AR.hidefile->POfill = AR.hidefile->PObuffer;
PUTZERO(AR.hidefile->POposition);
AC.HideLevel = 0;
if ( par == CLEARMODULE ) {
if ( DeleteStore(0) < 0 ) {
MesPrint("Cannot restart the storage file");
RetCode = -1;
}
else RetCode = 0;
CleanUp(1);
ResetVariables(2);
AM.gProcessBucketSize = AM.hProcessBucketSize;
AM.gparallelflag = PARALLELFLAG;
AM.gnumextrasym = AM.ggnumextrasym;
PruneExtraSymbols(AM.ggnumextrasym);
IniVars();
}
ClearSpectators(par);
}
else {
if ( CleanExpr(0) ) RetCode = -1;
if ( AC.DidClean ) CompactifyTree(AC.exprnames,EXPRNAMES);
ResetVariables(0);
CleanUpSort(-1);
}
clearcbuf(AC.cbufnum);
if ( AC.MultiBracketBuf != 0 ) {
for ( i = 0; i < MAXMULTIBRACKETLEVELS; i++ ) {
if ( AC.MultiBracketBuf[i] ) {
M_free(AC.MultiBracketBuf[i],"bracket buffer i");
AC.MultiBracketBuf[i] = 0;
}
}
AC.MultiBracketLevels = 0;
M_free(AC.MultiBracketBuf,"multi bracket buffer");
AC.MultiBracketBuf = 0;
}
return(RetCode);
}
/*
#] DoExecute :
#[ PutBracket :
Routine uses the bracket info to split a term into two pieces:
1: the part outside the bracket, and
2: the part inside the bracket.
These parts are separated by a subterm of type HAAKJE.
This subterm looks like: HAAKJE,3,level
The level is used for nestings of brackets. The print routines
cannot handle this yet (31-Mar-1988).
The Bracket selector is in AT.BrackBuf in the form of a regular term,
but without coefficient.
When AR.BracketOn < 0 we have a socalled antibracket. The main effect
is an exchange of the inner and outer part and where the coefficient goes.
Routine recoded to facilitate b p1,p2; etc for dotproducts and tensors
15-oct-1991
*/
WORD PutBracket(PHEAD WORD *termin)
{
GETBIDENTITY
WORD *t, *t1, *b, i, j, *lastfun;
WORD *t2, *s1, *s2;
WORD *bStop, *bb, *bf, *tStop;
WORD *term1,*term2, *m1, *m2, *tStopa;
WORD *bbb = 0, *bind, *binst = 0, bwild = 0, *bss = 0, *bns = 0, bset = 0;
term1 = AT.WorkPointer+1;
term2 = (WORD *)(((UBYTE *)(term1)) + AM.MaxTer);
if ( ( (WORD *)(((UBYTE *)(term2)) + AM.MaxTer) ) > AT.WorkTop ) return(MesWork());
if ( AR.BracketOn < 0 ) {
t2 = term1; t1 = term2; /* AntiBracket */
}
else {
t1 = term1; t2 = term2; /* Regular bracket */
}
b = AT.BrackBuf; bStop = b+*b; b++;
while ( b < bStop ) {
if ( *b == INDEX ) { bwild = 1; bbb = b+2; binst = b + b[1]; }
if ( *b == SETSET ) { bset = 1; bss = b+2; bns = b + b[1]; }
b += b[1];
}
t = termin; tStopa = t + *t; i = *(t + *t -1); i = ABS(i);
if ( AR.PolyFun && AT.PolyAct ) tStop = termin + AT.PolyAct;
else tStop = tStopa - i;
t++;
if ( AR.BracketOn < 0 ) {
lastfun = 0;
while ( t < tStop && *t >= FUNCTION
&& functions[*t-FUNCTION].commute ) {
b = AT.BrackBuf+1;
while ( b < bStop ) {
if ( *b == *t ) {
lastfun = t;
while ( t < tStop && *t >= FUNCTION
&& functions[*t-FUNCTION].commute ) t += t[1];
goto NextNcom1;
}
b += b[1];
}
if ( bset ) {
b = bss;
while ( b < bns ) {
if ( b[1] == CFUNCTION ) { /* Set of functions */
SETS set = Sets+b[0]; WORD i;
for ( i = set->first; i < set->last; i++ ) {
if ( SetElements[i] == *t ) {
lastfun = t;
while ( t < tStop && *t >= FUNCTION
&& functions[*t-FUNCTION].commute ) t += t[1];
goto NextNcom1;
}
}
}
b += 2;
}
}
if ( bwild && *t >= FUNCTION && functions[*t-FUNCTION].spec ) {
s1 = t + t[1];
s2 = t + FUNHEAD;
while ( s2 < s1 ) {
bind = bbb;
while ( bind < binst ) {
if ( *bind == *s2 ) {
lastfun = t;
while ( t < tStop && *t >= FUNCTION
&& functions[*t-FUNCTION].commute ) t += t[1];
goto NextNcom1;
}
bind++;
}
s2++;
}
}
t += t[1];
}
NextNcom1:
s1 = termin + 1;
if ( lastfun ) {
while ( s1 < lastfun ) *t2++ = *s1++;
while ( s1 < t ) *t1++ = *s1++;
}
else {
while ( s1 < t ) *t2++ = *s1++;
}
}
else {
lastfun = t;
while ( t < tStop && *t >= FUNCTION
&& functions[*t-FUNCTION].commute ) {
b = AT.BrackBuf+1;
while ( b < bStop ) {
if ( *b == *t ) { lastfun = t + t[1]; goto NextNcom; }
b += b[1];
}
if ( bset ) {
b = bss;
while ( b < bns ) {
if ( b[1] == CFUNCTION ) { /* Set of functions */
SETS set = Sets+b[0]; WORD i;
for ( i = set->first; i < set->last; i++ ) {
if ( SetElements[i] == *t ) {
lastfun = t + t[1];
goto NextNcom;
}
}
}
b += 2;
}
}
if ( bwild && *t >= FUNCTION && functions[*t-FUNCTION].spec ) {
s1 = t + t[1];
s2 = t + FUNHEAD;
while ( s2 < s1 ) {
bind = bbb;
while ( bind < binst ) {
if ( *bind == *s2 ) { lastfun = t + t[1]; goto NextNcom; }
bind++;
}
s2++;
}
}
NextNcom:
t += t[1];
}
s1 = termin + 1;
while ( s1 < lastfun ) *t1++ = *s1++;
while ( s1 < t ) *t2++ = *s1++;
}
/*
Now we have only commuting functions left. Move the b pointer to them.
*/
b = AT.BrackBuf + 1;
while ( b < bStop && *b >= FUNCTION
&& ( *b < FUNCTION || functions[*b-FUNCTION].commute ) ) {
b += b[1];
}
bf = b;
while ( t < tStop && ( bf < bStop || bwild || bset ) ) {
b = bf;
while ( b < bStop && *b != *t ) { b += b[1]; }
i = t[1];
if ( *t >= FUNCTION ) { /* We are in function territory */
if ( b < bStop && *b == *t ) goto FunBrac;
if ( bset ) {
b = bss;
while ( b < bns ) {
if ( b[1] == CFUNCTION ) { /* Set of functions */
SETS set = Sets+b[0]; WORD i;
for ( i = set->first; i < set->last; i++ ) {
if ( SetElements[i] == *t ) goto FunBrac;
}
}
b += 2;
}
}
if ( bwild && *t >= FUNCTION && functions[*t-FUNCTION].spec ) {
s1 = t + t[1];
s2 = t + FUNHEAD;
while ( s2 < s1 ) {
bind = bbb;
while ( bind < binst ) {
if ( *bind == *s2 ) goto FunBrac;
bind++;
}
s2++;
}
}
NCOPY(t2,t,i);
continue;
FunBrac: NCOPY(t1,t,i);
continue;
}
/*
We have left: DELTA, INDEX, VECTOR, DOTPRODUCT, SYMBOL
*/
if ( *t == DELTA ) {
if ( b < bStop && *b == DELTA ) {
b += b[1];
NCOPY(t1,t,i);
}
else { NCOPY(t2,t,i); }
}
else if ( *t == INDEX ) {
if ( bwild ) {
m1 = t1; m2 = t2;
*t1++ = *t; t1++; *t2++ = *t; t2++;
bind = bbb;
j = t[1] -2;
t += 2;
while ( --j >= 0 ) {
while ( *bind < *t && bind < binst ) bind++;
if ( *bind == *t && bind < binst ) {
*t1++ = *t++;
}
else if ( bset ) {
WORD *b3 = bss;
while ( b3 < bns ) {
if ( b3[1] == CVECTOR ) {
SETS set = Sets+b3[0]; WORD i;
for ( i = set->first; i < set->last; i++ ) {
if ( SetElements[i] == *t ) {
*t1++ = *t++;
goto nextind;
}
}
}
b3 += 2;
}
*t2++ = *t++;
}
else *t2++ = *t++;
nextind:;
}
m1[1] = WORDDIF(t1,m1);
if ( m1[1] == 2 ) t1 = m1;
m2[1] = WORDDIF(t2,m2);
if ( m2[1] == 2 ) t2 = m2;
}
else if ( bset ) {
m1 = t1; m2 = t2;
*t1++ = *t; t1++; *t2++ = *t; t2++;
j = t[1] -2;
t += 2;
while ( --j >= 0 ) {
WORD *b3 = bss;
while ( b3 < bns ) {
if ( b3[1] == CVECTOR ) {
SETS set = Sets+b3[0]; WORD i;
for ( i = set->first; i < set->last; i++ ) {
if ( SetElements[i] == *t ) {
*t1++ = *t++;
goto nextind2;
}
}
}
b3 += 2;
}
*t2++ = *t++;
nextind2:;
}
m1[1] = WORDDIF(t1,m1);
if ( m1[1] == 2 ) t1 = m1;
m2[1] = WORDDIF(t2,m2);
if ( m2[1] == 2 ) t2 = m2;
}
else {
NCOPY(t2,t,i);
}
}
else if ( *t == VECTOR ) {
if ( ( b < bStop && *b == VECTOR ) || bwild ) {
if ( b < bStop && *b == VECTOR ) {
bb = b + b[1]; b += 2;
}
else bb = b;
j = t[1] - 2;
m1 = t1; m2 = t2; *t1++ = *t; *t2++ = *t; t1++; t2++; t += 2;
while ( j > 0 ) {
j -= 2;
while ( b < bb && ( *b < *t ||
( *b == *t && b[1] < t[1] ) ) ) b += 2;
if ( b < bb && ( *t == *b && t[1] == b[1] ) ) {
*t1++ = *t++; *t1++ = *t++; goto nextvec;
}
else if ( bwild ) {
bind = bbb;
while ( bind < binst ) {
if ( *t == *bind || t[1] == *bind ) {
*t1++ = *t++; *t1++ = *t++;
goto nextvec;
}
bind++;
}
}
if ( bset ) {
WORD *b3 = bss;
while ( b3 < bns ) {
if ( b3[1] == CVECTOR ) {
SETS set = Sets+b3[0]; WORD i;
for ( i = set->first; i < set->last; i++ ) {
if ( SetElements[i] == *t ) {
*t1++ = *t++; *t1++ = *t++;
goto nextvec;
}
}
}
b3 += 2;
}
}
*t2++ = *t++; *t2++ = *t++;
nextvec:;
}
m1[1] = WORDDIF(t1,m1);
if ( m1[1] == 2 ) t1 = m1;
m2[1] = WORDDIF(t2,m2);
if ( m2[1] == 2 ) t2 = m2;
}
else if ( bset ) {
m1 = t1; *t1++ = *t; t1++;
m2 = t2; *t2++ = *t; t2++;
s2 = t + i; t += 2;
while ( t < s2 ) {
WORD *b3 = bss;
while ( b3 < bns ) {
if ( b3[1] == CVECTOR ) {
SETS set = Sets+b3[0]; WORD i;
for ( i = set->first; i < set->last; i++ ) {
if ( SetElements[i] == *t ) {
*t1++ = *t++; *t1++ = *t++;
goto nextvec2;
}
}
}
b3 += 2;
}
*t2++ = *t++; *t2++ = *t++;
nextvec2:;
}
m1[1] = WORDDIF(t1,m1);
if ( m1[1] == 2 ) t1 = m1;
m2[1] = WORDDIF(t2,m2);
if ( m2[1] == 2 ) t2 = m2;
}
else {
NCOPY(t2,t,i);
}
}
else if ( *t == DOTPRODUCT ) {
if ( ( b < bStop && *b == *t ) || bwild ) {
m1 = t1; *t1++ = *t; t1++;
m2 = t2; *t2++ = *t; t2++;
if ( b >= bStop || *b != *t ) { bb = b; s1 = b; }
else {
s1 = b + b[1]; bb = b + 2;
}
s2 = t + i; t += 2;
while ( t < s2 && ( bb < s1 || bwild || bset ) ) {
while ( bb < s1 && ( *bb < *t ||
( *bb == *t && bb[1] < t[1] ) ) ) bb += 3;
if ( bb < s1 && *bb == *t && bb[1] == t[1] ) {
*t1++ = *t++; *t1++ = *t++; *t1++ = *t++; bb += 3;
goto nextdot;
}
else if ( bwild ) {
bind = bbb;
while ( bind < binst ) {
if ( *bind == *t || *bind == t[1] ) {
*t1++ = *t++; *t1++ = *t++; *t1++ = *t++;
goto nextdot;
}
bind++;
}
}
if ( bset ) {
WORD *b3 = bss;
while ( b3 < bns ) {
if ( b3[1] == CVECTOR ) {
SETS set = Sets+b3[0]; WORD i;
for ( i = set->first; i < set->last; i++ ) {
if ( SetElements[i] == *t || SetElements[i] == t[1] ) {
*t1++ = *t++; *t1++ = *t++; *t1++ = *t++;
goto nextdot;
}
}
}
b3 += 2;
}
}
*t2++ = *t++; *t2++ = *t++; *t2++ = *t++;
nextdot:;
}
while ( t < s2 ) *t2++ = *t++;
m1[1] = WORDDIF(t1,m1);
if ( m1[1] == 2 ) t1 = m1;
m2[1] = WORDDIF(t2,m2);
if ( m2[1] == 2 ) t2 = m2;
}
else if ( bset ) {
m1 = t1; *t1++ = *t; t1++;
m2 = t2; *t2++ = *t; t2++;
s2 = t + i; t += 2;
while ( t < s2 ) {
WORD *b3 = bss;
while ( b3 < bns ) {
if ( b3[1] == CVECTOR ) {
SETS set = Sets+b3[0]; WORD i;
for ( i = set->first; i < set->last; i++ ) {
if ( SetElements[i] == *t || SetElements[i] == t[1] ) {
*t1++ = *t++; *t1++ = *t++; *t1++ = *t++;
goto nextdot2;
}
}
}
b3 += 2;
}
*t2++ = *t++; *t2++ = *t++; *t2++ = *t++;
nextdot2:;
}
m1[1] = WORDDIF(t1,m1);
if ( m1[1] == 2 ) t1 = m1;
m2[1] = WORDDIF(t2,m2);
if ( m2[1] == 2 ) t2 = m2;
}
else { NCOPY(t2,t,i); }
}
else if ( *t == SYMBOL ) {
if ( b < bStop && *b == *t ) {
m1 = t1; *t1++ = *t; t1++;
m2 = t2; *t2++ = *t; t2++;
s1 = b + b[1]; bb = b+2;
s2 = t + i; t += 2;
while ( bb < s1 && t < s2 ) {
while ( bb < s1 && *bb < *t ) bb += 2;
if ( bb >= s1 ) {
if ( bset ) goto TrySymbolSet;
break;
}
if ( *bb == *t ) { *t1++ = *t++; *t1++ = *t++; }
else if ( bset ) {
WORD *bbb;
TrySymbolSet:
bbb = bss;
while ( bbb < bns ) {
if ( bbb[1] == CSYMBOL ) { /* Set of symbols */
SETS set = Sets+bbb[0]; WORD i;
for ( i = set->first; i < set->last; i++ ) {
if ( SetElements[i] == *t ) {
*t1++ = *t++; *t1++ = *t++;
goto NextSymbol;
}
}
}
bbb += 2;
}
*t2++ = *t++; *t2++ = *t++;
}
else { *t2++ = *t++; *t2++ = *t++; }
NextSymbol:;
}
while ( t < s2 ) *t2++ = *t++;
m1[1] = WORDDIF(t1,m1);
if ( m1[1] == 2 ) t1 = m1;
m2[1] = WORDDIF(t2,m2);
if ( m2[1] == 2 ) t2 = m2;
}
else if ( bset ) {
WORD *bbb;
m1 = t1; *t1++ = *t; t1++;
m2 = t2; *t2++ = *t; t2++;
s2 = t + i; t += 2;
while ( t < s2 ) {
bbb = bss;
while ( bbb < bns ) {
if ( bbb[1] == CSYMBOL ) { /* Set of symbols */
SETS set = Sets+bbb[0]; WORD i;
for ( i = set->first; i < set->last; i++ ) {
if ( SetElements[i] == *t ) {
*t1++ = *t++; *t1++ = *t++;
goto NextSymbol2;
}
}
}
bbb += 2;
}
*t2++ = *t++; *t2++ = *t++;
NextSymbol2:;
}
m1[1] = WORDDIF(t1,m1);
if ( m1[1] == 2 ) t1 = m1;
m2[1] = WORDDIF(t2,m2);
if ( m2[1] == 2 ) t2 = m2;
}
else { NCOPY(t2,t,i); }
}
else {
NCOPY(t2,t,i);
}
}
if ( ( i = WORDDIF(tStop,t) ) > 0 ) NCOPY(t2,t,i);
if ( AR.BracketOn < 0 ) {
s1 = t1; t1 = t2; t2 = s1;
}
do { *t2++ = *t++; } while ( t < (WORD *)tStopa );
t = AT.WorkPointer;
i = WORDDIF(t1,term1);
*t++ = 4 + i + WORDDIF(t2,term2);
t += i;
*t++ = HAAKJE;
*t++ = 3;
*t++ = 0; /* This feature won't be used for a while */
i = WORDDIF(t2,term2);
t1 = term2;
if ( i > 0 ) NCOPY(t,t1,i);
AT.WorkPointer = t;
return(0);
}
/*
#] PutBracket :
#[ SpecialCleanup :
*/
VOID SpecialCleanup(PHEAD0)
{
GETBIDENTITY
if ( AT.previousEfactor ) M_free(AT.previousEfactor,"Efactor cache");
AT.previousEfactor = 0;
}
/*
#] SpecialCleanup :
#[ SetMods :
*/
#ifndef WITHPTHREADS
void SetMods()
{
int i, n;
if ( AN.cmod != 0 ) M_free(AN.cmod,"AN.cmod");
n = ABS(AN.ncmod);
AN.cmod = (UWORD *)Malloc1(sizeof(WORD)*n,"AN.cmod");
for ( i = 0; i < n; i++ ) AN.cmod[i] = AC.cmod[i];
}
#endif
/*
#] SetMods :
#[ UnSetMods :
*/
#ifndef WITHPTHREADS
void UnSetMods()
{
if ( AN.cmod != 0 ) M_free(AN.cmod,"AN.cmod");
AN.cmod = 0;
}
#endif
/*
#] UnSetMods :
#] DoExecute :
#[ Expressions :
#[ ExchangeExpressions :
*/
void ExchangeExpressions(int num1, int num2)
{
GETIDENTITY
WORD node1, node2, namesize, TMproto[SUBEXPSIZE];
INDEXENTRY *ind;
EXPRESSIONS e1, e2;
LONG a;
SBYTE *s1, *s2;
int i;
e1 = Expressions + num1;
e2 = Expressions + num2;
node1 = e1->node;
node2 = e2->node;
AC.exprnames->namenode[node1].number = num2;
AC.exprnames->namenode[node2].number = num1;
a = e1->name; e1->name = e2->name; e2->name = a;
namesize = e1->namesize; e1->namesize = e2->namesize; e2->namesize = namesize;
e1->node = node2;
e2->node = node1;
if ( e1->status == STOREDEXPRESSION ) {
/*
Find the name in the index and replace by the new name
*/
TMproto[0] = EXPRESSION;
TMproto[1] = SUBEXPSIZE;
TMproto[2] = num1;
TMproto[3] = 1;
{ int ie; for ( ie = 4; ie < SUBEXPSIZE; ie++ ) TMproto[ie] = 0; }
AT.TMaddr = TMproto;
ind = FindInIndex(num1,&AR.StoreData,0,0);
s1 = (SBYTE *)(AC.exprnames->namebuffer+e1->name);
i = e1->namesize;
s2 = ind->name;
NCOPY(s2,s1,i);
*s2 = 0;
SeekFile(AR.StoreData.Handle,&(e1->onfile),SEEK_SET);
if ( WriteFile(AR.StoreData.Handle,(UBYTE *)ind,
(LONG)(sizeof(INDEXENTRY))) != sizeof(INDEXENTRY) ) {
MesPrint("File error while exchanging expressions");
Terminate(-1);
}
FlushFile(AR.StoreData.Handle);
}
if ( e2->status == STOREDEXPRESSION ) {
/*
Find the name in the index and replace by the new name
*/
TMproto[0] = EXPRESSION;
TMproto[1] = SUBEXPSIZE;
TMproto[2] = num2;
TMproto[3] = 1;
{ int ie; for ( ie = 4; ie < SUBEXPSIZE; ie++ ) TMproto[ie] = 0; }
AT.TMaddr = TMproto;
ind = FindInIndex(num1,&AR.StoreData,0,0);
s1 = (SBYTE *)(AC.exprnames->namebuffer+e2->name);
i = e2->namesize;
s2 = ind->name;
NCOPY(s2,s1,i);
*s2 = 0;
SeekFile(AR.StoreData.Handle,&(e2->onfile),SEEK_SET);
if ( WriteFile(AR.StoreData.Handle,(UBYTE *)ind,
(LONG)(sizeof(INDEXENTRY))) != sizeof(INDEXENTRY) ) {
MesPrint("File error while exchanging expressions");
Terminate(-1);
}
FlushFile(AR.StoreData.Handle);
}
}
/*
#] ExchangeExpressions :
#[ GetFirstBracket :
*/
int GetFirstBracket(WORD *term, int num)
{
/*
Gets the first bracket of the expression 'num'
Puts it in term. If no brackets the answer is one.
Routine should be thread-safe
*/
GETIDENTITY
POSITION position, oldposition;
RENUMBER renumber;
FILEHANDLE *fi;
WORD type, *oldcomppointer, oldonefile, numword;
WORD *t, *tstop;
oldcomppointer = AR.CompressPointer;
type = Expressions[num].status;
if ( type == STOREDEXPRESSION ) {
WORD TMproto[SUBEXPSIZE];
TMproto[0] = EXPRESSION;
TMproto[1] = SUBEXPSIZE;
TMproto[2] = num;
TMproto[3] = 1;
{ int ie; for ( ie = 4; ie < SUBEXPSIZE; ie++ ) TMproto[ie] = 0; }
AT.TMaddr = TMproto;
PUTZERO(position);
if ( ( renumber = GetTable(num,&position,0) ) == 0 ) {
MesCall("GetFirstBracket");
SETERROR(-1)
}
if ( GetFromStore(term,&position,renumber,&numword,num) < 0 ) {
MesCall("GetFirstBracket");
SETERROR(-1)
}
/*
#ifdef WITHPTHREADS
*/
if ( renumber->symb.lo != AN.dummyrenumlist )
M_free(renumber->symb.lo,"VarSpace");
M_free(renumber,"Renumber");
/*
#endif
*/
}
else { /* Active expression */
oldonefile = AR.GetOneFile;
if ( type == HIDDENLEXPRESSION || type == HIDDENGEXPRESSION ) {
AR.GetOneFile = 2; fi = AR.hidefile;
}
else {
AR.GetOneFile = 0; fi = AR.infile;
}
if ( fi->handle >= 0 ) {
PUTZERO(oldposition);
/*
SeekFile(fi->handle,&oldposition,SEEK_CUR);
*/
}
else {
SETBASEPOSITION(oldposition,fi->POfill-fi->PObuffer);
}
position = AS.OldOnFile[num];
if ( GetOneTerm(BHEAD term,fi,&position,1) < 0
|| ( GetOneTerm(BHEAD term,fi,&position,1) < 0 ) ) {
MLOCK(ErrorMessageLock);
MesCall("GetFirstBracket");
MUNLOCK(ErrorMessageLock);
SETERROR(-1)
}
if ( fi->handle >= 0 ) {
/*
SeekFile(fi->handle,&oldposition,SEEK_SET);
if ( ISNEGPOS(oldposition) ) {
MLOCK(ErrorMessageLock);
MesPrint("File error");
MUNLOCK(ErrorMessageLock);
SETERROR(-1)
}
*/
}
else {
fi->POfill = fi->PObuffer+BASEPOSITION(oldposition);
}
AR.GetOneFile = oldonefile;
}
AR.CompressPointer = oldcomppointer;
if ( *term ) {
tstop = term + *term; tstop -= ABS(tstop[-1]);
t = term + 1;
while ( t < tstop ) {
if ( *t == HAAKJE ) break;
t += t[1];
}
if ( t >= tstop ) {
term[0] = 4; term[1] = 1; term[2] = 1; term[3] = 3;
}
else {
*t++ = 1; *t++ = 1; *t++ = 3; *term = t - term;
}
}
else {
term[0] = 4; term[1] = 1; term[2] = 1; term[3] = 3;
}
return(*term);
}
/*
#] GetFirstBracket :
#[ GetFirstTerm :
*/
int GetFirstTerm(WORD *term, int num)
{
/*
Gets the first term of the expression 'num'
Puts it in term.
Routine should be thread-safe
*/
GETIDENTITY
POSITION position, oldposition;
RENUMBER renumber;
FILEHANDLE *fi;
WORD type, *oldcomppointer, oldonefile, numword;
oldcomppointer = AR.CompressPointer;
type = Expressions[num].status;
if ( type == STOREDEXPRESSION ) {
WORD TMproto[SUBEXPSIZE];
TMproto[0] = EXPRESSION;
TMproto[1] = SUBEXPSIZE;
TMproto[2] = num;
TMproto[3] = 1;
{ int ie; for ( ie = 4; ie < SUBEXPSIZE; ie++ ) TMproto[ie] = 0; }
AT.TMaddr = TMproto;
PUTZERO(position);
if ( ( renumber = GetTable(num,&position,0) ) == 0 ) {
MesCall("GetFirstTerm");
SETERROR(-1)
}
if ( GetFromStore(term,&position,renumber,&numword,num) < 0 ) {
MesCall("GetFirstTerm");
SETERROR(-1)
}
/*
#ifdef WITHPTHREADS
*/
if ( renumber->symb.lo != AN.dummyrenumlist )
M_free(renumber->symb.lo,"VarSpace");
M_free(renumber,"Renumber");
/*
#endif
*/
}
else { /* Active expression */
oldonefile = AR.GetOneFile;
if ( type == HIDDENLEXPRESSION || type == HIDDENGEXPRESSION ) {
AR.GetOneFile = 2; fi = AR.hidefile;
}
else {
AR.GetOneFile = 0;
if ( Expressions[num].replace == NEWLYDEFINEDEXPRESSION )
fi = AR.outfile;
else fi = AR.infile;
}
if ( fi->handle >= 0 ) {
PUTZERO(oldposition);
/*
SeekFile(fi->handle,&oldposition,SEEK_CUR);
*/
}
else {
SETBASEPOSITION(oldposition,fi->POfill-fi->PObuffer);
}
position = AS.OldOnFile[num];
if ( GetOneTerm(BHEAD term,fi,&position,1) < 0
|| ( GetOneTerm(BHEAD term,fi,&position,1) < 0 ) ) {
MLOCK(ErrorMessageLock);
MesCall("GetFirstTerm");
MUNLOCK(ErrorMessageLock);
SETERROR(-1)
}
if ( fi->handle >= 0 ) {
/*
SeekFile(fi->handle,&oldposition,SEEK_SET);
if ( ISNEGPOS(oldposition) ) {
MLOCK(ErrorMessageLock);
MesPrint("File error");
MUNLOCK(ErrorMessageLock);
SETERROR(-1)
}
*/
}
else {
fi->POfill = fi->PObuffer+BASEPOSITION(oldposition);
}
AR.GetOneFile = oldonefile;
}
AR.CompressPointer = oldcomppointer;
return(*term);
}
/*
#] GetFirstTerm :
#[ GetContent :
*/
int GetContent(WORD *content, int num)
{
/*
Gets the content of the expression 'num'
Puts it in content.
Routine should be thread-safe
The content is defined as the term that will make the expression 'num'
with integer coefficients, no GCD and all common factors taken out,
all negative powers removed when we divide the expression by this
content.
*/
GETIDENTITY
POSITION position, oldposition;
RENUMBER renumber;
FILEHANDLE *fi;
WORD type, *oldcomppointer, oldonefile, numword, *term, i;
WORD *cbuffer = TermMalloc("GetContent");
WORD *oldworkpointer = AT.WorkPointer;
oldcomppointer = AR.CompressPointer;
type = Expressions[num].status;
if ( type == STOREDEXPRESSION ) {
WORD TMproto[SUBEXPSIZE];
TMproto[0] = EXPRESSION;
TMproto[1] = SUBEXPSIZE;
TMproto[2] = num;
TMproto[3] = 1;
{ int ie; for ( ie = 4; ie < SUBEXPSIZE; ie++ ) TMproto[ie] = 0; }
AT.TMaddr = TMproto;
PUTZERO(position);
if ( ( renumber = GetTable(num,&position,0) ) == 0 ) goto CalledFrom;
if ( GetFromStore(cbuffer,&position,renumber,&numword,num) < 0 ) goto CalledFrom;
for(;;) {
term = oldworkpointer;
AR.CompressPointer = oldcomppointer;
if ( GetFromStore(term,&position,renumber,&numword,num) < 0 ) goto CalledFrom;
if ( *term == 0 ) break;
/*
'merge' the two terms
*/
if ( ContentMerge(BHEAD cbuffer,term) < 0 ) goto CalledFrom;
}
/*
#ifdef WITHPTHREADS
*/
if ( renumber->symb.lo != AN.dummyrenumlist )
M_free(renumber->symb.lo,"VarSpace");
M_free(renumber,"Renumber");
/*
#endif
*/
}
else { /* Active expression */
oldonefile = AR.GetOneFile;
if ( type == HIDDENLEXPRESSION || type == HIDDENGEXPRESSION ) {
AR.GetOneFile = 2; fi = AR.hidefile;
}
else {
AR.GetOneFile = 0;
if ( Expressions[num].replace == NEWLYDEFINEDEXPRESSION )
fi = AR.outfile;
else fi = AR.infile;
}
if ( fi->handle >= 0 ) {
PUTZERO(oldposition);
/*
SeekFile(fi->handle,&oldposition,SEEK_CUR);
*/
}
else {
SETBASEPOSITION(oldposition,fi->POfill-fi->PObuffer);
}
position = AS.OldOnFile[num];
if ( GetOneTerm(BHEAD cbuffer,fi,&position,1) < 0 ) goto CalledFrom;
AR.CompressPointer = oldcomppointer;
if ( GetOneTerm(BHEAD cbuffer,fi,&position,1) < 0 ) goto CalledFrom;
/*
Now go through the terms. For each term we have to test whether
what is in cbuffer is also in that term. If not, we have to remove
it from cbuffer. Additionally we have to accumulate the GCD of the
numerators and the LCM of the denominators. This is all done in the
routine ContentMerge.
*/
for(;;) {
term = oldworkpointer;
AR.CompressPointer = oldcomppointer;
if ( GetOneTerm(BHEAD term,fi,&position,1) < 0 ) goto CalledFrom;
if ( *term == 0 ) break;
/*
'merge' the two terms
*/
if ( ContentMerge(BHEAD cbuffer,term) < 0 ) goto CalledFrom;
}
if ( fi->handle < 0 ) {
fi->POfill = fi->PObuffer+BASEPOSITION(oldposition);
}
AR.GetOneFile = oldonefile;
}
AR.CompressPointer = oldcomppointer;
for ( i = 0; i < *cbuffer; i++ ) content[i] = cbuffer[i];
TermFree(cbuffer,"GetContent");
AT.WorkPointer = oldworkpointer;
return(*content);
CalledFrom:
MLOCK(ErrorMessageLock);
MesCall("GetContent");
MUNLOCK(ErrorMessageLock);
SETERROR(-1)
}
/*
#] GetContent :
#[ CleanupTerm :
Removes noncommuting objects from the term
*/
int CleanupTerm(WORD *term)
{
WORD *tstop, *t, *tfill, *tt;
GETSTOP(term,tstop);
t = term+1;
while ( t < tstop ) {
if ( *t >= FUNCTION && ( functions[*t-FUNCTION].commute || *t == DENOMINATOR ) ) {
tfill = t; tt = t + t[1]; tstop = term + *term;
while ( tt < tstop ) *tfill++ = *tt++;
*term = tfill - term;
tstop -= ABS(tfill[-1]);
}
else {
t += t[1];
}
}
return(0);
}
/*
#] CleanupTerm :
#[ ContentMerge :
*/
WORD ContentMerge(PHEAD WORD *content, WORD *term)
{
GETBIDENTITY
WORD *cstop, csize, crsize, sign = 1, numsize, densize, i, tnsize, tdsize;
UWORD *num, *den, *tnum, *tden;
WORD *outfill, *outb = TermMalloc("ContentMerge"), *ct;
WORD *t, *tstop, tsize, trsize, *told;
WORD *t1, *t2, *c1, *c2, i1, i2, *out1;
WORD didsymbol = 0, diddotp = 0, tfirst;
cstop = content + *content;
csize = cstop[-1];
if ( csize < 0 ) { sign = -sign; csize = -csize; }
cstop -= csize;
numsize = densize = crsize = (csize-1)/2;
num = NumberMalloc("ContentMerge");
den = NumberMalloc("ContentMerge");
for ( i = 0; i < numsize; i++ ) num[i] = (UWORD)(cstop[i]);
for ( i = 0; i < densize; i++ ) den[i] = (UWORD)(cstop[i+crsize]);
while ( num[numsize-1] == 0 ) numsize--;
while ( den[densize-1] == 0 ) densize--;
/*
First we do the coefficient
*/
tstop = term + *term;
tsize = tstop[-1];
if ( tsize < 0 ) tsize = -tsize;
/* else { sign = 1; } */
tstop = tstop - tsize;
tnsize = tdsize = trsize = (tsize-1)/2;
tnum = (UWORD *)tstop; tden = (UWORD *)(tstop + trsize);
while ( tnum[tnsize-1] == 0 ) tnsize--;
while ( tden[tdsize-1] == 0 ) tdsize--;
GcdLong(BHEAD num, numsize, tnum, tnsize, num, &numsize);
if ( LcmLong(BHEAD den, densize, tden, tdsize, den, &densize) ) goto CalledFrom;
outfill = outb + 1;
ct = content + 1;
t = term + 1;
while ( ct < cstop ) {
switch ( *ct ) {
case SYMBOL:
didsymbol = 1;
t = term+1;
while ( t < tstop && *t != *ct ) t += t[1];
if ( t >= tstop ) break;
t1 = t+2; t2 = t+t[1];
c1 = ct+2; c2 = ct+ct[1];
out1 = outfill; *outfill++ = *ct; outfill++;
while ( c1 < c2 && t1 < t2 ) {
if ( *c1 == *t1 ) {
if ( t1[1] <= c1[1] ) {
*outfill++ = *t1++; *outfill++ = *t1++;
c1 += 2;
}
else {
*outfill++ = *c1++; *outfill++ = *c1++;
t1 += 2;
}
}
else if ( *c1 < *t1 ) {
if ( c1[1] < 0 ) {
*outfill++ = *c1++; *outfill++ = *c1++;
}
else { c1 += 2; }
}
else {
if ( t1[1] < 0 ) {
*outfill++ = *t1++; *outfill++ = *t1++;
}
else t1 += 2;
}
}
while ( c1 < c2 ) {
if ( c1[1] < 0 ) { *outfill++ = c1[0]; *outfill++ = c1[1]; }
c1 += 2;
}
while ( t1 < t2 ) {
if ( t1[1] < 0 ) { *outfill++ = t1[0]; *outfill++ = t1[1]; }
t1 += 2;
}
out1[1] = outfill - out1;
if ( out1[1] == 2 ) outfill = out1;
break;
case DOTPRODUCT:
diddotp = 1;
t = term+1;
while ( t < tstop && *t != *ct ) t += t[1];
if ( t >= tstop ) break;
t1 = t+2; t2 = t+t[1];
c1 = ct+2; c2 = ct+ct[1];
out1 = outfill; *outfill++ = *ct; outfill++;
while ( c1 < c2 && t1 < t2 ) {
if ( *c1 == *t1 && c1[1] == t1[1] ) {
if ( t1[2] <= c1[2] ) {
*outfill++ = *t1++; *outfill++ = *t1++; *outfill++ = *t1++;
c1 += 3;
}
else {
*outfill++ = *c1++; *outfill++ = *c1++; *outfill++ = *c1++;
t1 += 3;
}
}
else if ( *c1 < *t1 || ( *c1 == *t1 && c1[1] < t1[1] ) ) {
if ( c1[2] < 0 ) {
*outfill++ = *c1++; *outfill++ = *c1++; *outfill++ = *c1++;
}
else { c1 += 3; }
}
else {
if ( t1[2] < 0 ) {
*outfill++ = *t1++; *outfill++ = *t1++; *outfill++ = *t1++;
}
else t1 += 3;
}
}
while ( c1 < c2 ) {
if ( c1[2] < 0 ) { *outfill++ = c1[0]; *outfill++ = c1[1]; *outfill++ = c1[1]; }
c1 += 3;
}
while ( t1 < t2 ) {
if ( t1[2] < 0 ) { *outfill++ = t1[0]; *outfill++ = t1[1]; *outfill++ = t1[1]; }
t1 += 3;
}
out1[1] = outfill - out1;
if ( out1[1] == 2 ) outfill = out1;
break;
case INDEX:
t = term+1;
while ( t < tstop && *t != *ct ) t += t[1];
if ( t >= tstop ) break;
t1 = t+2; t2 = t+t[1];
c1 = ct+2; c2 = ct+ct[1];
out1 = outfill; *outfill++ = *ct; outfill++;
while ( c1 < c2 && t1 < t2 ) {
if ( *c1 == *t1 ) {
*outfill++ = *c1++;
t1 += 1;
}
else if ( *c1 < *t1 ) { c1 += 1; }
else { t1 += 1; }
}
out1[1] = outfill - out1;
if ( out1[1] == 2 ) outfill = out1;
break;
case VECTOR:
case DELTA:
t = term+1;
while ( t < tstop && *t != *ct ) t += t[1];
if ( t >= tstop ) break;
t1 = t+2; t2 = t+t[1];
c1 = ct+2; c2 = ct+ct[1];
out1 = outfill; *outfill++ = *ct; outfill++;
while ( c1 < c2 && t1 < t2 ) {
if ( *c1 == *t1 && c1[1] && t1[1] ) {
*outfill++ = *c1++; *outfill++ = *c1++;
t1 += 2;
}
else if ( *c1 < *t1 || ( *c1 == *t1 && c1[1] < t1[1] ) ) {
c1 += 2;
}
else {
t1 += 2;
}
}
out1[1] = outfill - out1;
if ( out1[1] == 2 ) outfill = out1;
break;
case GAMMA:
default: /* Functions */
told = t;
t = term+1;
while ( t < tstop ) {
if ( *t != *ct ) { t += t[1]; continue; }
if ( ct[1] != t[1] ) { t += t[1]; continue; }
if ( ct[2] != t[2] ) { t += t[1]; continue; }
t1 = t; t2 = ct; i1 = t1[1]; i2 = t2[1];
while ( i1 > 0 ) {
if ( *t1 != *t2 ) break;
t1++; t2++; i1--;
}
if ( i1 != 0 ) { t += t[1]; continue; }
t1 = t;
for ( i = 0; i < i2; i++ ) { *outfill++ = *t++; }
/*
Mark as 'used'. The flags must be different!
*/
t1[2] |= SUBTERMUSED1;
ct[2] |= SUBTERMUSED2;
t = told;
break;
}
break;
}
ct += ct[1];
}
if ( diddotp == 0 ) {
t = term+1; while ( t < tstop && *t != DOTPRODUCT ) t += t[1];
if ( t < tstop ) { /* now we need the negative powers */
tfirst = 1; told = outfill;
for ( i = 2; i < t[1]; i += 3 ) {
if ( t[i+2] < 0 ) {
if ( tfirst ) { *outfill++ = DOTPRODUCT; *outfill++ = 0; tfirst = 0; }
*outfill++ = t[i]; *outfill++ = t[i+1]; *outfill++ = t[i+2];
}
}
if ( outfill > told ) told[1] = outfill-told;
}
}
if ( didsymbol == 0 ) {
t = term+1; while ( t < tstop && *t != SYMBOL ) t += t[1];
if ( t < tstop ) { /* now we need the negative powers */
tfirst = 1; told = outfill;
for ( i = 2; i < t[1]; i += 2 ) {
if ( t[i+1] < 0 ) {
if ( tfirst ) { *outfill++ = SYMBOL; *outfill++ = 0; tfirst = 0; }
*outfill++ = t[i]; *outfill++ = t[i+1];
}
}
if ( outfill > told ) told[1] = outfill-told;
}
}
/*
Now put the coefficient back.
*/
if ( numsize < densize ) {
for ( i = numsize; i < densize; i++ ) num[i] = 0;
numsize = densize;
}
else if ( densize < numsize ) {
for ( i = densize; i < numsize; i++ ) den[i] = 0;
densize = numsize;
}
for ( i = 0; i < numsize; i++ ) *outfill++ = num[i];
for ( i = 0; i < densize; i++ ) *outfill++ = den[i];
csize = numsize+densize+1;
if ( sign < 0 ) csize = -csize;
*outfill++ = csize;
*outb = outfill-outb;
NumberFree(den,"ContentMerge");
NumberFree(num,"ContentMerge");
for ( i = 0; i < *outb; i++ ) content[i] = outb[i];
TermFree(outb,"ContentMerge");
/*
Now we have to 'restore' the term to its original.
We do not restore the content, because if anything was used the
new content overwrites the old. 6-mar-2018 JV
*/
t = term + 1;
while ( t < tstop ) {
if ( *t >= FUNCTION ) t[2] &= ~SUBTERMUSED1;
t += t[1];
}
return(*content);
CalledFrom:
MLOCK(ErrorMessageLock);
MesCall("GetContent");
MUNLOCK(ErrorMessageLock);
SETERROR(-1)
}
/*
#] ContentMerge :
#[ TermsInExpression :
*/
LONG TermsInExpression(WORD num)
{
LONG x = Expressions[num].counter;
if ( x >= 0 ) return(x);
return(-1);
}
/*
#] TermsInExpression :
#[ SizeOfExpression :
*/
LONG SizeOfExpression(WORD num)
{
LONG x = (LONG)(DIVPOS(Expressions[num].size,sizeof(WORD)));
if ( x >= 0 ) return(x);
return(-1);
}
/*
#] SizeOfExpression :
#[ UpdatePositions :
*/
void UpdatePositions()
{
EXPRESSIONS e = Expressions;
POSITION *old;
WORD *oldw;
int i;
if ( NumExpressions > 0 &&
( AS.OldOnFile == 0 || AS.NumOldOnFile < NumExpressions ) ) {
if ( AS.OldOnFile ) {
old = AS.OldOnFile;
AS.OldOnFile = (POSITION *)Malloc1(NumExpressions*sizeof(POSITION),"file pointers");
for ( i = 0; i < AS.NumOldOnFile; i++ ) AS.OldOnFile[i] = old[i];
AS.NumOldOnFile = NumExpressions;
M_free(old,"process file pointers");
}
else {
AS.OldOnFile = (POSITION *)Malloc1(NumExpressions*sizeof(POSITION),"file pointers");
AS.NumOldOnFile = NumExpressions;
}
}
if ( NumExpressions > 0 &&
( AS.OldNumFactors == 0 || AS.NumOldNumFactors < NumExpressions ) ) {
if ( AS.OldNumFactors ) {
oldw = AS.OldNumFactors;
AS.OldNumFactors = (WORD *)Malloc1(NumExpressions*sizeof(WORD),"numfactors pointers");
for ( i = 0; i < AS.NumOldNumFactors; i++ ) AS.OldNumFactors[i] = oldw[i];
M_free(oldw,"numfactors pointers");
oldw = AS.Oldvflags;
AS.Oldvflags = (WORD *)Malloc1(NumExpressions*sizeof(WORD),"vflags pointers");
for ( i = 0; i < AS.NumOldNumFactors; i++ ) AS.Oldvflags[i] = oldw[i];
AS.NumOldNumFactors = NumExpressions;
M_free(oldw,"vflags pointers");
}
else {
AS.OldNumFactors = (WORD *)Malloc1(NumExpressions*sizeof(WORD),"numfactors pointers");
AS.Oldvflags = (WORD *)Malloc1(NumExpressions*sizeof(WORD),"vflags pointers");
AS.NumOldNumFactors = NumExpressions;
}
}
for ( i = 0; i < NumExpressions; i++ ) {
AS.OldOnFile[i] = e[i].onfile;
AS.OldNumFactors[i] = e[i].numfactors;
AS.Oldvflags[i] = e[i].vflags;
}
}
/*
#] UpdatePositions :
#[ CountTerms1 : LONG CountTerms1()
Counts the terms in the current deferred bracket
Is mainly an adaptation of the routine Deferred in proces.c
*/
LONG CountTerms1(PHEAD0)
{
GETBIDENTITY
POSITION oldposition, startposition;
WORD *t, *m, *mstop, decr, i, *oldwork, retval;
WORD *oldipointer = AR.CompressPointer;
WORD oldGetOneFile = AR.GetOneFile, olddeferflag = AR.DeferFlag;
LONG numterms = 0;
AR.GetOneFile = 1;
oldwork = AT.WorkPointer;
AT.WorkPointer = (WORD *)(((UBYTE *)(AT.WorkPointer)) + AM.MaxTer);
AR.DeferFlag = 0;
startposition = AR.DefPosition;
/*
Store old position
*/
if ( AR.infile->handle >= 0 ) {
PUTZERO(oldposition);
/*
SeekFile(AR.infile->handle,&oldposition,SEEK_CUR);
*/
}
else {
SETBASEPOSITION(oldposition,AR.infile->POfill-AR.infile->PObuffer);
AR.infile->POfill = (WORD *)((UBYTE *)(AR.infile->PObuffer)
+BASEPOSITION(startposition));
}
/*
Look in the CompressBuffer where the bracket contents start
*/
t = m = AR.CompressBuffer;
t += *t;
mstop = t - ABS(t[-1]);
m++;
while ( *m != HAAKJE && m < mstop ) m += m[1];
if ( m >= mstop ) { /* No deferred action! */
numterms = 1;
AR.DeferFlag = olddeferflag;
AT.WorkPointer = oldwork;
AR.GetOneFile = oldGetOneFile;
return(numterms);
}
mstop = m + m[1];
decr = WORDDIF(mstop,AR.CompressBuffer)-1;
m = AR.CompressBuffer;
t = AR.CompressPointer;
i = *m;
NCOPY(t,m,i);
AR.TePos = 0;
AN.TeSuOut = 0;
/*
Status:
First bracket content starts at mstop.
Next term starts at startposition.
Decompression information is in AR.CompressPointer.
The outside of the bracket runs from AR.CompressBuffer+1 to mstop.
*/
AR.CompressPointer = oldipointer;
for(;;) {
numterms++;
retval = GetOneTerm(BHEAD AT.WorkPointer,AR.infile,&startposition,0);
if ( retval >= 0 ) AR.CompressPointer = oldipointer;
if ( retval <= 0 ) break;
t = AR.CompressPointer;
if ( *t < (1 + decr + ABS(*(t+*t-1))) ) break;
t++;
m = AR.CompressBuffer+1;
while ( m < mstop ) {
if ( *m != *t ) goto Thatsit;
m++; t++;
}
}
Thatsit:;
/*
Finished. Reposition the file, restore information and return.
*/
AT.WorkPointer = oldwork;
if ( AR.infile->handle >= 0 ) {
/*
SeekFile(AR.infile->handle,&oldposition,SEEK_SET);
*/
}
else {
AR.infile->POfill = AR.infile->PObuffer + BASEPOSITION(oldposition);
}
AR.DeferFlag = olddeferflag;
AR.GetOneFile = oldGetOneFile;
return(numterms);
}
/*
#] CountTerms1 :
#[ TermsInBracket : LONG TermsInBracket(term,level)
The function TermsInBracket_()
Syntax:
TermsInBracket_() : The current bracket in a Keep Brackets
TermsInBracket_(bracket) : This bracket in the current expression
TermsInBracket_(expression,bracket) : This bracket in the given expression
All other specifications don't have any effect.
*/
#define CURRENTBRACKET 1
#define BRACKETCURRENTEXPR 2
#define BRACKETOTHEREXPR 3
#define NOBRACKETACTIVE 4
LONG TermsInBracket(PHEAD WORD *term, WORD level)
{
WORD *t, *tstop, *b, *tt, *n1, *n2;
int type = 0, i, num;
LONG numterms = 0;
WORD *bracketbuffer = AT.WorkPointer;
t = term; GETSTOP(t,tstop);
t++; b = bracketbuffer;
while ( t < tstop ) {
if ( *t != TERMSINBRACKET ) { t += t[1]; continue; }
if ( t[1] == FUNHEAD || (
t[1] == FUNHEAD+2
&& t[FUNHEAD] == -SNUMBER
&& t[FUNHEAD+1] == 0
) ) {
if ( AC.ComDefer == 0 ) {
type = NOBRACKETACTIVE;
}
else {
type = CURRENTBRACKET;
}
*b = 0;
break;
}
if ( t[FUNHEAD] == -EXPRESSION ) {
if ( t[FUNHEAD+2] < 0 ) {
if ( ( t[FUNHEAD+2] <= -FUNCTION ) && ( t[1] == FUNHEAD+3 ) ) {
type = BRACKETOTHEREXPR;
*b++ = FUNHEAD+4; *b++ = -t[FUNHEAD+2]; *b++ = FUNHEAD;
for ( i = 2; i < FUNHEAD; i++ ) *b++ = 0;
*b++ = 1; *b++ = 1; *b++ = 3;
break;
}
else if ( ( t[FUNHEAD+2] > -FUNCTION ) && ( t[1] == FUNHEAD+4 ) ) {
type = BRACKETOTHEREXPR;
tt = t + FUNHEAD+2;
switch ( *tt ) {
case -SYMBOL:
*b++ = 8; *b++ = SYMBOL; *b++ = 4; *b++ = tt[1];
*b++ = 1; *b++ = 1; *b++ = 1; *b++ = 3;
break;
case -SNUMBER:
if ( tt[1] == 1 ) {
*b++ = 4; *b++ = 1; *b++ = 1; *b++ = 3;
}
else goto IllBraReq;
break;
default:
goto IllBraReq;
}
break;
}
}
else if ( ( t[FUNHEAD+2] == (t[1]-FUNHEAD-2) ) &&
( t[FUNHEAD+2+ARGHEAD] == (t[FUNHEAD+2]-ARGHEAD) ) ) {
type = BRACKETOTHEREXPR;
tt = t + FUNHEAD + ARGHEAD; num = *tt;
for ( i = 0; i < num; i++ ) *b++ = *tt++;
break;
}
}
else {
if ( t[FUNHEAD] < 0 ) {
if ( ( t[FUNHEAD] <= -FUNCTION ) && ( t[1] == FUNHEAD+1 ) ) {
type = BRACKETCURRENTEXPR;
*b++ = FUNHEAD+4; *b++ = -t[FUNHEAD+2]; *b++ = FUNHEAD;
for ( i = 2; i < FUNHEAD; i++ ) *b++ = 0;
*b++ = 1; *b++ = 1; *b++ = 3; *b = 0;
break;
}
else if ( ( t[FUNHEAD] > -FUNCTION ) && ( t[1] == FUNHEAD+2 ) ) {
type = BRACKETCURRENTEXPR;
tt = t + FUNHEAD+2;
switch ( *tt ) {
case -SYMBOL:
*b++ = 8; *b++ = SYMBOL; *b++ = 4; *b++ = tt[1];
*b++ = 1; *b++ = 1; *b++ = 1; *b++ = 3;
break;
case -SNUMBER:
if ( tt[1] == 1 ) {
*b++ = 4; *b++ = 1; *b++ = 1; *b++ = 3;
}
else goto IllBraReq;
break;
default:
goto IllBraReq;
}
break;
}
}
else if ( ( t[FUNHEAD] == (t[1]-FUNHEAD) ) &&
( t[FUNHEAD+ARGHEAD] == (t[FUNHEAD]-ARGHEAD) ) ) {
type = BRACKETCURRENTEXPR;
tt = t + FUNHEAD + ARGHEAD; num = *tt;
for ( i = 0; i < num; i++ ) *b++ = *tt++;
break;
}
else {
IllBraReq:;
MLOCK(ErrorMessageLock);
MesPrint("Illegal bracket request in termsinbracket_ function.");
MUNLOCK(ErrorMessageLock);
Terminate(-1);
}
}
t += t[1];
}
AT.WorkPointer = b;
if ( AT.WorkPointer + *term +4 > AT.WorkTop ) {
MLOCK(ErrorMessageLock);
MesWork();
MesPrint("Called from termsinbracket_ function.");
MUNLOCK(ErrorMessageLock);
return(-1);
}
/*
We are now in the position to look for the bracket
*/
switch ( type ) {
case CURRENTBRACKET:
/*
The code here should be rather similar to when we pick up
the contents of the bracket. In our case we only count the
terms though.
*/
numterms = CountTerms1(BHEAD0);
break;
case BRACKETCURRENTEXPR:
/*
Not implemented yet.
*/
MLOCK(ErrorMessageLock);
MesPrint("termsinbracket_ function currently only handles Keep Brackets.");
MUNLOCK(ErrorMessageLock);
return(-1);
case BRACKETOTHEREXPR:
MLOCK(ErrorMessageLock);
MesPrint("termsinbracket_ function currently only handles Keep Brackets.");
MUNLOCK(ErrorMessageLock);
return(-1);
case NOBRACKETACTIVE:
numterms = 1;
break;
}
/*
Now we have the number in numterms. We replace the function by it.
*/
n1 = term; n2 = AT.WorkPointer; tstop = n1 + *n1;
while ( n1 < t ) *n2++ = *n1++;
i = numterms >> BITSINWORD;
if ( i == 0 ) {
*n2++ = LNUMBER; *n2++ = 4; *n2++ = 1; *n2++ = (WORD)(numterms & WORDMASK);
}
else {
*n2++ = LNUMBER; *n2++ = 5; *n2++ = 2;
*n2++ = (WORD)(numterms & WORDMASK); *n2++ = i;
}
n1 += n1[1];
while ( n1 < tstop ) *n2++ = *n1++;
AT.WorkPointer[0] = n2 - AT.WorkPointer;
AT.WorkPointer = n2;
if ( Generator(BHEAD n1,level) < 0 ) {
AT.WorkPointer = bracketbuffer;
MLOCK(ErrorMessageLock);
MesPrint("Called from termsinbracket_ function.");
MUNLOCK(ErrorMessageLock);
return(-1);
}
/*
Finished. Reset things and return.
*/
AT.WorkPointer = bracketbuffer;
return(numterms);
}
/*
#] TermsInBracket : LONG TermsInBracket(term,level)
#] Expressions :
*/
form-master/sources/extcmd.c 0000664 0000000 0000000 00000143330 13565763364 0016453 0 ustar 00root root 0000000 0000000 /** @file extcmd.c
*
* The system that takes care of communication with external programs.
*/
/* #[ License : */
/*
* Copyright (C) 1984-2017 J.A.M. Vermaseren
* When using this file you are requested to refer to the publication
* J.A.M.Vermaseren "New features of FORM" math-ph/0010025
* This is considered a matter of courtesy as the development was paid
* for by FOM the Dutch physics granting agency and we would like to
* be able to track its scientific use to convince FOM of its value
* for the community.
*
* This file is part of FORM.
*
* FORM 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.
*
* FORM 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 FORM. If not, see .
*/
/* #] License : */
/*
#[ Documentation :
This module is written by M.Tentyukov as a part of implementation of
interaction between FORM and external processes, first release
09.04.2004. A part of this code is copyied from the DIANA project
written by M. Tentyukov and published under the GPL version 2 as
published by the Free Software Foundation. The code of this module
is NOT covered by GPL; it can be used under the terms of the FORM
License http://www.nikhef.nl/~form/license.html
This file is completely re-written by M.Tentyukov in May 2006.
Since the interface was changed, the public function were changed,
also. A new publc functions were added: initPresetExternalChannels()
(see comments just before this function in the present file) and
setKillModeForExternalChannel (a pointer, not a function).
If a macro WITHEXTERNALCHANNEL is not defined, all public punctions
are stubs returning failure.
The idea is to start an external command swallowing
its stdin and stdout. This can be done by means of the function
int openExternalChannel(cmd,daemonize,shellname,stderrname), where
cmd is a command to run,
daemonize: if !=0 then start the command in the "daemon" mode,
shellname: if !=NULL, execute the command in a subshell,
stderrname: if != NULL, redirect stderr of the command to this file.
The function returns some small positive integer number (the
descriptor of a newly created external channel), or -1 on failure.
After the command is started, it becomes a _current_ opened external
channel. The buffer can be sent to its stdin by a function
int writeBufToExtChannel(buf, n)
(here buf is a pointer to the buffer, n is the length in bytes; the
function returns 0 in success, or -1 on failure),
or one character can be read from its stdout by
means of the function
int getcFromExtChannel().
The latter returns the
character casted to integer, or something <0. This can be -2 (if there
is no current external channel) or EOF, if the external program closes
its stdout, or if the external program outputs a string coinciding
with a _terminator_.
By default, the terminator if an empty line. For the current external
channel it can be set by means of the function
int setTerminatorForExternalChannel(newterminaror).
The function returns 0 in success, or !0 if something is wrong (no
current channel, too long terminator).
After getcFromExtChannel() returns EOF, the current channel becomes
undefined. Any further attempts to read information by
getcFromExtChannel() result in -2. To set (re-set) a current channel,
the function
int selectExternalChannel(n)
can be used. This function accepts the valid external channel
descriptor (returned by openExternalChannel) and returns the
descriptor of a previous current channel (0, if there was no current
channel, or -1, if the external channel descriptor is invalid).
If n == 0, the function undefine the current external channel.
The function
int closeExternalChannel(n)
destroys the opened external channel with the descriptor n. It returns
0 in success, or -1 on failure. If the corresponding external channel
was the current one, the current channel becomes undefined. If n==0,
the function closes the current external channel.
The function
int getCurrentExternalChannel(void)
returns the descriptor if the current external channel, or 0 , if
there is no current external channel.
The function
void closeAllExternalChannels(void)
destroys all opened external channels.
List of all public functions:
int openExternalChannel(UBYTE *cmd,int daemonize,UBYTE *shellname, UBYTE * stderrname);
int initPresetExternalChannels(UBYTE *theline, int thetimeout);
int setTerminatorForExternalChannel(char *newterminaror);
int setKillModeForExternalChannel(int signum, int sentToWholeGroup);
int closeExternalChannel(int n);
int selectExternalChannel(int n);
int writeBufToExtChannel(char *buf,int n);
int getcFromExtChannel(void);
int getCurrentExternalChannel(void);
void closeAllExternalChannels(void);
ATTENTION!
Four of them:
1 setTerminatorForExternalChannel
2 setKillModeForExternalChannel
3 writeBufToExtChannel
4 getcFromExtChannel
are NOT functions, but variables (pointers) of a corrsponding type.
They are initialised by proper values to avoid repeated error checking.
All public functions are independent of realization hidden in this module.
All other functions may have a returned type/parameters type local w.r.t.
this module; they are not declared outside of this file.
#] Documentation :
#[ Selftest initializing:
*/
/*
Uncomment to get a self-consistent program:
#define SELFTEST 1
*/
#ifdef SELFTEST
#define WITHEXTERNALCHANNEL 1
#ifdef _MSC_VER
#define FORM_INLINE __inline
#else
#define FORM_INLINE inline
#endif
/*
from declare.h:
*/
#define VOID void
/*
From form3.h:
*/
typedef unsigned char UBYTE;
/*The following variables should be defined in variable.h:*/
extern int (*writeBufToExtChannel)(char *buffer, size_t n);
extern int (*getcFromExtChannel)();
extern int (*setTerminatorForExternalChannel)(char *buffer);
extern int (*setKillModeForExternalChannel)(int signum, int sentToWholeGroup);
#else /*ifdef SELFTEST*/
#include "form3.h"
#endif /*ifdef SELFTEST ... else*/
/*
pid_t getExternalChannelPid(VOID);
*/
/*
#] Selftest initializing:
#[ Includes :
*/
#ifdef WITHEXTERNALCHANNEL
#include
#ifndef _MSC_VER
#include
#endif
#include
#include
#ifndef _MSC_VER
#include
#include
#endif
#include
#include
#include
/*
#] Includes :
#[ FailureFunctions:
*/
/*Non-initialized variant of public functions:*/
int writeBufToExtChannelFailure(char *buf, size_t count)
{
DUMMYUSE(buf); DUMMYUSE(count);
return(-1);
}/*writeBufToExtChannelFailure*/
int setTerminatorForExternalChannelFailure(char *newTerminator)
{
DUMMYUSE(newTerminator);
return(-1);
}/*setTerminatorForExternalChannelFailure*/
int setKillModeForExternalChannelFailure(int signum, int sentToWholeGroup)
{
DUMMYUSE(signum); DUMMYUSE(sentToWholeGroup);
return(-1);
}/*setKillModeForExternalChannelFailure*/
int getcFromExtChannelFailure()
{
return(-2);
}/*getcFromExtChannelFailure*/
int (*writeBufToExtChannel)(char *buffer, size_t n) = &writeBufToExtChannelFailure;
int (*setTerminatorForExternalChannel)(char *buffer) =
&setTerminatorForExternalChannelFailure;
int (*setKillModeForExternalChannel)(int signum, int sentToWholeGroup) =
&setKillModeForExternalChannelFailure;
int (*getcFromExtChannel)() = &getcFromExtChannelFailure;
#endif
/*
#] FailureFunctions:
#[ Stubs :
*/
#ifndef WITHEXTERNALCHANNEL
/*Stubs for public functions:*/
int openExternalChannel(UBYTE *cmd, int daemonize, UBYTE *shellname, UBYTE *stderrname)
{ DUMMYUSE(cmd); DUMMYUSE(daemonize); DUMMYUSE(shellname); DUMMYUSE(stderrname); return(-1); };
int initPresetExternalChannels(UBYTE *theline, int thetimeout) { DUMMYUSE(theline); DUMMYUSE(thetimeout); return(-1); };
int closeExternalChannel(int n) { DUMMYUSE(n); return(-1); };
int selectExternalChannel(int n) { DUMMYUSE(n); return(-1); };
int getCurrentExternalChannel() { return(0); };
void closeAllExternalChannels() {};
#else /*ifndef WITHEXTERNALCHANNEL*/
/*
#] Stubs :
#[ Local types :
*/
/*First argument for the function signal:*/
#ifndef INTSIGHANDLER
typedef void (*mysighandler_t)(int);
#else
/* Sometimes, this nonsense may occurs:*/
/*typedef int (*mysighandler_t)(int);*/
#endif
/*Input IO buffer size increment -- each time the buffer
is expired it will be increased by this value (in bytes):*/
#define DELTA_EXT_BUF 128
/*Re-allocatable array containing External Channel
handlers increased each time by this value:*/
#define DELTA_EXT_LIST 8
/*How many times I/O routines may attempt to continue their work
in some failures:*/
#define MAX_FAILS_IO 2
/*The external channel handler structure:*/
typedef struct ExternalChannel {
pid_t pid; /*PID of the external process*/
pid_t gpid; /*process group ID of the external process.
If <=0, not used, if >0, the kill signals
is sent to the whole group */
FILE *frec; /*stdout of the external process*/
char *INbuf; /*External channel buffer*/
char *IBfill; /*Position in INbuf from which the next input character will be read*/
char *IBfull; /*End of read INbuf*/
char *IBstop; /*end of allocated space for INbuf*/
char *terminator;/* Terminator - when extern. program outputs ONLY this string,
it is assumed that the answer is ready, and getcFromExtChannel
returns EOF. Should not be longer then the minimal buffer!*/
/*Info fields, not changable after creating a channel:*/
char *cmd; /*the command*/
char *shellname;
char *stderrname;/*filename to redirect stderr, or NULL*/
int fsend; /*stdin of the external process*/
int killSignal; /*signal to kill*/
int daemonize;/*0 --neither setsid nor daemonize, !=0 -- full daemonization*/
PADPOINTER(0,3,0,0);
} EXTHANDLE;
static EXTHANDLE *externalChannelsList=0;
/*Here integers are better than pointers: */
static int externalChannelsListStop=0;
static int externalChannelsListFill=0;
/*"current" external channel:*/
static EXTHANDLE *externalChannelsListTop=0;
/*
#] Local types :
#[ Selftest functions :
*/
#ifdef SELFTEST
/*For malloc prototype:*/
#include
/*StrLen, Malloc1, M_free and strDup1 are defined in tools.c -- here only emulation:*/
int StrLen(char *pattern)
{
register char *p=(char*)pattern;
while(*p)p++;
return((int) ((p-(char*)pattern)) );
}/*StrLen*/
void *Malloc1(int l, char *c)
{
return(malloc(l));
}
void M_free(void *p,char *c)
{
return(free(p));
}
char *strDup1(UBYTE *instring, char *ifwrong)
{
UBYTE *s = instring, *to;
while ( *s ) s++;
to = s = (UBYTE *)Malloc1((s-instring)+1,ifwrong);
while ( *instring ) *to++ = *instring++;
*to = 0;
return(s);
}
/*PutPreVar from pre.c -- just ths stub:*/
int PutPreVar(UBYTE *a,UBYTE *b,UBYTE *c,int i)
{
return(0);
}
#endif
/*
#] Selftest functions :
#[ Local functions :
*/
/*Initialize one cell of handler:*/
static FORM_INLINE VOID extHandlerInit(EXTHANDLE *h)
{
h->pid=-1;
h->gpid=-1;
h->fsend=0;
h->killSignal=SIGKILL;
h->daemonize=1;
h->frec=NULL;
h->INbuf=h->IBfill=h->IBfull=h->IBstop=
h->terminator=h->cmd=h->shellname=h->stderrname=NULL;
}/*extHandlerInit*/
/* Copies each field of handler:*/
static FORM_INLINE VOID extHandlerSwallowCopy(EXTHANDLE *to, EXTHANDLE *from)
{
to->pid=from->pid;
to->gpid=from->gpid;
to->fsend=from->fsend;
to->killSignal=from->killSignal;
to->daemonize=from->daemonize;
to->frec=from->frec;
to->INbuf=from->INbuf;
to->IBfill=from->IBfill;
to->IBfull=from->IBfull;
to->IBstop=from->IBstop;
to->terminator=from->terminator;
to->cmd=from->cmd;
to->shellname=from->shellname;
to->stderrname=from->stderrname;
}/*extHandlerSwallow*/
/*Allocates memory for fields of handler which have no fixed
storage size and initializes some fields:*/
static FORM_INLINE VOID
extHandlerAlloc(EXTHANDLE *h, char *cmd, char *shellname, char *stderrname)
{
h->IBfill=h->IBfull=h->INbuf=
Malloc1(DELTA_EXT_BUF,"External channel buffer");
h->IBstop=h->INbuf+DELTA_EXT_BUF;
/*Initialize a terminator:*/
*(h->terminator=Malloc1(DELTA_EXT_BUF,"External channel terminator"))='\n';
(h->terminator)[1]='\0';/*By default the terminator is '\n'*/
/*Deep copy all strings:*/
if(cmd!=NULL)
h->cmd=(char *)strDup1((UBYTE *)cmd,"External channel command");
else/*cmd cannot be NULL! If this is NULL then force it to be something special*/
h->cmd=(char *)strDup1((UBYTE *)"/","External channel command");
if(shellname!=NULL)
h->shellname=
(char *)strDup1((UBYTE *)shellname,"External channel shell name");
if(stderrname!=NULL)
h->stderrname=
(char *)strDup1((UBYTE *)stderrname,"External channel stderr name");
}/*extHandlerAlloc*/
/*Disallocates dynamically allocated fields of a handler:*/
static FORM_INLINE VOID extHandlerFree(EXTHANDLE *h)
{
if(h->stderrname) M_free(h->stderrname,"External channel stderr name");
if(h->shellname) M_free(h->shellname,"External channel shell name");
if(h->cmd) M_free(h->cmd,"External channel command");
if(h->terminator)M_free(h->terminator,"External channel terminator");
if(h->INbuf)M_free(h->INbuf,"External channel buffer");
extHandlerInit(h);
}/*extHandlerFree*/
/* Closes all descriptors, kills the external process, frees all internal fields,
BUT does NOT free the main container:*/
static VOID destroyExternalChannel(EXTHANDLE *h)
{
/*Note, this function works in parallel mode correctly, see comments below.*/
/*Note, for slaves in a parallel mode h->pid == 0:*/
if( (h->pid > 0) && (h->killSignal > 0) ){
int chstatus;
if( h->gpid > 0)
chstatus=kill(-h->gpid,h->killSignal);
else
chstatus=kill(h->pid,h->killSignal);
if(chstatus==0)
/*If the process will not be killed by this signal, FORM hangs up here!:*/
waitpid(h->pid, &chstatus, 0);
}/*if( (h->pid > 0) && (h->killSignal > 0) )*/
/*Note, for slaves in a parallel mode h->frec == h->fsend == 0:*/
if(h->frec) fclose(h->frec);
if( h->fsend > 0) close(h->fsend);
extHandlerFree(h);
/*Does not do "free(h)"!*/
}/*destroyExternalChannel*/
/*Wrapper to the read() syscall, to handle possible interrupts by unblocked signals:*/
static FORM_INLINE ssize_t read2b(int fd, char *buf, size_t count)
{
ssize_t res;
if( (res=read(fd,buf,count)) <1 )/*EOF or read is interrupted by a signal?:*/
while( (errno == EINTR)&&(res <1) )
/*The call was interrupted by a signal before any data was read, try again:*/
res=read(fd,buf,count);
return (res);
}/*read2b*/
/*Wrapper to the write() syscall, to handle possible interrupts by unblocked signals:*/
static FORM_INLINE ssize_t writeFromb(int fd, char *buf, size_t count)
{
ssize_t res;
if( (res=write(fd,buf,count)) <1 )/*Is write interrupted by a signal?:*/
while( (errno == EINTR)&&(res <1) )
/*The call was interrupted by a signal before any data was written, try again:*/
res=write(fd,buf,count);
return (res);
}/*writeFromb*/
/* Read one (binary) PID from the file descriptor fd:*/
static FORM_INLINE pid_t readpid(int fd)
{
pid_t tmp;
if(read2b(fd,(char*)&tmp,sizeof(pid_t))!=sizeof(pid_t))
return (pid_t)-1;
return tmp;
}/*readpid*/
/* Writeone (binary) PID to the file descriptor fd:*/
static FORM_INLINE pid_t writepid(int fd, pid_t thepid)
{
if(writeFromb(fd,(char*)&thepid,sizeof(pid_t))!=sizeof(pid_t))
return (pid_t)-1;
return (pid_t)0;
}/*readpid*/
/*Wrtites exactly count bytes from the buffer buf into the descriptor fd, independently on
nonblocked signals and the MPU/buffer hits. Returns 0 or -1:
*/
static FORM_INLINE int writexactly(int fd, char *buf, size_t count)
{
ssize_t i;
int j=0,n=0;
for(;;){
if( (i=writeFromb(fd, buf+j, count-j)) < 0 ) return(-1);
j+=i;
if ( ((size_t)j) == count ) break;
if(i==0)n++;
else n=0;
if(n>MAX_FAILS_IO)return (-1);
}/*for(;;)*/
return (0);
}/*writexactly*/
/* Set the FD_CLOEXEC flag of desc if value is nonzero,
or clear the flag if value is 0.
Return 0 on success, or -1 on error with errno set. */
static int set_cloexec_flag(int desc, int value)
{
int oldflags = fcntl (desc, F_GETFD, 0);
/* If reading the flags failed, return error indication now.*/
if (oldflags < 0)
return (oldflags);
/* Set just the flag we want to set. */
if (value != 0)
oldflags |= FD_CLOEXEC;
else
oldflags &= ~FD_CLOEXEC;
/* Store modified flag word in the descriptor. */
return (fcntl(desc, F_SETFD, oldflags));
}/*set_cloexec_flag*/
/* Adds the integer fd to the array fifo of length top+1 so that
the array is ascendantly ordered. It is supposed that all 0 -- top-1
elements in the array are already ordered:*/
static VOID pushDescriptor(int *fifo, int top, int fd)
{
if ( top == 0 ) {
fifo[top] = fd;
} else {
int ins=top-1;
if( fifo[ins]<=fd )
fifo[top]=fd;
else{
/*Find the position:*/
while( (ins>=0)&&(fifo[ins]>fd) )ins--;
/*Move all elements starting from the position to the right:*/
for(ins++;top>ins; top--)
fifo[top]=fifo[top-1];
/*Put the element:*/
fifo[ins]=fd;
}
}
}/*pushDescriptor*/
/*Close all descriptors greate or equal than startFrom except those
listed in the ascendantly ordered array usedFd of length top:*/
static FORM_INLINE VOID closeAllDescriptors(int startFrom, int *usedFd, int top)
{
int n,maxfd;
for(n=0;n ' ');
if(*cmd != '\0')
*cmd++ = '\0';
n++;
}/*if(*cmd != '\0')*/
}/*while(*cmd != '\0')*/
argv[n]=NULL;
if(n==0)return -1;
return n;
}/*parseline*/
/*Reads positive decimal number (not bigger than maxnum)
from the string and returns it;
the pointer *b is set to the next non-converted character:*/
static LONG str2i(char *str, char **b, LONG maxnum)
{
LONG n=0;
/*Eat trailing spaces:*/
while(*str<=' ')if(*str++ == '\0')return(-1);
(*b)=str;
while (*str>='0'&&*str<='9')
if( (n=10*n + *str++ - '0')>maxnum )
return(-1);
if((*b)==str)/*No single number!*/
return(-1);
(*b)=str;
return(n);
}
/*Converts long integer to a decimal representation.
For portability reasons we cannot use LongCopy from tools.c
since theoretically LONG may be smaller than pid_t:*/
static char *l2s(LONG x, char *to)
{
char *s;
int i = 0, j;
s = to;
do { *s++ = (x % 10)+'0'; i++; } while ( ( x /= 10 ) != 0 );
*s-- = '\0';
j = ( i - 1 ) >> 1;
while ( j >= 0 ) {
i = to[j]; to[j] = s[-j]; s[-j] = (char)i; j--;
}
return(s+1);
}
/*like strcat() but returns the pointer to the end of the
resulting string:*/
static FORM_INLINE char *addStr(char *to, char *from)
{
while( (*to++ = *from++)!='\0' );
return(to-1);
}/*addStr*/
/*Try to write (atomically) short buffer (of length count) to fd.
timeout is a timeout in millisecs. Returns number of writen bytes or -1:*/
static FORM_INLINE ssize_t writeSome(int fd, char *buf, size_t count, int timeout)
{
ssize_t res = 0;
fd_set wfds;
struct timeval tv;
int nrep=5;/*five attempts it interrupted by a non-blocking signal*/
int flags = fcntl(fd, F_GETFL,0);
/*Add O_NONBLOCK:*/
fcntl(fd,F_SETFL, flags | O_NONBLOCK);
/* important -- in order to avoid blocking of short rceiver buffer*/
do{
FD_ZERO(&wfds);
FD_SET(fd, &wfds);
/* Wait up to timeout. */
tv.tv_sec =timeout /1000;
tv.tv_usec = (timeout % 1000)*1000;
nrep--;
switch(select(fd+1, NULL, &wfds, NULL, &tv)){
case -1:
if((nrep == 0)||( errno != EINTR) ){
perror("select()");
res=-1;
nrep=0;
}/*else -- A non blocked signal was caught, just repeat*/
break;
case 0:/*timeout*/
res=-1;
nrep=0;
break;
default:
if( (res=write(fd,buf,count)) <0 )/*Signal?*/
while( (errno == EINTR)&&(res <0) )
res=write(fd,buf,count);
nrep=0;
}/*switch*/
}while(nrep);
/*restore the flags:*/
fcntl(fd,F_SETFL, flags);
return (res);
}/*writeSome*/
/*Try to read short buffer (of length not more than count)
from fd. timeout is a timeout in millisecs. Returns number
of writen bytes or -1: */
static FORM_INLINE ssize_t readSome(int fd, char *buf, size_t count, int timeout)
{
ssize_t res = 0;
fd_set rfds;
struct timeval tv;
int nrep=5;/*five attempts it interrupted by a non-blocking signal*/
do{
FD_ZERO(&rfds);
FD_SET(fd, &rfds);
/* Wait up to timeout. */
tv.tv_sec = timeout/1000;
tv.tv_usec = (timeout % 1000)*1000;
nrep--;
switch(select(fd+1, &rfds, NULL, NULL, &tv)){
case -1:
if((nrep == 0)||( errno != EINTR) ){
perror("select()");
res=-1;
nrep=0;
}/*else -- A non blocked signal was caught, just repeat*/
break;
case 0:/*timeout*/
res=-1;
nrep=0;
break;
default:
if( (res=read(fd,buf,count)) <0 )/*Signal?*/
while( (errno == EINTR)&&(res <0) )
res=read(fd,buf,count);
nrep=0;
}/*switch*/
}while(nrep);
return (res);
}/*readSome*/
/*
#] Local functions :
#[ Ok functions:
*/
/*Copies (deep copy) newTerminator to thehandler->terminator. Returns 0 if
newTerminator fits to the buffer, or !0 if it does not fit. ATT! In the
latter case thehandler->terminator is NOT '\0' terminated! */
int setTerminatorForExternalChannelOk(char *newTerminator)
{
int i=DELTA_EXT_BUF;
/*
No problems with externalChannelsListTop are
possible since this function may be invoked only
when the current channel is defined and externalChannelsListTop
is set properly
*/
char *t=externalChannelsListTop->terminator;
for(; i>1; i--)
if( (*t++ = *newTerminator++)=='\0' )
break;
/*Add trailing '\n', if absent:*/
if( (i == DELTA_EXT_BUF)/*newTerminator == '\0'*/
||(*(t-2)!='\n') ){
*(t-1)='\n';*t='\0';
}
return(i==1);
}/*setTerminatorForExternalChannelOk*/
/*Interface to change handler fields "killSignal" and "gpid"*/
int setKillModeForExternalChannelOk(int signum, int sentToWholeGroup)
{
if(signum<0)
return(-1);
/*
No problems with externalChannelsListTop are
possible since this function may be invoked only
when the current channel is defined and externalChannelsListTop
is set properly
*/
externalChannelsListTop->killSignal=signum;
if(sentToWholeGroup){/*gpid must be >0*/
if(externalChannelsListTop->gpid <= 0)
externalChannelsListTop->gpid=-externalChannelsListTop->gpid;
}else{/*gpid must be <=0*/
if(externalChannelsListTop->gpid>0)
externalChannelsListTop->gpid=-externalChannelsListTop->gpid;
}
return(0);
}/*setKillModeForExternalChannelOk*/
/*
#[ getcFromExtChannelOk
*/
/*Returns one character from the external channel. It the input is expired,
returns EOF. If the external process is finished completely, the function closes
the channel (and returns EOF). If the external process was finished, the function
returns EOF:*/
int getcFromExtChannelOk()
{
mysighandler_t oldPIPE = 0;
EXTHANDLE *h;
int ret;
if (externalChannelsListTop->IBfill < externalChannelsListTop->IBfull)
/*in buffer*/
return( *(externalChannelsListTop->IBfill++) );
/*else -- the buffer is empty*/
ret=EOF;
h= externalChannelsListTop;
#ifdef WITHMPI
if ( PF.me == MASTER ){
#endif
/* Temporary ignore this signal:*/
/* if compiler fails here, try to change the definition of
mysighandler_t on the beginning of this file
(just define INTSIGHANDLER).*/
oldPIPE=signal(SIGPIPE,SIG_IGN);
#ifdef WITHMPI
if( fgets(h->INbuf,h->IBstop - h->INbuf, h->frec) == 0 )/*Fail! EOF?*/
*(h->INbuf)='\0';/*Empty line may not appear here!*/
#else
if( (fgets(h->INbuf,h->IBstop - h->INbuf, h->frec) == 0)/*Fail! EOF?*/
||( *(h->INbuf) == '\0')/*Empty line? This shouldn't be!*/
){
closeExternalChannel(externalChannelsListTop-externalChannelsList+1);
/*Note, this code is only for the sequential mode! */
goto getcFromExtChannelReady;
/*Here we assume that fgets is never interrupted by singals*/
}/*if( fgets(h->INbuf,h->IBstop - h->INbuf, h->frec) == 0 )*/
#endif
#ifdef WITHMPI
}/*if ( PF.me == MASTER */
/*Master broadcasts result to slaves, slaves read it from the master:*/
if( PF_BroadcastString((UBYTE *)h->INbuf) ){/*Fail!*/
MesPrint("Fail broadcasting external channel results");
Terminate(-1);
}/*if( PF_BroadcastString((UBYTE *)h->INbuf) )*/
if( *(h->INbuf) == '\0'){/*Empty line? This shouldn't be!*/
closeExternalChannel(externalChannelsListTop-externalChannelsList+1);
goto getcFromExtChannelReady;
}/*if( *(h->INbuf) == '\0')*/
#endif
{/*Block*/
char *t=h->terminator;
/*Move IBfull to the end of read line and compare the line with the terminator.
Note, by construction the terminator fits to the first read line, see
the function setTerminatorForExternalChannel.*/
for(h->IBfull=h->INbuf; *(h->IBfull)!='\0'; (h->IBfull)++)
if( *t== *(h->IBfull) )
t++;
else
break;/*not a terminator*/
/*Continue moving IBfullto the end of read line:*/
while(*(h->IBfull)!='\0')(h->IBfull)++;
if( (t-h->terminator) == (h->IBfull-h->INbuf) ){
/*Terminator!*/
/*Reset the channel*/
h->IBfull=h->IBfill=h->INbuf;
externalChannelsListTop=0;/*Undefine the current channel*/
writeBufToExtChannel=&writeBufToExtChannelFailure;
getcFromExtChannel=&getcFromExtChannelFailure;
setTerminatorForExternalChannel=&setTerminatorForExternalChannelFailure;
setKillModeForExternalChannel=&setKillModeForExternalChannelFailure;
goto getcFromExtChannelReady;
}/*if(t == (h->IBfull-h->INbuf) )*/
}/*Block*/
/*Does the buffer have enough capacity?*/
while( *(h->IBfull - 1) != '\n' ){/*Buffer is not enough!*/
/*Extend the buffer:*/
int l= (h->IBstop - h->INbuf)+DELTA_EXT_BUF;
char *newbuf=Malloc1(l,"External channel buffer");
/*We wouldn't like to use realloc.*/
/*Copy the buffer:*/
char *n=newbuf,*o=h->INbuf;
while( (*n++ = *o++)!='\0' );
/*Att! The order of the following operators is important!:*/
h->IBfull= newbuf+(h->IBfull-h->INbuf);
M_free(h->INbuf,"External channel buffer");
h->INbuf = newbuf;
h->IBstop = h->INbuf+l;
#ifdef WITHMPI
if ( PF.me == MASTER ){
(h->IBfull)[1]='\0';/*Will mark (h->IBfull)[1] as '!' for failure*/
if( fgets(h->IBfull,h->IBstop - h->IBfull, h->frec) == 0 ){
/*EOF! No trailing '\n'?*/
/*Mark:*/
(h->IBfull)[0]='\0';
(h->IBfull)[1]='!';
(h->IBfull)[2]='\0';
/*The string "\0!\0" is used as an image of NULL.*/
}/*if( fgets(h->IBfull,h->IBstop - h->IBfull, h->frec) == 0 )*/
}/*if ( PF.me == MASTER )*/
/*Master broadcasts results to slaves, slaves read it from the master:*/
if( PF_BroadcastString((UBYTE *)h->IBfull) ){/*Fail!*/
MesPrint("Fail broadcasting external channel results");
Terminate(-1);
}/*if( PF_BroadcastString(h->IBfull) )*/
/*The string "\0!\0" is used as the image of NULL.*/
if(
( (h->IBfull)[0]=='\0' )
&&( (h->IBfull)[1]=='!' )
&&( (h->IBfull)[2]=='\0' )
)/*EOF! No trailing '\n'?*/
break;
#else
if( fgets(h->IBfull,h->IBstop - h->IBfull, h->frec) == 0 )
/*EOF! No trailing '\n'?*/
break;
#endif
while( *(h->IBfull)!='\0' )(h->IBfull)++;
}/*while( *(h->IBfull - 1) != '\n' )*/
/*In h->INbuf we have a fresh string.*/
ret=*(h->IBfill=h->INbuf);
h->IBfill++;/*Next time a new, isn't it?*/
getcFromExtChannelReady:
#ifdef WITHMPI
if ( PF.me == MASTER ){
#endif
signal(SIGPIPE,oldPIPE);
#ifdef WITHMPI
}/*if ( PF.me == MASTER )*/
#endif
return(ret);
}/*getcFromExtChannelOk*/
/*
#] getcFromExtChannelOk
*/
/*Writes exactly count bytes from the buffer buf to the external channel thehandler
Returns 0 (on success) or -1:
*/
int writeBufToExtChannelOk(char *buf, size_t count)
{
int ret;
mysighandler_t oldPIPE;
#ifdef WITHMPI
/*Only master communicates with the external program:*/
if ( PF.me == MASTER ){
#endif
/* Temporary ignore this signal:*/
/* if compiler fails here, try to change the definition of
mysighandler_t on the beginning of this file
(just define INTSIGHANDLER)*/
oldPIPE=signal(SIGPIPE,SIG_IGN);
ret=writexactly( externalChannelsListTop->fsend, buf, count);
signal(SIGPIPE,oldPIPE);
#ifdef WITHMPI
}else{
/*Do not wait the master status: this would be too slow!*/
ret=0;
}
#endif
return(ret);
}/*writeBufToExtChannel*/
/*
#] Ok functions:
#[ do_run_cmd :
*/
/*The function returns PID of the started command*/
static FORM_INLINE pid_t do_run_cmd(
int *fdsend,
int *fdreceive,
int *gpid, /*returns group process ID*/
int ttymode,
/*
&8 - daemonizeing
&16 - setsid()*/
char *cmd,
char *argv[],
char *stderrname
)
{
int fdin[2]={-1,-1}, fdout[2]={-1,-1}, fdsig[2]={-1,-1};
/*initialised by -1 for possible rollback at failure, see closepipe() above*/
pid_t childpid,fatherchildpid = (pid_t)0;
mysighandler_t oldPIPE=NULL;
if(
(pipe(fdsig)!=0)/*This pipe will be used by a child to tell the father if fail.*/
||(pipe(fdin)!=0)
||(pipe(fdout)!=0)
)goto fail_do_run_cmd;
if((childpid = fork()) == -1){
perror("fork");
goto fail_do_run_cmd;
}/*if((childpid = fork()) == -1)*/
if(childpid == 0){/*Child.*/
int fifo[3], top=0;
/*
To be thread safely we can't rely on ascendant order of opened
file descriptors. So we put each of descriptor we have to
preserve into the array fifo.
Note, in _this_ process there are no any threads but descriptors
were created in frame of the parent process which may have
multiple threads.
*/
/*Mark descriptors which will NOT be closed:*/
pushDescriptor(fifo,top++,fdsig[1]);
pushDescriptor(fifo,top++,fdin[0]);
pushDescriptor(fifo,top++,fdout[1]);
/*Close all except stdin, stdout, stderr and placed into fifo:*/
closeAllDescriptors(3,fifo, top);
/*Now reopen stdin and stdout.*/
/*thread-safety is not a problem here since there are no any threads up to now:*/
if(
(close(0) == -1 )||/* Use fdin as stdin :*/
(dup(fdin[0]) == -1 )||
(close(1)==-1)||/* Use fdout as stdout:*/
(dup(fdout[1]) == -1 )
)
{/*Fail!*/
/*Signal to parent:*/
writepid(fdsig[1],(pid_t)-2);
_exit(1);
}
if(stderrname != NULL){
if(
(close(2) != 0 )||
(open(stderrname,O_WRONLY)<0)
)
{/*Fail!*/
writepid(fdsig[1],(pid_t)-2);
_exit(1);
}
}/*if(stderrname != NULL)*/
if( ttymode & 16 )/* create a session and sets the process group ID */
setsid();
/* */
if(set_cloexec_flag (fdsig[1], 1)!=0){/*Error?*/
/*Signal to parent:*/
writepid(fdsig[1],(pid_t)-2);
_exit(1);
}/*if(set_cloexec_flag (fdsig[1], 1)!=0)*/
if( ttymode & 8 ){/*Daemonize*/
int fdsig2[2];/*To check exec() success*/
if(
pipe(fdsig2)||
(set_cloexec_flag (fdsig2[1], 1)!=0)
)
{/*Error?*/
/*Signal to parent:*/
writepid(fdsig[1],(pid_t)-2);
_exit(1);
}
set_cloexec_flag (fdsig2[0], 1);
switch(childpid=fork()){
case 0:/*grandchild*/
/*Execute external command:*/
execvp(cmd, argv);
/* Control can reach this point only on error!*/
writepid(fdsig2[1],(pid_t)-2);
break;
case -1:
/* Control can reach this point only on error!*/
/*Inform the father about the failure*/
writepid(fdsig[1],(pid_t)-2);
_exit(1);/*The child, just exit, not return*/
default:/*Son of his father*/
close(fdsig2[1]);
/*Ignore SIGPIPE (up to the end of the process):*/
signal(SIGPIPE,SIG_IGN);
/*Wait on read() while the granchild close the pipe
(on success) or send -2 (if exec() fails).*/
/*There are two possibilities:
-1 -- this is ok, the pipe was closed on exec,
the program was successfully executed;
-2 -- something is wrong, exec failed since the
grandchild sends -2 after exec.
*/
if( readpid(fdsig2[0]) != (pid_t)-1 )/*something is wrong*/
writepid(fdsig[1],(pid_t)-1);
else/*ok, send PID of the granchild to the father:*/
writepid(fdsig[1],childpid);
/*Die and free the life space for the grandchild:*/
_exit(0);/*The child, just exit, not return*/
}/*switch(childpid=fork())*/
}else{/*if( ttymode & 8 )*/
execvp(cmd, argv);
/* Control can reach this point only on error!*/
writepid(fdsig[1],(pid_t)-2);
_exit(2);/*The child, just exit, not return*/
}/*if( ttymode & 8 )...else*/
}else{/* The (grand)father*/
close(fdsig[1]);
/*To prevent closing fdsig in rollback:*/
fdsig[1]=-1;
close(fdin[0]);
close(fdout[1]);
*fdsend = fdin[1];
*fdreceive = fdout[0];
/*Get the process group ID.*/
/*Avoid to use getpgid() which is non-standard.*/
if( ttymode & 16)/*setsid() was invoked, the child is a group leader:*/
*gpid=childpid;
else/*the child belongs to the same process group as the this process:*/
*gpid=getpgrp();/*if compiler fails here, try getpgrp(0) instead!*/
/*
Rationale: getpgrp conform to POSIX.1 while 4.3BSD provides a
getpgrp() function that returns the process group ID for a
specified process.
*/
/* Temporary ignore this signal:*/
/* if compiler fails here, try to change the definition of
mysighandler_t on the beginning of this file
(just define INTSIGHANDLER)*/
oldPIPE=signal(SIGPIPE,SIG_IGN);
if( ttymode & 8 ){/*Daemonize*/
/*Read the grandchild PID from the son.*/
fatherchildpid=childpid;
if( (childpid=readpid(fdsig[0]))<0 ){
/*Daemonization process fails for some reasons!*/
childpid=fatherchildpid;/*for rollback*/
goto fail_do_run_cmd;
}
}else{
/*fdsig[1] should be closed on exec and this read operation
must fail on success:*/
if( readpid(fdsig[0])!= (pid_t)-1 )
goto fail_do_run_cmd;
}/*if( ttymode & 8 ) ... else*/
}/*if(childpid == 0)...else*/
/*Here can be ONLY the father*/
close(fdsig[0]);
/*To prevent closing fdsig in rollback after goto fail_flnk_do_runcmd:*/
fdsig[0]=-1;
if( ttymode & 8 ){/*Daemonize*/
int i;
/*Wait while the father of a grandchild dies:*/
waitpid(fatherchildpid,&i,0);
}
/*Restore the signal:*/
signal(SIGPIPE,oldPIPE);
return(childpid);
fail_do_run_cmd:
closepipe(&fdout);
closepipe(&fdin);
closepipe(&fdsig);
return((pid_t)-1);
}/*do_run_cmd*/
/*
#] do_run_cmd :
#[ run_cmd :
*/
/*Starts the command cmd (directly, if shellpath is NULL, or in a subshell),
swallowing its stdin and stdout;
stderr will be re-directed to stderrname (if !=NULL). Returns PID of
the started process. Stdin will be available as fdsend, and stdout
will be available as fdreceive:*/
static FORM_INLINE pid_t run_cmd(char *cmd,
int *fdsend,
int *fdreceive,
int *gpid,
int daemonize,
char *shellpath,
char *stderrname )
{
char **argv;
pid_t thepid;
cmd=(char*)strDup1((UBYTE*)cmd, "run_cmd: cmd");/*detouch cmd*/
/* Prepare arguments for execvp:*/
if(shellpath != NULL){/*Run in a subshell.*/
int nopt;
/*Allocate space which is definitely enough:*/
argv=Malloc1(StrLen((UBYTE*)shellpath)*sizeof(char*)+2,"run_cmd:argv");
shellpath=(char*)strDup1((UBYTE*)shellpath, "run_cmd: shellpath");/*detouch shellpath*/
/*Parse a shell (e.g., "/bin/sh -c"):*/
nopt=parseline(argv, shellpath);
/* and add the command as a shell argument:*/
argv[nopt]=cmd;
argv[nopt+1]=NULL;
}else{/*Run the command directly:*/
/*Allocate space which is definitely enough:*/
argv=Malloc1(StrLen((UBYTE*)cmd)*sizeof(char*)+1,"run_cmd:argv");
parseline(argv, cmd);
}
thepid=do_run_cmd(
fdsend,
fdreceive,
gpid,
(daemonize)?(8|16):0,
argv[0],
argv,
stderrname
);
M_free(argv,"run_cmd:argv");
if(shellpath)
M_free(shellpath,"run_cmd:argv");
M_free(cmd, "run_cmd: cmd");
return(thepid);
}/*run_cmd*/
/*
#] run_cmd :
#[ createExternalChannel :
*/
/*The structure to pass parameters to createExternalChannel
and openExternalChannel in case of preset channel (instead of
shellname):*/
typedef struct{
int fdin;
int fdout;
pid_t theppid;
}ECINFOSTRUCT;
/* Creates a new external channel starting the command cmd (if cmd !=NULL)
or using informaion from (ECINFOSTRUCT *)shellname, if cmd ==NULL:*/
static FORM_INLINE void *createExternalChannel(
EXTHANDLE *h,
char *cmd, /*Command to run or NULL*/
/*0 --neither setsid nor daemonize, !=0 -- full daemonization:*/
int daemonize,
char *shellname,/* The shell (like "/bin/sh -c") or NULL*/
char *stderrname/*filename to redirect stderr or NULL*/
)
{
int fdreceive=0;
int gpid = 0;
ECINFOSTRUCT *psetInfo;
#ifdef WITHMPI
char statusbuf[2]={'\0','\0'};/*'\0' if run_cmd retuns ok, '!' othervise.*/
#endif
extHandlerInit(h);
h->pid=0;
if( cmd==NULL ){/*Instead of strting a new command, use preset channel:*/
psetInfo=(ECINFOSTRUCT *)shellname;
shellname=NULL;
h->killSignal=0;
h->daemonize=0;
}
/*Create a channel:*/
#ifdef WITHMPI
if ( PF.me == MASTER ){
#endif
if(cmd!=NULL)
h->pid=run_cmd (cmd, &(h->fsend),
&fdreceive,&gpid,daemonize,shellname,stderrname);
else{
gpid=-psetInfo->theppid;
h->pid=psetInfo->theppid;
h->fsend=psetInfo->fdout;
fdreceive=psetInfo->fdin;
}
#ifdef WITHMPI
if(h->pid<0)
statusbuf[0]='!';/*Brodcast fail to slaves*/
}
/*else: Keep h->pid = 0 and h->fsend = 0 for slaves in parallel mode!*/
/*Master broadcasts status to slaves, slaves read it from the master:*/
if( PF_BroadcastString((UBYTE *)statusbuf) ){/*Fail!*/
h->pid=-1;
}else if( statusbuf[0]=='!')/*Master fails*/
h->pid=-1;
#endif
if(h->pid<0)goto createExternalChannelFails;
#ifdef WITHMPI
if ( PF.me == MASTER ){
#endif
h->gpid=gpid;
/*Open stdout of a newly created program as FILE* :*/
if( (h->frec=fdopen(fdreceive,"r")) == 0 )goto createExternalChannelFails;
#ifdef WITHMPI
}
#endif
/*Initialize buffers:*/
extHandlerAlloc(h,cmd,shellname,stderrname);
return(h);
/*Something is wrong?*/
createExternalChannelFails:
destroyExternalChannel(h);
return(NULL);
}/*createExternalChannel*/
/*
#] createExternalChannel :
#[ openExternalChannel :
*/
int openExternalChannel(UBYTE *cmd, int daemonize, UBYTE *shellname, UBYTE *stderrname)
{
EXTHANDLE *h=externalChannelsListTop;
int i=0;
for(externalChannelsListTop=0;i