ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/Development/ray/src/util/pvsum.c
(Generate patch)

Comparing ray/src/util/pvsum.c (file contents):
Revision 2.8 by greg, Fri Oct 24 22:41:10 2025 UTC vs.
Revision 2.9 by greg, Wed Oct 29 02:48:50 2025 UTC

# Line 35 | Line 35 | char   pixasp[48] = "";                /* PIXASPECT= line from header
35   int     gargc;                          /* global argc */
36   char    **gargv;                        /* global argv */
37  
38 < RMATRIX *cmtx = NULL;                   /* coefficient matrix */
38 > RMATRIX *cmtx;                          /* coefficient matrix */
39 > int     row0, rowN;                     /* rows for current pass */
40  
41   /* does the given spec contain integer format? */
42   int
# Line 125 | Line 126 | get_iotypes(void)
126                  long    data_start = ftell(fp);         /* make sure input flat */
127                  off_t   dend = lseek(fileno(fp), 0, SEEK_END);
128                  if (dend < data_start + 4L*xres*yres) {
129 <                        fputs("Warning - multi-processing requires flat input files\n",
130 <                                        stderr);
129 >                        fprintf(stderr, "%s: warning - multi-processing requires flat input files\n",
130 >                                        gargv[0]);
131                          nprocs = 1;
132                  }
133          }
# Line 143 | Line 144 | get_iotypes(void)
144                  rmx_free(cmtx);
145                  cmtx = nmtx;
146          } else if (cmtx->ncomp != ncomp) {
147 <                fprintf(stderr, "operation %s needs %d components, has %d\n",
148 <                                cmtx->ncols == 1 ? "vector" : "matrix",
147 >                fprintf(stderr, "%s: operation %s needs %d components, has %d\n",
148 >                                gargv[0], cmtx->ncols == 1 ? "vector" : "matrix",
149                                  ncomp, cmtx->ncomp);
150                  return(0);
151          }
152          if ((in_type != DTrgbe) & (in_type != DTxyze) & (in_type != DTspec) &
153                          (in_type != DTfloat)) {
154 <                fprintf(stderr, "%s unsupported input data type: %s\n",
154 >                fprintf(stderr, "%s: unsupported input data type '%s'\n",
155                                  fbuf, cm_fmt_id[in_type]);
156                  return(0);
157          }
# Line 162 | Line 163 | get_iotypes(void)
163          return(1);
164   }
165  
166 + struct hdata {
167 +        int     xr, yr;         /* resolution */
168 +        int     fno;            /* frame # */
169 +        char    fmt[MAXFMTLEN]; /* format */
170 + };
171 +
172   /* check subsequent headers match initial file */
173   int
174   checkline(char *s, void *p)
175   {
176          static int      exposWarned = 0;
177 <        int             *xyres = (int *)p;
171 <        char            fmt[MAXFMTLEN];
177 >        struct hdata    *hp = (struct hdata *)p;
178  
179          if (!strncmp(s, "NCOLS=", 6)) {
180 <                xyres[0] = atoi(s+6);
181 <                if (xyres[0] <= 0)
180 >                hp->xr = atoi(s+6);
181 >                if (hp->xr <= 0)
182                          return(-1);
183                  return(1);
184          }
185          if (!strncmp(s, "NROWS=", 6)) {
186 <                xyres[1] = atoi(s+6);
187 <                if (xyres[1] <= 0)
186 >                hp->yr = atoi(s+6);
187 >                if (hp->yr <= 0)
188                          return(-1);
189                  return(1);
190          }
191 +        if (!strncmp(s, "FRAME=", 6)) {
192 +                hp->fno = atoi(s+6);
193 +                return(1);
194 +        }
195          if (isncomp(s)) {
196                  if (ncompval(s) != ncomp)
197                          return(-1);
# Line 189 | Line 199 | checkline(char *s, void *p)
199          }
200          if (isexpos(s)) {
201                  if (!exposWarned && fabs(1. - exposval(s)) > 0.04) {
202 <                        fputs("Warning - ignoring EXPOSURE setting(s)\n",
203 <                                        stderr);
202 >                        fprintf(stderr, "%s: warning - ignoring EXPOSURE setting(s)\n",
203 >                                        gargv[0]);
204                          exposWarned++;
205                  }
206                  return(1);
207          }
208 <        if (formatval(fmt, s)) {
199 <                if (strcmp(fmt, cm_fmt_id[in_type]))
200 <                        return(-1);
208 >        if (formatval(hp->fmt, s))
209                  return(1);
210 <        }
210 >
211          return(0);
212   }
213  
214 < /* open and check input file */
214 > /* open and check input/output file, read/write mode if fno >= 0 */
215   FILE *
216 < open_input(char *fname)
216 > open_iofile(char *fname, int fno)
217   {
218 <        int     xyres[2];
219 <        FILE    *fp = fopen(fname, "rb");
218 >        struct hdata    hd;
219 >        FILE            *fp = fopen(fname, fno>=0 ? "r+b" : "rb");
220  
221          if (!fp) {
222 <                fprintf(stderr, "%s: cannot open for reading\n", fname);
222 >                fprintf(stderr, "%s: cannot open for reading%s\n",
223 >                                fname, fno>=0 ? "/writing" : "");
224                  return(NULL);
225          }
226 <        xyres[0] = xyres[1] = 0;
227 <        if (getheader(fp, checkline, xyres) < 0) {
226 >        hd.xr = hd.yr = 0;
227 >        hd.fno = -1;
228 >        hd.fmt[0] = '\0';
229 >        if (getheader(fp, checkline, &hd) < 0) {
230                  fprintf(stderr, "%s: bad/inconsistent header\n", fname);
231                  fclose(fp);
232                  return(NULL);
233          }
234 <        if ((xyres[0] <= 0) | (xyres[1] <= 0) &&
235 <                        !fscnresolu(&xyres[0], &xyres[1], fp)) {
234 >        if ((hd.fno >= 0) & (fno >= 0) & (hd.fno != fno)) {
235 >                fprintf(stderr, "%s: unexpected frame number (%d != %d)\n",
236 >                                fname, hd.fno, fno);
237 >                fclose(fp);
238 >                return(NULL);
239 >        }
240 >        if (strcmp(hd.fmt, cm_fmt_id[fno>=0 ? out_type : in_type])) {
241 >                fprintf(stderr, "%s: wrong format\n", fname);
242 >                fclose(fp);
243 >                return(NULL);
244 >        }
245 >        if ((hd.xr <= 0) | (hd.yr <= 0) &&
246 >                        !fscnresolu(&hd.xr, &hd.yr, fp)) {
247                  fprintf(stderr, "%s: missing resolution\n", fname);
248                  fclose(fp);
249                  return(NULL);
250          }
251 <        if ((xyres[0] != xres) | (xyres[1] != yres)) {
251 >        if ((hd.xr != xres) | (hd.yr != yres)) {
252                  fprintf(stderr, "%s: mismatched resolution\n", fname);
253                  fclose(fp);
254                  return(NULL);
# Line 234 | Line 256 | open_input(char *fname)
256          return(fp);
257   }
258  
259 + /* read in previous pixel data from output and rewind to data start */
260 + int
261 + reload_data(float *osum, FILE *fp)
262 + {
263 +        long    dstart;
264 +
265 +        if (!osum | !fp)
266 +                return(0);
267 +        if ((dstart = ftell(fp)) < 0) {
268 +                fprintf(stderr, "%s: ftell() error in reload_data()\n",
269 +                                gargv[0]);
270 +                return(0);
271 +        }
272 +        if (in_type == DTfloat) {
273 +                if (fread(osum, sizeof(float)*ncomp, (size_t)xres*yres, fp) !=
274 +                                (size_t)xres*yres) {
275 +                        fprintf(stderr, "%s: fread() error\n", gargv[0]);
276 +                        return(0);
277 +                }
278 +        } else {
279 +                int     y;
280 +                for (y = 0; y < yres; y++, osum += ncomp*xres)
281 +                        if (freadsscan(osum, ncomp, xres, fp) < 0) {
282 +                                fprintf(stderr, "%s: freadsscan() error\n", gargv[0]);
283 +                                return(0);
284 +                        }
285 +        }
286 +        if (fseek(fp, dstart, SEEK_SET) < 0) {
287 +                fprintf(stderr, "%s: fseek() error in reload_data()\n",
288 +                                gargv[0]);
289 +                return(0);
290 +        }
291 +        return(1);
292 + }
293 +
294   /* open output file or command (if !NULL) and write info header */
295   FILE *
296   open_output(char *ospec, int fno)
# Line 245 | Line 302 | open_output(char *ospec, int fno)
302                  fp = stdout;
303          } else if (ospec[0] == '!') {
304                  if (!(fp = popen(ospec+1, "w"))) {
305 <                        fprintf(stderr, "Cannot start: %s\n", ospec);
305 >                        fprintf(stderr, "%s: cannot start: %s\n", gargv[0], ospec);
306                          return(NULL);
307                  }
308          } else if (!(fp = fopen(ospec, "w"))) {
# Line 292 | Line 349 | open_output(char *ospec, int fno)
349                  fprtresolu(xres, yres, fp);
350                  break;
351          default:
352 <                fputs("Unsupported output type!\n", stderr);
352 >                fprintf(stderr, "%s: unsupported output type!\n", gargv[0]);
353                  return(NULL);
354          }
355          if (fflush(fp) < 0) {
# Line 313 | Line 370 | solo_process(void)
370          int     c;
371  
372          if (!osum | !iscan) {
373 <                fprintf(stderr, "Cannot allocate %dx%d %d-component accumulator\n",
374 <                                xres, yres, ncomp);
373 >                fprintf(stderr, "%s: annot allocate %dx%d %d-component accumulator\n",
374 >                                gargv[0], xres, yres, ncomp);
375                  return(0);
376          }
377          if (sizeof(float) != sizeof(COLORV)) {
378 <                fputs("Code Error 1 in solo_process()\n", stderr);
378 >                fprintf(stderr, "%s: Code Error 1 in solo_process()\n", gargv[0]);
379                  return(0);
380          }
381 <        for (c = 0; c < cmtx->ncols; c++) {     /* run through each column/output */
381 >                                        /* run through each column/output */
382 >        for (c = 0; c < cmtx->ncols; c++) {
383 >                int     rc = rowN - row0;
384                  FILE    *fout;
385                  int     y;
386 <                int     rc = cmtx->nrows;
387 <                if (c > 0)              /* clear accumulator? */
386 >                                        /* open output (load if multipass) */
387 >                if (out_spec) {         /* file or command */
388 >                        if (cmtx->ncols > 1 && !hasFormat(out_spec)) {
389 >                                fprintf(stderr, "%s: sequential result must go to stdout\n",
390 >                                                gargv[0]);
391 >                                return(0);
392 >                        }
393 >                        sprintf(fbuf, out_spec, c);
394 >                        if (row0) {     /* another pass -- get prev. data */
395 >                                fout = open_iofile(fbuf, c);
396 >                                if (!reload_data(osum, fout))
397 >                                        return(0);
398 >                        } else          /* else new output (clobber prev. files) */
399 >                                fout = open_output(fbuf, c-(cmtx->ncols==1));
400 >                } else {                        /* else stdout */
401 >                        if ((out_type == DTfloat) & (cmtx->ncols > 1)) {
402 >                                fprintf(stderr, "%s: float outputs must have separate destinations\n",
403 >                                                gargv[0]);
404 >                                return(0);
405 >                        }
406 >                        strcpy(fbuf, "<stdout>");
407 >                        fout = open_output(NULL, c-(cmtx->ncols==1));
408 >                }
409 >                if (!fout)
410 >                        return(0);      /* assume error was reported */
411 >                if (!row0 & (c > 0))    /* clear accumulator? */
412                          memset(osum, 0, sizeof(float)*ncomp*xres*yres);
413                  while (rc-- > 0) {      /* run through each input file */
414 <                        const int       r = c&1 ? rc : cmtx->nrows-1 - rc;
414 >                        const int       r = c&1 ? row0 + rc : rowN-1 - rc;
415                          const rmx_dtype *cval = rmx_val(cmtx, r, c);
416                          FILE            *finp;
417                          int             i, x;
# Line 337 | Line 420 | solo_process(void)
420                          if (i < 0)      /* this coefficient is zero, skip */
421                                  continue;
422                          sprintf(fbuf, in_spec, r);
423 <                        finp = open_input(fbuf);
423 >                        finp = open_iofile(fbuf, -1);
424                          if (!finp)
425                                  return(0);
426                          for (y = 0; y < yres; y++) {
# Line 353 | Line 436 | solo_process(void)
436                                                  dst[i] += cval[i]*iscan[x*ncomp + i];
437                          }
438                          fclose(finp);
439 <                }                       /* write out accumulated column result... */
357 <                if (out_spec) {         /* ...to file or command */
358 <                        if (cmtx->ncols > 1 && !hasFormat(out_spec)) {
359 <                                fputs("Sequential result must go to stdout\n", stderr);
360 <                                return(0);
361 <                        }
362 <                        sprintf(fbuf, out_spec, c);
363 <                        fout = open_output(fbuf, c-(cmtx->ncols==1));
364 <                } else {                /* ...to stdout */
365 <                        if ((out_type == DTfloat) & (cmtx->ncols > 1)) {
366 <                                fputs("Float outputs must have separate destinations\n",
367 <                                                stderr);
368 <                                return(0);
369 <                        }
370 <                        strcpy(fbuf, "<stdout>");
371 <                        fout = open_output(NULL, c-(cmtx->ncols==1));
372 <                }
373 <                if (!fout)
374 <                        return(0);      /* assume error was reported */
439 >                }                       /* write accumulated picture */
440                  if (out_type != DTfloat) {
441                          for (y = 0; y < yres; y++)
442                                  if (fwritesscan(osum + (size_t)y*xres*ncomp,
# Line 383 | Line 448 | solo_process(void)
448  
449                  if (fbuf[0] == '!') {
450                          if (pclose(fout) != 0) {
451 <                                fprintf(stderr, "Bad status from: %s\n", fbuf);
451 >                                fprintf(stderr, "%s: bad status from: %s\n", gargv[0], fbuf);
452                                  return(0);
453                          }
454                  } else if (fout != stdout && fclose(fout) == EOF)
# Line 412 | Line 477 | scramble(int n)
477          int     i;
478  
479          if (!scarr) {
480 <                fprintf(stderr, "Out of memory in scramble(%d)\n", n);
480 >                fprintf(stderr, "%s: out of memory in scramble(%d)\n", gargv[0], n);
481                  exit(1);
482          }
483          for (i = n; i--; )
# Line 433 | Line 498 | multi_process(void)
498   {
499          int     coff = nprocs;
500          int     odd = 0;
501 +        float   *osum = NULL;
502 +        int     *syarr = NULL;
503          char    fbuf[512];
437        float   *osum;
438        int     *syarr;
504          int     c;
505                                          /* sanity check */
506          if (sizeof(float) != sizeof(COLORV)) {
507 <                fputs("Code Error 1 in multi_process()\n", stderr);
507 >                fprintf(stderr, "%s: code Error 1 in multi_process()\n", gargv[0]);
508                  return(0);
509          }
510          while (--coff > 0) {            /* parent births children */
511                  int     pid = fork();
512                  if (pid < 0) {
513 <                        fputs("fork() call failed!\n", stderr);
513 >                        fprintf(stderr, "%s: fork() call failed!\n", gargv[0]);
514                          return(0);
515                  }
516                  if (pid == 0) break;    /* child gets to work */
517          }
518 <        osum = (float *)calloc((size_t)xres*yres, sizeof(float)*ncomp);
519 <        if (!osum) {
520 <                fprintf(stderr, "Cannot allocate %dx%d %d-component accumulator\n",
521 <                                xres, yres, ncomp);
522 <                return(0);
518 >        if (!row0 | (out_type != DTfloat)) {
519 >                osum = (float *)calloc((size_t)xres*yres, sizeof(float)*ncomp);
520 >                if (!osum) {
521 >                        fprintf(stderr, "%s: cannot allocate %dx%d %d-component accumulator\n",
522 >                                        gargv[0], xres, yres, ncomp);
523 >                        return(0);
524 >                }
525          }
526          srandom(113*coff + 5669);       /* randomize row access for this process */
527          syarr = scramble(yres);
528                                          /* run through our unique set of columns */
529          for (c = coff; c < cmtx->ncols; c += nprocs) {
530 +                int     rc = rowN - row0;
531 +                void    *omap = NULL;
532 +                size_t  omaplen = 0;
533 +                long    dstart;
534                  FILE    *fout;
535                  int     y;
536 <                int     rc = cmtx->nrows;
537 <                if (c > coff)           /* clear accumulator? */
538 <                        memset(osum, 0, sizeof(float)*ncomp*xres*yres);
536 >                                        /* create/load output */
537 >                sprintf(fbuf, out_spec, c);
538 >                if (row0) {             /* making another pass? */
539 >                        fout = open_iofile(fbuf, c);
540 >                        if (!fout) return(0);
541 >                        if (out_type == DTfloat) {
542 >                                dstart = ftell(fout);
543 >                                if ((dstart < 0) | (dstart % sizeof(float))) {
544 >                                        fprintf(stderr, "%s: bad seek/alignment\n", fbuf);
545 >                                        return(0);
546 >                                }
547 >                                omaplen = dstart + sizeof(float)*ncomp*xres*yres;
548 >                                omap = mmap(NULL, omaplen, PROT_READ|PROT_WRITE,
549 >                                                MAP_FILE|MAP_SHARED, fileno(fout), 0);
550 >                                if (omap == MAP_FAILED) {
551 >                                        fprintf(stderr, "%s: cannot map file '%s'\n",
552 >                                                        gargv[0], fbuf);
553 >                                        return(0);
554 >                                }
555 >                                osum = (float *)((char *)omap + dstart);
556 >                        } else if (!reload_data(osum, fout))
557 >                                return(0);
558 >                } else {                /* else new output (clobber prev. files) */
559 >                        fout = open_output(fbuf, c);
560 >                        if (!fout) return(0);
561 >                        if (c > coff)   /* clear accumulator? */
562 >                                memset(osum, 0, sizeof(float)*ncomp*xres*yres);
563 >                }
564                  while (rc-- > 0) {      /* map & sum each input file */
565 <                        const int       r = odd ? rc : cmtx->nrows-1 - rc;
565 >                        const int       r = odd ? row0 + rc : rowN-1 - rc;
566                          const rmx_dtype *cval = rmx_val(cmtx, r, c);
567 <                        long            dstart;
472 <                        size_t          maplen;
567 >                        size_t          imaplen;
568                          void            *imap;
569                          FILE            *finp;
570                          float           *dst;
# Line 479 | Line 574 | multi_process(void)
574                          if (i < 0)      /* this coefficient is zero, skip */
575                                  continue;
576                          sprintf(fbuf, in_spec, r);
577 <                        finp = open_input(fbuf);
577 >                        finp = open_iofile(fbuf, -1);
578                          if (!finp)
579                                  return(0);
580                          dstart = ftell(finp);
# Line 492 | Line 587 | multi_process(void)
587                                  return(0);
588                          }
589                          i = in_type==DTfloat ? ncomp*(int)sizeof(float) : ncomp+1;
590 <                        maplen = dstart + (size_t)yres*xres*i;
591 <                        imap = mmap(NULL, maplen, PROT_READ,
590 >                        imaplen = dstart + (size_t)yres*xres*i;
591 >                        imap = mmap(NULL, imaplen, PROT_READ,
592                                          MAP_FILE|MAP_SHARED, fileno(finp), 0);
593                          fclose(finp);           /* will read from map (randomly) */
594                          if (imap == MAP_FAILED) {
# Line 520 | Line 615 | multi_process(void)
615                                          dst[i] += cval[i]*(cvp[i]+(rmx_dtype).5)*fe;
616                                  }
617                              }
618 <                        munmap(imap, maplen);
619 <                }                       /* write accumulated column picture/matrix */
525 <                sprintf(fbuf, out_spec, c);
526 <                fout = open_output(fbuf, c);
527 <                if (!fout)
528 <                        return(0);      /* assume error was reported */
618 >                        munmap(imap, imaplen);
619 >                }                       /* write accumulated column picture */
620                  if (out_type != DTfloat) {
621                          for (y = 0; y < yres; y++)
622                                  if (fwritesscan(osum + (size_t)y*xres*ncomp,
623                                                          ncomp, xres, fout) < 0)
624                                          goto writerr;
625 +                } else if (omap) {
626 +                        if (munmap(omap, omaplen) < 0)
627 +                                goto writerr;
628 +                        osum = NULL;
629                  } else if (fwrite(osum, sizeof(float)*ncomp, (size_t)xres*yres, fout) !=
630                                          (size_t)xres*yres)
631                          goto writerr;
632  
633                  if (fbuf[0] == '!') {
634                          if (pclose(fout) != 0) {
635 <                                fprintf(stderr, "Bad status from: %s\n", fbuf);
635 >                                fprintf(stderr, "%s: bad status from: %s\n", gargv[0], fbuf);
636                                  return(0);
637                          }
638                  } else if (fclose(fout) == EOF)
639                          goto writerr;
640                  odd = !odd;             /* go back & forth to milk page cache */
641          }
642 <        free(osum);
642 >        if (osum) free(osum);
643          free(syarr);
644 <        if (coff)                       /* child processes return here... */
645 <                return(1);
644 >        if (coff)                       /* child exits here... */
645 >                _exit(0);
646          c = 0;                          /* ...but parent waits for children */
647          while (++coff < nprocs) {
648                  int     st;
649 <                if (wait(&st) < 0)
649 >                if (wait(&st) < 0) {
650 >                        fprintf(stderr, "%s: warning - wait() call failed unexpectedly\n", gargv[0]);
651                          break;
652 +                }
653                  if (st) c = st;
654          }
655          return(c == 0);
# Line 566 | Line 663 | writerr:
663   int
664   main(int argc, char *argv[])
665   {
666 +        double  cacheGB = 0;
667 +        int     rintvl;
668          int     a;
669  
670          gargc = argc;                   /* for header output */
# Line 588 | Line 687 | main(int argc, char *argv[])
687                                  goto badopt;
688                          }
689                          break;
690 +                case 'm':               /* cache size in GigaBytes */
691 +                        cacheGB = atof(argv[++a]);
692 +                        break;
693                  case 'N':               /* number of desired processes */
694                  case 'n':               /* quietly supported alternate */
695                          nprocs = atoi(argv[++a]);
# Line 605 | Line 707 | badopt:                        fprintf(stderr, "%s: bad option: %s\n", argv
707          cmtx = rmx_load(argv[a+1]);     /* loads from stdin if a+1==argc */
708          if (cmtx == NULL)
709                  return(1);              /* error reported */
710 +        cacheGB *= (cmtx->ncols > 1);
711 +        if (cacheGB > 0 && (!out_spec || *out_spec == '!')) {
712 +                fprintf(stderr, "%s: -m option incompatible with output to %s\n",
713 +                                argv[0], out_spec ? "command" : "stdout");
714 +                return(1);
715 +        }
716          if (nprocs > cmtx->ncols)
717                  nprocs = cmtx->ncols;
718   #if defined(_WIN32) || defined(_WIN64)
# Line 631 | Line 739 | badopt:                        fprintf(stderr, "%s: bad option: %s\n", argv
739          }
740          if (!get_iotypes())
741                  return(1);
742 <        if (!(nprocs == 1 ? solo_process() : multi_process()))
743 <                return(1);
742 >        if (cacheGB > 1e-4) {           /* figure out # of passes => rintvl */
743 >                size_t  inp_bytes = (in_type==DTfloat ? sizeof(float)*ncomp
744 >                                                : (size_t)(ncomp+1)) * xres*yres;
745 >                size_t  mem_bytes = sizeof(float)*ncomp*xres*yres;
746 >                int     npasses = (double)inp_bytes*cmtx->nrows /
747 >                                (cacheGB*(1L<<30) - (double)mem_bytes*nprocs) + 1;
748 >                if ((npasses <= 0) | (npasses*8 >= cmtx->nrows))
749 >                        npasses = 1;    /* let's not go there... */
750 >                rintvl = cmtx->nrows / npasses;
751 >                rintvl += (rintvl*npasses < cmtx->nrows);
752 >        } else
753 >                rintvl = cmtx->nrows;
754 >                                        /* make our passes */
755 >        for (row0 = 0; row0 < cmtx->nrows; row0 += rintvl) {
756 >                if ((rowN = row0 + rintvl) > cmtx->nrows)
757 >                        rowN = cmtx->nrows;
758 >                if (nprocs==1 ? !solo_process() : !multi_process())
759 >                        return(1);
760 >        }
761          return(0);
762   userr:
763 <        fprintf(stderr, "Usage: %s [-oc | -of][-o ospec][-N nproc] inpspec [mtx]\n",
763 >        fprintf(stderr, "Usage: %s [-oc | -of][-o ospec][-N nproc][-m cacheGB] inpspec [mtx]\n",
764                                  argv[0]);
765          return(1);
766   }

Diff Legend

Removed lines
+ Added lines
< Changed lines (old)
> Changed lines (new)