Team:TUDelft/js/plotfit

function plot(params) {

   $("main.container").addClass("progress");
   progress(params, plot);
   //Globals:
   var DATA;
   // ################
   // The rest defines the functions:
   // ################
   // Gets data from form and validates.
   function progress(params, plot) {
       // Print an error
       // Empty alert box and results box
       $('#alert').html("");
       $('#results').html("");
       Message("Processing...");
       // Getting all the data from the form.
       var data = params.data.value;
       var thousands_seperator = params.thousands_seperator.value;
       var thousands_checked = params.thousands_usage.checked;
       var value_seperator = params.value_seperator.value;
       var decimal_seperator = params.decimal_seperator.value;
       Message("Validating format...");
       // validation and manipulation
       if (thousands_checked) {
           if (thousands_seperator == value_seperator) {
               Warning("Your thousands seperator is the same as the value seperator.");
           }
           if (thousands_seperator == decimal_seperator) {
               Warning("Your thousands seperator is the same as the value seperator.");
           }
           data = data.replace(thousands_seperator, );
       }
       if (value_seperator == decimal_seperator) {
           return Error("Your value seperator is the same as the decimal seperator.");
       }
       // turn value seperator into ',' and decimal seperator into '.', and preventing those to be mixed up.
       if (data.indexOf('¬') === -1) {
           data = data.replace(value_seperator, '¬').replace(decimal_seperator, '.').replace('¬', ',');
       } else {
           return Error("¬ cannot be used as delimiter.");
       }
       data = data.replace("\"", "");
       Message("Parsing...");
       aa = numeric.parseCSV(data);
       Message("Validating data...")
       if (aa.length > 2) {
           aa = numeric.transpose(aa);
       }
       if (aa.length !== 2) {
           return Error("Enter data with 2 collumns or rows.");
       }
       if (aa[0].length !== aa[1].length) {
           Warning("Your two collumns or rows are of different length. The longest one is trancutated to match the other.");
           var min = Math.min(a[0].length !== a[1].length);
           if (aa[0].length == min) {
               aa[1] = aa[1].slice(0, min);
           } else {
               aa[0] = aa[0].slice(0, min);
           }
       }
       aa = numeric.transpose(aa);
       DATA = aa;
       Message("Plotting...");
       plot(aa, fit);
   }
   // Plots the data
   function plot(data, fit) {
       result("Your data has size " + data.length + ".");
       $.plot($("#placeholder"), [{
           color: 'yellow',
           data: data
       }]);
       fit(data, plotfit);
   }
   function fit(data, plotfit) {
       data = numeric.transpose(data);
       var t = data[0];
       var N = data[1];
       var initial = [N[0], N[N.length - 1], 1];
       var objective = makeObjective(logistic, t, N);
       var minimiser = numeric.uncmin(objective, initial);
       result("Solution found after " + minimiser.iterations + " iterations.");
       result(minimiser.message);
       draw(minimiser.solution);
       var t = numeric.linspace(t[0], t[t.length - 1], t.length);
       var N = vectorize(
           setParamInFunc(minimiser.solution, logistic), t
       );
       //        var N = (func, x) - > {
       //            var res = Array(x.length);
       //            for (var i = 0; i < x.length; i++) {
       //                res[i] = func(x[i]);
       //            }
       //            return res;
       //        }((func, params) - > {
       //            return (x) - >
       //                return func(params, x);
       //        }, t);
       var datafit = [t, N];
       datafit = numeric.transpose(datafit);
       data = numeric.transpose(data);
       plotfit(datafit, data, callback);
   }


   function plotfit(datafit, data, callback) {
       $.plot($("#placeholder"), [{
           label: "fitted data",
           data: datafit,
           color: 'green'
       }, {
           label: "input data",
           data: data,
           color: 'yellow'
       }], {
           legend: {
               position: "nw"
           }
       });
       callback();
   }
   // Tells user we are done.
   function callback() {
       $("main.container").removeClass("progress");
   }
   // ################
   // The following functions are out of the callback work loop
   // ################
   // Decorator for functions to perform vectorized
   function vectorize(func, x) {
       var res = new Array(x.length);
       testtest = func;
       for (var i = 0; i < x.length; i++) {
           res[i] = func(x[i]);
       }
       return res;
   }
   // Decorator for functions to set params fixed
   function setParamInFunc(params, func) {
       function resFunc(x) {
           return func(params, x);
       }
       return resFunc;
   }
   // Function to fit
   // N = logistic(params,t);
   function logistic(params, t) {
       var N0, K, r;
   [N0, K, r] = params;
       return (K * N0 * Math.exp(r * t)) / (K + N0 * (Math.exp(r * t) - 1));
   }
   // Least square objective
   // Build as factory with a decorator
   function makeObjective(targetFunc, x, y) {
       function objective(params) {
           var total = 0.0
           for (var i = 0; i < y.length; i++) {
               var result = targetFunc(params, x[i])
               var delta = result - y[i];
               total += delta * delta;
           }
           return total;
       }
       return objective;
   }
   function draw(params) {
       param_names = ["N0", "K", "r"];
       for (var i = 0; i < param_names.length; i++) {
           result(param_names[i] + " is " + params[i]);
       }
       var latex = katex.renderToString("N(t) = \\frac{K \\cdot N_0 \\cdot e^{rt}}{K + N_0 \\cdot (e^{rt}-1)}");
       console.log(latex);
       result("For the function:")
       resultHTML(latex);
   }
   function result(e) {
       $("#results").append($("<p />", {
           class: "result",
           text: e
       }));
   }
   function resultHTML(e) {
       $("#results").append($("<p />", {
           class: "result",
           html: e
       }));
   }
   // Print an error (This function is usually returned.)
   function Error(e) {
       $("#alert").prepend($('<p />', {
           class: 'error',
           text: e
       }));
       $("main.container").removeClass("progress");
   }
   // Print a warming
   function Warning(e) {
       $("#alert").append($('<p />', {
           class: 'warning',
           text: e
       }));
   }
   // Print a message
   function Message(e) {
       $("#alert").append($('<p />', {
           class: 'message',
           text: e
       }));
   }

}