|  |  A.1.2 Writing procedures and libraries 
The user may add their own commands to the commands already available in
SINGULAR by writing SINGULAR procedures. There are basically
two kinds of procedures:
 
procedures written in the SINGULAR programming language (which are
usually collected in SINGULAR libraries).
procedures written in C/C++ (collected in dynamic modules).
 
At this point, we restrict ourselves to describing the first kind of
(library) procedures, which are sufficient for most applications. The
syntax and general structure of a library (procedure) is described in
 Procedures, and  Libraries.
 
The probably most efficient way of
writing a new library is to use one of the official SINGULAR
libraries, say ring.libas a sample. On a Unix-like operating
system, typeLIB "ring.lib";to get information on where the
libraries are stored on your disk. 
SINGULAR provides several commands and tools, which may be useful
when writing a procedure, for instance, to have a look at intermediate
results (see  Debugging tools).
 
If such a library should be contributed to SINGULAR some formal
requirements are needed:
 
the library header must explain the purpose of the library and
(for non-trivial algorithm) a pointer to the algorithm (text book, article, etc.)
all global procedures must have a help string and an example which shows its usage.
it is strongly recommend also to provide test scripts which test the functionality: one should test the essential functionality of the
  library/command in a relatively short time (say, in no more than 30s),
  other tests  should check the functionality of the
  library/command in detail so that, if possible, all relevant
  cases/results are tested. Nevertheless, such a test should not run
  longer than, say, 10 minutes.
 
We give short examples of procedures to demonstrate the following:
 
 
Write procedures which return an integer (ring independent), see also
 Milnor and Tjurina number. (Here we restrict ourselves to the main
body of the procedures).
  The procedure milnorNumbermust be called with one parameter, a
  polynomial.
  The name g is local to the procedure and is killed automatically when
  leaving the procedure.milnorNumberreturns the Milnor number (and displays a comment).
  The procedure tjurinaNumberhas no specified number of
  parameters. Here, the parameters are referred to by#[1]for
  the 1st,#[2]for the 2nd parameter, etc.tjurinaNumberreturns the Tjurina number (and displays a comment).
  the procedure milnor_tjurinawhich returns a list consisting of two
  integers, the Milnor and the Tjurina number. 
Write a procedure which creates a new ring and returns data dependent on
this new ring (two numbers) and an int. In this example, we also show
how to write a help text for the procedure (which is optional, but
recommended).
 
 |  | proc milnorNumber (poly g)
{
   "Milnor number:";
   return(vdim(std(jacob(g))));
}
proc tjurinaNumber
{
   "Tjurina number:";
   return(vdim(std(jacob(#[1])+#[1])));
}
proc milnor_tjurina (poly f)
{
   ideal j=jacob(f);
   list L=vdim(std(j)),vdim(std(j+f));
   return(L);
}
proc real_sols (number b, number c)
"USAGE: real_sols (b,c);  b,c number
ASSUME: active basering has characteristic 0
RETURN: list: first entry is an integer (the number of different real
        solutions). If this number is non-negative, the list has as second
        entry a ring in which the list SOL of real solutions of x^2+bx+c=0
        is stored (as floating point number, precision 30 digits).
NOTE:   This procedure calls laguerre_solve from solve.lib.
"
{
  def oldring = basering;  // assign name to the ring active when
                           // calling the procedure
  number disc = b^2-4*c;
  if (disc>0) { int n_of_sols = 2; }
  if (disc==0) { int n_of_sols = 1; }
  string s = nameof(var(1));  // name of first ring variable
  if (disc>=0) {
    execute("ring rinC =(complex,30),("+s+"),lp;");
    if (not(defined(laguerre_solve))) { LIB "solve.lib"; }
    poly f = x2+imap(oldring,b)*x+imap(oldring,c);
                        // f is a local ring-dependent variable
    list SOL = laguerre_solve(f,30);
    export SOL;         // make SOL a global ring-dependent variable
                        // such variables are still accessible when the
                        // ring is among the return values of the proc
    setring oldring;
    return(list(n_of_sols,rinC));
  }
  else {
    return(list(0));
  }
}
//
// We now apply the procedures which are defined by the
// lines of code above:
//
ring r = 0,(x,y),ds;
poly f = x7+y7+(x-y)^2*x2y2;
milnorNumber(f);
==> Milnor number:
==> 28
tjurinaNumber(f);
==> Tjurina number:
==> 24
milnor_tjurina(f);     // a list containing Milnor and Tjurina number
==> [1]:
==>    28
==> [2]:
==>    24
def L=real_sols(2,1);
L[1];                  // number of real solutions of x^2+2x+1
==> 1
def R1=L[2];
setring R1;
listvar(R1);           // only global ring-dependent objects are still alive
==> // R1                             [0]  *ring
==> // SOL                            [0]  list, size: 2
SOL;                   // the real solutions
==> [1]:
==> -1
==> [2]:
==> -1
setring r;
L=real_sols(1,1);
L[1];                  // number of reals solutions of x^2+x+1
==> 0
setring r;
L=real_sols(1,-5);
L[1];                  // number of reals solutions of x^2+x-5
==> 2
def R3=L[2];
setring R3; SOL;       // the real solutions
==> [1]:
==> -2.79128784747792000329402359686
==> [2]:
==> 1.79128784747792000329402359686
 | 
 
Writing a dynamic module is not as simple as writing a library
procedure, since it does not only require some knowledge of C/C++, but
also about the way the SINGULAR kernel works.
See also  Dynamic modules.
 
 |