view tools/op.c @ 0:5242703e91d3 tip

Initial checkin for AIM92 aimR8.2 (last updated May 1997).
author tomwalters
date Fri, 20 May 2011 15:19:45 +0100
parents
children
line wrap: on
line source
/*
    op.c        Ordered sequence of operations on input stream.
   ------

    Operations on each input item are done in the order they appear on the
    command line. Operations can be repeated as many times as needed on the
    command line.

    Input items may be binary numbers (short or float) or ascii lines
    (terminated by CR) according to the type option.

    The list of operations is as follows. Some operations take valued
    arguments (which are real numbers), others are flags, as indicated:

    operation           result for each input item x
    -----------------   --------------------------------------------
    add=<value>         x+<value>
    negate=on           -x
    multiply=<value>    x*<value>
    divide=<value>      x/<value>
    remainder=<value>   real remainder of x/<value>
    inverse=on          1/x
    round=on            x rounded to nearest integer
    absolute=on         |x|
    power=<value>       x^<value>
    exponent=<value>    <value>^x
    e-exponent=on       e^x
    log=<value>         log(x) to base <value>
    e-log=on            log(x) to base e.
    sin=on              sin(x)
    cos=on              cos(x)
    tan=on              tan(x)
    threshold=<value>   if ( x  <value> ) 0 else if ( x = <value> ) 1
    diff1=on            first difference
    diff2=on            second difference
    diff3=on            third difference
    cusum=on            cumulative sum
    cumean=on           cumulative (recursive) mean an
    cumin=on            cumulative min
    cumax=on            cumulative max

    The `type' option sets the data type for all input and output items.
    Recognised data types are: short, float, ascii.


    Ascii data is read in line-by-line, and comment lines are allowed.
    The following operation apply only to ascii input items, enabling
    comment lines to be stripped or echoed directly to the output.

    strip=<value>       ignore lines beginning with <value> string
    echo=<value>        echo lines beginning with <value> string
    number=on           number output lines
			If number=on then stripped lines are
			not numbered; they simply dissappear.

    If there are no operations, (other than comment stripping/echoing or line
    numbering), then ascii lines are echoed.


Examples:

1. To specify the following ordered sequence of operations on each binary
float in the input stream: invert (take reciprocal), scale up by 100,
square (raise to power 2), invert (take reciprocal), square root (raise to
power 0.5).

op type=float inverse=on multiply=100 power=2 inverse=on power=.5  file


2. To use as an interactive "desk calculator", eg to print out the result
of log2(17/16):

echo 17 | op type=ascii -div16 -log2

3. To interactively operate on a list, eg add 1 to each of the list 1,2,3,
(note how to put newlines into echo'd lists):

echo "1\
2\
3" | op type=ascii -add1

4. To read ascii data, echoing any comment lines beginning with the string
"**", and for each number x in infile, o/p the number 149-x, do:

op type=ascii echo="**" neg=on -add149  infile > outfile

*/

#include <stdio.h>
#include <math.h>
#include "options.h"
#include "strmatch.h"

char applic[] = "Ordered sequence of operations on each item in input stream.\n     (Input and output in binary shorts)." ;

static char *helpstr, *debugstr,
		*addstr ,
		*negstr ,
		*mulstr ,
		*divstr ,
		*remstr ,
		*invstr ,
		*rndstr ,
		*absstr ,
		*powstr ,
		*expstr ,
		*eexpstr,
		*logstr ,
		*elogstr,
		*sinstr ,
		*cosstr ,
		*tanstr ,
		*threshstr,
		*diff1str  ,
		*diff2str  ,
		*diff3str  ,
		*cusumstr   ,
		*cumeanstr  ,
		*cuminstr   ,
		*cumaxstr   ,
		*typestr ,
		*stripstr ,
		*echostr  ,
		*numstr    ;

static Options option[] = {
    {   "help"      ,   "off"       ,  &helpstr     ,   "help"                                                  , DEBUG   },
    {   "debug"     ,   "off"       ,  &debugstr    ,   "debugging switch"                                      , DEBUG   },
    {   "add"       ,   "off"       ,  &addstr      ,   "Add                    x = x+value  "                  , VAL     },
    {   "negate"    ,   "off"       ,  &negstr      ,   "Negate                 x = -x     "                    , SETFLAG },
    {   "multiply"  ,   "off"       ,  &mulstr      ,   "Multiply               x = x*value  "                  , VAL     },
    {   "divide"    ,   "off"       ,  &divstr      ,   "Divide                 x = x/value  "                  , VAL     },
    {   "remainder" ,   "off"       ,  &remstr      ,   "Remainder              x = real remainder of x/value"  , VAL     },
    {   "inverse"   ,   "off"       ,  &invstr      ,   "Inverse                x = 1/x    "                    , SETFLAG },
    {   "round"     ,   "off"       ,  &rndstr      ,   "Round                  x = x, to nearest integer"      , SETFLAG },
    {   "absolute"  ,   "off"       ,  &absstr      ,   "Absolute               x = |x|    "                    , SETFLAG },
    {   "power"     ,   "off"       ,  &powstr      ,   "Power                  x = x^value  "                  , VAL     },
    {   "exponent"  ,   "off"       ,  &expstr      ,   "Exponent               x = value^x  "                  , VAL     },
    {   "e-exponent",   "off"       ,  &eexpstr     ,   "e-exponent             x = e^x    "                    , SETFLAG },
    {   "log"       ,   "off"       ,  &logstr      ,   "Log                    x = log(x), base `value'."      , VAL     },
    {   "e-log"     ,   "off"       ,  &elogstr     ,   "elog                   x = log(x), base e.    "        , SETFLAG },
    {   "sin"       ,   "off"       ,  &sinstr      ,   "sin                    x = sin(x) "                    , SETFLAG },
    {   "cos"       ,   "off"       ,  &cosstr      ,   "cos                    x = cos(x) "                    , SETFLAG },
    {   "tan"       ,   "off"       ,  &tanstr      ,   "tan                    x = tan(x) "                    , SETFLAG },
    {   "threshold" ,   "off"       ,  &threshstr   ,   "Threshold              x = 0 (x<value) or 1 (x>=value)", VAL     },
    {   "diff1"     ,   "off"       ,  &diff1str    ,   "First difference "                                     , SETFLAG },
    {   "diff2"     ,   "off"       ,  &diff1str    ,   "Second difference "                                    , SETFLAG },
    {   "diff3"     ,   "off"       ,  &diff1str    ,   "Third difference "                                     , SETFLAG },
    {   "cusum"     ,   "off"       ,  &cusumstr    ,   "Cumulative sum         x = current sum"                , SETFLAG },
    {   "cumean"    ,   "off"       ,  &cumeanstr   ,   "Cumulative mean        x = current (recursive) mean"   , SETFLAG },
    {   "cumin"     ,   "off"       ,  &cuminstr    ,   "Cumulative min         x = current min  "              , SETFLAG },
    {   "cumax"     ,   "off"       ,  &cumaxstr    ,   "Cumulative max         x = current max  "              , SETFLAG },
    {   "type"      ,   "short"     ,  &typestr     ,   "Data type (short, float, ascii)"                       , VAL     },
    {   "strip"     ,   "off"       ,  &stripstr    ,   "Ignore lines beginning with value string"              , VAL     },
    {   "echo"      ,   "off"       ,  &echostr     ,   "Echo lines beginning with value string"                , VAL     },
    {   "number"    ,   "off"       ,  &numstr      ,   "Number output lines"                                   , SETFLAG },
   ( char * ) 0 } ;


#define SIZE        64  /* Max number of program command-line arguments */

#define Add          2  /* Functions */
#define Negate       3
#define Multiply     4
#define Divide       5
#define Remainder    6
#define Inverse      7
#define Round        8
#define Absolute     9
#define Power       10
#define Exp         11
#define Eexp        12
#define Log         13
#define Elog        14
#define Sin         15
#define Cos         16
#define Tan         17
#define Threshold   18
#define Diff1       19
#define Diff2       20
#define Diff3       21
#define Sum         22
#define Mean        23
#define Min         24
#define Max         25
#define Ascii       26
#define Strip       27
#define Echo        28
#define Number      29


/* data types */

#define SHORT        0
#define FLOAT        1
#define ASCII        2

int     Type ;

int     STRIP=0;
char    Stripstr[]="*";         /* Default strip comment string */

int     ECHO=0;
char    Echostr[]="**";         /* Default echo comment string */

int     NUMBER=0;               /* Flag for numbering ascii lines */
int     n=0;

/*
 Rounding for both +ve and -ve numbers. Real numbers are truncated to ints in
 the direction of zero (EG both 0.9 and -0.9 are truncated to 0). Using round,
 0.9 becomes 1 and -0.9 becomes -1. (Note also 0.5 becomes 1, -0.5 becomes -1.
*/
#define round(x)    ((x >= 0) ? ((int)(x+0.5)) : ((int)(x-0.5)))

/* Remainder of real division for +ve and -ve numbers */
#define rem(x,y)    ((x >= 0) ? (fabs((x) - (y)*(int)((x)/(y)))) : (fabs((x) - (y)*(int)((x)/(y)-0.5))))

/* Threshold and convert to binary (0,1) value */
#define threshold(x,T)  ((x >= T) ? 1 : 0)

float diff1();
float diff2();
float diff3();
float mean();
float min();
float max();
float sum();

struct opstr {      /* array of operations: functions and (optional) arg */
    char  func;
    float arg;
} op[SIZE];

int k=0;            /* number of operations stored in array */

main(argc, argv)
int   argc ;
char *argv[] ;
{
    FILE *fp, *fopen();
    float xf, ops();
    short xs ;
    char  s[256], stripcomment[32], echocomment[32];
    char *prog, *val ;
    int   i, index, span ;

    strcpy(stripcomment, Stripstr);
    strcpy(echocomment,  Echostr );

    for (i=0 ; option[i].name != (char *)0 ; i++)
	*(option[i].val) = option[i].dflt ;

    prog = argv[0] ;
    while ( --argc > 0 && ++argv ) {
	if ( ( i = whichopt( option, *argv, &index, &span ) ) == UNKNOWN )
	    break ;
	if ( ( val = checksyntax( *argv, span, option[index].type ) ) == (char *)0 )
	    break ;
	if ( i == AMBIGUOUS ) {
	    fprintf(stderr,"%s: ambiguous option [%s]\n", prog, *argv ) ;
	    exit ( 1 ) ;
	}
	switch ( index ) {
	    case  0 : operate( option, index,  val ) ;                      break;
	    case  1 : operate( option, index,  val ) ;                      break;
	    case  2 : op[k].func = Add;         op[k].arg = atof(val); k++; break;
	    case  3 : op[k].func = Negate;                             k++; break;
	    case  4 : op[k].func = Multiply;    op[k].arg = atof(val); k++; break;
	    case  5 : op[k].func = Divide;      op[k].arg = atof(val); k++; break;
	    case  6 : op[k].func = Remainder;   op[k].arg = atof(val); k++; break;
	    case  7 : op[k].func = Inverse;                            k++; break;
	    case  8 : op[k].func = Round;                              k++; break;
	    case  9 : op[k].func = Absolute;                           k++; break;
	    case 10 : op[k].func = Power;       op[k].arg = atof(val); k++; break;
	    case 11 : op[k].func = Exp;         op[k].arg = atof(val); k++; break;
	    case 12 : op[k].func = Eexp;                               k++; break;
	    case 13 : op[k].func = Log;         op[k].arg = atof(val); k++; break;
	    case 14 : op[k].func = Elog;                               k++; break;
	    case 15 : op[k].func = Sin;                                k++; break;
	    case 16 : op[k].func = Cos;                                k++; break;
	    case 17 : op[k].func = Tan;                                k++; break;
	    case 18 : op[k].func = Threshold;   op[k].arg = atof(val); k++; break;
	    case 19 : op[k].func = Diff1;                              k++; break;
	    case 20 : op[k].func = Diff2;                              k++; break;
	    case 21 : op[k].func = Diff3;                              k++; break;
	    case 22 : op[k].func = Sum;                                k++; break;
	    case 23 : op[k].func = Mean;                               k++; break;
	    case 24 : op[k].func = Min;                                k++; break;
	    case 25 : op[k].func = Max;                                k++; break;
	    case 26 : Type = checktype( val ) ;                             break;
	    case 27 : STRIP++; strcpy(stripcomment, val);                   break;
	    case 28 : ECHO++;  strcpy(echocomment,  val);                   break;
	    case 29 : NUMBER++;                                             break;
	}

    }
    if ( !isoff( helpstr ) )
	helpopts( helpstr, prog, applic, option ) ;

    if (argc <= 0) fp = stdin;
    else if ((fp = fopen(*argv, "r")) == NULL) {
	    fprintf(stderr,"can't open %s\n", *argv);
	    exit(1);
	}

    switch ( Type ) {

	case SHORT :
	    while ( fread( &xs, sizeof(short), 1, fp ) ) {
		xs = (short)ops( (float)xs ) ;
		fwrite( &xs, sizeof(short), 1, stdout ) ;
	    }
	    break ;

	case FLOAT :
	    while ( fread( &xf, sizeof(float), 1, fp ) ) {
		xf = ops( xf ) ;
		fwrite( &xf, sizeof(float), 1, stdout ) ;
	    }
	    break ;

	case ASCII :
	    while (fgets(s, 256, fp)) {
		if (STRIP && match(s,stripcomment)==0)
		    ;                       /* strip comment (ie do nothing) */
		else {
		    if (NUMBER) printf("%d: ", ++n);
		    if (ECHO && match(s,echocomment)==0)
			fputs(s,stdout);        /* echo comment */
		    else {
			if ( k>0 ) {
			    xf = ops( atof(s) ) ;
			    printf("%.3f\n", xf ) ;
			}
			else fputs(s,stdout);   /* echo line (case of no ops) */
		    }
		}
	    }
	    break ;
    }

    fclose(fp);
}


checktype( s )
char *s ;
{
    if ( iststr( s, "short" ) )  return SHORT ;
    if ( iststr( s, "float" ) )  return FLOAT ;
    if ( iststr( s, "ascii" ) )  return ASCII ;
    fprintf( stderr,"unknown datatype [%s]\n", s ) ;
    exit( 1 ) ;
}


float ops(x)
float x;
{
    int i;

    for (i=0 ; i<k ; i++)
	switch (op[i].func) {
	    case Add:           x = x+op[i].arg;            break;
	    case Negate:        x = (-x);                   break;
	    case Multiply:      x = x*op[i].arg;            break;
	    case Divide:        x = x/op[i].arg;            break;
	    case Remainder:     x = rem(x,op[i].arg);       break;
	    case Inverse:       x = 1.0/x;                  break;
	    case Round:         x = round(x);               break;
	    case Absolute:      x = fabs(x);                break;
	    case Power:         x = pow(x,op[i].arg);       break;
	    case Exp:           x = pow(op[i].arg,x);       break;
	    case Eexp:          x = exp(x);                 break;
	    case Log:           x = log(x)/log(op[i].arg);  break;
	    case Elog:          x = log(x);                 break;
	    case Sin:           x = sin(x);                 break;
	    case Cos:           x = cos(x);                 break;
	    case Tan:           x = tan(x);                 break;
	    case Threshold:     x = threshold(x,op[i].arg); break;
	    case Diff1:         x = diff1(x);               break;
	    case Diff2:         x = diff2(x);               break;
	    case Diff3:         x = diff3(x);               break;
	    case Sum:           x = sum(x);                 break;
	    case Mean:          x = mean(x);                break;
	    case Min:           x = min(x);                 break;
	    case Max:           x = max(x);                 break;
	}
    return x;
}


match(s1,s2)
char *s1, *s2;
{
    int  n1, n2;

    if ((n1=strlen(s1)) < (n2=strlen(s2)))
	return strncmp(s1,s2,n1);
    else
	return strncmp(s1,s2,n2);
}


/****************************************************************************
 The past values used to compute differences are, (in the order they would
 appear in an array, where x is the current value):
	1st diffs           ... x11 x
	2nd diffs       ... x22 x21 x
	3rd diffs   ... x33 x32 x31 x
*****************************************************************************/
float diff1(x)
float x;
{
    static float x11=0;
    float  d;

    d=fabs(x11-x);
    x11=x;
    return d;
}

float diff2(x)
float x;
{
    static float x21=0, x22=0;
    float  d;

    d=fabs( fabs(x22-x21)-fabs(x21-x) );
    x22=x21;
    x21=x;
    return d;
}

float diff3(x)
float x;
{
    static float x31=0, x32=0, x33=0;
    float  d;

    d=fabs( fabs( fabs(x33-x32)-fabs(x32-x31) ) - fabs( fabs(x32-x31)-fabs(x31-x) ) );
    x33=x32;
    x32=x31;
    x31=x;
    return d;
}

/****************************************************************************
 Recurisve estimation of the mean
 (Ref: Young, eqn 2.14 (p14), eqn VI(2) (p63), eqn 5.17 (p65))
****************************************************************************/
float mean(x)
float x;
{
    static float a = 0;         /* initial mean value */
    static float p = 10000;     /* initial gain factor [Young p65] */

    p = p/(1+p);                /* update p (the gain factor)   */
    a = a - p*(a-x);            /* update a (the mean estimate) */
    return a;
}

/****************************************************************************
 Min, Max, and Sum
****************************************************************************/
float min(x)
float x;
{
    static float MIN=1.0e30;

    if (x < MIN) MIN = x;
    return MIN;
}

float max(x)
float x;
{
    static float MAX=0;

    if (x > MAX) MAX = x;
    return MAX;
}

float sum(x)
float x;
{
    static float SUM=0;

    SUM += x;
    return SUM;
}