aboutsummaryrefslogtreecommitdiff
path: root/pkg/images/imcoords/src/t_wcsctran.x
blob: 7459ec488b0eb897bd4d2d67f40a57d865b7f02f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
include <imio.h>
include <fset.h>
include <ctype.h>
include <imhdr.h>
include <ctotok.h>
include <mwset.h>

# Define some limits on the input file

define	MAX_FIELDS	100		# maximum number of fields in the list
define	TABSIZE		8		# spacing of the tab stops

# Define the supported units

define	WT_UNITSTR	"|hours|native|"
define	WT_UHOURS	1
define	WT_UNATIVE	2

define	WT_WCSSTR	"|logical|tv|physical|world|"
define	WT_LOGICAL	1
define	WT_TV		2
define	WT_PHYSICAL	3
define	WT_WORLD	4

# Define the supported wcs.
# T_WCSCTRAN -- Transform a list of image coordinates from one coordinate 
# system to another using world coordinate system information stored in
# the header of a reference image.

procedure t_wcsctran()

bool	verbose
int	i, csp, imlist,inlist, outlist, limlist, linlist, loutlist
int	icl, ocl, ndim, wcsndim, ncolumns, nunits, inwcs, outwcs, min_sigdigits
pointer	sp, image, columns, units, iwcs, owcs, fmtstr, fmtptrs
pointer	str, name, im, mw, ct, tmp

bool	clgetb()
int	imtopenp(), imtlen(), imtgetim(), fntopnb(), fntlenb(), fntgfnb()
int	open(), mw_stati(), wt_getlabels(), ctoi(), strdic(), clgeti(), nscan()
int	errget()
pointer	immap(), mw_openim(), mw_sctran()
errchk	mw_openim(), mw_gwattrs(), mw_sctran()

begin
	call smark (sp)
	call salloc (image, SZ_FNAME, TY_CHAR)
	call salloc (columns, IM_MAXDIM, TY_INT)
	call salloc (units, IM_MAXDIM, TY_INT)
	call salloc (iwcs, SZ_FNAME, TY_CHAR)
	call salloc (owcs, SZ_FNAME, TY_CHAR)
	call salloc (fmtstr, SZ_FNAME, TY_CHAR)
	call salloc (fmtptrs, IM_MAXDIM, TY_POINTER)
	call salloc (str, SZ_FNAME, TY_CHAR)
	call salloc (name, SZ_FNAME, TY_CHAR)

	# Get the input and output image and file lists.
	imlist = imtopenp ("image")
	limlist = imtlen (imlist)
	call clgstr ("input", Memc[str], SZ_FNAME)
	inlist = fntopnb (Memc[str], NO)
	linlist = fntlenb (inlist)
	call clgstr ("output", Memc[str], SZ_FNAME)
	if (Memc[str] == EOS)
	    call strcpy ("STDOUT", Memc[str], SZ_FNAME)
	outlist = fntopnb (Memc[str], NO)
	loutlist = fntlenb (outlist)

	# Get the input coordinate file format.
	call clgstr ("columns", Memc[str], SZ_FNAME)
	ncolumns = 0
	csp = 1
	while (wt_getlabels (Memc[str], csp, Memc[name], SZ_FNAME) != EOF) {
	    i = 1
	    if (ctoi(Memc[name], i, Memi[columns+ncolumns]) <= 0)
		break
	    ncolumns = ncolumns + 1
	}

	# Get the input coordinate units. Fill in any missing information
	# with native units
	call clgstr ("units", Memc[str], SZ_FNAME)
	nunits = 0
	csp = 1
	while (wt_getlabels (Memc[str], csp, Memc[name], SZ_FNAME) != EOF) {
	    i = strdic (Memc[name], Memc[name], SZ_FNAME, WT_UNITSTR)
	    if (i <= 0)
		break
	    Memi[units+nunits] = i
	    nunits = nunits + 1
	}
	do i = nunits + 1, IM_MAXDIM
	    Memi[units+i-1] = WT_UNATIVE

	# Get the input and output transform.
	call clgstr ("inwcs", Memc[iwcs], SZ_FNAME)
	inwcs = strdic (Memc[iwcs], Memc[iwcs], SZ_FNAME, WT_WCSSTR)
	call clgstr ("outwcs", Memc[owcs], SZ_FNAME)
	outwcs = strdic (Memc[owcs], Memc[owcs], SZ_FNAME, WT_WCSSTR)

	# Get the format strings and minimum number of significant digits.
	call clgstr ("formats", Memc[fmtstr], SZ_FNAME)
	min_sigdigits = clgeti ("min_sigdigits")

	# Get the remaining parameters.
	verbose = clgetb ("verbose")

	# Check that the image and output list lengths match. The number
	# of input coordinate lists must be 1 or equal to the number of
	# input images.
	if (limlist < 1 || (linlist > 1 && linlist != limlist)) {
	    call imtclose (imlist)
	    call fntclsb (inlist)
	    call fntclsb (outlist)
	    call error (0,
	        "Incompatable image and input coordinate list lengths.")
	}

	# Check that the image and output list lengths match. The number
	# of output coordinate lists must be 1 or equal to the number of
	# input images.
	if (loutlist > 1 && loutlist != limlist) {
	    call imtclose (imlist)
	    call fntclsb (inlist)
	    call fntclsb (outlist)
	    call error (0,
	        "Incompatable image and output coordinate list lengths.")
	}

	# Loop over the input images.
	while (imtgetim (imlist, Memc[image], SZ_FNAME) != EOF) {

	    # Open the input image.
	    im = immap (Memc[image], READ_ONLY, 0)
	    ndim = IM_NDIM(im)

	    # Open the input coordinate file.
	    if (linlist <= 0)
	        icl = NULL
	    else if (fntgfnb (inlist, Memc[str], SZ_FNAME) != EOF)
	        icl = open (Memc[str], READ_ONLY, TEXT_FILE)
	    else 
		call seek (icl, BOF)

	    # Open the output coordinate file.
	    if (fntgfnb (outlist, Memc[str], SZ_FNAME) != EOF) {
	        ocl = open (Memc[str], NEW_FILE, TEXT_FILE)
		if (ocl == STDOUT)
		    call fseti (ocl, F_FLUSHNL, YES)
	    }

	    # Print optional banner string.
	    if (verbose) {
		call fprintf (ocl, "\n# Image: %s  Wcsin: %s Wcsout: %s\n")
		    call pargstr (Memc[image])
		    call pargstr (Memc[iwcs])
		    call pargstr (Memc[owcs])
	    }

	    # Set up the coordinate transform.
	    mw = NULL
	    iferr {

	        tmp = mw_openim (im); mw = tmp

		call mw_seti (mw, MW_USEAXMAP, NO)
		if (inwcs == WT_TV && outwcs == WT_TV)
		    ct = mw_sctran (mw, "logical", "logical", 0)
		else if (inwcs == WT_TV)
		    ct = mw_sctran (mw, "logical", Memc[owcs], 0)
		else if (outwcs == WT_TV)
		    ct = mw_sctran (mw, Memc[iwcs], "logical", 0)
		else
		    ct = mw_sctran (mw, Memc[iwcs], Memc[owcs], 0)
		wcsndim = mw_stati (mw, MW_NPHYSDIM)

		if (ndim == 0)
		    ndim = wcsndim

		call sscan (Memc[fmtstr])
		do i = 1, IM_MAXDIM {
	    	    call malloc (Memi[fmtptrs+i-1], SZ_FNAME, TY_CHAR)
		    call gargwrd (Memc[Memi[fmtptrs+i-1]], SZ_FNAME)
		    if (nscan() != i || Memc[Memi[fmtptrs+i-1]] == EOS) {
			if (outwcs == WT_WORLD) {
			    iferr (call mw_gwattrs (mw, i, "format",
			        Memc[Memi[fmtptrs+i-1]], SZ_FNAME))
			        Memc[Memi[fmtptrs+i-1]] = EOS
			} else
			    Memc[Memi[fmtptrs+i-1]] = EOS
		    }
		}

	    } then {
		if (verbose) {
		    i = errget (Memc[str], SZ_LINE)
		    call fprintf (ocl, "# \tWarning: %s\n")
			call pargstr (Memc[str])
		}
		if (mw != NULL)
		    call mw_close (mw)
		mw = NULL
		ct = NULL
	    }

	    # Check that the transform is valid.
	    if (ct == NULL) {

		# Skip the image if the transform is undefined.
		if (verbose) {
		    call fprintf (ocl,
		        "# \tSkipping: Unable to compile requested transform\n")
		}

	    # For input or output tv coordinates the image must be 2D
	    } else if (ndim != 2 && (inwcs == WT_TV || outwcs == WT_TV)) {

		# Skip the image if the transform is undefined.
		if (verbose) {
		    call fprintf (ocl,
		    "# \tSkipping: Image must be 2D for wcs type tv\n")
		}

	    # Check that the number of input columns is enough for images.
	    } else if ((ncolumns < ndim) || (ncolumns < wcsndim && inwcs !=
	        WT_LOGICAL && inwcs != WT_TV)) {

		if (verbose) {
		    call fprintf (ocl,
		        "# \tSkipping: Too few input coordinate columns\n")
		}

	    } else {

	        # Check the dimension of the wcs versus the dimension of the
	        # image and issue a warning if dimensional reduction has taken
	        # place.
	        if (wcsndim > ndim) {
		    if (verbose) {
		        call fprintf (ocl,
		        "# \tWarning: Image has been dimensionally reduced\n")
		    }
	        }
	        if (verbose) {
		    call fprintf (ocl, "\n")
	        }

		# Transform the coordinate file.
		call wt_transform (im, icl, ocl, Memi[columns], Memi[units],
		    ndim, inwcs, outwcs, mw, ct, Memi[fmtptrs], wcsndim,
		    min_sigdigits)

	    }

	    # Free the format pointers.
	    do i = 1, IM_MAXDIM
	        call mfree (Memi[fmtptrs+i-1], TY_CHAR)

	    # Close the input image.
	    if (mw != NULL)
		call mw_close (mw)
	    call imunmap (im)

	    # Close the input coordinate file if it is not going to be used.
	    if (linlist == limlist)
		call close (icl)

	    # Close the output coordinate file if it is not going to be
	    # appended to.
	    if (loutlist == limlist)
		call close (ocl)
	}

	# Close the input coordinate file
	if (linlist > 0 && linlist < limlist)
	    call close (icl)
	if (loutlist < limlist)
	    call close (ocl)

	call imtclose (imlist)
	call fntclsb (inlist)
	call fntclsb (outlist)

	call sfree (sp)
end


# WT_TRANSFORM -- Transform the input coordinates from the input coordinate
# system to the output coordinate system.

procedure wt_transform (im, icl, ocl, columns, units, ndim, inwcs, outwcs, mw,
	ct, fmtptrs, wcsndim, min_sigdigits)

pointer	im			#I the input image descriptor
int	icl			#I the input coordinate file descriptor
int	ocl			#I the output coordinate file descriptor
int	columns[ARB]		#I the input coordinate columns
int	units[ARB]		#I the input coordinate units
int	ndim			#I the number of input coordinates
int	inwcs			#I the input wcs type
int	outwcs			#I the output wcs type
pointer	mw			#I the wcs descriptor
pointer	ct			#I the pointer to the compiled transformation
pointer	fmtptrs[ARB]		#I the array of format pointers
int	wcsndim			#I the dimensions of the wcs
int	min_sigdigits		#I the minimum number of significant digits

int	nline, ip, nread, nwrite, max_fields, nfields, offset
pointer	sp, inbuf, linebuf, field_pos, outbuf, voff, vstep, paxno, laxno, incoo
pointer	lincoo, outcoo, nsig
int	getline(), li_get_numd()

begin
	# Allocate working space.
	call smark (sp)
	call salloc (inbuf, SZ_LINE, TY_CHAR)
	call salloc (linebuf, SZ_LINE, TY_CHAR)
	call salloc (field_pos, MAX_FIELDS, TY_INT)
	call salloc (outbuf, SZ_LINE, TY_CHAR)

	call salloc (voff, wcsndim, TY_DOUBLE)
	call salloc (vstep, wcsndim, TY_DOUBLE)
	call salloc (paxno, wcsndim, TY_INT)
	call salloc (laxno, wcsndim, TY_INT)
	call salloc (incoo, wcsndim, TY_DOUBLE)
	call salloc (lincoo, wcsndim, TY_DOUBLE)
	call salloc (outcoo, wcsndim, TY_DOUBLE)
	call salloc (nsig, wcsndim, TY_INT)

	call mw_gaxmap (mw, Memi[paxno], Memi[laxno], wcsndim)
	call wt_laxmap (outwcs, Memi[paxno], wcsndim, Memi[laxno], ndim)
	call wt_vmap (im, Memd[voff], Memd[vstep], ndim)

	# Compute the number of coordinates to be read and written.
	if (inwcs == WT_LOGICAL && ndim < wcsndim)
	    nread = ndim
	else
	    nread = wcsndim
	if (outwcs == WT_LOGICAL && ndim < wcsndim)
	    nwrite = ndim
	else
	    nwrite = wcsndim
	call amovkd (INDEFD, Memd[outcoo], wcsndim)

	max_fields = MAX_FIELDS
	for (nline = 1; getline (icl, Memc[inbuf]) != EOF; nline = nline + 1) {

	    # Skip over leading white space.
	    for (ip = inbuf; IS_WHITE(Memc[ip]); ip = ip + 1)
		;

	    # Pass on comment and blank lines unchanged.
	    if (Memc[ip] == '#') {
                # Pass comment lines on to the output unchanged.
                call putline (ocl, Memc[inbuf])
                next
            } else if (Memc[ip] == '\n' || Memc[ip] == EOS) {
                # Blank lines too.
                call putline (ocl, Memc[inbuf])
                next
            }

	    # Expand tabs into blanks, determine field offsets.
            call strdetab (Memc[inbuf], Memc[linebuf], SZ_LINE, TABSIZE)
            call li_find_fields (Memc[linebuf], Memi[field_pos], max_fields,
                nfields)

	    # Decode the coordinates checking for valid input.
	    call aclri (Memi[nsig], wcsndim)
	    do ip = 1, nread {

		if (columns[ip] > nfields) {
		    call fstats (icl, F_FILENAME, Memc[outbuf], SZ_LINE)
		    call eprintf ("\tNot enough fields in file %s line %d\n")
			call pargstr (Memc[outbuf])
			call pargi (nline)
		    call putline (ocl, Memc[linebuf])
		    break
		}

		offset = Memi[field_pos+columns[ip]-1]
		if (li_get_numd (Memc[linebuf+offset-1],
		    Memd[incoo+ip-1], Memi[nsig+ip-1]) == 0) {
		    call fstats (icl, F_FILENAME, Memc[outbuf], SZ_LINE)
		    call eprintf ("\tBad value in file %s line %d column %d\n")
			call pargstr (Memc[outbuf])
			call pargi (nline)
			call pargi (ip)
		    call putline (ocl, Memc[linebuf])
		    break
		}

		if (IS_INDEFD(Memd[incoo+ip-1])) {
		    call fstats (icl, F_FILENAME, Memc[outbuf], SZ_LINE)
		    call eprintf ("\tBad value in file %s line %d column %d\n")
			call pargstr (Memc[outbuf])
			call pargi (nline)
			call pargi (ip)
		    call putline (ocl, Memc[linebuf])
		    break
		}

	    }

	    # Skip to next line if too few fields were read.
	    if (ip <= nread)
		next

	    # Adjust the input coordinate units if necessary.
	    switch (inwcs) {
	    case WT_TV:
		call wt_tvlogd (Memd[incoo], Memd[incoo], nread, Memd[voff],
		    Memd[vstep])
	    case WT_WORLD:
		call wt_cunits (Memd[incoo], units, nread)
	    default:
		;
	    }

	    # Compute the transform.
	    call wt_ctrand (ct, Memd[incoo], Memd[lincoo], Memi[paxno],
	        Memd[outcoo], wcsndim, nread)

	    # Adjust the output coordinate units if necessary.
	    switch (outwcs) {
	    case WT_TV:
		call wt_logtvd (Memd[outcoo], Memd[outcoo], wcsndim,
		    Memi[laxno], Memd[voff], Memd[vstep])
	    default:
		;
	    }

	    # Create the output file line.
	    call rg_apack_lined (Memc[linebuf], Memc[outbuf], SZ_LINE,
	        Memi[field_pos], nfields, columns, nread, Memd[outcoo],
	        Memi[laxno], fmtptrs, Memi[nsig], nwrite, min_sigdigits)
		    
	    # Write out the reformatted output line.
	    call putline (ocl, Memc[outbuf])

	}

	call sfree (sp)
end


# WT_LAXMAP (paxno, wcsndim, laxno, ndim)

procedure wt_laxmap (outwcs, paxno, wcsndim, laxno, ndim)

int	outwcs			#I the output wcs
int	paxno[ARB]		#I the physical axis map
int	wcsndim			#I the number of physical axis dimensions
int	laxno[ARB]		#O the physical axis map
int	ndim			#I the number of logical axis dimensions

int	i, j

begin
	if (outwcs == WT_LOGICAL && ndim < wcsndim) {
	    do i = 1, ndim {
	        laxno[i] = 0
	        do j = 1, wcsndim {
		    if (paxno[j] != i)
		        next
		    laxno[i] = j
		    break
	        }
	    }
	    do i = ndim + 1, wcsndim
		laxno[i] = 0
	} else {
	    do i = 1, wcsndim
		laxno[i] = i
	}
end


# WT_VMAP -- Fetch the image i/o section map. Tecnically this routine
# violates a system interface and uses the internal definitions in
# the imio.h file. However this routine is required to support tv coordinates
# which are coordinates with respect to the current section, and not identical
# to physcial coordinates.

procedure wt_vmap (im, voff, vstep, ndim)

pointer	im			#I the input image descriptor
double	voff[ARB]		#O the array of offsets
double	vstep[ARB]		#O the array of step sizes
int	ndim			#I the number of dimensions

int	i, dim

begin
	do i = 1, ndim {
	    dim = IM_VMAP(im,i)
	    voff[i] = IM_VOFF(im,dim)
	    vstep[i] = IM_VSTEP(im,dim)
	}
end


# WT_UNITS -- Correct the units of the input coordinates if necessary.

procedure wt_cunits (incoo, units, ncoo)

double	incoo[ARB]		#I the array of input coordinates
int	units[ARB]		#I the array of units
int	ncoo			#I the number of coordinates

int	i

begin
	do i = 1, ncoo {
	    switch (units[i]) {
	    case WT_UHOURS:
		incoo[i] = 15.0d0 * incoo[i]
	    default:
		;
	    }
	}
end


# WT_TVLOGD -- Linearly transform a vector of coordinates using an
# array of voffsets and scale factors.

procedure wt_tvlogd (incoo, outcoo, ndim, voff, vstep)

double	incoo[ARB]		#I array of input coordinates
double	outcoo[ARB]		#O array of output coordinates
int	ndim			#I number of coordinates
double	voff[ARB]		#I array of zero points
double	vstep[ARB]		#I array of scale factors

int	i

begin
	do i = 1, ndim
	    outcoo[i] = (incoo[i] - voff[i]) / vstep[i]
end


# WT_CTRAND -- Transform the coordinates.

procedure wt_ctrand (ct, incoo, lincoo, paxno, outcoo, wcsndim, nread)

pointer	ct			#I pointer to the compiled transform
double	incoo[ARB]		#I array of input coordinates
double	lincoo[ARB]		#U scratch array of input coordinates
int	paxno[ARB]		#I the physical axis map
double	outcoo[ARB]		#O array of output coordinates
int	wcsndim			#I the dimension of the wcs
int	nread			#I the number of input coordinates.

int	i

begin
	if (nread < wcsndim) {
	    do i = 1, wcsndim {
		if (paxno[i] == 0)
		    lincoo[i] = 1.0d0
		else
		    lincoo[i] = incoo[paxno[i]]
	    }
	    if (ct == NULL)
		call amovd (lincoo, outcoo, wcsndim)
	    else
		call mw_ctrand (ct, lincoo, outcoo, wcsndim)

	} else {
	    if (ct == NULL)
		call amovd (incoo, outcoo, wcsndim)
	    else
		call mw_ctrand (ct, incoo, outcoo, wcsndim)
	}

end


# WT_LOGTVD -- Linearly transform a vector of coordinates using an
# array of voffsets and scale factors.

procedure wt_logtvd (incoo, outcoo, wcsndim, laxno, voff, vstep)

double	incoo[ARB]		#I array of input coordinates
double	outcoo[ARB]		#O array of output coordinates
int	wcsndim			#I number of coordinates
int	laxno[ARB]		#I the logical axis map
double	voff[ARB]		#I array of zero points
double	vstep[ARB]		#I array of scale factors

int	i

begin
	do i = 1, wcsndim {
	    if (laxno[i] != 0)
	        outcoo[laxno[i]] = (incoo[laxno[i]] * vstep[laxno[i]]) +
		    voff[laxno[i]]
	}
end


# WT_GETLABELS -- Get the next label from a list of labels.

int procedure wt_getlabels (list, ip, label, maxch)

char    list[ARB]               #I list of labels
int     ip                      #U pointer in to the list of labels
char    label[ARB]              #O the output label
int     maxch                   #I maximum length of a column name

int     op, token
int     ctotok(), strlen()

begin
        # Decode the column labels.
        op = 1
        while (list[ip] != EOS) {

            token = ctotok (list, ip, label[op], maxch)
            if (label[op] == EOS)
                next
            if ((token == TOK_UNKNOWN) || (token == TOK_CHARCON))
                break
            if ((token == TOK_PUNCTUATION) && (label[op] == ',')) {
                if (op == 1)
                    next
                else
                    break
            }

            op = op + strlen (label[op])
            break
        }

        label[op] = EOS
        if ((list[ip] == EOS) && (op == 1))
            return (EOF)
        else
            return (op - 1)
end