Singular Manual: Writing procedures and libraries

| ### A.1.2 Writing procedures and libraries The user may add their own commands to the commands already available in by writing procedures. There are basically two kinds of procedures: procedures written in the programming language (which are usually collected in 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 libraries, say `ring.lib` as a sample. On a Unix-like operating system, type `LIB "ring.lib";` to get information on where the libraries are stored on your disk. 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 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 `milnorNumber` must be called with one parameter, a polynomial. The name g is local to the procedure and is killed automatically when leaving the procedure. `milnorNumber` returns the Milnor number (and displays a comment). The procedure `tjurinaNumber` has no specified number of parameters. Here, the parameters are referred to by `#[1]` for the 1st, `#[2]` for the 2nd parameter, etc. `tjurinaNumber` returns the Tjurina number (and displays a comment). the procedure `milnor_tjurina` which 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 kernel works. See also Dynamic modules.

User manual for Singular version 4.4.1, 2025, generated by texi2html.