// following are for using spidermonkey from command line. // // see: http://www.thefrontside.net/blog/learning_javascript_from_the_command_line // /** options( 'strict' ); options( 'werror' ); load( 'jquery-1.3.2.min.js' ); load( 'jquery.flot.js' ); load( 'query-ui-1.7.2.custom.min.js' ); load( 'utility_functions.js' ); load( 'selectToUISlider.jQuery.js' ); var alert = function( message ){ print( message ); } */ var py = 1; // price of good y fixed at 1 and not changed anywhere var demandCurvePoints = new Array(); var demandCurveSmoothed = new Array(); var budgetConstraints = new Array(); var budgetConstraintOptima = new Array(); var indifferenceCurves = new Array(); var utility = getUtilityFunction( 0.8, 0.5 ); /** * optimal x,y consumption given our current utility function (which * is the global variable utility). * * @param px - price of good 1 * @param py - price of good 2 (in this sim this is always 1) * @param income - money income * @return array with [0] = optimal x [1] = optimal y */ function getOptimum( px, py, income ){ var px_py = px/py; var dx = utility.getDemand( px_py, income ); var dy = (income - dx*px)/py; return [ dx, dy ]; } /** * Simple callback for the sort routines. *@param a - array *@param b - array *@return a[0] - b[0] - so positive if a[0] > b[0] and so on. */ function compareFirstArrayValues( a, b ){ return a[0] - b[0]; } /** * Create a single indifference curve including the point optimum * and push it on to the list of indifference curves * @param optimum x,y pair giving the optimum combo for * some price level */ function addIndifferenceCurve( optimum ){ var u = utility.getUtility( optimum[0], optimum[1] ); /** * Check if we already have this one */ for( p in indifferenceCurves ){ var ic = indifferenceCurves[p]; if( Math.abs( ic.utility, u ) < 0.01 ){ return; } } ic = { data: new Array( optimum ), lines: { show: true, fill: false, lineWidth:0.3 }, color: "lightgrey", utility: u } var x = 0.1; do{ y = utility.getIndifferentY( u, x ); ic.data.push( [ x, y ] ); x += 0.01; } while ( x < 20.0 ); ic.data.sort( compareFirstArrayValues ); indifferenceCurves.push( ic ); } /** * FIXME we only need the first and last points here * Given the selected pounts on the demand curve, return a set of points that * we can use to produce a smoothed curve through them. * * @param demandPoints a Data class dataset with the points, the income for that demand, and colo[r]. * @return an array of (price,demand for x) points 0.05 x's apart on the demand curve */ function smoothOutDemandCurve( demandPoints ){ var endP = demandPoints.data[0]; var len = demandPoints.data.length-1; var startP = demandPoints.data[ len ]; data = new Array(); data.push( endP ); var px = startP[1]; var x_intercept = 0; var y_intercept = 0; var optimum = 0; do{ px += 0.01; if( px < endP[1] ){ x_intercept = demandPoints.income / px; y_intercept = demandPoints.income / py; // py1 == 1 implicitly optimum = getOptimum( px, py, demandPoints.income ); point = [ optimum[0], px/py ]; data.push( point ); } } while( ( px ) < endP[1] ); data.push( startP ); return data.sort( compareFirstArrayValues ); } /** * Add this point to the demand curve points. If there is now more * than one point, add a smoothed demand curve between the * two extreme points to the set of such curves, * for some optimum. * * @param income - current income level * @param px - current price of x * @param optimum - (x,y) pair from utility function * @param color - */ function addToDemandCurves( income, px, optimum, color ){ px_py = px / py; point = [ optimum[0], px_py ]; for( i in demandCurvePoints ){ dcPoints = demandCurvePoints[i]; dcSmooth = demandCurveSmoothed[i]; if( dcPoints.income == income ){ dcPoints.data.push( point ); dcPoints.data.sort( compareFirstArrayValues ); if( dcPoints.data.length > 1 ){ dcSmooth.data = smoothOutDemandCurve( dcPoints ); } return; } } dc = { data: new Array( point ), points: { show: true }, color: color, income: income } demandCurvePoints.push( dc ); dc = { data: new Array(), lines: { show: true, fill:false, lineWidth: 1 }, color: color, income: income, label: "£"+income } demandCurveSmoothed.push( dc ) } function mergeDemand(){ out = new Array(); for( i in demandCurvePoints ){ out.push( demandCurveSmoothed[i] ); out.push( demandCurvePoints[i] ); } return out; } function mergeBudgetConstraints(){ out = new Array(); for( i in budgetConstraints ){ out.push( budgetConstraints[i] ); out.push( budgetConstraintOptima[i] ); out.push( indifferenceCurves[i] ); } return out; } function getColourForIncome( income ){ for( i in budgetConstraints ){ bc = budgetConstraints[i]; if( bc.income == income ){ return bc.color; } } color = budgetConstraints.length; return color; } function makeBCAndDemand( income, px, drawBC ){ var x_intercept = income / px; var y_intercept = income / py; // py1 == 1 implicitly var optimum = getOptimum( px, py, income ); var bc = new Array( [ 0, y_intercept ], optimum, [ x_intercept, 0 ] ); var color = getColourForIncome( income ); addToDemandCurves( income, px, optimum, color ); if( drawBC ){ var a = { income: income, data: bc, lines: { show: true, fill: false, lineWidth: 1 }, color: color, px: px, py: py }; var o = { income: income, data: new Array( optimum ), points: { show: true }, color: color, }; budgetConstraints.push( a ); budgetConstraintOptima.push( o ); addIndifferenceCurve( optimum ); } } /** * For when the utility function parameters have changed. * Keep the budget constraints but redraw everything else. * * @param e - elasticity of substitution * @param a - share parameter */ function redrawIndifferenceAndDemand( e, a ){ // idiot check for no change if(( utility.e == e ) && ( utility.a == a )){ return; } utility = getUtilityFunction( e, a ); demandCurvePoints = new Array(); demandCurveSmoothed = new Array(); budgetConstraintOptima = new Array(); indifferenceCurves = new Array(); for( b in budgetConstraints ){ var bc = budgetConstraints[b]; var optimum = getOptimum( bc.px, bc.py, bc.income ); var o = { income: income, data: new Array( optimum ), points: { show: true }, color: bc.color }; addToDemandCurves( bc.income, bc.px, optimum, bc.color ); budgetConstraintOptima.push( o ); addIndifferenceCurve( optimum ); } } /** * FIXME don't really need this?? */ function makeBCDataset( income, px ){ makeBCAndDemand( income, px, 1 ); } /** * Extract the marginal rate of subsitution from the datapoint that's * currently been hovered over, and push it into a tooltip. * Multiple problems here: tooltip wrong place, mr not always accurate.. * If you change the graph sizes you'll need to change the calculations * of the hover top and left points. */ function calcAndDisplayMRS( event_pos, datapoint, data ){ for( i in data ){ if( (data[i][0] == datapoint[0]) && (data[i][1] == datapoint[1])){ if( i > 0 ){ var p1= data[i]; var p0 = data[i-1]; var mr = (p1[1] - p0[1])/(p0[0]-p1[0]); var prec = mr < 0.25 ? 3 : 2; var mrs = mr.toFixed( prec ); var s = "MRS here: "+mrs+" bars of chocolate per packet of crisps "; var chart_pos = $("#bc_chart").offset(); // // FIXME: // the X,Ys here are really messed up. 14.5 is the approx pixels // per chocolate bar on the y-axis, by trial and error // var startx = chart_pos.left + 60; var starty = chart_pos.top + 290; $( '