// 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;
}