// plot4.cpp // Copyright (c) Menno Rubingh 2016. Web: [http://rubinghsoftware.de] // // MR Mar 2016. // // Print to stdout a crude ASCII-art plot of y as a function of x, // as defined by the Reverse Polish expession specified as a command-line argument. // // The plot ranges over x = 0.0 to x = 1.0, and the plotted y range is [0.0, 1.0]. // //--- // Uses: // rpinterpreter.h -- RPInterpreter. // rpinterpreter.a -- Static library containing RPInterpreter implementation. // // llgp_between.h -- BETWEEN_II(). // //--- // Build: // g++ -Wall -ggdb -o plot4 plot4.cpp rpinterpreter.a -lm // #include "rpinterpreter.h" //RPInterpreter. #include <stdio.h> #include <assert.h> #include <math.h> //round(), isnan(), isinf(), signbit(). #include <limits.h> //INT_MIN, INT_MAX. //------------------------------------------------------------------------------------- // Print individual line of the plot. //------------------------------------------------------------------------------------- // This is a class with only class functions and class constants. // Objects of this class are never instantiated. // I'm using a class and not a namespace, because the class allows elements // in it to be private. class AsciiPlotter { // Number of columns of the plot. static int const NWID = 65; //static int const NWID = 51; // For given y, return the column number where to print the marker. // If beyond the printing column range, then return a number < 0 (if below), // or > NWID (if above). static int yToCol( double iY, double iYMin, //} Plot range for y. double iYMax ) //} { assert( iYMin < iYMax ); assert( !isnan(iY) ); double f = (iY-iYMin)/(iYMax-iYMin); double colDbl = round( (double)NWID * f ); //DEBUG printf( "[f=%+10.3e d=%+10.3e c=%+10d] ",f, colDbl, (int)colDbl ); // Numerical range of double is outside the signed int numerical range, // so need to check the signed int range before can cast to (int). // These int range checks also handle the case that colDbl == +- Inf, // which are values that also do not pass through the (int) cast correctly. if ( colDbl <= (double)INT_MIN ) { return -10; /*Arbitrary value < 0 */ } if ( colDbl >= (double)INT_MAX ) { return NWID+10;/*Arbitrary value > NWID*/ } return (int)colDbl; } public: static void printLine( double iY, double iYMin, //} Plot range for y. double iYMax ) //} { int colY = yToCol( iY, iYMin, iYMax ); printf( "%c ", ( (colY < 0) ? '<' : ' ' ) ); for ( int k = 0; k <= NWID; k++ ) { char c = ' '; if ( k == 0 ) { c = '|'; } if ( k == NWID ) { c = '|'; } if ( k == colY ) { c = '*'; } printf( "%c", c ); } printf( " %c", ( (colY > NWID ) ? '>' : ' ' ) ); } }; //------------------------------------------------------------------------------------- // For one x value, evaluate the expression and print the plot line. //------------------------------------------------------------------------------------- void evalAndPlotOneX( RPInterpreter * uRP, double iX, //Input x value. double iYMin, //} Plot range for y. double iYMax ) //} { // Set variable 'x', clear the other vars to zero. uRP->clearVars(); uRP->putVar( 'x', iX ); // Evaluate. uRP->evaluate(); // Get the output y. double yOut; bool f = uRP->getVar( 'y', &yOut ); // Print the plot line for this x. printf( "x=%+10.3e ", iX ); if ( !f ) { printf( "EDOM " ); } else { printf( "y=%+10.3e ", yOut ); AsciiPlotter::printLine( yOut, iYMin, iYMax ); } printf( "\n" ); } //------------------------------------------------------------------------------------- // MAIN: // - Read RP expression string from cmd line; // - Loop through a range of x values; // for each x value, evaluate expression to compute y, and plot the y value. //------------------------------------------------------------------------------------- void usage() { fprintf( stderr, "Usage: plot4 'RPEXPRESSION'\n" "Note: Put RPEXPRESSION between single quotes to prevent shell expansion.\n" ); fprintf( stderr, "\n%s\n", RPInterpreter::helptxt() ); } int main( int argc, char ** argv ) { if ( argc < 2 ) { usage(); return -1; } char const * arg_expr = argv[1]; printf( "expr = [%s]\n", arg_expr ); // Parse the expression. RPInterpreter rp; if ( ! rp.scanFromString( arg_expr ) ) { return -1; } rp.dump( stdout ); // Loop through a range of x values, and plot. double xMin = 0.0; double xMax = 1.0; double xStep = 0.025; double epsilon = xStep/100.0; for ( double x = xMin; x <= (xMax+epsilon); x += xStep ) { evalAndPlotOneX( &rp, x, 0.0, 1.0 ); } return 0; }