Go to the first, previous, next, last section, table of contents.


Mathematical Packages

Bit-Twiddling

(require 'logical)

The bit-twiddling functions are made available through the use of the logical package. logical is loaded by inserting (require 'logical) before the code that uses these functions. These functions behave as though operating on integers in two's-complement representation.

Bitwise Operations

Function: logand n1 n1
Returns the integer which is the bit-wise AND of the two integer arguments.

Example:

(number->string (logand #b1100 #b1010) 2)
   => "1000"

Function: logior n1 n2
Returns the integer which is the bit-wise OR of the two integer arguments.

Example:

(number->string (logior #b1100 #b1010) 2)
   => "1110"

Function: logxor n1 n2
Returns the integer which is the bit-wise XOR of the two integer arguments.

Example:

(number->string (logxor #b1100 #b1010) 2)
   => "110"

Function: lognot n
Returns the integer which is the 2s-complement of the integer argument.

Example:

(number->string (lognot #b10000000) 2)
   => "-10000001"
(number->string (lognot #b0) 2)
   => "-1"

Function: bitwise-if mask n0 n1
Returns an integer composed of some bits from integer n0 and some from integer n1. A bit of the result is taken from n0 if the corresponding bit of integer mask is 1 and from n1 if that bit of mask is 0.

Function: logtest j k
(logtest j k) == (not (zero? (logand j k)))

(logtest #b0100 #b1011) => #f
(logtest #b0100 #b0111) => #t

Function: logcount n
Returns the number of bits in integer n. If integer is positive, the 1-bits in its binary representation are counted. If negative, the 0-bits in its two's-complement binary representation are counted. If 0, 0 is returned.

Example:

(logcount #b10101010)
   => 4
(logcount 0)
   => 0
(logcount -2)
   => 1

Bit Within Word

Function: logbit? index j
(logbit? index j) == (logtest (integer-expt 2 index) j)

(logbit? 0 #b1101) => #t
(logbit? 1 #b1101) => #f
(logbit? 2 #b1101) => #t
(logbit? 3 #b1101) => #t
(logbit? 4 #b1101) => #f

Function: copy-bit index from bit
Returns an integer the same as from except in the indexth bit, which is 1 if bit is #t and 0 if bit is #f.

Example:

(number->string (copy-bit 0 0 #t) 2)       => "1"
(number->string (copy-bit 2 0 #t) 2)       => "100"
(number->string (copy-bit 2 #b1111 #f) 2)  => "1011"

Fields of Bits

Function: bit-field n start end
Returns the integer composed of the start (inclusive) through end (exclusive) bits of n. The startth bit becomes the 0-th bit in the result.

This function was called bit-extract in previous versions of SLIB.

Example:

(number->string (bit-field #b1101101010 0 4) 2)
   => "1010"
(number->string (bit-field #b1101101010 4 9) 2)
   => "10110"

Function: copy-bit-field to start end from
Returns an integer the same as to except possibly in the start (inclusive) through end (exclusive) bits, which are the same as those of from. The 0-th bit of from becomes the startth bit of the result.

Example:

(number->string (copy-bit-field #b1101101010 0 4 0) 2)
        => "1101100000"
(number->string (copy-bit-field #b1101101010 0 4 -1) 2)
        => "1101101111"

Function: ash int count
Returns an integer equivalent to (inexact->exact (floor (* int (expt 2 count)))).

Example:

(number->string (ash #b1 3) 2)
   => "1000"
(number->string (ash #b1010 -1) 2)
   => "101"

Function: integer-length n
Returns the number of bits neccessary to represent n.

Example:

(integer-length #b10101010)
   => 8
(integer-length 0)
   => 0
(integer-length #b1111)
   => 4

Function: integer-expt n k
Returns n raised to the non-negative integer exponent k.

Example:

(integer-expt 2 5)
   => 32
(integer-expt -3 3)
   => -27

Modular Arithmetic

(require 'modular)

Function: extended-euclid n1 n2
Returns a list of 3 integers (d x y) such that d = gcd(n1, n2) = n1 * x + n2 * y.

Function: symmetric:modulus n
Returns (quotient (+ -1 n) -2) for positive odd integer n.

Function: modulus->integer modulus
Returns the non-negative integer characteristic of the ring formed when modulus is used with modular: procedures.

Function: modular:normalize modulus n
Returns the integer (modulo n (modulus->integer modulus)) in the representation specified by modulus.

The rest of these functions assume normalized arguments; That is, the arguments are constrained by the following table:

For all of these functions, if the first argument (modulus) is:

positive?
Work as before. The result is between 0 and modulus.
zero?
The arguments are treated as integers. An integer is returned.
negative?
The arguments and result are treated as members of the integers modulo (+ 1 (* -2 modulus)), but with symmetric representation; i.e. (<= (- modulus) n modulus).

If all the arguments are fixnums the computation will use only fixnums.

Function: modular:invertable? modulus k
Returns #t if there exists an integer n such that k * n == 1 mod modulus, and #f otherwise.

Function: modular:invert modulus k2
Returns an integer n such that 1 = (n * k2) mod modulus. If k2 has no inverse mod modulus an error is signaled.

Function: modular:negate modulus k2
Returns (-k2) mod modulus.

Function: modular:+ modulus k2 k3
Returns (k2 + k3) mod modulus.

Function: modular:- modulus k2 k3
Returns (k2 - k3) mod modulus.

Function: modular:* modulus k2 k3
Returns (k2 * k3) mod modulus.

The Scheme code for modular:* with negative modulus is not completed for fixnum-only implementations.

Function: modular:expt modulus k2 k3
Returns (k2 ^ k3) mod modulus.

Prime Numbers

(require 'factor)

Variable: prime:prngs

prime:prngs is the random-state (see section Random Numbers) used by these procedures. If you call these procedures from more than one thread (or from interrupt), random may complain about reentrant calls.

Function: jacobi-symbol p q

Returns the value (+1, -1, or 0) of the Jacobi-Symbol of exact non-negative integer p and exact positive odd integer q.

Variable: prime:trials

prime:trials the maxinum number of iterations of Solovay-Strassen that will be done to test a number for primality.

Function: prime? n

Returns #f if n is composite; #t if n is prime. There is a slight chance (expt 2 (- prime:trials)) that a composite will return #t.

Function: primes< start count

Returns a list of the first count prime numbers less than start. If there are fewer than count prime numbers less than start, then the returned list will have fewer than start elements.

Function: primes> start count

Returns a list of the first count prime numbers greater than start.

Function: factor k

Returns a list of the prime factors of k. The order of the factors is unspecified. In order to obtain a sorted list do (sort! (factor k) <).

Random Numbers

(require 'random)

A pseudo-random number generator is only as good as the tests it passes. George Marsaglia of Florida State University developed a battery of tests named DIEHARD (http://stat.fsu.edu/~geo/diehard.html). `diehard.c' has a bug which the patch ftp://swissnet.ai.mit.edu/pub/users/jaffer/diehard.c.pat corrects.

SLIB's new PRNG generates 8 bits at a time. With the degenerate seed `0', the numbers generated pass DIEHARD; but when bits are combined from sequential bytes, tests fail. With the seed `http://swissnet.ai.mit.edu/~jaffer/SLIB.html', all of those tests pass.

It would be better if there were no bad seeds. For now, use seeds of at least 30 bytes.

Procedure: random n
Procedure: random n state
Accepts a positive integer or real n and returns a number of the same type between zero (inclusive) and n (exclusive). The values returned have a uniform distribution.

The optional argument state must be of the type produced by (make-random-state). It defaults to the value of the variable *random-state*. This object is used to maintain the state of the pseudo-random-number generator and is altered as a side effect of the random operation.

Variable: *random-state*
Holds a data structure that encodes the internal state of the random-number generator that random uses by default. The nature of this data structure is implementation-dependent. It may be printed out and successfully read back in, but may or may not function correctly as a random-number state object in another implementation.

Procedure: make-random-state
Procedure: make-random-state state
Returns a new object of type suitable for use as the value of the variable *random-state* and as a second argument to random. If argument state is given, a copy of it is returned. Otherwise a copy of *random-state* is returned.

If inexact numbers are supported by the Scheme implementation, `randinex.scm' will be loaded as well. `randinex.scm' contains procedures for generating inexact distributions.

Procedure: random:uniform state
Returns an uniformly distributed inexact real random number in the range between 0 and 1.

Procedure: random:solid-sphere! vect
Procedure: random:solid-sphere! vect state
Fills vect with inexact real random numbers the sum of whose squares is less than 1.0. Thinking of vect as coordinates in space of dimension n = (vector-length vect), the coordinates are uniformly distributed within the unit n-shere. The sum of the squares of the numbers is returned.

Procedure: random:hollow-sphere! vect
Procedure: random:hollow-sphere! vect state
Fills vect with inexact real random numbers the sum of whose squares is equal to 1.0. Thinking of vect as coordinates in space of dimension n = (vector-length vect), the coordinates are uniformly distributed over the surface of the unit n-shere.

Procedure: random:normal
Procedure: random:normal state
Returns an inexact real in a normal distribution with mean 0 and standard deviation 1. For a normal distribution with mean m and standard deviation d use (+ m (* d (random:normal))).

Procedure: random:normal-vector! vect
Procedure: random:normal-vector! vect state
Fills vect with inexact real random numbers which are independent and standard normally distributed (i.e., with mean 0 and variance 1).

Procedure: random:exp
Procedure: random:exp state
Returns an inexact real in an exponential distribution with mean 1. For an exponential distribution with mean u use (* u (random:exp)).

Cyclic Checksum

(require 'make-crc)

Function: make-port-crc
Function: make-port-crc degree
Function: make-port-crc degree generator
Returns an expression for a procedure of one argument, a port. This procedure reads characters from the port until the end of file and returns the integer checksum of the bytes read.

The integer degree, if given, specifies the degree of the polynomial being computed -- which is also the number of bits computed in the checksums. The default value is 32.

The integer generator specifies the polynomial being computed. The power of 2 generating each 1 bit is the exponent of a term of the polynomial. The bit at position degree is implicit and should not be part of generator. This allows systems with numbers limited to 32 bits to calculate 32 bit checksums. The default value of generator when degree is 32 (its default) is:

(make-port-crc 32 #b00000100110000010001110110110111)

Creates a procedure to calculate the P1003.2/D11.2 (POSIX.2) 32-bit checksum from the polynomial:

     32    26    23    22    16    12    11
  ( x   + x   + x   + x   + x   + x   + x   +

      10    8    7    5    4    2    1
     x   + x  + x  + x  + x  + x  + x  + 1 )  mod 2
(require 'make-crc)
(define crc32 (slib:eval (make-port-crc)))
(define (file-check-sum file) (call-with-input-file file crc32))
(file-check-sum (in-vicinity (library-vicinity) "ratize.scm"))

=> 3553047446

Plotting on Character Devices

(require 'charplot)

The plotting procedure is made available through the use of the charplot package. charplot is loaded by inserting (require 'charplot) before the code that uses this procedure.

Variable: charplot:height
The number of rows to make the plot vertically.

Variable: charplot:width
The number of columns to make the plot horizontally.

Procedure: plot! coords x-label y-label
coords is a list of pairs of x and y coordinates. x-label and y-label are strings with which to label the x and y axes.

Example:

(require 'charplot)
(set! charplot:height 19)
(set! charplot:width 45)

(define (make-points n)
  (if (zero? n)
      '()
      (cons (cons (/ n 6) (sin (/ n 6))) (make-points (1- n)))))

(plot! (make-points 37) "x" "Sin(x)")
-|
  Sin(x)   ______________________________________________
      1.25|-                                             |
          |                                              |
         1|-       ****                                  |
          |      **    **                                |
  750.0e-3|-    *        *                               |
          |    *          *                              |
  500.0e-3|-  *            *                             |
          |  *                                           |
  250.0e-3|-                *                            |
          | *                *                           |
         0|-------------------*--------------------------|
          |                                     *        |
 -250.0e-3|-                   *               *         |
          |                     *             *          |
 -500.0e-3|-                     *                       |
          |                       *          *           |
 -750.0e-3|-                       *        *            |
          |                         **    **             |
        -1|-                          ****               |
          |____________:_____._____:_____._____:_________|
        x              2           4      

Root Finding

(require 'root)

Function: newtown:find-integer-root f df/dx x0
Given integer valued procedure f, its derivative (with respect to its argument) df/dx, and initial integer value x0 for which df/dx(x0) is non-zero, returns an integer x for which f(x) is closer to zero than either of the integers adjacent to x; or returns #f if such an integer can't be found.

To find the closest integer to a given integers square root:

(define (integer-sqrt y)
  (newton:find-integer-root
   (lambda (x) (- (* x x) y))
   (lambda (x) (* 2 x))
   (ash 1 (quotient (integer-length y) 2))))

(integer-sqrt 15) => 4

Function: integer-sqrt y
Given a non-negative integer y, returns the rounded square-root of y.

Function: newton:find-root f df/dx x0 prec
Given real valued procedures f, df/dx of one (real) argument, initial real value x0 for which df/dx(x0) is non-zero, and positive real number prec, returns a real x for which abs(f(x)) is less than prec; or returns #f if such a real can't be found.

If prec is instead a negative integer, newton:find-root returns the result of -prec iterations.

H. J. Orchard, The Laguerre Method for Finding the Zeros of Polynomials, IEEE Transactions on Circuits and Systems, Vol. 36, No. 11, November 1989, pp 1377-1381.

There are 2 errors in Orchard's Table II. Line k=2 for starting value of 1000+j0 should have Z_k of 1.0475 + j4.1036 and line k=2 for starting value of 0+j1000 should have Z_k of 1.0988 + j4.0833.

Function: laguerre:find-root f df/dz ddf/dz^2 z0 prec
Given complex valued procedure f of one (complex) argument, its derivative (with respect to its argument) df/dx, its second derivative ddf/dz^2, initial complex value z0, and positive real number prec, returns a complex number z for which magnitude(f(z)) is less than prec; or returns #f if such a number can't be found.

If prec is instead a negative integer, laguerre:find-root returns the result of -prec iterations.

Function: laguerre:find-polynomial-root deg f df/dz ddf/dz^2 z0 prec
Given polynomial procedure f of integer degree deg of one argument, its derivative (with respect to its argument) df/dx, its second derivative ddf/dz^2, initial complex value z0, and positive real number prec, returns a complex number z for which magnitude(f(z)) is less than prec; or returns #f if such a number can't be found.

If prec is instead a negative integer, laguerre:find-polynomial-root returns the result of -prec iterations.

Commutative Rings

Scheme provides a consistent and capable set of numeric functions. Inexacts implement a field; integers a commutative ring (and Euclidean domain). This package allows one to use basic Scheme numeric functions with symbols and non-numeric elements of commutative rings.

(require 'commutative-ring)

The commutative-ring package makes the procedures +, -, *, /, and ^ careful in the sense that any non-numeric arguments they do not reduce appear in the expression output. In order to see what working with this package is like, self-set all the single letter identifiers (to their corresponding symbols).

(define a 'a)
...
(define z 'z)

Or just (require 'self-set). Now try some sample expressions:

(+ (+ a b) (- a b)) => (* a 2) 
(* (+ a b) (+ a b)) => (^ (+ a b) 2) 
(* (+ a b) (- a b)) => (* (+ a b) (- a b)) 
(* (- a b) (- a b)) => (^ (- a b) 2) 
(* (- a b) (+ a b)) => (* (+ a b) (- a b)) 
(/ (+ a b) (+ c d)) => (/ (+ a b) (+ c d)) 
(^ (+ a b) 3) => (^ (+ a b) 3) 
(^ (+ a 2) 3) => (^ (+ 2 a) 3) 

Associative rules have been applied and repeated addition and multiplication converted to multiplication and exponentiation.

We can enable distributive rules, thus expanding to sum of products form:

(set! *ruleset* (combined-rulesets distribute* distribute/))

(* (+ a b) (+ a b)) => (+ (* 2 a b) (^ a 2) (^ b 2)) 
(* (+ a b) (- a b)) => (- (^ a 2) (^ b 2)) 
(* (- a b) (- a b)) => (- (+ (^ a 2) (^ b 2)) (* 2 a b)) 
(* (- a b) (+ a b)) => (- (^ a 2) (^ b 2)) 
(/ (+ a b) (+ c d)) => (+ (/ a (+ c d)) (/ b (+ c d))) 
(/ (+ a b) (- c d)) => (+ (/ a (- c d)) (/ b (- c d))) 
(/ (- a b) (- c d)) => (- (/ a (- c d)) (/ b (- c d))) 
(/ (- a b) (+ c d)) => (- (/ a (+ c d)) (/ b (+ c d))) 
(^ (+ a b) 3) => (+ (* 3 a (^ b 2)) (* 3 b (^ a 2)) (^ a 3) (^ b 3)) 
(^ (+ a 2) 3) => (+ 8 (* a 12) (* (^ a 2) 6) (^ a 3)) 

Use of this package is not restricted to simple arithmetic expressions:

(require 'determinant)

(determinant '((a b c) (d e f) (g h i))) =>
(- (+ (* a e i) (* b f g) (* c d h)) (* a f h) (* b d i) (* c e g))

Currently, only +, -, *, /, and ^ support non-numeric elements. Expressions with - are converted to equivalent expressions without -, so behavior for - is not defined separately. / expressions are handled similarly.

This list might be extended to include quotient, modulo, remainder, lcm, and gcd; but these work only for the more restrictive Euclidean (Unique Factorization) Domain.

Rules and Rulesets

The commutative-ring package allows control of ring properties through the use of rulesets.

Variable: *ruleset*
Contains the set of rules currently in effect. Rules defined by cring:define-rule are stored within the value of *ruleset* at the time cring:define-rule is called. If *ruleset* is #f, then no rules apply.

Function: make-ruleset rule1 ...
Function: make-ruleset name rule1 ...
Returns a new ruleset containing the rules formed by applying cring:define-rule to each 4-element list argument rule. If the first argument to make-ruleset is a symbol, then the database table created for the new ruleset will be named name. Calling make-ruleset with no rule arguments creates an empty ruleset.

Function: combined-rulesets ruleset1 ...
Function: combined-rulesets name ruleset1 ...
Returns a new ruleset containing the rules contained in each ruleset argument ruleset. If the first argument to combined-ruleset is a symbol, then the database table created for the new ruleset will be named name. Calling combined-ruleset with no ruleset arguments creates an empty ruleset.

Two rulesets are defined by this package.

Constant: distribute*
Contain the ruleset to distribute multiplication over addition and subtraction.
Constant: distribute/
Contain the ruleset to distribute division over addition and subtraction.

Take care when using both distribute* and distribute/ simultaneously. It is possible to put / into an infinite loop.

You can specify how sum and product expressions containing non-numeric elements simplify by specifying the rules for + or * for cases where expressions involving objects reduce to numbers or to expressions involving different non-numeric elements.

Function: cring:define-rule op sub-op1 sub-op2 reduction
Defines a rule for the case when the operation represented by symbol op is applied to lists whose cars are sub-op1 and sub-op2, respectively. The argument reduction is a procedure accepting 2 arguments which will be lists whose cars are sub-op1 and sub-op2.

Function: cring:define-rule op sub-op1 'identity reduction
Defines a rule for the case when the operation represented by symbol op is applied to a list whose car is sub-op1, and some other argument. Reduction will be called with the list whose car is sub-op1 and some other argument.

If reduction returns #f, the reduction has failed and other reductions will be tried. If reduction returns a non-false value, that value will replace the two arguments in arithmetic (+, -, and *) calculations involving non-numeric elements.

The operations + and * are assumed commutative; hence both orders of arguments to reduction will be tried if necessary.

The following rule is the definition for distributing * over +.

(cring:define-rule
 '* '+ 'identity
 (lambda (exp1 exp2)
   (apply + (map (lambda (trm) (* trm exp2)) (cdr exp1))))))

How to Create a Commutative Ring

The first step in creating your commutative ring is to write procedures to create elements of the ring. A non-numeric element of the ring must be represented as a list whose first element is a symbol or string. This first element identifies the type of the object. A convenient and clear convention is to make the type-identifying element be the same symbol whose top-level value is the procedure to create it.

(define (n . list1)
  (cond ((and (= 2 (length list1))
              (eq? (car list1) (cadr list1)))
         0)
        ((not (term< (first list1) (last1 list1)))
         (apply n (reverse list1)))
        (else (cons 'n list1))))

(define (s x y) (n x y))

(define (m . list1)
  (cond ((neq? (first list1) (term_min list1))
         (apply m (cyclicrotate list1)))
        ((term< (last1 list1) (cadr list1))
         (apply m (reverse (cyclicrotate list1))))
        (else (cons 'm list1))))

Define a procedure to multiply 2 non-numeric elements of the ring. Other multiplicatons are handled automatically. Objects for which rules have not been defined are not changed.

(define (n*n ni nj)
  (let ((list1 (cdr ni)) (list2 (cdr nj)))
    (cond ((null? (intersection list1 list2)) #f)
          ((and (eq? (last1 list1) (first list2))
                (neq? (first list1) (last1 list2)))
           (apply n (splice list1 list2)))
          ((and (eq? (first list1) (first list2))
                (neq? (last1 list1) (last1 list2)))
           (apply n (splice (reverse list1) list2)))
          ((and (eq? (last1 list1) (last1 list2))
                (neq? (first list1) (first list2)))
           (apply n (splice list1 (reverse list2))))
          ((and (eq? (last1 list1) (first list2))
                (eq? (first list1) (last1 list2)))
           (apply m (cyclicsplice list1 list2)))
          ((and (eq? (first list1) (first list2))
                (eq? (last1 list1) (last1 list2)))
           (apply m (cyclicsplice (reverse list1) list2)))
          (else #f))))

Test the procedures to see if they work.

;;; where cyclicrotate(list) is cyclic rotation of the list one step
;;; by putting the first element at the end
(define (cyclicrotate list1)
  (append (rest list1) (list (first list1))))
;;; and where term_min(list) is the element of the list which is
;;; first in the term ordering.
(define (term_min list1)
  (car (sort list1 term<)))
(define (term< sym1 sym2)
  (string<? (symbol->string sym1) (symbol->string sym2)))
(define first car)
(define rest cdr)
(define (last1 list1) (car (last-pair list1)))
(define (neq? obj1 obj2) (not (eq? obj1 obj2)))
;;; where splice is the concatenation of list1 and list2 except that their
;;; common element is not repeated.
(define (splice list1 list2)
  (cond ((eq? (last1 list1) (first list2))
         (append list1 (cdr list2)))
        (else (error 'splice list1 list2))))
;;; where cyclicsplice is the result of leaving off the last element of
;;; splice(list1,list2).
(define (cyclicsplice list1 list2)
  (cond ((and (eq? (last1 list1) (first list2))
              (eq? (first list1) (last1 list2)))
         (butlast (splice list1 list2) 1))
        (else (error 'cyclicsplice list1 list2))))

(N*N (S a b) (S a b)) => (m a b)

Then register the rule for multiplying type N objects by type N objects.

(cring:define-rule '* 'N 'N N*N))

Now we are ready to compute!

(define (t)
  (define detM
    (+ (* (S g b)
          (+ (* (S f d)
                (- (* (S a f) (S d g)) (* (S a g) (S d f))))
             (* (S f f)
                (- (* (S a g) (S d d)) (* (S a d) (S d g))))
             (* (S f g)
                (- (* (S a d) (S d f)) (* (S a f) (S d d))))))
       (* (S g d)
          (+ (* (S f b)
                (- (* (S a g) (S d f)) (* (S a f) (S d g))))
             (* (S f f)
                (- (* (S a b) (S d g)) (* (S a g) (S d b))))
             (* (S f g)
                (- (* (S a f) (S d b)) (* (S a b) (S d f))))))
       (* (S g f)
          (+ (* (S f b)
                (- (* (S a d) (S d g)) (* (S a g) (S d d))))
             (* (S f d)
                (- (* (S a g) (S d b)) (* (S a b) (S d g))))
             (* (S f g)
                (- (* (S a b) (S d d)) (* (S a d) (S d b))))))
       (* (S g g)
          (+ (* (S f b)
                (- (* (S a f) (S d d)) (* (S a d) (S d f))))
             (* (S f d)
                (- (* (S a b) (S d f)) (* (S a f) (S d b))))
             (* (S f f)
                (- (* (S a d) (S d b)) (* (S a b) (S d d))))))))
  (* (S b e) (S c a) (S e c)
     detM
     ))
(pretty-print (t))
-|
(- (+ (m a c e b d f g)
      (m a c e b d g f)
      (m a c e b f d g)
      (m a c e b f g d)
      (m a c e b g d f)
      (m a c e b g f d))
   (* 2 (m a b e c) (m d f g))
   (* (m a c e b d) (m f g))
   (* (m a c e b f) (m d g))
   (* (m a c e b g) (m d f)))

Determinant

(require 'determinant)
(determinant '((1 2) (3 4))) => -2
(determinant '((1 2 3) (4 5 6) (7 8 9))) => 0
(determinant '((1 2 3 4) (5 6 7 8) (9 10 11 12))) => 0


Go to the first, previous, next, last section, table of contents.