<commit_before>// add icelandic to number formatter
numfmt.locale['is'] = {
  thousands_separator : '.'
, decimal_separator   : ','
};

// run app
jQuery(function ($) {

  // returns the payment amount for a loan based on an interest rate and a constant payment schedule
  function PMT ( irate, npayments, PV ) {
    return PV * irate * Math.pow((1 + irate), npayments) / ( Math.pow((1 + irate), npayments) - 1 );
  }

  var colors = [ '#000099', '#ed561b' ];
  var current_data;
  var current_driver;

  // these are called by the amortizer to calculate each payment
  // the init/calculate functions are called with this === loan, first parameter === payment row
  var loan_drivers = {
    'idx_eq': {
      indexed: true,
      calculate: function ( row ) {
        row.capital_payment    = row.period_captial_start / ( this.period - row.index );
        row.amount_payed       = row.capital_payment + row.interest;
        row.period_captial_end = row.period_captial_start - row.capital_payment;
        row.period_captial_end += (row.period_captial_end * this.inflation);
        return row;
      }
    },
    'idx_pmt': {
      indexed: true,
      calculate  : function ( row ) {
        row.amount_payed       = PMT( this.interest, (this.period - row.index), row.period_captial_start );
        row.capital_payment    = row.amount_payed - row.interest;
        row.period_captial_end = row.period_captial_start - row.capital_payment;
        row.period_captial_end += (row.period_captial_end * this.inflation);
        return row;
      }
    },
    'eq': {
      indexed: false,
      calculate: function ( row ) {
        row.capital_payment    = this.principal / this.period;
        row.amount_payed       = row.capital_payment + row.interest;
        row.period_captial_end = row.period_captial_start - row.capital_payment;
        return row;
      }
    },
    'pmt': {
      indexed: false,
      calculate: function ( row ) {
        row.amount_payed       = PMT( this.interest, this.period, this.principal );
        row.capital_payment    = row.amount_payed - row.interest;
        row.period_captial_end = row.period_captial_start - row.capital_payment;
        return row;
      }
    }
  };

  // these are called by the plotter to convert payments to {x:# y:#} format
  // the init/calculate functions are called with this === loan
  var display_drivers = {
    loan_std_capital: {
      title: "Þróun höfuðstóls yfir lánstímabil",
      plot: 'line',
      calculate: function ( d ) {
        return { x: d.index + 1, y: d.period_captial_end };
      }
    },
    loan_std_payments: {
      title: "Afborganir á ári yfir lánstímabil",
      plot: 'bar',
      calculate: function ( d ) {
        return { x: d.index + 1
               , y: d.amount_payed
               , stack: [ d.capital_payment, d.interest ]
               };
      }
    },
    loan_vs_property1: {
      title: "Höfuðstóll sem hlutfall af markaðvirði eignar",
      plot: 'line',
      extra_controls: 'loan_vs_property',
      percent: true,
      init: function () {
        this.property_value  = Number( $( '#property_value' ).val() );
        this.property_growth = Number( $( '#property_growth' ).val() ) / 100;
      },
      calculate: function ( d ) {
        var upvalue = this.property_value * ( 1 + this.property_growth * d.index );
        var y = (d.period_captial_end / upvalue) * 100;
        return { x: d.index + 1, y: y };
      }
    },
    loan_vs_property2: {
      title: "Verðmæti eignar umfram áhvílandi höfuðstól",
      plot: 'line',
      extra_controls: 'loan_vs_property',
      init: function () {
        this.property_growth = ( $( '#property_growth' ).val() * 1 / 100 );
        this.property_value = $( '#property_value' ).val() * 1;
      },
      calculate: function ( d ) {
        var upvalue = this.property_value * ( 1 + this.property_growth * d.index );
        var y = (upvalue - d.period_captial_end);
        return { x: d.index + 1, y: y };
      }
    },
    loan_vs_income1: {
      title: "Afborganir sem hlutfall af ráðstöfunartekjum",
      plot: 'bar',
      percent: true,
      extra_controls: 'loan_vs_income',
      init: function () {
        this.income_post_taxes = Number( $( '#income_post_taxes' ).val() ) * 12;
        this.income_growth     = Number( $( '#income_growth' ).val() ) / 100;
      },
      calculate: function ( d ) {
        var upvalue = this.income_post_taxes * ( 1 + this.income_growth * d.index );
        var y = ( d.amount_payed / upvalue ) * 100;
        return { x: d.index + 1, y: y };
      }
    },
    loan_vs_income2: {
      title: "Ráðstöfunartekjur umfram afborgun",
      plot: 'bar',
      extra_controls: 'loan_vs_income',
      init: function () {
        this.income_post_taxes = Number( $( '#income_post_taxes' ).val() ) * 12;
        this.income_growth     = Number( $( '#income_growth' ).val() ) / 100;
      },
      calculate: function ( d ) {
        var upvalue = this.income_post_taxes * ( 1 + this.income_growth * d.index );
        var y = upvalue - d.amount_payed;  // TODO: allow negatives?
        return { x: d.index + 1, y: y };
      }
    }
  };


  // read the properties of a loan for the UI and return a customized "driver"
  function get_loan_properties ( loan_elm_id ) {
    var type = $( loan_elm_id + '_type' ).val();
    var driver = $.extend( {}, loan_drivers[ type ] );
    driver.principal = Number( $( loan_elm_id + '_principal' ).val() || 0 );
    driver.interest  = Number( $( loan_elm_id + '_interest' ).val() || 0 ) / 100;
    driver.period    = Number( $( loan_elm_id + '_period' ).val() || 1 );
    driver.inflation = Number( $( '#inflation' ).val() || 0 ) / 100;
    driver.active    = $( loan_elm_id + "_on" )[ 0 ].checked;
    return driver;
  }


  // amortization
  function amortize ( loan ) {
    if ( typeof loan.init === 'function' ) {
      loan.init();
    }
    var current_capital = loan.principal;
    loan.payments = pv.range( loan.period ).map(function ( i ) {
      var row = loan.calculate({
        'index': i,
        'period_captial_start': current_capital,
        'interest': current_capital * loan.interest,
        'capital_payment': null,
        'amount_payed': null,
        'currency_worth': Math.pow( 1 / (1 + loan.inflation), i ),
        'payment_upcalc': null,
        'period_captial_end': null
      });
      // allow calculation methods to skip setting period_captial_end
      if ( null === row.period_captial_end && 
           null !== row.period_captial_start && 
           null !== row.capital_payment ) {
        row.period_captial_end = ( row.period_captial_start - row.capital_payment );
      }
      // núvirðing
      if ( null === row.payment_upcalc ) {
        row.payment_upcalc = row.amount_payed * row.currency_worth;
      }
      //
      current_capital = row.period_captial_end;
      return row;
    });
    return loan;
  }



  function plot ( loans, display_driver ) {

    var y_min = Infinity;
    var y_max = -Infinity;
    var x_min = Infinity;
    var x_max = -Infinity;
    var stacks = 0;

    var data = loans
      .map(function ( loan ) {
        if ( typeof display_driver.init === 'function' ) {
          display_driver.init.call( loan );
        }
        if ( !loan.active ) { return []; }
        var series = loan.payments
              .map( display_driver.calculate, loan )
              .filter(function ( d ) {
                if ( isFinite(d.x) && isFinite(d.y) ) {
                  if ( d.stack && d.stack.length > stacks ) {
                    stacks = d.stack.length;
                  }
                  if ( y_min > d.y ) { y_min = d.y; }
                  if ( y_max < d.y ) { y_max = d.y; }
                  if ( x_min > d.x ) { x_min = d.x; }
                  if ( x_max < d.x ) { x_max = d.x; }
                  return true;
                }
                return false;
              })
              ;
        return series;
      })
      ;

    // prevent floating point artifacts from triggering axis
    if ( Math.abs( y_min ) > 0 && Math.abs( y_min ) < 1e-8 ) {
      y_min = 0;
    }

    var fmt_ptn = display_driver.percent ? "#0'%'" : "#,##0.##";
    var fmt = numfmt( fmt_ptn, 'is' );

    // make sure ranges are finite
    if ( !isFinite( y_max ) ) { y_max = 1000000; }
    if ( !isFinite( y_min ) ) { y_min = 0; }
    if ( !isFinite( x_max ) ) { x_max = 10; }
    if ( !isFinite( x_min ) ) { x_min = 0; }

    var fit_space = $( '#col2' ).innerWidth();

    var margin_top = 35
      , margin_right = 10
      , margin_bottom = 20
      , margin_left = 65
      , w = fit_space - (margin_left + margin_right) // 480
      , aspect = 1 / 1.618
      , h = w * aspect
      , x_scale = pv.Scale.linear( x_min, x_max )
      , y_scale = pv.Scale.linear( y_min < 0 ? y_min : 0, y_max ).nice( 0 )
      , y_axis_color = "#555"
      , x_axis_color = "#555"
      ;

    // Y-axis
    var y_axis_top = 0;
    var y_axis_bottom = 0;
    var y = y_scale.range( 0, h );
    var n_y_ticks = ~~( h / 25 );
    var y_ticks = y.ticks( Math.max( 2, n_y_ticks ) );

    // frame
    var vis = new pv.Panel()
        .canvas( document.getElementById('plot_area') )
        //.fillStyle('#ddd')
        .width( w )<commit_msg>Settinga an upper bound for ticks and better ranges for empty charts.<commit_after>// add icelandic to number formatter
numfmt.locale['is'] = {
  thousands_separator : '.'
, decimal_separator   : ','
};

// run app
jQuery(function ($) {

  // returns the payment amount for a loan based on an interest rate and a constant payment schedule
  function PMT ( irate, npayments, PV ) {
    return PV * irate * Math.pow((1 + irate), npayments) / ( Math.pow((1 + irate), npayments) - 1 );
  }

  var colors = [ '#000099', '#ed561b' ];
  var current_data;
  var current_driver;

  // these are called by the amortizer to calculate each payment
  // the init/calculate functions are called with this === loan, first parameter === payment row
  var loan_drivers = {
    'idx_eq': {
      indexed: true,
      calculate: function ( row ) {
        row.capital_payment    = row.period_captial_start / ( this.period - row.index );
        row.amount_payed       = row.capital_payment + row.interest;
        row.period_captial_end = row.period_captial_start - row.capital_payment;
        row.period_captial_end += (row.period_captial_end * this.inflation);
        return row;
      }
    },
    'idx_pmt': {
      indexed: true,
      calculate  : function ( row ) {
        row.amount_payed       = PMT( this.interest, (this.period - row.index), row.period_captial_start );
        row.capital_payment    = row.amount_payed - row.interest;
        row.period_captial_end = row.period_captial_start - row.capital_payment;
        row.period_captial_end += (row.period_captial_end * this.inflation);
        return row;
      }
    },
    'eq': {
      indexed: false,
      calculate: function ( row ) {
        row.capital_payment    = this.principal / this.period;
        row.amount_payed       = row.capital_payment + row.interest;
        row.period_captial_end = row.period_captial_start - row.capital_payment;
        return row;
      }
    },
    'pmt': {
      indexed: false,
      calculate: function ( row ) {
        row.amount_payed       = PMT( this.interest, this.period, this.principal );
        row.capital_payment    = row.amount_payed - row.interest;
        row.period_captial_end = row.period_captial_start - row.capital_payment;
        return row;
      }
    }
  };

  // these are called by the plotter to convert payments to {x:# y:#} format
  // the init/calculate functions are called with this === loan
  var display_drivers = {
    loan_std_capital: {
      title: "Þróun höfuðstóls yfir lánstímabil",
      plot: 'line',
      calculate: function ( d ) {
        return { x: d.index + 1, y: d.period_captial_end };
      }
    },
    loan_std_payments: {
      title: "Afborganir á ári yfir lánstímabil",
      plot: 'bar',
      calculate: function ( d ) {
        return { x: d.index + 1
               , y: d.amount_payed
               , stack: [ d.capital_payment, d.interest ]
               };
      }
    },
    loan_vs_property1: {
      title: "Höfuðstóll sem hlutfall af markaðvirði eignar",
      plot: 'line',
      extra_controls: 'loan_vs_property',
      percent: true,
      init: function () {
        this.property_value  = Number( $( '#property_value' ).val() );
        this.property_growth = Number( $( '#property_growth' ).val() ) / 100;
      },
      calculate: function ( d ) {
        var upvalue = this.property_value * ( 1 + this.property_growth * d.index );
        var y = (d.period_captial_end / upvalue) * 100;
        return { x: d.index + 1, y: y };
      }
    },
    loan_vs_property2: {
      title: "Verðmæti eignar umfram áhvílandi höfuðstól",
      plot: 'line',
      extra_controls: 'loan_vs_property',
      init: function () {
        this.property_growth = ( $( '#property_growth' ).val() * 1 / 100 );
        this.property_value = $( '#property_value' ).val() * 1;
      },
      calculate: function ( d ) {
        var upvalue = this.property_value * ( 1 + this.property_growth * d.index );
        var y = (upvalue - d.period_captial_end);
        return { x: d.index + 1, y: y };
      }
    },
    loan_vs_income1: {
      title: "Afborganir sem hlutfall af ráðstöfunartekjum",
      plot: 'bar',
      percent: true,
      extra_controls: 'loan_vs_income',
      init: function () {
        this.income_post_taxes = Number( $( '#income_post_taxes' ).val() ) * 12;
        this.income_growth     = Number( $( '#income_growth' ).val() ) / 100;
      },
      calculate: function ( d ) {
        var upvalue = this.income_post_taxes * ( 1 + this.income_growth * d.index );
        var y = ( d.amount_payed / upvalue ) * 100;
        return { x: d.index + 1, y: y };
      }
    },
    loan_vs_income2: {
      title: "Ráðstöfunartekjur umfram afborgun",
      plot: 'bar',
      extra_controls: 'loan_vs_income',
      init: function () {
        this.income_post_taxes = Number( $( '#income_post_taxes' ).val() ) * 12;
        this.income_growth     = Number( $( '#income_growth' ).val() ) / 100;
      },
      calculate: function ( d ) {
        var upvalue = this.income_post_taxes * ( 1 + this.income_growth * d.index );
        var y = upvalue - d.amount_payed;  // TODO: allow negatives?
        return { x: d.index + 1, y: y };
      }
    }
  };


  // read the properties of a loan for the UI and return a customized "driver"
  function get_loan_properties ( loan_elm_id ) {
    var type = $( loan_elm_id + '_type' ).val();
    var driver = $.extend( {}, loan_drivers[ type ] );
    driver.principal = Number( $( loan_elm_id + '_principal' ).val() || 0 );
    driver.interest  = Number( $( loan_elm_id + '_interest' ).val() || 0 ) / 100;
    driver.period    = Number( $( loan_elm_id + '_period' ).val() || 1 );
    driver.inflation = Number( $( '#inflation' ).val() || 0 ) / 100;
    driver.active    = $( loan_elm_id + "_on" )[ 0 ].checked;
    return driver;
  }


  // amortization
  function amortize ( loan ) {
    if ( typeof loan.init === 'function' ) {
      loan.init();
    }
    var current_capital = loan.principal;
    loan.payments = pv.range( loan.period ).map(function ( i ) {
      var row = loan.calculate({
        'index': i,
        'period_captial_start': current_capital,
        'interest': current_capital * loan.interest,
        'capital_payment': null,
        'amount_payed': null,
        'currency_worth': Math.pow( 1 / (1 + loan.inflation), i ),
        'payment_upcalc': null,
        'period_captial_end': null
      });
      // allow calculation methods to skip setting period_captial_end
      if ( null === row.period_captial_end && 
           null !== row.period_captial_start && 
           null !== row.capital_payment ) {
        row.period_captial_end = ( row.period_captial_start - row.capital_payment );
      }
      // núvirðing
      if ( null === row.payment_upcalc ) {
        row.payment_upcalc = row.amount_payed * row.currency_worth;
      }
      //
      current_capital = row.period_captial_end;
      return row;
    });
    return loan;
  }



  function plot ( loans, display_driver ) {

    var y_min = Infinity;
    var y_max = -Infinity;
    var x_min = Infinity;
    var x_max = -Infinity;
    var stacks = 0;

    var data = loans
      .map(function ( loan ) {
        if ( typeof display_driver.init === 'function' ) {
          display_driver.init.call( loan );
        }
        if ( !loan.active ) { return []; }
        var series = loan.payments
              .map( display_driver.calculate, loan )
              .filter(function ( d ) {
                if ( isFinite(d.x) && isFinite(d.y) ) {
                  if ( d.stack && d.stack.length > stacks ) {
                    stacks = d.stack.length;
                  }
                  if ( y_min > d.y ) { y_min = d.y; }
                  if ( y_max < d.y ) { y_max = d.y; }
                  if ( x_min > d.x ) { x_min = d.x; }
                  if ( x_max < d.x ) { x_max = d.x; }
                  return true;
                }
                return false;
              })
              ;
        return series;
      })
      ;

    // prevent floating point artifacts from triggering axis
    if ( Math.abs( y_min ) > 0 && Math.abs( y_min ) < 1e-8 ) {
      y_min = 0;
    }

    var fmt_ptn = display_driver.percent ? "#0'%'" : "#,##0.##";
    var fmt = numfmt( fmt_ptn, 'is' );

    // make sure ranges are finite
    if ( !isFinite( y_max ) ) { y_max = display_driver.percent ? 100 : 10; }
    if ( !isFinite( y_min ) ) { y_min = 0; }
    if ( !isFinite( x_max ) ) { x_max = 10; }
    if ( !isFinite( x_min ) ) { x_min = 0; }

    var fit_space = $( '#col2' ).innerWidth();

    var margin_top = 35
      , margin_right = 10
      , margin_bottom = 20
      , margin_left = 65
      , w = fit_space - (margin_left + margin_right) // 480
      , aspect = 1 / 1.618
      , h = w * aspect
      , x_scale = pv.Scale.linear( x_min, x_max )
      , y_scale = pv.Scale.linear( y_min < 0 ? y_min : 0, y_max ).nice( 0 )
      , y_axis_color = "#555"
      , x_axis_color = "#555"
      ;

    // Y-axis
    var y_axis_top = 0;
    var y_axis_bottom = 0;
    var y = y_scale.range( 0, h );
    var n_y_ticks = ~~( h / 25 );
    var y_ticks = y.ticks( Math.min( Math.max( 2, n_y_ticks ), 10 ) );

    // frame
    var vis = new pv.Panel()
        .canvas( document.getElementById('plot_area') )
        //.fillStyle('#ddd')
        .width( w )
{'fraction_non_alphanumeric': 0.13304674905964536, 'fraction_numerical': 0.01214400859752821, 'mean_word_length': 2.0320951449983706, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1582281', 'n_tokens_mistral': 6596, 'n_tokens_neox': 6174, 'n_words': 1457}