| 1 | 
greg | 
2.1 | 
#ifndef lint | 
| 2 | 
greg | 
2.22 | 
static const char RCSid[] = "$Id: rmtxop.c,v 2.21 2023/11/21 01:30:20 greg Exp $"; | 
| 3 | 
greg | 
2.1 | 
#endif | 
| 4 | 
  | 
  | 
/* | 
| 5 | 
  | 
  | 
 * General component matrix operations. | 
| 6 | 
  | 
  | 
 */ | 
| 7 | 
  | 
  | 
 | 
| 8 | 
  | 
  | 
#include <stdlib.h> | 
| 9 | 
greg | 
2.11 | 
#include <errno.h> | 
| 10 | 
greg | 
2.22 | 
#include <ctype.h> | 
| 11 | 
greg | 
2.1 | 
#include "rtio.h" | 
| 12 | 
  | 
  | 
#include "resolu.h" | 
| 13 | 
  | 
  | 
#include "rmatrix.h" | 
| 14 | 
greg | 
2.10 | 
#include "platform.h" | 
| 15 | 
greg | 
2.1 | 
 | 
| 16 | 
greg | 
2.22 | 
#define MAXCOMP         MAXCSAMP        /* #components we support */ | 
| 17 | 
greg | 
2.1 | 
 | 
| 18 | 
greg | 
2.22 | 
/* Unary matrix operation(s) */ | 
| 19 | 
greg | 
2.1 | 
typedef struct { | 
| 20 | 
  | 
  | 
        double          sca[MAXCOMP];           /* scalar coefficients */ | 
| 21 | 
  | 
  | 
        double          cmat[MAXCOMP*MAXCOMP];  /* component transformation */ | 
| 22 | 
greg | 
2.13 | 
        short           nsf;                    /* number of scalars */ | 
| 23 | 
  | 
  | 
        short           clen;                   /* number of coefficients */ | 
| 24 | 
greg | 
2.22 | 
        char            csym[11];               /* symbolic coefficients */ | 
| 25 | 
  | 
  | 
        char            transpose;              /* do transpose? */ | 
| 26 | 
greg | 
2.13 | 
} RUNARYOP; | 
| 27 | 
  | 
  | 
 | 
| 28 | 
greg | 
2.22 | 
/* Matrix input source and requested operation(s) */ | 
| 29 | 
greg | 
2.13 | 
typedef struct { | 
| 30 | 
  | 
  | 
        const char      *inspec;                /* input specification */ | 
| 31 | 
greg | 
2.18 | 
        RMPref          rmp;                    /* matrix preference */ | 
| 32 | 
greg | 
2.13 | 
        RUNARYOP        preop;                  /* unary operation(s) */ | 
| 33 | 
  | 
  | 
        RMATRIX         *mtx;                   /* original matrix if loaded */ | 
| 34 | 
  | 
  | 
        int             binop;                  /* binary op with next (or 0) */ | 
| 35 | 
  | 
  | 
} ROPMAT; | 
| 36 | 
greg | 
2.1 | 
 | 
| 37 | 
  | 
  | 
int     verbose = 0;                    /* verbose reporting? */ | 
| 38 | 
  | 
  | 
 | 
| 39 | 
greg | 
2.13 | 
/* Load matrix */ | 
| 40 | 
  | 
  | 
static int | 
| 41 | 
  | 
  | 
loadmatrix(ROPMAT *rop) | 
| 42 | 
greg | 
2.1 | 
{ | 
| 43 | 
greg | 
2.20 | 
        if (rop->mtx != NULL)           /* already loaded? */ | 
| 44 | 
greg | 
2.13 | 
                return(0); | 
| 45 | 
  | 
  | 
 | 
| 46 | 
greg | 
2.18 | 
        rop->mtx = rmx_load(rop->inspec, rop->rmp); | 
| 47 | 
greg | 
2.13 | 
        if (rop->mtx == NULL) { | 
| 48 | 
  | 
  | 
                fputs(rop->inspec, stderr); | 
| 49 | 
  | 
  | 
                fputs(": cannot load matrix\n", stderr); | 
| 50 | 
  | 
  | 
                return(-1); | 
| 51 | 
  | 
  | 
        } | 
| 52 | 
  | 
  | 
        return(1); | 
| 53 | 
greg | 
2.1 | 
} | 
| 54 | 
  | 
  | 
 | 
| 55 | 
greg | 
2.22 | 
/* Compute conversion row from spectrum to one channel of RGB */ | 
| 56 | 
  | 
  | 
static void | 
| 57 | 
  | 
  | 
rgbrow(ROPMAT *rop, int r, int p) | 
| 58 | 
  | 
  | 
{ | 
| 59 | 
  | 
  | 
        const int       nc = rop->mtx->ncomp; | 
| 60 | 
  | 
  | 
        const float *   wlp = rop->mtx->wlpart; | 
| 61 | 
  | 
  | 
        int             i; | 
| 62 | 
  | 
  | 
 | 
| 63 | 
  | 
  | 
        for (i = nc; i--; ) { | 
| 64 | 
  | 
  | 
                int     nmEnd = wlp[0] + (wlp[3] - wlp[0])*i/nc; | 
| 65 | 
  | 
  | 
                int     nmStart = wlp[0] + (wlp[3] - wlp[0])*(i+1)/nc; | 
| 66 | 
  | 
  | 
                COLOR   crgb; | 
| 67 | 
  | 
  | 
                spec_rgb(crgb, nmStart, nmEnd); | 
| 68 | 
  | 
  | 
                rop->preop.cmat[r*nc+i] = crgb[p]; | 
| 69 | 
  | 
  | 
        } | 
| 70 | 
  | 
  | 
} | 
| 71 | 
  | 
  | 
 | 
| 72 | 
  | 
  | 
/* Compute conversion row from spectrum to one channel of XYZ */ | 
| 73 | 
  | 
  | 
static void | 
| 74 | 
  | 
  | 
xyzrow(ROPMAT *rop, int r, int p) | 
| 75 | 
  | 
  | 
{ | 
| 76 | 
  | 
  | 
        const int       nc = rop->mtx->ncomp; | 
| 77 | 
  | 
  | 
        const float *   wlp = rop->mtx->wlpart; | 
| 78 | 
  | 
  | 
        int             i; | 
| 79 | 
  | 
  | 
 | 
| 80 | 
  | 
  | 
        for (i = nc; i--; ) { | 
| 81 | 
  | 
  | 
                int     nmEnd = wlp[0] + (wlp[3] - wlp[0])*i/nc; | 
| 82 | 
  | 
  | 
                int     nmStart = wlp[0] + (wlp[3] - wlp[0])*(i+1)/nc; | 
| 83 | 
  | 
  | 
                COLOR   cxyz; | 
| 84 | 
  | 
  | 
                spec_cie(cxyz, nmStart, nmEnd); | 
| 85 | 
  | 
  | 
                rop->preop.cmat[r*nc+i] = cxyz[p]; | 
| 86 | 
  | 
  | 
        } | 
| 87 | 
  | 
  | 
} | 
| 88 | 
  | 
  | 
 | 
| 89 | 
  | 
  | 
/* Use the spectral sensitivity function to compute matrix coefficients */ | 
| 90 | 
  | 
  | 
static void | 
| 91 | 
  | 
  | 
sensrow(ROPMAT *rop, int r, double (*sf)(SCOLOR sc, int ncs, const float wlpt[4])) | 
| 92 | 
  | 
  | 
{ | 
| 93 | 
  | 
  | 
        const int       nc = rop->mtx->ncomp; | 
| 94 | 
  | 
  | 
        int             i; | 
| 95 | 
  | 
  | 
 | 
| 96 | 
  | 
  | 
        for (i = nc; i--; ) { | 
| 97 | 
  | 
  | 
                SCOLOR  sclr; | 
| 98 | 
  | 
  | 
                scolorblack(sclr); | 
| 99 | 
  | 
  | 
                sclr[i] = 1; | 
| 100 | 
  | 
  | 
                rop->preop.cmat[r*nc+i] = (*sf)(sclr, nc, rop->mtx->wlpart); | 
| 101 | 
  | 
  | 
        } | 
| 102 | 
  | 
  | 
} | 
| 103 | 
  | 
  | 
 | 
| 104 | 
  | 
  | 
/* Check/set symbolic transform */ | 
| 105 | 
  | 
  | 
static int | 
| 106 | 
  | 
  | 
checksymbolic(ROPMAT *rop) | 
| 107 | 
  | 
  | 
{ | 
| 108 | 
  | 
  | 
        const int       nc = rop->mtx->ncomp; | 
| 109 | 
  | 
  | 
        const int       dt = rop->mtx->dtype; | 
| 110 | 
  | 
  | 
        int             i, j; | 
| 111 | 
  | 
  | 
 | 
| 112 | 
  | 
  | 
        if (nc < 3) { | 
| 113 | 
  | 
  | 
                fprintf(stderr, "%s: -c '%s' requires at least 3 components\n", | 
| 114 | 
  | 
  | 
                                rop->inspec, rop->preop.csym); | 
| 115 | 
  | 
  | 
                return(-1); | 
| 116 | 
  | 
  | 
        } | 
| 117 | 
  | 
  | 
        rop->preop.clen = strlen(rop->preop.csym) * nc; | 
| 118 | 
  | 
  | 
        if (rop->preop.clen > MAXCOMP*MAXCOMP) { | 
| 119 | 
  | 
  | 
                fprintf(stderr, "%s: -c '%s' results in too many components\n", | 
| 120 | 
  | 
  | 
                                rop->inspec, rop->preop.csym); | 
| 121 | 
  | 
  | 
                return(-1); | 
| 122 | 
  | 
  | 
        } | 
| 123 | 
  | 
  | 
        for (j = 0; rop->preop.csym[j]; j++) { | 
| 124 | 
  | 
  | 
                int     comp = 0; | 
| 125 | 
  | 
  | 
                switch (rop->preop.csym[j]) { | 
| 126 | 
  | 
  | 
                case 'B': | 
| 127 | 
  | 
  | 
                        ++comp; | 
| 128 | 
  | 
  | 
                        /* fall through */ | 
| 129 | 
  | 
  | 
                case 'G': | 
| 130 | 
  | 
  | 
                        ++comp; | 
| 131 | 
  | 
  | 
                        /* fall through */ | 
| 132 | 
  | 
  | 
                case 'R': | 
| 133 | 
  | 
  | 
                        if (dt == DTxyze) { | 
| 134 | 
  | 
  | 
                                for (i = 3; i--; ) | 
| 135 | 
  | 
  | 
                                        rop->preop.cmat[j*nc+i] = 1./WHTEFFICACY * | 
| 136 | 
  | 
  | 
                                                        xyz2rgbmat[comp][i]; | 
| 137 | 
  | 
  | 
                        } else if (nc == 3) | 
| 138 | 
  | 
  | 
                                rop->preop.cmat[j*nc+comp] = 1.; | 
| 139 | 
  | 
  | 
                        else | 
| 140 | 
  | 
  | 
                                rgbrow(rop, j, comp); | 
| 141 | 
  | 
  | 
                        break; | 
| 142 | 
  | 
  | 
                case 'Z': | 
| 143 | 
  | 
  | 
                        ++comp; | 
| 144 | 
  | 
  | 
                        /* fall through */ | 
| 145 | 
  | 
  | 
                case 'Y': | 
| 146 | 
  | 
  | 
                        ++comp; | 
| 147 | 
  | 
  | 
                        /* fall through */ | 
| 148 | 
  | 
  | 
                case 'X': | 
| 149 | 
  | 
  | 
                        if (dt == DTxyze) { | 
| 150 | 
  | 
  | 
                                rop->preop.cmat[j*nc+comp] = 1.; | 
| 151 | 
  | 
  | 
                        } else if (nc == 3) { | 
| 152 | 
  | 
  | 
                                for (i = 3; i--; ) | 
| 153 | 
  | 
  | 
                                        rop->preop.cmat[j*nc+i] = | 
| 154 | 
  | 
  | 
                                                        rgb2xyzmat[comp][i]; | 
| 155 | 
  | 
  | 
                        } else if (comp == CIEY) | 
| 156 | 
  | 
  | 
                                sensrow(rop, j, scolor2photopic); | 
| 157 | 
  | 
  | 
                        else | 
| 158 | 
  | 
  | 
                                xyzrow(rop, j, comp); | 
| 159 | 
  | 
  | 
 | 
| 160 | 
  | 
  | 
                        for (i = nc*(dt != DTxyze); i--; ) | 
| 161 | 
  | 
  | 
                                rop->preop.cmat[j*nc+i] *= WHTEFFICACY; | 
| 162 | 
  | 
  | 
                        break; | 
| 163 | 
  | 
  | 
                case 'S': | 
| 164 | 
  | 
  | 
                        sensrow(rop, j, scolor2scotopic); | 
| 165 | 
  | 
  | 
                        for (i = nc; i--; ) | 
| 166 | 
  | 
  | 
                                rop->preop.cmat[j*nc+i] *= WHTSCOTOPIC; | 
| 167 | 
  | 
  | 
                        break; | 
| 168 | 
  | 
  | 
                case 'M': | 
| 169 | 
  | 
  | 
                        sensrow(rop, j, scolor2melanopic); | 
| 170 | 
  | 
  | 
                        for (i = nc; i--; ) | 
| 171 | 
  | 
  | 
                                rop->preop.cmat[j*nc+i] *= WHTMELANOPIC; | 
| 172 | 
  | 
  | 
                        break; | 
| 173 | 
  | 
  | 
                default: | 
| 174 | 
  | 
  | 
                        fprintf(stderr, "%s: -c '%c' unsupported\n", | 
| 175 | 
  | 
  | 
                                rop->inspec, rop->preop.csym[j]); | 
| 176 | 
  | 
  | 
                        return(-1); | 
| 177 | 
  | 
  | 
                } | 
| 178 | 
  | 
  | 
        } | 
| 179 | 
  | 
  | 
                                        /* return recommended output type */ | 
| 180 | 
  | 
  | 
        if (!strcmp(rop->preop.csym, "XYZ")) { | 
| 181 | 
  | 
  | 
                if (dt <= DTspec) | 
| 182 | 
  | 
  | 
                        return(DTxyze); | 
| 183 | 
  | 
  | 
        } else if (!strcmp(rop->preop.csym, "RGB")) { | 
| 184 | 
  | 
  | 
                if (dt <= DTspec) | 
| 185 | 
  | 
  | 
                        return(DTrgbe); | 
| 186 | 
  | 
  | 
        } | 
| 187 | 
  | 
  | 
        if ((nc > 3) & (dt <= DTspec)) | 
| 188 | 
  | 
  | 
                return(DTfloat);        /* probably not actual spectrum */ | 
| 189 | 
  | 
  | 
        return(0); | 
| 190 | 
  | 
  | 
} | 
| 191 | 
  | 
  | 
 | 
| 192 | 
greg | 
2.13 | 
/* Get matrix and perform unary operations */ | 
| 193 | 
greg | 
2.1 | 
static RMATRIX * | 
| 194 | 
greg | 
2.13 | 
loadop(ROPMAT *rop) | 
| 195 | 
greg | 
2.1 | 
{ | 
| 196 | 
greg | 
2.22 | 
        int     outtype = 0; | 
| 197 | 
greg | 
2.13 | 
        RMATRIX *mres; | 
| 198 | 
greg | 
2.22 | 
        int     i, j; | 
| 199 | 
greg | 
2.1 | 
 | 
| 200 | 
greg | 
2.13 | 
        if (loadmatrix(rop) < 0)                /* make sure we're loaded */ | 
| 201 | 
greg | 
2.1 | 
                return(NULL); | 
| 202 | 
greg | 
2.13 | 
 | 
| 203 | 
greg | 
2.22 | 
        if (rop->preop.csym[0] &&               /* symbolic transform? */ | 
| 204 | 
  | 
  | 
                        (outtype = checksymbolic(rop)) < 0) | 
| 205 | 
  | 
  | 
                goto failure; | 
| 206 | 
  | 
  | 
        if (rop->preop.clen > 0) {              /* apply component transform? */ | 
| 207 | 
  | 
  | 
                if (rop->preop.clen % rop->mtx->ncomp) { | 
| 208 | 
  | 
  | 
                        fprintf(stderr, "%s: -c must have N x %d coefficients\n", | 
| 209 | 
  | 
  | 
                                        rop->inspec, rop->mtx->ncomp); | 
| 210 | 
  | 
  | 
                        goto failure; | 
| 211 | 
  | 
  | 
                } | 
| 212 | 
  | 
  | 
                if (rop->preop.nsf > 0) {       /* scale transform, first */ | 
| 213 | 
  | 
  | 
                        if (rop->preop.nsf == 1) { | 
| 214 | 
  | 
  | 
                                for (i = rop->preop.clen; i--; ) | 
| 215 | 
  | 
  | 
                                        rop->preop.cmat[i] *= rop->preop.sca[0]; | 
| 216 | 
  | 
  | 
                        } else if (rop->preop.nsf != rop->mtx->ncomp) { | 
| 217 | 
  | 
  | 
                                fprintf(stderr, "%s: -s must have one or %d factors\n", | 
| 218 | 
  | 
  | 
                                                rop->inspec, rop->mtx->ncomp); | 
| 219 | 
  | 
  | 
                                goto failure; | 
| 220 | 
  | 
  | 
                        } else { | 
| 221 | 
  | 
  | 
                                for (j = rop->preop.clen/rop->preop.nsf; j--; ) | 
| 222 | 
  | 
  | 
                                        for (i = rop->preop.nsf; i--; ) | 
| 223 | 
  | 
  | 
                                                rop->preop.cmat[j*rop->preop.nsf+i] *= | 
| 224 | 
  | 
  | 
                                                                rop->preop.sca[i]; | 
| 225 | 
  | 
  | 
                        } | 
| 226 | 
  | 
  | 
                } | 
| 227 | 
  | 
  | 
                mres = rmx_transform(rop->mtx, rop->preop.clen/rop->mtx->ncomp, | 
| 228 | 
  | 
  | 
                                        rop->preop.cmat); | 
| 229 | 
  | 
  | 
                if (mres == NULL) { | 
| 230 | 
  | 
  | 
                        fprintf(stderr, "%s: matrix transform failed\n", | 
| 231 | 
  | 
  | 
                                                rop->inspec); | 
| 232 | 
greg | 
2.13 | 
                        goto failure; | 
| 233 | 
greg | 
2.1 | 
                } | 
| 234 | 
greg | 
2.22 | 
                if (verbose) | 
| 235 | 
  | 
  | 
                        fprintf(stderr, "%s: applied %d x %d transform%s\n", | 
| 236 | 
  | 
  | 
                                        rop->inspec, mres->ncomp, | 
| 237 | 
  | 
  | 
                                        rop->mtx->ncomp, | 
| 238 | 
  | 
  | 
                                        rop->preop.nsf ? " (* scalar)" : ""); | 
| 239 | 
  | 
  | 
                rop->preop.nsf = 0; | 
| 240 | 
  | 
  | 
                if ((mres->ncomp > 3) & (mres->dtype <= DTspec)) | 
| 241 | 
  | 
  | 
                        outtype = DTfloat;      /* probably not actual spectrum */ | 
| 242 | 
  | 
  | 
                rmx_free(rop->mtx); | 
| 243 | 
  | 
  | 
                rop->mtx = mres; | 
| 244 | 
  | 
  | 
        } | 
| 245 | 
  | 
  | 
        if (rop->preop.nsf > 0) {               /* apply scalar(s)? */ | 
| 246 | 
greg | 
2.13 | 
                if (rop->preop.nsf == 1) { | 
| 247 | 
  | 
  | 
                        for (i = rop->mtx->ncomp; --i; ) | 
| 248 | 
  | 
  | 
                                rop->preop.sca[i] = rop->preop.sca[0]; | 
| 249 | 
  | 
  | 
                } else if (rop->preop.nsf != rop->mtx->ncomp) { | 
| 250 | 
greg | 
2.1 | 
                        fprintf(stderr, "%s: -s must have one or %d factors\n", | 
| 251 | 
greg | 
2.13 | 
                                        rop->inspec, rop->mtx->ncomp); | 
| 252 | 
  | 
  | 
                        goto failure; | 
| 253 | 
greg | 
2.1 | 
                } | 
| 254 | 
greg | 
2.13 | 
                if (!rmx_scale(rop->mtx, rop->preop.sca)) { | 
| 255 | 
  | 
  | 
                        fputs(rop->inspec, stderr); | 
| 256 | 
greg | 
2.1 | 
                        fputs(": scalar operation failed\n", stderr); | 
| 257 | 
greg | 
2.13 | 
                        goto failure; | 
| 258 | 
greg | 
2.1 | 
                } | 
| 259 | 
  | 
  | 
                if (verbose) { | 
| 260 | 
greg | 
2.13 | 
                        fputs(rop->inspec, stderr); | 
| 261 | 
greg | 
2.1 | 
                        fputs(": applied scalar (", stderr); | 
| 262 | 
greg | 
2.13 | 
                        for (i = 0; i < rop->preop.nsf; i++) | 
| 263 | 
  | 
  | 
                                fprintf(stderr, " %f", rop->preop.sca[i]); | 
| 264 | 
greg | 
2.1 | 
                        fputs(" )\n", stderr); | 
| 265 | 
  | 
  | 
                } | 
| 266 | 
  | 
  | 
        } | 
| 267 | 
greg | 
2.22 | 
        if (rop->preop.transpose) {             /* transpose matrix? */ | 
| 268 | 
greg | 
2.13 | 
                mres = rmx_transpose(rop->mtx); | 
| 269 | 
  | 
  | 
                if (mres == NULL) { | 
| 270 | 
  | 
  | 
                        fputs(rop->inspec, stderr); | 
| 271 | 
greg | 
2.12 | 
                        fputs(": transpose failed\n", stderr); | 
| 272 | 
greg | 
2.13 | 
                        goto failure; | 
| 273 | 
greg | 
2.12 | 
                } | 
| 274 | 
  | 
  | 
                if (verbose) { | 
| 275 | 
greg | 
2.13 | 
                        fputs(rop->inspec, stderr); | 
| 276 | 
greg | 
2.12 | 
                        fputs(": transposed rows and columns\n", stderr); | 
| 277 | 
  | 
  | 
                } | 
| 278 | 
greg | 
2.13 | 
                rmx_free(rop->mtx); | 
| 279 | 
  | 
  | 
                rop->mtx = mres; | 
| 280 | 
  | 
  | 
        } | 
| 281 | 
  | 
  | 
        mres = rop->mtx; | 
| 282 | 
  | 
  | 
        rop->mtx = NULL; | 
| 283 | 
greg | 
2.22 | 
        if (outtype) | 
| 284 | 
  | 
  | 
                mres->dtype = outtype; | 
| 285 | 
greg | 
2.13 | 
        return(mres); | 
| 286 | 
  | 
  | 
failure: | 
| 287 | 
  | 
  | 
        rmx_free(rop->mtx); | 
| 288 | 
  | 
  | 
        return(rop->mtx = NULL); | 
| 289 | 
  | 
  | 
} | 
| 290 | 
  | 
  | 
 | 
| 291 | 
  | 
  | 
/* Execute binary operation, free matrix arguments and return new result */ | 
| 292 | 
  | 
  | 
static RMATRIX * | 
| 293 | 
  | 
  | 
binaryop(const char *inspec, RMATRIX *mleft, int op, RMATRIX *mright) | 
| 294 | 
  | 
  | 
{ | 
| 295 | 
  | 
  | 
        RMATRIX *mres = NULL; | 
| 296 | 
  | 
  | 
        int     i; | 
| 297 | 
  | 
  | 
 | 
| 298 | 
  | 
  | 
        if ((mleft == NULL) | (mright == NULL)) | 
| 299 | 
  | 
  | 
                return(NULL); | 
| 300 | 
  | 
  | 
        switch (op) { | 
| 301 | 
  | 
  | 
        case '.':                       /* concatenate */ | 
| 302 | 
greg | 
2.16 | 
                if (mleft->ncomp != mright->ncomp) { | 
| 303 | 
  | 
  | 
                        fputs(inspec, stderr); | 
| 304 | 
  | 
  | 
                        fputs(": # components do not match\n", stderr); | 
| 305 | 
  | 
  | 
                } else if (mleft->ncols != mright->nrows) { | 
| 306 | 
  | 
  | 
                        fputs(inspec, stderr); | 
| 307 | 
  | 
  | 
                        fputs(": mismatched dimensions\n", | 
| 308 | 
  | 
  | 
                                        stderr); | 
| 309 | 
  | 
  | 
                } else | 
| 310 | 
  | 
  | 
                        mres = rmx_multiply(mleft, mright); | 
| 311 | 
greg | 
2.13 | 
                rmx_free(mleft); | 
| 312 | 
greg | 
2.12 | 
                rmx_free(mright); | 
| 313 | 
greg | 
2.1 | 
                if (mres == NULL) { | 
| 314 | 
greg | 
2.13 | 
                        fputs(inspec, stderr); | 
| 315 | 
greg | 
2.16 | 
                        fputs(": concatenation failed\n", stderr); | 
| 316 | 
greg | 
2.1 | 
                        return(NULL); | 
| 317 | 
  | 
  | 
                } | 
| 318 | 
  | 
  | 
                if (verbose) { | 
| 319 | 
greg | 
2.13 | 
                        fputs(inspec, stderr); | 
| 320 | 
greg | 
2.1 | 
                        fputs(": concatenated matrix\n", stderr); | 
| 321 | 
  | 
  | 
                } | 
| 322 | 
greg | 
2.13 | 
                break; | 
| 323 | 
  | 
  | 
        case '+': | 
| 324 | 
  | 
  | 
                if (!rmx_sum(mleft, mright, NULL)) { | 
| 325 | 
  | 
  | 
                        fputs(inspec, stderr); | 
| 326 | 
greg | 
2.1 | 
                        fputs(": matrix sum failed\n", stderr); | 
| 327 | 
greg | 
2.13 | 
                        rmx_free(mleft); | 
| 328 | 
greg | 
2.1 | 
                        rmx_free(mright); | 
| 329 | 
  | 
  | 
                        return(NULL); | 
| 330 | 
  | 
  | 
                } | 
| 331 | 
  | 
  | 
                if (verbose) { | 
| 332 | 
greg | 
2.13 | 
                        fputs(inspec, stderr); | 
| 333 | 
greg | 
2.1 | 
                        fputs(": added in matrix\n", stderr); | 
| 334 | 
  | 
  | 
                } | 
| 335 | 
  | 
  | 
                rmx_free(mright); | 
| 336 | 
greg | 
2.13 | 
                mres = mleft; | 
| 337 | 
  | 
  | 
                break; | 
| 338 | 
  | 
  | 
        case '*': | 
| 339 | 
  | 
  | 
        case '/': { | 
| 340 | 
  | 
  | 
                const char *    tnam = (op == '/') ? | 
| 341 | 
greg | 
2.11 | 
                                        "division" : "multiplication"; | 
| 342 | 
  | 
  | 
                errno = 0; | 
| 343 | 
greg | 
2.13 | 
                if (!rmx_elemult(mleft, mright, (op == '/'))) { | 
| 344 | 
greg | 
2.11 | 
                        fprintf(stderr, "%s: element-wise %s failed\n", | 
| 345 | 
greg | 
2.13 | 
                                        inspec, tnam); | 
| 346 | 
  | 
  | 
                        rmx_free(mleft); | 
| 347 | 
greg | 
2.11 | 
                        rmx_free(mright); | 
| 348 | 
  | 
  | 
                        return(NULL); | 
| 349 | 
  | 
  | 
                } | 
| 350 | 
  | 
  | 
                if (errno) | 
| 351 | 
  | 
  | 
                        fprintf(stderr, | 
| 352 | 
  | 
  | 
                                "%s: warning - error during element-wise %s\n", | 
| 353 | 
greg | 
2.13 | 
                                        inspec, tnam); | 
| 354 | 
greg | 
2.11 | 
                else if (verbose) | 
| 355 | 
greg | 
2.13 | 
                        fprintf(stderr, "%s: element-wise %s\n", inspec, tnam); | 
| 356 | 
greg | 
2.11 | 
                rmx_free(mright); | 
| 357 | 
greg | 
2.13 | 
                mres = mleft; | 
| 358 | 
  | 
  | 
                } break; | 
| 359 | 
  | 
  | 
        default: | 
| 360 | 
  | 
  | 
                fprintf(stderr, "%s: unknown operation '%c'\n", inspec, op); | 
| 361 | 
  | 
  | 
                rmx_free(mleft); | 
| 362 | 
greg | 
2.1 | 
                rmx_free(mright); | 
| 363 | 
  | 
  | 
                return(NULL); | 
| 364 | 
  | 
  | 
        } | 
| 365 | 
greg | 
2.13 | 
        return(mres); | 
| 366 | 
  | 
  | 
} | 
| 367 | 
  | 
  | 
 | 
| 368 | 
  | 
  | 
/* Perform matrix operations from left to right */ | 
| 369 | 
  | 
  | 
static RMATRIX * | 
| 370 | 
  | 
  | 
op_left2right(ROPMAT *mop) | 
| 371 | 
  | 
  | 
{ | 
| 372 | 
  | 
  | 
        RMATRIX *mleft = loadop(mop); | 
| 373 | 
  | 
  | 
 | 
| 374 | 
  | 
  | 
        while (mop->binop) { | 
| 375 | 
  | 
  | 
                if (mleft == NULL) | 
| 376 | 
  | 
  | 
                        break; | 
| 377 | 
  | 
  | 
                mleft = binaryop(mop[1].inspec, | 
| 378 | 
  | 
  | 
                                mleft, mop->binop, loadop(mop+1)); | 
| 379 | 
  | 
  | 
                mop++; | 
| 380 | 
  | 
  | 
        } | 
| 381 | 
greg | 
2.1 | 
        return(mleft); | 
| 382 | 
  | 
  | 
} | 
| 383 | 
  | 
  | 
 | 
| 384 | 
greg | 
2.13 | 
/* Perform matrix operations from right to left */ | 
| 385 | 
  | 
  | 
static RMATRIX * | 
| 386 | 
  | 
  | 
op_right2left(ROPMAT *mop) | 
| 387 | 
  | 
  | 
{ | 
| 388 | 
  | 
  | 
        RMATRIX *mright; | 
| 389 | 
  | 
  | 
        int     rpos = 0; | 
| 390 | 
  | 
  | 
                                        /* find end of list */ | 
| 391 | 
  | 
  | 
        while (mop[rpos].binop) | 
| 392 | 
greg | 
2.14 | 
                if (mop[rpos++].binop != '.') { | 
| 393 | 
  | 
  | 
                        fputs( | 
| 394 | 
  | 
  | 
                "Right-to-left evaluation only for matrix multiplication!\n", | 
| 395 | 
  | 
  | 
                                        stderr); | 
| 396 | 
  | 
  | 
                        return(NULL); | 
| 397 | 
  | 
  | 
                } | 
| 398 | 
greg | 
2.13 | 
        mright = loadop(mop+rpos); | 
| 399 | 
  | 
  | 
        while (rpos-- > 0) { | 
| 400 | 
  | 
  | 
                if (mright == NULL) | 
| 401 | 
  | 
  | 
                        break; | 
| 402 | 
greg | 
2.20 | 
                mright = binaryop(mop[rpos+1].inspec, | 
| 403 | 
greg | 
2.13 | 
                                loadop(mop+rpos), mop[rpos].binop, mright); | 
| 404 | 
  | 
  | 
        } | 
| 405 | 
  | 
  | 
        return(mright); | 
| 406 | 
  | 
  | 
} | 
| 407 | 
  | 
  | 
 | 
| 408 | 
  | 
  | 
#define t_nrows(mop)    ((mop)->preop.transpose ? (mop)->mtx->ncols \ | 
| 409 | 
  | 
  | 
                                                : (mop)->mtx->nrows) | 
| 410 | 
  | 
  | 
#define t_ncols(mop)    ((mop)->preop.transpose ? (mop)->mtx->nrows \ | 
| 411 | 
  | 
  | 
                                                : (mop)->mtx->ncols) | 
| 412 | 
  | 
  | 
 | 
| 413 | 
  | 
  | 
/* Should we prefer concatenating from rightmost matrix towards left? */ | 
| 414 | 
  | 
  | 
static int | 
| 415 | 
  | 
  | 
prefer_right2left(ROPMAT *mop) | 
| 416 | 
  | 
  | 
{ | 
| 417 | 
  | 
  | 
        int     mri = 0; | 
| 418 | 
  | 
  | 
 | 
| 419 | 
  | 
  | 
        while (mop[mri].binop)          /* find rightmost matrix */ | 
| 420 | 
  | 
  | 
                if (mop[mri++].binop != '.') | 
| 421 | 
  | 
  | 
                        return(0);      /* pre-empt reversal for other ops */ | 
| 422 | 
  | 
  | 
 | 
| 423 | 
  | 
  | 
        if (mri <= 1) | 
| 424 | 
  | 
  | 
                return(0);              /* won't matter */ | 
| 425 | 
  | 
  | 
 | 
| 426 | 
  | 
  | 
        if (loadmatrix(mop+mri) < 0)    /* load rightmost cat */ | 
| 427 | 
  | 
  | 
                return(1);              /* fail will bail in a moment */ | 
| 428 | 
  | 
  | 
 | 
| 429 | 
  | 
  | 
        if (t_ncols(mop+mri) == 1) | 
| 430 | 
  | 
  | 
                return(1);              /* definitely better R->L */ | 
| 431 | 
  | 
  | 
 | 
| 432 | 
  | 
  | 
        if (t_ncols(mop+mri) >= t_nrows(mop+mri)) | 
| 433 | 
  | 
  | 
                return(0);              /* ...probably worse */ | 
| 434 | 
  | 
  | 
 | 
| 435 | 
  | 
  | 
        if (loadmatrix(mop) < 0)        /* load leftmost */ | 
| 436 | 
  | 
  | 
                return(0);              /* fail will bail in a moment */ | 
| 437 | 
  | 
  | 
 | 
| 438 | 
  | 
  | 
        return(t_ncols(mop+mri) < t_nrows(mop)); | 
| 439 | 
  | 
  | 
} | 
| 440 | 
  | 
  | 
 | 
| 441 | 
greg | 
2.1 | 
static int | 
| 442 | 
  | 
  | 
get_factors(double da[], int n, char *av[]) | 
| 443 | 
  | 
  | 
{ | 
| 444 | 
  | 
  | 
        int     ac; | 
| 445 | 
  | 
  | 
 | 
| 446 | 
  | 
  | 
        for (ac = 0; ac < n && isflt(av[ac]); ac++) | 
| 447 | 
  | 
  | 
                da[ac] = atof(av[ac]); | 
| 448 | 
  | 
  | 
        return(ac); | 
| 449 | 
  | 
  | 
} | 
| 450 | 
  | 
  | 
 | 
| 451 | 
greg | 
2.13 | 
static ROPMAT * | 
| 452 | 
  | 
  | 
grow_moparray(ROPMAT *mop, int n2alloc) | 
| 453 | 
  | 
  | 
{ | 
| 454 | 
  | 
  | 
        int     nmats = 0; | 
| 455 | 
  | 
  | 
 | 
| 456 | 
  | 
  | 
        while (mop[nmats++].binop) | 
| 457 | 
  | 
  | 
                ; | 
| 458 | 
  | 
  | 
        mop = (ROPMAT *)realloc(mop, n2alloc*sizeof(ROPMAT)); | 
| 459 | 
  | 
  | 
        if (mop == NULL) { | 
| 460 | 
  | 
  | 
                fputs("Out of memory in grow_moparray()\n", stderr); | 
| 461 | 
  | 
  | 
                exit(1); | 
| 462 | 
  | 
  | 
        } | 
| 463 | 
  | 
  | 
        if (n2alloc > nmats) | 
| 464 | 
  | 
  | 
                memset(mop+nmats, 0, (n2alloc-nmats)*sizeof(ROPMAT)); | 
| 465 | 
  | 
  | 
        return(mop); | 
| 466 | 
  | 
  | 
} | 
| 467 | 
  | 
  | 
 | 
| 468 | 
greg | 
2.1 | 
/* Load one or more matrices and operate on them, sending results to stdout */ | 
| 469 | 
  | 
  | 
int | 
| 470 | 
  | 
  | 
main(int argc, char *argv[]) | 
| 471 | 
  | 
  | 
{ | 
| 472 | 
greg | 
2.7 | 
        int     outfmt = DTfromHeader; | 
| 473 | 
greg | 
2.13 | 
        int     nall = 2; | 
| 474 | 
  | 
  | 
        ROPMAT  *mop = (ROPMAT *)calloc(nall, sizeof(ROPMAT)); | 
| 475 | 
  | 
  | 
        int     nmats = 0; | 
| 476 | 
greg | 
2.1 | 
        RMATRIX *mres = NULL; | 
| 477 | 
greg | 
2.13 | 
        int     stdin_used = 0; | 
| 478 | 
greg | 
2.1 | 
        int     i; | 
| 479 | 
  | 
  | 
                                        /* get options and arguments */ | 
| 480 | 
greg | 
2.13 | 
        for (i = 1; i < argc; i++) { | 
| 481 | 
greg | 
2.11 | 
                if (argv[i][0] && !argv[i][1] && | 
| 482 | 
greg | 
2.13 | 
                                strchr(".+*/", argv[i][0]) != NULL) { | 
| 483 | 
greg | 
2.15 | 
                        if (!nmats || mop[nmats-1].binop) { | 
| 484 | 
greg | 
2.14 | 
                                fprintf(stderr, | 
| 485 | 
greg | 
2.15 | 
                        "%s: missing matrix argument before '%c' operation\n", | 
| 486 | 
greg | 
2.14 | 
                                                argv[0], argv[i][0]); | 
| 487 | 
greg | 
2.13 | 
                                return(1); | 
| 488 | 
  | 
  | 
                        } | 
| 489 | 
greg | 
2.15 | 
                        mop[nmats-1].binop = argv[i][0]; | 
| 490 | 
greg | 
2.1 | 
                } else if (argv[i][0] != '-' || !argv[i][1]) { | 
| 491 | 
greg | 
2.13 | 
                        if (argv[i][0] == '-') { | 
| 492 | 
  | 
  | 
                                if (stdin_used++) { | 
| 493 | 
  | 
  | 
                                        fprintf(stderr, | 
| 494 | 
  | 
  | 
                        "%s: standard input used for more than one matrix\n", | 
| 495 | 
  | 
  | 
                                                argv[0]); | 
| 496 | 
  | 
  | 
                                        return(1); | 
| 497 | 
  | 
  | 
                                } | 
| 498 | 
  | 
  | 
                                mop[nmats].inspec = stdin_name; | 
| 499 | 
  | 
  | 
                        } else | 
| 500 | 
  | 
  | 
                                mop[nmats].inspec = argv[i]; | 
| 501 | 
  | 
  | 
                        if (nmats > 0 && !mop[nmats-1].binop) | 
| 502 | 
  | 
  | 
                                mop[nmats-1].binop = '.'; | 
| 503 | 
  | 
  | 
                        nmats++; | 
| 504 | 
greg | 
2.1 | 
                } else { | 
| 505 | 
  | 
  | 
                        int     n = argc-1 - i; | 
| 506 | 
  | 
  | 
                        switch (argv[i][1]) {   /* get option */ | 
| 507 | 
  | 
  | 
                        case 'v': | 
| 508 | 
greg | 
2.14 | 
                                verbose++; | 
| 509 | 
greg | 
2.1 | 
                                break; | 
| 510 | 
  | 
  | 
                        case 'f': | 
| 511 | 
  | 
  | 
                                switch (argv[i][2]) { | 
| 512 | 
  | 
  | 
                                case 'd': | 
| 513 | 
  | 
  | 
                                        outfmt = DTdouble; | 
| 514 | 
  | 
  | 
                                        break; | 
| 515 | 
  | 
  | 
                                case 'f': | 
| 516 | 
  | 
  | 
                                        outfmt = DTfloat; | 
| 517 | 
  | 
  | 
                                        break; | 
| 518 | 
  | 
  | 
                                case 'a': | 
| 519 | 
  | 
  | 
                                        outfmt = DTascii; | 
| 520 | 
  | 
  | 
                                        break; | 
| 521 | 
  | 
  | 
                                case 'c': | 
| 522 | 
  | 
  | 
                                        outfmt = DTrgbe; | 
| 523 | 
  | 
  | 
                                        break; | 
| 524 | 
  | 
  | 
                                default: | 
| 525 | 
  | 
  | 
                                        goto userr; | 
| 526 | 
  | 
  | 
                                } | 
| 527 | 
  | 
  | 
                                break; | 
| 528 | 
  | 
  | 
                        case 't': | 
| 529 | 
greg | 
2.13 | 
                                mop[nmats].preop.transpose = 1; | 
| 530 | 
greg | 
2.1 | 
                                break; | 
| 531 | 
  | 
  | 
                        case 's': | 
| 532 | 
  | 
  | 
                                if (n > MAXCOMP) n = MAXCOMP; | 
| 533 | 
greg | 
2.13 | 
                                i += mop[nmats].preop.nsf = | 
| 534 | 
  | 
  | 
                                        get_factors(mop[nmats].preop.sca, | 
| 535 | 
  | 
  | 
                                                        n, argv+i+1); | 
| 536 | 
greg | 
2.22 | 
                                if (mop[nmats].preop.nsf <= 0) { | 
| 537 | 
  | 
  | 
                                        fprintf(stderr, "%s: -s missing arguments\n", | 
| 538 | 
  | 
  | 
                                                        argv[0]); | 
| 539 | 
  | 
  | 
                                        goto userr; | 
| 540 | 
  | 
  | 
                                } | 
| 541 | 
greg | 
2.1 | 
                                break; | 
| 542 | 
  | 
  | 
                        case 'c': | 
| 543 | 
greg | 
2.22 | 
                                if (n && isupper(argv[i+1][0])) { | 
| 544 | 
  | 
  | 
                                        strlcpy(mop[nmats].preop.csym, | 
| 545 | 
  | 
  | 
                                                argv[++i], | 
| 546 | 
  | 
  | 
                                                sizeof(mop[0].preop.csym)); | 
| 547 | 
  | 
  | 
                                        mop[nmats].preop.clen = 0; | 
| 548 | 
  | 
  | 
                                        break; | 
| 549 | 
  | 
  | 
                                } | 
| 550 | 
greg | 
2.1 | 
                                if (n > MAXCOMP*MAXCOMP) n = MAXCOMP*MAXCOMP; | 
| 551 | 
greg | 
2.13 | 
                                i += mop[nmats].preop.clen = | 
| 552 | 
  | 
  | 
                                        get_factors(mop[nmats].preop.cmat, | 
| 553 | 
  | 
  | 
                                                        n, argv+i+1); | 
| 554 | 
greg | 
2.22 | 
                                if (mop[nmats].preop.clen <= 0) { | 
| 555 | 
  | 
  | 
                                        fprintf(stderr, "%s: -c missing arguments\n", | 
| 556 | 
  | 
  | 
                                                        argv[0]); | 
| 557 | 
  | 
  | 
                                        goto userr; | 
| 558 | 
  | 
  | 
                                } | 
| 559 | 
  | 
  | 
                                mop[nmats].preop.csym[0] = '\0'; | 
| 560 | 
greg | 
2.1 | 
                                break; | 
| 561 | 
greg | 
2.18 | 
                        case 'r': | 
| 562 | 
  | 
  | 
                                if (argv[i][2] == 'f') | 
| 563 | 
  | 
  | 
                                        mop[nmats].rmp = RMPreflF; | 
| 564 | 
  | 
  | 
                                else if (argv[i][2] == 'b') | 
| 565 | 
  | 
  | 
                                        mop[nmats].rmp = RMPreflB; | 
| 566 | 
  | 
  | 
                                else | 
| 567 | 
  | 
  | 
                                        goto userr; | 
| 568 | 
  | 
  | 
                                break; | 
| 569 | 
greg | 
2.1 | 
                        default: | 
| 570 | 
  | 
  | 
                                fprintf(stderr, "%s: unknown operation '%s'\n", | 
| 571 | 
  | 
  | 
                                                argv[0], argv[i]); | 
| 572 | 
  | 
  | 
                                goto userr; | 
| 573 | 
  | 
  | 
                        } | 
| 574 | 
  | 
  | 
                } | 
| 575 | 
greg | 
2.14 | 
                if (nmats >= nall) | 
| 576 | 
  | 
  | 
                        mop = grow_moparray(mop, nall += 2); | 
| 577 | 
greg | 
2.13 | 
        } | 
| 578 | 
  | 
  | 
        if (mop[0].inspec == NULL)      /* nothing to do? */ | 
| 579 | 
greg | 
2.1 | 
                goto userr; | 
| 580 | 
greg | 
2.15 | 
        if (mop[nmats-1].binop) { | 
| 581 | 
  | 
  | 
                fprintf(stderr, | 
| 582 | 
  | 
  | 
                        "%s: missing matrix argument after '%c' operation\n", | 
| 583 | 
  | 
  | 
                                argv[0], mop[nmats-1].binop); | 
| 584 | 
  | 
  | 
                return(1); | 
| 585 | 
  | 
  | 
        } | 
| 586 | 
greg | 
2.13 | 
                                        /* favor quicker concatenation */ | 
| 587 | 
greg | 
2.14 | 
        mop[nmats].mtx = prefer_right2left(mop) ? op_right2left(mop) | 
| 588 | 
  | 
  | 
                                                : op_left2right(mop); | 
| 589 | 
  | 
  | 
        if (mop[nmats].mtx == NULL) | 
| 590 | 
  | 
  | 
                return(1); | 
| 591 | 
  | 
  | 
                                        /* apply trailing unary operations */ | 
| 592 | 
  | 
  | 
        mop[nmats].inspec = "trailing_ops"; | 
| 593 | 
  | 
  | 
        mres = loadop(mop+nmats); | 
| 594 | 
  | 
  | 
        if (mres == NULL) | 
| 595 | 
greg | 
2.13 | 
                return(1); | 
| 596 | 
greg | 
2.22 | 
        if (outfmt == DTfromHeader)     /* check data type */ | 
| 597 | 
greg | 
2.6 | 
                outfmt = mres->dtype; | 
| 598 | 
greg | 
2.22 | 
        if (outfmt == DTrgbe) { | 
| 599 | 
  | 
  | 
                if (mres->ncomp > 3) | 
| 600 | 
  | 
  | 
                        outfmt = DTspec; | 
| 601 | 
  | 
  | 
                else if (mres->dtype == DTxyze) | 
| 602 | 
  | 
  | 
                        outfmt = DTxyze; | 
| 603 | 
  | 
  | 
        } | 
| 604 | 
  | 
  | 
        if (outfmt != DTascii)          /* write result to stdout */ | 
| 605 | 
greg | 
2.10 | 
                SET_FILE_BINARY(stdout); | 
| 606 | 
greg | 
2.1 | 
        newheader("RADIANCE", stdout); | 
| 607 | 
  | 
  | 
        printargs(argc, argv, stdout); | 
| 608 | 
greg | 
2.5 | 
        if (!rmx_write(mres, outfmt, stdout)) { | 
| 609 | 
greg | 
2.1 | 
                fprintf(stderr, "%s: error writing result matrix\n", argv[0]); | 
| 610 | 
  | 
  | 
                return(1); | 
| 611 | 
  | 
  | 
        } | 
| 612 | 
greg | 
2.13 | 
        /* rmx_free(mres); free(mop); */ | 
| 613 | 
greg | 
2.1 | 
        return(0); | 
| 614 | 
  | 
  | 
userr: | 
| 615 | 
  | 
  | 
        fprintf(stderr, | 
| 616 | 
greg | 
2.19 | 
        "Usage: %s [-v][-f[adfc][-t][-s sf .. | -c ce ..][-rf|-rb] m1 [.+*/] .. > mres\n", | 
| 617 | 
greg | 
2.1 | 
                        argv[0]); | 
| 618 | 
  | 
  | 
        return(1); | 
| 619 | 
  | 
  | 
} |