Software Developer Notes for Braid version 31.2


1. Introduction

This document is intended as an initial guide to the source code published with the braid programme, it contains information not included in the user documentation that is relevant only to those wishing to extend, customize or otherwise modify the code. It is not a detailed design spec, but contains various comments and definitions on the following topics:

  1. Debugging information
  2. Automating regression testing
  3. Local classess
  4. General source comments

2. Debugging information

Version 11.1 introduced a structured debug subsystem across classes and the main braid function. Prior to this, various booleans controlled debug output but only a subset of them were accessible from the command line. The debug subsystem allows full debugging control as well as default debug options in a flexible and extensible manner.

Debug information is requested using the # option, followed optionally by a debug specification enclosed in square brackets []. The debug specification is a list of options, separated by colons; an option may be followed by one or more option parameters, enclosed in braces {} and also separated by colons. No spaces are permitted in a debug specification. Thus the general format is of the form:

braid -#[option:option{parameter:parameter}:option]

If no debug specification is given then the default programme debug options are set.

The available debug options can be displayed by running the braid programme with the both the debug option and the hidden help option described above:

[bart@wormwood current]$ braid -#H!

    debug options may be specified using the syntax -#[option:option{parameter:parameter}:option]
    supported options and parameters:
            bigint{san:+:-:*:/:%:rs:ls:bc:==:gt:out:in:sum:diff:num_len:gcd:all}, bitmap: no default
            matrix{det:inv:*:all}, bitmap: no default
            poly{no-gen:san:+:*:/:in:gcd:all}, bitmap: general debug included unless no-gen specified
            rational, boolean
            quaternion, boolean
            braid{summary|1:basic|2:intermediate|3:detail|4}, integer: default 0=off, no parameters sets summary
            vogel, boolean
    default options set by omitting [...]
    default options:
            braid{summary}

Each option corresponds to a class (bigint, matrix, etc.) or a functional area of the programme (braid, vogel). As can be seen from the above output, there are currently three different types of debug control used by the debug options: bitmap, integer and boolean each of which is described below. The option parameters for each of the classes are set by the function set_debug_option_parameter in the file debug.cpp and for the braid programme specific options by set_main_debug_option_parameter in the file main.cpp.

A bitmap debug control variable requires parameters to specify what debug information is required, as an example the bigint_control structure is defined as follows:

struct bigint_control
{
    static unsigned int DEBUG; //bitmap
    enum parameters
    {
        general =     0x00000001, // unused
        sanitize =    0x00000002, 
        add =         0x00000004,
        subtract =    0x00000008,
        multiply =    0x00000010,
        divide =      0x00000020,
        remainder =   0x00000020,
        equal =       0x00000040,
        greater =     0x00000080,
        output =      0x00000100,
        input =       0x00000200,
        sum =         0x00000400,
        diff =        0x00000800,
        num_len =     0x00001000,
        r_shift =     0x00002000,
        l_shift =     0x00004000,
        bool_conv =   0x00008000,
        gcd =         0x00010000,
        // 'all' also supported
    };
};

An integer debug control variable defines different levels of debug information to be provided, as in the case of the braid programme's debug control structure:

struct braid_control 
{
    enum level {OFF, SUMMARY, BASIC, INTERMEDIATE, DETAIL};
    static int DEBUG;
    static bool VOGEL_DEBUG;
};

Finally, a boolean debug control variable, the simplest form of debug control, requires no parameters an example being VOGEL_DEBUG in the braid_control structure above. The debug control subsystem is mainly coded in the separate source file debug.cpp, though the braid programme specific debug information is contained in main.cpp.

Debug output is written to the file braid.dbg in the current directory, it is formatted as a list of records that indicate the source of the debug information. Here is a sample debug output when reading the string 2x-1 into a polynomial<quaternion<scalar> > using the biging scalar variant, the debug option used to generate this output was -#[poly{in}:bigint{in}:quaternion].

debug: setting debug option polynomial_control::general
debug: setting debug option polynomial_control::input
debug: setting debug option bigint_control::input
debug: setting debug option quaternion_control::DEBUG
polynomial<T,U>::>>: presented with string: (2x-1)
polynomial<T,U>::>>: polynomial begins with '(', checking for parentheses
polynomial<T,U>::>>: open parentheses detected
polynomial<T,U>::>>: close parentheses detected
polynomial<T,U>::>>: input buffer after removal of parentheses: 2x-1
polynomial<T,U>::>>: number of variables = 1
polynomial<T,U>::>>: variable characters: x
polynomial<T,U>::>>: variable degrees: 1
polynomial<T,U>::>>: length of virtual pterm list = 4
polynomial<T,U>::>>: reading term 1:
polynomial<T,U>::>>:   implicit sign = 1
quaternion::operator >> : initial character from istream: (
bigint::operator >> : initial character from istream: 2
bigint::operator >> : putting back digit 2 for main loop execution
bigint::operator >> : next character from istream: 2
bigint::operator >> :   a = 2
bigint::operator >> : final value of a = 2
quaternion::operator >> : no comma found after reading real part setting all other quaternionic parts to zero
polynomial<T,U>::>>:   absolute coefficient = 2
polynomial<T,U>::>>:   coefficient value = 2
polynomial<T,U>::>>:   read variable x
polynomial<T,U>::>>:   index = 0
polynomial<T,U>::>>:   set expoenent = 1
polynomial<T,U>::>>: term place = 2
polynomial<T,U>::>>: reading term 2:
polynomial<T,U>::>>:   explicit sign = -1
quaternion::operator >> : initial character from istream: 1
quaternion::operator >> : no opening '(' detected
quaternion::operator >> : digit found, putting 1 back into istream ready to read real part
bigint::operator >> : initial character from istream: 1
bigint::operator >> : putting back digit 1 for main loop execution
bigint::operator >> : next character from istream: 1
bigint::operator >> :   a = 1
bigint::operator >> : final value of a = 1
quaternion::operator >> : setting all other quaternionic parts to zero
polynomial<T,U>::>>:   absolute coefficient = 1
polynomial<T,U>::>>:   coefficient value = -1
polynomial<T,U>::>>: term place = 0
polynomial<T,U>::>>: final polynomial value: 2x-1
polynomial<T,U>::>>: final polynomial structure:

polynomial located at    0xbfba4588
number of variables      = 1
variable characters      = x
variable degrees         = 1
number of virtual pterms = 4
polynomial pterms:       2 present

1 0x8cd2670: x^0
pterm.n = -1
pterm.pl = 0    pterm.prev = NULL       pterm.next = 0x8cd1358
2 0x8cd1358: x^1
pterm.n = 2
pterm.pl = 2    pterm.prev = 0x8cd2670  pterm.next = NULL

back to top

2. Automating regression testing

The source distribution for the braid programme includes support for automated regression testing via the run-test utility. The programme run-test can be built from the Makefile supplied using make run-test. Note, however, that to build run-test, you need to comment out the hash-defines in the file initialize.h, which can be found in the include directory. This file contains the following definitions that are required for debugging the braid programme only.

    #define INITIALIZE_BIGINT
    #define INITIALIZE_MATRIX
    #define INITIALIZE_POLYNOMIAL
    #define INITIALIZE_QUATERNION
    #define INITIALIZE_RATIONAL
    #define INITIALIZE_MOD_P
    #define INITIALIZE_SCALAR
The usage for run-test is as follows:

    Usage: 'run-test <filename>' runs every test in <filename>
           'run-test <filename> <testname>' runs <testname> in <filename>
           'run-test --suite <suite-name>' runs the suite of test files listed in <suite-name>
           'run-test --suite --summary <suite-name>' runs a summary of the suite of test files listed in <suite-name>

The run-test programme makes use of the standard #define, #include and #end preprocessor directives plus the following test specific directives:

The script directive is used specify a test script that may include one or more tests. All of the tests in the test scripts are run.

	#define TEST /home/bart/source/braid/prog/test
	
	; full set of test scripts, intended for use with the --summary option
	; run-test --suite --summary ../test/full-suite
	
	#script $(TEST)/alexander.test
	#script $(TEST)/birack-polynomial-br.test
	#script $(TEST)/birack-polynomial-pc.test
	#script $(TEST)/bracket.test
	#script $(TEST)/cocycle-invariant.test
	#script $(TEST)/gauss-to-peer.test
	#script $(TEST)/JG-virtual.test
	#script $(TEST)/knotoid.test
	#script $(TEST)/vogel.test

A test script may include one or more #common option specifications and one or more tests, as in the following example:

	#define TEST /home/bart/source/braid/prog/test
	#define INPUT  $(TEST)/input
	#define RESULTS $(TEST)/results
	
	; common options
	#common [raw-output,silent]
	;
	; testcases
	;
	; Kauffman bracket
	#test Kauffman-bracket [kauffman-bracket]
	#result Kauffman-bracket $(RESULTS)/kauffman-bracket-results
	
	; Jones polynomial
	#test Jones-polynomial [jones-polynomial]
	#result Jones-polynomial $(RESULTS)/jones-polynomial-results
	
	; arrow polynomial
	#test arrow-polynomial [arrow-polynomial]
	#result arrow-polynomial $(RESULTS)/arrow-polynomial-results
	
	
	; parity bracket
	#test parity-bracket [parity-bracket]
	#result parity-bracket $(RESULTS)/parity-bracket-results
	
	
	; parity arrow
	#test parity-arrow [parity-arrow]
	#result parity-arrow $(RESULTS)/parity-arrow-results
	
	
	#include $(INPUT)/bracket-test
	#include $(INPUT)/knots-up-to-7-pc
	#include $(INPUT)/knots-8-pc
	#include $(INPUT)/knots-9-pc
	#include $(INPUT)/knots-10-pc

The summary option to run-test is used with the suite option and runs the suite of tests obtained by taking one line of input from every #included file in every test script listing in a test suite.

For each test to be run, run-test produces an inputfile, _tmpfile, that is submitted to the braid programme. The contents of the resulting braid.out file is then compared with the file identified by the corresponding #result directive to determine a pass or fail result for the test.

back to top

4. Local classes

As the code has evolved, local classes for things like quaternions, rationals, matrices big integers and big-rationals have been developed. These have been written because "standard" implementations were not available, to ensure I had control over the code, or just as an exercice. However, I have made no attempt to replace them with "standard" implementations as they have become available, to minimize risk in making code changes.

Version 31.0 included a major re-write of the polynomial class, replacing a bespoke doubly-linked-list implementation with one using the standard library list and map containers. This class includes the ability to support polynomials with graphical or string variables via a variable mapping mechanism but hopefully the source code is sufficiently self-explanatory.

In particular, the local scalar class has been developed to allow run-time selection of an object's type. This class is documented on the scalars page of my website.

6. General source comments

I lay out my code in a way that I find easy to read, quick to debug and aesthetically pleasing (to me). It is not quite standard but it is, I hope, clear. My style of writing evolved from working with ALGOL68 many moons ago, so as an example, I always write

    char*  foo;
    char** bar;

rather than

    char   *foo,**bar;

because ALGOL68 would write

    REF CHAR     foo;
    REF REF CHAR bar;

and I like the ability to look down the left side of the page and see what type of variable I have. Also, as in all the above code snippets, I do not open clauses with a brace at the end of a line, rather I always put delimiting braces on their own line. This is again motivated by ALGOL68 and I find it easier to look down code whose indentation delimiters are vertically aligned. Sorry if you find this weird.

You will find lots of comments in my code, which I hope will be useful if you use any of it for your own work. I would be very happy to provide assistance to anyone trying to understand what I've done, provided I have the time. Email me here if you are experiencing problems understanding what I've done.

You are very welcome to use any of my source code in your programmes, but bear in mind that although it is provided in good faith and to the best of my knowledge the code is accurate, if you decide to use this software then I accept no responsibility for the consequences of any errors it may contain. That said, it would be nice if you included a comment to say where you got it, and sent me an email to say you've used it, just so I get some feedback regarding how useful it actually is!

back to top