/*
 * vec_h.c -- vec.h generator.
 *
 * Outputs a file containing vector macros for dimensions 2..maxdim,
 * where maxdim is specified on the command line.
 *
 * Usage: vec_h <maxdim>
 */

static const char *intro[] = {
 " vec.h --  Vector macros for %2..d-1, and %d dimensions,",
 "           for any  combination of C scalar types.",
 "",
 " Author:              Don Hatch (hatch@hadron.org)",
 " Last modified:       Tue Apr 13 21:58:39 PDT 1999",
 "",
 " General description:",
 "",
 "      The macro name describes its arguments; e.g.",
 "              MXS3 is \"matrix times scalar in 3 dimensions\";",
 "              VMV2 is \"vector minus vector in 2 dimensions\".",
 "",
 "      If the result of an operation is a scalar, then the macro \"returns\"",
 "      the value; e.g.",
 "              result = DOT3(v,w);",
 "              result = DET4(m);",
 "",
 "      If the result of an operation is a vector or matrix, then",
 "      the first argument is the destination; e.g.",
 "              SET2(tovec, fromvec);",
 "              MXM3(result, m1, m2);",
 "",
 "  WARNING: For the operations that are not done \"componentwise\"",
 "          (e.g. vector cross products and matrix multiplies)",
 "          the destination should not be either of the arguments,",
 "          for obvious reasons.  For example, the following is wrong:",
 "              VXM2(v,v,m);",
 "          For such \"unsafe\" macros, there are safe versions provided,",
 "          but you have to specify a type for the temporary",
 "          result vector or matrix.  For example, the safe versions",
 "          of VXM2 are:",
 "              VXM2d(v,v,m)    if v's scalar type is double or float",
 "              VXM2f(v,v,m)    if v's scalar type is float",
 "              VXM2i(v,v,m)    if v's scalar type is int or char",
 "              VXM2l(v,v,m)    if v's scalar type is long",
 "              VXM2r(v,v,m)    if v's scalar type is real",
 "              VXM2safe(type,v,v,m) for other scalar types.",
 "",
 "          These \"safe\" macros and INVERTMAT do not evaluate to C expressions",
 "          (so, for example, they can't be used inside the parentheses of",
 "          a for(...)).",
 "",
 "  Specific descriptions:",
 "",
 "      The \"?\"'s in the following can be 2, 3, or 4.",
 "",
 "      EXPAND?(v)                      comma-separated list of elements of v",
 "",
 "      SET?(to,from)                   to = from",
 "      SETMAT?(to,from)                to = from",
 "      ROUNDVEC?(to,from)              to = from with entries rounded",
 "                                                      to nearest integer",
 "      ROUNDMAT?(to,from)              to = from with entries rounded",
 "                                                      to nearest integer",
 "      FILLVEC?(v,s)                   set each entry of vector v to be s",
 "      FILLMAT?(m,s)                   set each entry of matrix m to be s",
 "      ZEROVEC?(v)                     v = 0",
 "      ISZEROVEC?(v)                   v == 0",
 "      EQVEC?(v,w)                     v == w",
 "      EQMAT?(m1,m2)                   m1 == m2",
 "      ZEROMAT?(m)                     m = 0",
 "      IDENTMAT?(m)                    m = 1",
 "      TRANSPOSE?(to,from)             (matrix to) = (transpose of matrix from)",
 "      ADJOINT?(to,from)               (matrix to) = (adjoint of matrix from)",
 "                                       i.e. its determinant times its inverse",
 "      INVERTMAT?{d,f,i,l,r}(to,from)  (matrix to) = (inverse of matrix from)",
 "                               with temp adjoint and determinant type",
 "                               double, float, int, long, or real respectively",
 "",
 "      V{P,M}V?(to,v,w)                to = v {+,-} w",
 "      M{P,M}M?(to,m1,m2)              to = m1 {+,-} m2",
 "      SX{V,M}?(to,s,from)             to = s * from",
 "      VPSXV?(to,v,s,w)                to = v + s*w",
 "      VPVXS?(to,v,w,s)                to = v + w*s",
 "      M{V,M}?(to,from)                to = -from",
 "      {V,M}{X,D}S?(to,from,s)         to = from {*,/} s",
 "      MXM?(to,m1,m2)                  to = m1 * m2",
 "      VXM?(to,v,m)                    (row vec to) = (row vec v) * m",
 "      MXV?(to,m,v)                    (column vec to) = m * (column vec v)",
 "      VMODS?(to,v,s)                  to = v mod s (always >= 0)",
 "      VMODV?(to,v0,v1)                to = v0 mod v1 componentwise",
 "      VDIVS?(to,v,s)                  to = (v-(v mod s))/s",
 "      VDIVV?(to,v0,v1)                to = (v0-(v0 mod v1))/v1 componentwise",
 "      V{MIN,MAX}S?(to,v,s)            to = {MIN,MAX}(v, s)",
 "      V{MIN,MAX}V?(to,v0,v1)          to = {MIN,MAX}(v0, v1)",
 "      LERP?(to,v0,v1,t)               to = v0 + t*(v1-v0)",
 "",
 "      DET?(m)                         determinant of m",
 "      TRACE?(m)                       trace (sum of diagonal entries) of m",
 "      DOT?(v,w)                       dot (scalar) product of v and w",
 "      NORMSQRD?(v)                    square of |v|",
 "      DISTSQRD?(v,w)                  square of |v-w|",
 "",
 "      XV2(to,v)                       to = v rotated by 90 degrees",
 "      VXV3(to,v1,v2)                  to = cross (vector) product of v1 and v2",
 "      VXVXV4(to,v1,v2,v3)             to = 4-dimensional vector cross product",
 "                                       of v1,v2,v3 (a vector orthogonal to",
 "                                       v1,v2,v3 whose length equals the",
 "                                       volume of the spanned parallelotope)",
 "      VXV2(v0,v1)                     determinant of matrix with rows v0,v1",
 "      VXVXV3(v0,v1,v2)                determinant of matrix with rows v0,v1,v2",
 "      VXVXVXV4(v0,v1,v2,v3)           determinant of matrix with rows v0,..,v3",
 "",
 "   The following macros mix objects from different dimensions.",
 "   For example, V3XM4 would be used to apply a composite",
 "   4x4 rotation-and-translation matrix to a 3d vector.",
 "",
 "      SET3from2(to,from,pad)          (3d vec to) = (2d vec from) with pad",
 "      SET4from3(to,from,pad)          (4d vec to) = (3d vec from) with pad",
 "      SETMAT3from2(to,from,pad0,pad1) (3x3 mat to) = (2x2 mat from)",
 "                                       padded with pad0 on the sides",
 "                                       and pad1 in the corner",
 "      SETMAT4from3(to,from,pad0,pad1) (4x4 mat to) = (3x3 mat from)",
 "                                       padded with pad0 on the sides",
 "                                       and pad1 in the corner",
 "      V2XM3(to2,v2,m3)       (2d row vec to2) = (2d row vec v2) * (3x3 mat m3)",
 "      V3XM4(to3,v3,m4)       (3d row vec to3) = (3d row vec v2) * (4x4 mat m4)",
 "      M3XV2(to2,m3,v2)       (2d col vec to2) = (3x3 mat m3) * (2d col vec v2)",
 "      M4XV3(to3,m4,v3)       (3d col vec to3) = (4x4 mat m4) * (3d col vec v3)",
 "      M2XM3(to3,m2,m3)       (3x3 mat to3) = (2x2 mat m2) * (3x3 mat m3)",
 "      M3XM4(to4,m3,m4)       (4x4 mat to4) = (3x3 mat m3) * (4x4 mat m4)",
 "      M3XM2(to3,m3,m2)       (3x3 mat to3) = (3x3 mat m3) * (2x2 mat m2)",
 "      M4XM3(to4,m4,m3)       (4x4 mat to4) = (4x4 mat m4) * (3x3 mat m3)",
 "",
 "",
 "   This file is machine-generated and can be regenerated",
 "   for any number of dimensions.",
 "   The program that generated it is available upon request.",
0
};

static struct {
    const char *abbr, *full;
} types[] = {
    {"d", "double"},
    {"f", "float"},
    {"i", "int"},
    {"l", "long"},
    {"r", "real"},
};

static struct definition {
    int safety; /* 0 means already safe, i.e. returns a scalar or is done
                   "componentwise".  1 means unsafe and returns a vector.
                                     2 means unsafe and returns a matrix. */
    const char *name_and_args, *all_but_last, *sep, *last;
} defs[] = {
    {0, "SET%d(to,from)",     "(to)[%i] = (from)[%i]",                     ","},
    {0, "SETMAT%d(to,from)",  "SET%d((to)[%i], (from)[%i])",               ","},
    {0, "ROUNDVEC%d(to,from)","(to)[%i] = floor((from)[%i]+.5)",           ","},
    {0, "ROUNDMAT%d(to,from)","ROUNDVEC%d((to)[%i], (from)[%i])",          ","},
    {0, "FILLVEC%d(v,s)",     "(v)[%i] = (s)",                             ","},
    {0, "FILLMAT%d(m,s)",     "FILLVEC%d((m)[%i], s)",                     ","},
    {0, "ZEROVEC%d(v)",       "(v)[%i] = 0",                               ","},
    {0, "ISZEROVEC%d(v)",     "(v)[%i] == 0",                            " &&"},
    {0, "EQVEC%d(v,w)",       "(v)[%i] == (w)[%i]",                      " &&"},
    {0, "EQMAT%d(m1,m2)",     "EQVEC%d((m1)[%i], (m2)[%i])",             " &&"},
    {0, "ZEROMAT%d(m)",       "ZEROVEC%d((m)[%i])",                        ","},
    {0, "IDENTMAT%d(m)",      "ZEROVEC%d((m)[%i]), (m)[%i][%i]=1",         ","},
    {2, "TRANSPOSE%d(to,from)", "_SETcol%d((to)[%i], from, %i)",           ","},
    {0, "VPSXV%d(to,v,s,w)",  "(to)[%i] = (v)[%i] + (s) * (w)[%i]",        ","},
    {0, "VPVXS%d(to,v,w,s)",  "(to)[%i] = (v)[%i] + (w)[%i] * (s)",        ","},
    {0, "VPV%d(to,v,w)",      "(to)[%i] = (v)[%i] + (w)[%i]",              ","},
    {0, "VMV%d(to,v,w)",      "(to)[%i] = (v)[%i] - (w)[%i]",              ","},
    {0, "MPM%d(to,m1,m2)",    "VPV%d((to)[%i], (m1)[%i], (m2)[%i])",       ","},
    {0, "MMM%d(to,m1,m2)",    "VMV%d((to)[%i], (m1)[%i], (m2)[%i])",       ","},
    {0, "SXV%d(to,s,from)",   "(to)[%i] = (s) * (from)[%i]",               ","},
    {0, "SXM%d(to,s,from)",   "SXV%d((to)[%i], s, (from)[%i])",            ","},
    {0, "MV%d(to,from)",      "(to)[%i] = -(from)[%i]",                    ","},
    {0, "MM%d(to,from)",      "MV%d((to)[%i], (from)[%i])",                ","},
    {0, "VXS%d(to,from,s)",   "(to)[%i] = (from)[%i] * (s)",               ","},
    {0, "VDS%d(to,from,s)",   "(to)[%i] = (from)[%i] / (s)",               ","},
    {0, "MXS%d(to,from,s)",   "VXS%d((to)[%i], (from)[%i], s)",            ","},
    {0, "MDS%d(to,from,s)",   "VDS%d((to)[%i], (from)[%i], s)",            ","},
    {2, "MXM%d(to,m1,m2)",    "VXM%d((to)[%i], (m1)[%i], m2)",             ","},
    {1, "VXM%d(to,v,m)",      "(to)[%i] = _DOTcol%d(v, m, %i)",            ","},
    {1, "MXV%d(to,m,v)",      "(to)[%i] = DOT%d((m)[%i], v)",              ","},
    {0, "VMODS%d(to,v,s)",    "(to)[%i] = SMODS1((v)[%i], s)",             ","},
    {0, "VMODV%d(to,v0,v1)",  "(to)[%i] = SMODS1((v0)[%i], (v1)[%i])",     ","},
    {0, "VDIVS%d(to,v,s)",    "(to)[%i] = SDIVS1((v)[%i], s)",             ","},
    {0, "VDIVV%d(to,v0,v1)",  "(to)[%i] = SDIVS1((v0)[%i], (v1)[%i])",     ","},
    {0, "VMINS%d(to,v,s)",    "(to)[%i] = SMINS1((v)[%i], s)",             ","},
    {0, "VMINV%d(to,v0,v1)",  "(to)[%i] = SMINS1((v0)[%i], (v1)[%i])",     ","},
    {0, "VMAXS%d(to,v,s)",    "(to)[%i] = SMAXS1((v)[%i], s)",             ","},
    {0, "VMAXV%d(to,v0,v1)",  "(to)[%i] = SMAXS1((v0)[%i], (v1)[%i])",     ","},
    {0, "LERP%d(to,v0,v1,t)", "(to)[%i]=(v0)[%i]+(t)*((v1)[%i]-(v0)[%i])", ","},
    {0, "TRACE%d(m)",         "(m)[%i][%i]",                              " +"},
    {0, "DOT%d(v,w)",         "(v)[%i] * (w)[%i]",                        " +"},
    {0, "NORMSQRD%d(v)",      "(v)[%i] * (v)[%i]",                        " +"},
    {0, "DISTSQRD%d(v,w)",    "((v)[%i]-(w)[%i])*((v)[%i]-(w)[%i])",      " +"},
    {0, "_DOTcol%d(v,m,j)",   "(v)[%i] * (m)[%i][j]",                     " +"},
    /* following two aren't really "safe", but shouldn't be used anyway */
    {0, "_SETcol%d(v,m,j)",   "(v)[%i] = (m)[%i][j]",                      ","},
    {0, "_MXVcol%d(to,m,M,j)","(to)[%i][j] = _DOTcol%d((m)[%i],M,j)",      ","},
    {0, "_DET%d(v%0..d-1,i%0..d-1)","(v0)[i%i]*%-_DET%d-1(v%1..d-1,i%~i)"," +"},
    {1, "%XV%d(to,v%1..d-1)",     "(to)[%i] = %-%-_DET%d-1(v%1..d-1, %~i)",","},
    /* careful! don't use v%0..d-1 for the above or the hack utility routines
       for making "safe" macros won't be able to find the first arg. */

    /*
     * dimension-mixing macros for which d (= the dimension of the destination)
     * is the larger of the two dimensions.  This is deduced from
     * the fact that the macro name contains %d-1.
     */
    {0, "SET%dfrom%d-1(to,from,pad)", "(to)[%i] = (from)[%i]", ",",
                                      "(to)[%i] = (pad)"},
    {0, "SETMAT%dfrom%d-1(to,from,pad0,pad1)",
                        "SET%dfrom%d-1((to)[%i], (from)[%i], pad0)", ",",
                        "FILLVEC%d-1((to)[%i], (pad0)), (to)[%i][%i] = (pad1)"},
    {2, "M%d-1XM%d(to%d,m%d-1,m%d)",
       "_MXVcol%d-1(to%d,m%d-1,m%d,%i), (to%d)[%d-1][%i]=(m%d)[%d-1][%i]", ","},
    {2, "M%dXM%d-1(to%d,m%d,m%d-1)",
  "VXM%d-1((to%d)[%i],(m%d)[%i],m%d-1), (to%d)[%i][%d-1]=(m%d)[%i][%d-1]", ","},

    /*
     * dimension-mixing macros for which d (= the dimension of the destination)
     * is the smaller of the two dimensions.  This is deduced from
     * the fact that the macro name contains %d+1.
     */
    {1, "V%dXM%d+1(to%d,v%d,m%d+1)",
            "(to%d)[%i] = _DOTcol%d(v%d,m%d+1,%i) + (m%d+1)[%d][%i]",   ","},
    {1, "M%d+1XV%d(to%d,m%d+1,v%d)",
            "(to%d)[%i] = DOT%d((m%d+1)[%i],v%d) + (m%d+1)[%i][%d]",    ","},

    /*
     * definitions that don't get vector-expanded.
     * This is deduced from the fact that the "sep" field is empty.
     */
    {0, "_DET1(v0,i0)",         "(v0)[i0]"},
    {0, "%VXV%d(v%0..d-1)",     "_DET%d(v%0..d-1,%0..d-1)"},
    {0, "DET%d(m)",             "%VXV%d((m)[%0..d-1])"},
    {0, "SMODS1(a,b)",          "(((a)%%(b)+(b))%%(b))"},
    {0, "SDIVS1(a,b)",          "(((a)-SMODS1(a,b))/(b))"},
    {0, "SMINS1(a,b)",          "((a) < (b) ? (a) : (b))"},
    {0, "SMAXS1(a,b)",          "((a) > (b) ? (a) : (b))"},

    /*
     * New adjoint stuff.
     */
    {2, "ADJOINT%d(to,m)",      "%__ADJOINTcol%d(to,%i,m,%~i)", ","},
    {0, "_ADJOINTcol%d(to,col,m,i%1..d-1)",
                          "(to)[%i][col] = %-_DET%d-1((m)[i%1..d-1], %~i)",  ","},
    {0, "__ADJOINTcol%d(to,col,m,i%1..d-1)",
                          "(to)[%i][col] = %--_DET%d-1((m)[i%1..d-1], %~i)", ","},
    /*
     * Inverse-- completely special case, defined in terms of adjoint and det
     */
    {2, "INVERTMAT%d(to,from)"}
};

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <ctype.h>
#include <string.h>
#define MIN(a,b) ((a)<(b)?(a):(b))
#define MAX(a,b) ((a)>(b)?(a):(b))
#define numberof(sextoys)  (sizeof(sextoys) / sizeof(*(sextoys)))

static int isprefix(const char *pre, const char *s)
{
    return strncmp(pre, s, strlen(pre)) == 0;
}

/*
 * s is pointing to the last char of the prefix;
 * to a space, ',' or a '(', taking into account nesting parens
 */
static const char *getprefix(const char *s)
{
    static char buf[100];
    char *bufptr = buf + sizeof(buf);
    int i, nest = 0;
    *--bufptr = 0;
    for (i = 0; nest || !strchr(" ,(", s[i]); --i) {
        nest += (s[i] == ')') - (s[i] == '(');
        *--bufptr = s[i];
    }
    return bufptr;
}

/*
 * s is pointing to the beginning of the suffix;
 * get the suffix up to but not including a space, ',' or ')'.
 */
static const char *getsuffix(const char *s)
{
    static char buf[100];
    int i;
    for (i = 0; !strchr(" ,)", s[i]); ++i)
        buf[i] = s[i];
    buf[i] = 0;
    return buf;
}

/*
 * Print the string s with the following substitutions:
 *      %d turns into d
 *      %i turns into i
 *      %- turns into "-" if i is odd, " " otherwise
 *      %-- turns into "-" if i is even, " " otherwise
 *      %_ turns into "_" if i is odd, " " otherwise
 *      %-%- turns into "-" if d+i is even, " " otherwise (sorry this is stupid)
 *      %i+1 turns into (i+1)%d etc.
 *      %d-1 turns into d-1 etc.
 *      %% turns into %
 *      blah%0..d-1bleh turns into blah0bleh,blah1bleh,...,blahd-1bleh
 *      blah%~ibleh  is same as blah%0..d-1bleh but excluding i
 *      %XV turns into VXVX...XV with d-1 V's (or XV if d==2)
 *      %VXV turns into VXVX...XV with d V's 
 */
static void printformatted_to(const char *s, const char *tochars, int d, int i)
{
    const char *prefix, *suffix;
    int num, j, lastj;

    for (; *s && !strchr(tochars, *s); s++) {
        if (*s == '%') {
            ++s;

            prefix = NULL;
            if (isdigit(*s) && !strncmp(s+1, "..d-1", 5)) {
                prefix = getprefix(s-2);        /* one char before the % */
                suffix = getsuffix(s+6);
            } else if (!strncmp(s, "~i", 2)) {
                prefix = getprefix(s-2);        /* one char before the % */
                suffix = getsuffix(s+2);
            }
            if (prefix) {
                lastj = d-1 - (*s == '~');
                for (j = atoi(s); j <= lastj; ++j) {
                    printf("%d", j + (*s == '~' && j >= i));
                    printf("%s", suffix);
                    if (j < lastj)
                        printf(",%s", prefix);
                }
                s += (*s == '~' ? 2 : 6) + strlen(suffix) - 1;
            } else {
                switch(*s) {
                    case '-': case '_':
                        if (!strncmp(s, "-%-", 3)) {
                            printf("%c", (i+d)%2==0 ? *s : ' ');
                            s += 2;
                        } else if (!strncmp(s, "--", 2)) {
                            printf("%c", i%2==0 ? *s : ' ');
                            s++;
                        } else {
                            printf("%c", i%2==1 ? *s : ' ');
                        }
                        break;
                    case 'i': case 'd':
                        num = (*s == 'i' ? i : d);
                        if (strchr("+-", s[1])) {
                            num += (s[1] == '-' ? -1 : 1) * atoi(s+2);
                            if (*s == 'i')
                                num = (num+d) % d;
                            s += 2;
                            /* s is now pointing to the first digit */
                            while (isdigit(s[1]))
                                s++;
                            /* s is now pointing to the last digit */
                        }
                        printf("%d", num);
                        break;
                    case 'X': case 'V':
                        if (*s == 'X' && d == 2)
                            printf("X");
                        for (j = 0; j < d-2; ++j)
                            printf("VX");
                        if (*s == 'V')
                            printf("V");
                        break;
                    case '%':
                            printf("%%");
                        break;
                    default:
                        assert(0);
                }
            }
        } else
            putchar(*s);
    }
}

static void printformatted(const char *s, int d, int i)
{
    printformatted_to(s, "", d, i);
}

static void define(int d, const char *name_and_args, const char *all_but_last, const char *sep, const char *last)
{
    int i;

    printf("#define ");
    printformatted(name_and_args, d, 0);
    printf("\t\\\n\t\t");
    printf("(");

        if (sep)
            for (i = 0; i < d-1; ++i) {         /* loop for all but last */
                printformatted(all_but_last, d, i);
                printformatted(sep, d, i);
                printf(" \\\n\t\t ");
            }
        printformatted(last, d, i);

    printf(")\n");
}

static int is_substring(const char *a, const char *b)
{
    for (; *b; b++)
        if (strncmp(a, b, strlen(a)) == 0)
            return 1;
    return 0;
}


/*
 * Hack utility routines...
 */
static void print_name_and_args_with_stuff_inserted(const char *name_and_arg, const char *suff, const char *arg0, int d)
{
    printformatted_to(name_and_arg, "(", d, 0);
    printformatted(suff, d, 0);
    printf("(");
    if (arg0) {
        printformatted(arg0, d, 0);
        printf(",");
    }
    printformatted(strchr(name_and_arg, '(' /*)*/ ) + 1, d, 0);
}

static void print_name_and_args_with_first_arg_changed(const char *name_and_arg, const char *arg0, int d)
{
    printformatted_to(name_and_arg, "(", d, 0);
    printf("(");
    printformatted(arg0, d, 0);
    printformatted(strchr(name_and_arg, ','), d, 0);
}

static void print_first_arg(const char *name_and_arg, int d)
{
    printformatted_to(strchr(name_and_arg, '(' /*)*/ ) + 1, ",)", d, 0);
}

static void print_second_arg(const char *name_and_arg, int d)
{
    printformatted_to(strchr(name_and_arg, ',') + 1, ",)", d, 0);
}


#define MINDIM 2

extern int
main(int argc, char **argv)
{
    int i, d, maxdim;

    if (argc != 2 || ! (maxdim = atoi(argv[argc-1])))
        fprintf(stderr, "Usage: %s [<maxdim>]\n", argv[0]), exit(1);


    printf("/*\n");
    for (i  = 0; intro[i]; ++i) {
        printf(" *");
        printformatted(intro[i], maxdim, 0);
        printf("\n");
    }
    printf(" */\n\n");

    printf("#ifndef VEC_H\n");
    printf("#define VEC_H %d\n", maxdim);
    printf("\n");
    printf("#undef TRACE2\n");
    printf("#undef TRACE3\n");
    printf("\n");
    printf("#include <math.h>   /* for definition of floor() */\n");
    printf("\n");

    for (d = MINDIM; d <= maxdim; ++d) {
        printf("#define EXPAND%d(v) ", d);
        for (i = 0; i < d; ++i)
            printf("%s(v)[%d]", i==0 ? "" : ", ", i);
        printf("\n");
    }
    for (d = MINDIM; d <= maxdim; ++d)
        for (i = 0; i < numberof(defs); ++i) {
            if ((is_substring("%d-1", defs[i].name_and_args) && d-1 < MINDIM)
             || (is_substring("%d+1", defs[i].name_and_args) && d+1 > maxdim))
                continue;
            if (!is_substring("%d", defs[i].name_and_args) && d != MINDIM)
                continue;       /* don't redefine if it's the same */
            if (!defs[i].all_but_last)
                continue;       /* no definition here */
            define(d, defs[i].name_and_args,
                      defs[i].all_but_last,
                      defs[i].sep,
                      defs[i].last ? defs[i].last : defs[i].all_but_last);
        }
    for (d = MINDIM; d <= maxdim; ++d)
        for (i = 0; i < numberof(defs); ++i) {
            if ((is_substring("%d-1", defs[i].name_and_args) && d-1 < MINDIM)
             || (is_substring("%d+1", defs[i].name_and_args) && d+1 > maxdim))
                continue;
            if (!is_substring("%d", defs[i].name_and_args) && d != MINDIM)
                continue;       /* don't redefine if it's the same */
            if (defs[i].safety > 0) {
                int t;
                printf("#define ");
                print_name_and_args_with_stuff_inserted(defs[i].name_and_args,
                                                            "safe", "type", d);
                printf(" \\\n\t\t");
                printformatted("do {type _vec_h_temp_[%d]", d, 0);
                if (defs[i].safety > 1)
                    printformatted("[%d]", d, 0);
                printf("; \\\n\t\t    ");

                if (isprefix("INVERTMAT", defs[i].name_and_args)) {
                    printf("type _vec_h_temp_invdet_;\\\n\t\t    ");
                    printformatted("ADJOINT%d(_vec_h_temp_, ", d, 0);
                    print_second_arg(defs[i].name_and_args, d);
                    printf("); \\\n\t\t    ");
                    printformatted("_vec_h_temp_invdet_ = (type)1/(type)DET%d(from); \\\n\t\t    ", d, 0);
                    printformatted("SXM%d(", d, 0);
                    print_first_arg(defs[i].name_and_args, d);
                    printf(", _vec_h_temp_invdet_");
                } else {
                    print_name_and_args_with_first_arg_changed(
                            defs[i].name_and_args, "_vec_h_temp_", d);
                    printf("; \\\n\t\t    ");

                    if (defs[i].safety > 1)
                        printformatted("SETMAT%d(", d, 0);
                    else
                        printformatted("SET%d(", d, 0);
                    print_first_arg(defs[i].name_and_args, d);
                }
                printf(", _vec_h_temp_); \\\n\t\t} while (0)\n");

                for (t = 0; t < numberof(types); ++t) {
                    printf("#define ");
                    print_name_and_args_with_stuff_inserted(
                        defs[i].name_and_args, types[t].abbr, (char *)NULL, d);
                    printf(" ");
                    print_name_and_args_with_stuff_inserted(
                        defs[i].name_and_args, "safe", types[t].full, d);
                    printf("\n");
                }
            }
        }
    printf("#endif /* VEC_H */\n");

    return 0;
}

