text: (function (global, factory) {
  	typeof exports === 'object' && typeof module !== 'undefined' ? factory() :
  	typeof define === 'function' && define.amd ? define(factory) :
  	(factory());
  }(this, (function () { 'use strict';
  
  function graphInputNodes (data) {
  
      var grapher = this;
      // JOIN
      var inputGroups = this.neuralNetworkG.selectAll(this.svgElement + " .input-group")
          .data(data);
  
      // EXIT old elements not present in new data.
      inputGroups.exit()
          .attr("class", "exit")
          .remove();
  
      // UPDATE old elements present in new data.
      inputGroups.attr("class", "input-group")
          .attr("transform", function (d) {
              return "translate(" +
                  (d.x)
                  + ","
                  + (d.y) + ")";
          }.bind(this));
  
      inputGroups.select("#input-name")
          .text(function (d) {
              return "X" + (d.index + 1)
          });
  
  
      // ENTER new elements present in new data.
      var inputs = inputGroups.enter()
          .append("g")
          .attr("class", "input-group")
          .attr("transform", function (d) {
              return "translate(" +
                  (d.x)
                  + ","
                  + (d.y) + ")";
          }.bind(this))
          .on("mouseover", function (d) {
  
              // Highlight the lines connected to this node
              d3.selectAll(grapher.svgElement + " line.input-" + d.index)
                  .classed("nn-arrow-highlighted", true);
  
              // Highlight the weights connected to the node
              var weightGroups = d3.selectAll(grapher.svgElement + " .input-" + d.index + "-weight");
              weightGroups.selectAll("ellipse")
                  .classed("nn-node-highlighted", true);
              var layerInputs = d3.selectAll(grapher.svgElement + " .layer-1-input-groups .input-" + d.index)
                  .classed("layer-input-value-highlighted", true);
              //.classed("layer-input-value-highlighted", true);
          })
          .on("mouseout", function (d) {
  
  
              // Stop highlighting the lines connected to this node
              d3.selectAll(grapher.svgElement + " .input-" + d.index)
                  .classed("nn-arrow-highlighted", false);
  
  
              // Highlight the weights connected to the node
              var weightGroups = d3.selectAll(grapher.svgElement + " .input-" + d.index + "-weight");
              weightGroups.selectAll("ellipse")
                  .classed("nn-node-highlighted", false);
  
              var layerInputs = d3.selectAll(grapher.svgElement + " .layer-1-input-groups .input-" + d.index)
                  .classed("layer-input-value-highlighted", false);
          });
  
      inputs
          .append("circle")
          .attr("class", function (d) {
              if (d.type == "input") return "outlined-input-node nn-node";
              else if (d.type == "bias") return "outlined-bias-node nn-node";
          })
          .attr("r", this.nodeRadius)
          .attr("cx", 0)
          .attr("cy", 0);
  
      inputs.append("text")
          .attr("id", "input-name")
          .attr("class", "node-text")
          .attr("text-anchor", "middle")
          .attr("x", 0)
          .attr("y", 5)
          .text(function (d) {
              if (d.type == "input")
                  if(d.index == 0)
                  return "Age";
                  else
                  return "Sex";
              else if (d.type == "bias") return "Bias";
          });
  
      inputGroups.moveUp();
  }
  
  
  function updateInputNodesText(newinputs) {
      var inputTexts = this.neuralNetworkG.selectAll(this.svgElement + " .input-group .node-text");
      // Display the text in the input nodes
      inputTexts.text(function (d) {
          if (d.type == "input") {
              return newinputs[d.index];
          }
          else if (d.type == "bias") return "+1";
      });
  }
  
  
  function getInputNodeCoordinates(inputNodeCount, inputLayerX, getYCoordinateOfNodeInInputLayer) {
  
      var inputLayerCoordinates = d3.range(inputNodeCount).map(function (i) {
          var y = getYCoordinateOfNodeInInputLayer(i);
          return {x: inputLayerX, y: y, index: i, type: "input"};
      }.bind(this));
  
      //  Add the bias node to the end of the input layer
      inputLayerCoordinates.push({
          x: inputLayerX, y: getYCoordinateOfNodeInInputLayer(inputNodeCount),
          index: inputNodeCount, type: "bias"
      });
  
      return inputLayerCoordinates;
  }
  
  /**
   * Created by alammar on 2/7/17.
   */
  
  function graphWeightNodes(data) {
      var radius = this.nodeRadius * this.weightNodeSizeScale;
      var grapher = this;
      var t = d3.transition()
          .duration(750);
  
      // JOIN
      var groupElements = this.neuralNetworkG.selectAll(this.svgElement + " .weight-group")
          .data(data);
  
      // EXIT old elements not present in new data.
      groupElements.exit()
          .attr("class", "exit")
          .remove();
  
      // UPDATE old elements present in new data.
      groupElements.attr("class", function (d) {
              return "weight-group input-" + d.inputIndex + "-weight output-" + d.outputIndex + "-weight"
          })
          .attr("transform", function (d) {
              return "translate(" +
                  (d.x)
                  + ","
                  + (d.y) + ")";
          });
  
      groupElements.select("#weight0Value")
          .text(function (d) {
              return "W" + (d.outputIndex + 1) + "," + (d.inputIndex + 1);
          });
  
      // ENTER new elements present in new data.
      var groups = groupElements.enter()
          .append("g")
          .attr("class", function (d) {
              return "weight-group input-" + d.inputIndex + "-weight output-" + d.outputIndex + "-weight"
          })
          .attr("transform", function (d) {
              return "translate(" +
                  (d.x)
                  + ","
                  + (d.y) + ")";
          })
          .on("mouseover", function (d) {
  
              // Expand the node
              var g = d3.select(this);
              g.select("ellipse")
                  .transition()
                  .duration(50)
                  .attr("rx", 50);
  
              // Make text darker
              g.select("text")
                  .classed("weightNodeText-highlighted", true)
                  .transition()
                  .text(function (d) {
                      return d.value;
                  });
              //nn-node-highlighted
              g.moveUp();
              // Show tooltip
              //grapher.tooltipDiv.transition()
              //    .duration(100)
              //    .style("opacity", 1);
              //grapher.tooltipDiv.html(d.value )
              //    .style("left", (d3.event.pageX) + "px")
              //    .style("top", (d3.event.pageY - 28) + "px");
          })
          .on("mouseout", function (d) {
              var g = d3.select(this);
              g.select("ellipse")
                  .transition()
                  .duration(50)
                  .attr("rx", 22);
  
  
              g.select("text")
                  .classed("weightNodeText-highlighted", false)
                  .transition()
                  .delay(30)
                  .attr("font-size", "12px")
                  .text(function (d) {
                      return numberFormatter(d.value);
                  });
  
              //grapher.tooltipDiv.transition()
              //    .duration(200)
              //    .style("opacity", 0);
          });
  
      groups
          .append("ellipse")
          .attr("class", function (d) {
              if (d.type == "weight") return "outlined-weight-node nn-node";
              else if (d.type == "bias") return "outlined-bias-node nn-node";
          })
          .attr("rx", radius * this.weightNodeWidthRatio)
          .attr("ry", radius)
          .attr("cx", 0)
          .attr("cy", 0);
  
  
      groups.append("text")
          .attr("id", "weight0Value")
          .attr("class", "weightNodeText")
          .attr("text-anchor", "middle")
          .attr("y", 3)
          .text(function (d) {
              return numberFormatter(d.value);
          });
  
      groupElements.moveUp();
  }
  
  
  function getWeightNodeCoordinates(inputLayerNodeCount,
                                                                             inputNodeCount, outputNodeCount,
                                                                             inputLayerCoordinates, weightLayerXCoordinates,
                                                                             outputLayerCoordinates,
                                                                             biasLayerX, inputLayerX ) {
  
      // Calculate the angles of the all the Input-to_Bias lines
      var lineAngles = [],
          weightLayerCoordinates = [],
          weightLayerYCoordinates = [];
  
      for (var j = 0; j < outputNodeCount; j++)
          for (var i = 0; i < inputLayerNodeCount; i++) {
  
              var opposite = inputLayerCoordinates[i].y - outputLayerCoordinates[j].y,
                  adjacent = biasLayerX - inputLayerX,
                  angle = Math.atan(opposite / adjacent),
                  yCoordinate = inputLayerCoordinates[i].y -
                      Math.tan(angle) *
                      (weightLayerXCoordinates[j] - inputLayerX);
  
              if (j == 0) {
                  weightLayerYCoordinates[i] = [];
                  lineAngles[i] = [];
              }
              lineAngles[i][j] = angle;
              weightLayerYCoordinates[i][j] = yCoordinate;
  
              if (i < inputNodeCount) // Weight
                  weightLayerCoordinates.push({
                      x: this.weightLayerXCoordinates[j],
                      y: yCoordinate,
                      outputIndex: j,
                      inputIndex: i,
                      value: this.weights[i][j],
                      type: "weight"
                  });
              else // Bias
                  weightLayerCoordinates.push({
                      x: this.weightLayerXCoordinates[j],
                      y: yCoordinate,
                      outputIndex: j,
                      inputIndex: i,
                      value: this.biases[j],
                      type: "bias"
                  });
          }
      return {lineAngles: lineAngles,
          weightLayerCoordinates: weightLayerCoordinates
          //weightLayerYCoordinates: weightLayerYCoordinates
          };
  }
  
  /**
   * Created by alammar on 2/7/17.
   */
  function graphNode(data) {
  
      var grapher = this;
      // JOIN
      var inputGroups = this.neuralNetworkG.selectAll(this.svgElement + " .output-group")
          .data(data);
  
      // EXIT old elements not present in new data.
      inputGroups.exit()
          .attr("class", "exit")
          .remove();
  
      // UPDATE old elements present in new data.
      inputGroups.attr("class", "output-group")
          .attr("transform", function (d) {
              return "translate(" +
                  (d.x)
                  + ","
                  + (d.y) + ")";
          }.bind(this));
  
      inputGroups.select("#output-name")
          .text(function (d) {
              return "Y" + (d.index + 1)
          });
  
  
      // ENTER new elements present in new data.
      var inputs = inputGroups.enter()
          .append("g")
          .attr("class", "output-group")
          .attr("transform", function (d) {
              return "translate(" +
                  (d.x)
                  + ","
                  + (d.y) + ")";
          }.bind(this))
          .on("mouseover", function (d) {
  
              // Highlight the lines connected to this node
              d3.selectAll(grapher.svgElement + " line.output-" + d.index)
                  .classed("nn-arrow-highlighted", true);
  
              // Highlight the weights connected to the node
              var weightGroups = d3.selectAll(grapher.svgElement + " .output-" + d.index + "-weight");
              weightGroups.selectAll("ellipse")
                  .classed("nn-node-highlighted", true);
  
  
              var layerInputs = d3.selectAll(grapher.svgElement + " .layer-1-input-groups .output-" + d.index)
                  .classed("layer-input-value-highlighted", true);
              var xValue = d.x;
  
              // Set up the tooltip to show the inputs and their sum
              var rows = '<table class="input-calculation">', total = 0,
                  classToColorNumber = 'weight-layer-input';
              rows = rows + "<tr><th style='text-align:center'>#</th><th>input value</th></tr> ";
              for (var i = 0; i < grapher.layerInputs[1].length; i++) {
  
                  if( i + 1 == grapher.layerInputs[1].length)
                      classToColorNumber = 'bias-layer-input';
  
                  rows = rows + "<tr><td class='importance-10'>" + (i + 1) + "</td><td class='number "+
                      classToColorNumber+"'>" +
                      roundPrecision(grapher.layerInputs[1][i][d.index], 5) + "</td></tr>";
  
                  total = total + grapher.layerInputs[1][i][d.index];
              }
              rows = rows + "<tr class='total-row'><td class='importance-10'>Sum:</td><td class='sum-value number'>"
                  + roundPrecision(total, 5) + "</td></tr>";
              rows = rows + "</table>";
  
  
              var matrix = this.getScreenCTM()
                  .translate(+ this.getAttribute("cx"), + this.getAttribute("cy"));
              //tooltip.html(d)
              //    .style("left", (window.pageXOffset + matrix.e + 15) + "px")
              //    .style("top", (window.pageYOffset + matrix.f - 30) + "px");
  
              grapher.tooltipDiv.transition()
                  .duration(100)
                  .style("opacity", 1);
              grapher.tooltipDiv.html(rows)
                  .style("left", (d3.event.pageX) + "px")
                  .style("top", (d3.event.pageY) + "px");
  
          })
          .on("mouseout", function (d) {
  
              // Stop highlighting the lines connected to this node
              d3.selectAll(grapher.svgElement + " .output-" + d.index)
                  .classed("nn-arrow-highlighted", false);
  
  
              // Stop highlighting the weights connected to the node
              var weightGroups = d3.selectAll(grapher.svgElement + " .output-" + d.index + "-weight");
              weightGroups.selectAll("ellipse")
                  .classed("nn-node-highlighted", false);
  
  
              // Stop highlighting the weights connected to the node
              var layerInputs = d3.selectAll(grapher.svgElement + " .layer-1-input-groups .output-" + d.index)
                  .classed("layer-input-value-highlighted", false);
              //weightGroups.selectAll("ellipse")
              //    .classed("nn-node-highlighted", false);
  
  
              grapher.tooltipDiv.transition()
                  .duration(200)
                  .style("opacity", 0);
          });
      
  
      inputs
          .append("circle")
          .attr("class", "outlined-output-node nn-node")
          .attr("r", this.nodeRadius)
          .attr("cx", 0)
          .attr("cy", 0);
  
      inputs.append("text")
          .attr("id", "output-name")
          .attr("class", "node-text")
          .attr("text-anchor", "middle")
          .attr("x", 0)
          .attr("y", 5);
      //.text(function(d){
      //    return "Y" + (d.index +1)
      //});
  
  
  }
  
  
  function updateOutputNodesText() {
      var outputTexts = this.neuralNetworkG.selectAll(this.svgElement + " .output-group .node-text");
      outputTexts.text(function (d) {
          //return roundPrecision(this.layerValues[1][d.index], 2);
          return numberFormatter(this.layerValues[1][d.index], 2);
      }.bind(this));
  }
  
  
  function getOutputNodeCoordinates(outputNodeCount, biasLayerX, getYCoordinateOfNodeInOutputLayer) {
      var outputLayerCoordinates = d3.range(outputNodeCount).map(function (i) {
          var y = getYCoordinateOfNodeInOutputLayer(i);
          return {x: biasLayerX, y: y, index: i, type: "input"};
      }.bind(this));
  
      return outputLayerCoordinates;
  }
  
  /**
   * Created by alammar on 2/7/17.
   */
  function graphLayerInputs(layerInputsArray, layerNumber) {
  
  
      var layerInputs = layerInputsArray[layerNumber], layerInputsObjects = [];
  
      for (var i = 0; i < layerInputs.length; i++)
          for (var j = 0; j < layerInputs[i].length; j++) {
              layerInputsObjects.push({
                  input: i,
                  output: j,
                  value: layerInputs[i][j],
                  angle: this.lineAngles[i][j],
                  type: i == (layerInputs.length - 1) ? "bias" : "input"
              });
          }
  
  
      var layerInputGroups = this.neuralNetworkG.selectAll(this.svgElement + " .layer-" + layerNumber + "-input-groups")
          .data(layerInputsObjects);
  
      layerInputGroups.select("text").text(function (d) {
          return numberFormatter(d.value, 5);
      });
      //var texts = layerInputGroups.selectAll("text");
  
      //texts.text(function(d){
      //    console.log(d.value, numberFormatter(d.value, 5))
      //    return numberFormatter(d.value, 5);
      //});
  
  
      // Possible change: Make into its own function. Create elements on initialization, only update text on input change
      var grapher = this, xCoordinate = 225, offset = 4;
      var layerInputselements = layerInputGroups.enter()
          .append("g")
          .attrs({
              class: "layer-" + layerNumber + "-input-groups",
              transform: function (d) {
                  // Some geometry to calculate the y coordinate of the input value
                  // The Y coordinate relative to the input node is really the opposite of the angle
                  // Its length is the tan of the angle * the adjacent
                  var yRelativeToInputNode = Math.tan(grapher.lineAngles[d.input][d.output]) *
                          (xCoordinate - grapher.inputLayerX),
                  // Now to transform the relative Y to absolute Y
                      yCoordinate = grapher.inputLayerCoordinates[d.input].y - yRelativeToInputNode - offset,
                      opposite = grapher.outputLayerCoordinates[d.output].y - yCoordinate,
                      adjacent = grapher.lineAngles[d.input][d.output] != 0 ? opposite / Math.tan(-grapher.lineAngles[d.input][d.output]) : 0.0001,
                      adjustedXCoordinate = xCoordinate - adjacent;
  
                  return "translate(" + xCoordinate + "," + yCoordinate + ")";
              }
          });
  
  
      layerInputselements.append("text")
          .attrs({
              class: function (d) {
                  var colorClass = d.type == "input" ? "layer-input-value-from-input" : "layer-input-value-from-bias";
                  return "layer-" + layerNumber + "-inputs layer-input-value output-" + d.output + " input-" + d.input + " " + colorClass
              },
              "text-anchor": "middle",
              x: 0,
              y: 0,
              transform: function (d) {
                  return "rotate(" + (-Math.degrees(d.angle)) + ")"
              }
          })
          .text(function (d) {
              return numberFormatter(d.value, 5);
              //if (d.value > 1) return Math.round(d.value);
              //if (d.value > 0.0001) return roundPrecision(d.value, 5);
              //else
              //    return d.value.toExponential(2)
          });
  }
  
  
  function updateLayerInputs(layerInputsArray, layerNumber) {
      var layerInputtext = this.neuralNetworkG.selectAll(this.svgElement + " .layer-" + layerNumber + "-input-groups layer-" + layerNumber + "-input-groups text");
  
      layerInputtext.text(function (d) {
          return numberFormatter(layerInputsArray[1][d.index], 5);
      }.bind(this));
  
  }
  
  /**
   * Created by alammar on 2/7/17.
   */
  
  function graphSoftmax(data) {
  
      var softmax = this.neuralNetworkG.selectAll(this.svgElement + " .softmax")
          .data([data]);
  
  
      var softmaxGroup = softmax.enter()
          .append("g")
          .attr("class", "softmax")
          .attr("transform", function (d) {
              return "translate(" +
                  (d.x)
                  + ","
                  + (d.y) + ")";
          });
  
      // Define function to create and open tooltip
      var activate = function(d){
          if( viz.layerValues[1] ){
              // Set up the tooltip to show the inputs and their sum
              var rows = '<table class="softmax-calculation">', total = 0;
              rows = rows + "<tr>" +
                  "<th>Softmax input</th>" +
                  "<th>Calculation</th>" +
                  "<th>Softmax output</th>" +
                  "<th>Class</th>" +
                  "</tr>";
  
              var sum_term = viz.layerValues[1].map(function(d){return "e^{" + numberFormatter(d, 2) + "}"}).join(" + ");
              var exponential_terms = [];
              var softMaxTopResult = viz.softmaxOutputs.indexOf(d3.max(viz.softmaxOutputs));
              for( var i = 0; i < viz.layerValues[1].length; i ++ ){
                  exponential_terms[i] = " e^{" + numberFormatter(viz.layerValues[1][i], 2)+"}";
                  var eq =  katex.renderToString("\\frac{"+exponential_terms[i]+"}{"+sum_term+"}");
                  // If the node has the top softmax value, highlight it
                  var highlight_cell = softMaxTopResult == i ? "highlighted" : "";
  
                  var node_row = "<tr>" +
                      "<td class='input'>"+numberFormatter(viz.layerValues[1][i], 2)+"</td>" +
                      "<td class='calculation'> " + eq +"</td>" +
                      "<td class='"+highlight_cell+"'>"+ numberFormatter(viz.softmaxOutputs[i], 2)+"</td>" +
                      "<td class='class-name "+highlight_cell+"'>"+ viz.classes[i]+"</td>" +
                      "</tr>";
  
                  rows = rows + node_row;
              }
  
              viz.tooltipDiv.transition()
                  .duration(100)
                  .style("opacity", 1);
              viz.tooltipDiv.html(rows)
                  .style("left", (d3.event.pageX) + "px")
                  .style("top", (d3.event.pageY) + "px");
          }
          else{
              var html = "Hover-over or tap one of the row in the table, then you'll see the softmax calculation here.";
  
              viz.tooltipDiv.transition()
                  .duration(100)
                  .style("opacity", 1);
              viz.tooltipDiv.html(html)
                  .style("left", (d3.event.pageX) + "px")
                  .style("top", (d3.event.pageY) + "px");
          }
      };
  
      // Define function to close tooltip
      var deactivate = function(d){
          viz.tooltipDiv.transition()
              .duration(200)
              .style("opacity", 0);
      };
  
  
      var viz = this;
      softmaxGroup
          .append("rect")
          .attr("class", "outlined-softmax-node nn-node")
          .attr("width", this.nodeRadius * 1.5)
          .attr("height", this.neuralNetworkHeight)
          .attr("rx", this.nodeRadius / 4)
          .attr("ry", this.nodeRadius / 4)
          .attr("x", 0 - this.nodeRadius / 2)
          .attr("y", 0)
          .on("mouseover", activate)
          .on("touchenter", activate)
          .on("touchmove", function(d){
              d3.event.preventDefault();
          })
          .on("mouseout", deactivate)
          .on("touchleave", deactivate);
  
  
      softmaxGroup.append("text")
          .attr("id", "biasValue")
          .attr("text-anchor", "middle")
          .attr("x", this.neuralNetworkHeight / 2)
          .attr("y", -2)
          .attr("transform", "rotate(90)")
          .text("softmax");
  
  }
  
  
  function graphSoftmaxOutputs (softmaxOutputs) {
      var grapher = this;
      var data = [];
      for (var i = 0; i < this.softmaxOutputs.length; i++) {
          data.push({
              index: i,
              value: softmaxOutputs[i]
          });
      }
  
      var maxValueIndex = d3.max(this.softmaxOutputs);
  
      var softmaxOutputGroups = this.neuralNetworkG.selectAll(this.svgElement + " .softmax-output-group")
          .data(data);
  
      softmaxOutputGroups.select("text")
          .attr("class", function(d){
              if (d.value == maxValueIndex)
                  return "highlighted-softmax-output";
              else
                  return "dim-softmax-output";
          })
          .text(function (d) {
  
              return numberFormatter(d.value * 100, 2) + "%"
          });
  
      var softmaxOutputElements = softmaxOutputGroups.enter()
          .append("g")
          .attrs({
              class: "softmax-output-group",
              transform: function (d) {
                  return "translate(" + (grapher.softmaxOutputsX - 8) + ", " + (grapher.getYCoordinateOfNodeInOutputLayer(d.index) + 5) + ")";
              },
              //"font-size": function(d){return 10 + d.value * 8 }
          });
  
      softmaxOutputElements.append("text")
          .attr("class", function(d){
              if (d.value == maxValueIndex)
                  return "highlighted-softmax-output"
              else
                  return "dim-softmax-output"
  
          })
          .text(function (d) {
              return numberFormatter(d.value * 100, 2) + "%"
          });
  }
  
  
  
  
  function highlightTopSoftmaxOutput(){
  
      var highlightOrDimClass = '', topClassIndex = -1;
      if(this.softmaxOutputs){
          topClassIndex = this.softmaxOutputs.indexOf(d3.max(this.softmaxOutputs));
      }
  
      var softmaxOutputClassName = this.neuralNetworkG.selectAll(this.svgElement + " .softmax-output-class-name");
      softmaxOutputClassName
          .attr("class", function(d, i){
              var classValue = "softmax-output-class-name ";
  
              if(d.outputNode == topClassIndex)
                  classValue = classValue+ "highlighted-softmax-output";
              else
                  classValue = classValue+ "dim-softmax-output";
  
              return classValue
          });
  
  }
  
  
  
  
  softmax = function (input){
  
      if( !(input instanceof Array))
          throw(TypeError("Input passed into softmax() is not an array. softmax() expects an array. Input: "+ input, "nnVizUtils.js"));
  
      var output = [], expSum = 0;
  
      // Calculate the sum of exp's
      for (var i = 0; i < input.length; i++){
          if( !(typeof(input[i]) == "number") ){
              throw(TypeError("An element in the array passed to softmax() is not a number. softmax() expects an array of numbers. Input: "+ input[i], "nnVizUtils.js"));
          }
          expSum = expSum + Math.exp(input[i]);
      }
  
      for (var j = 0; j < input.length; j++)
          output[j] = Math.exp(input[j]) / expSum;
  
      return output;
  };
  
  /**
   * Created by alammar on 2/11/17.
   */
  
  
  
  function graphSigmoid(data) {
  
      var sigmoid = this.neuralNetworkG.selectAll(this.svgElement + " .sigmoid")
          .data([data]);
  
  
      var sigmoidGroup = sigmoid.enter()
          .append("g")
          .attr("class", "sigmoid activation")
          .attr("transform", function (d) {
              return "translate(" +
                  (d.x)
                  + ","
                  + (d.y) + ")";
          });
  
  
      var viz = this,
          height = this.neuralNetworkHeight / 2,
          width = this.nodeRadius * 1.5,
          yDisplacement = -(height / 2);
  
      // Define function to create and open tooltip
      var activate = function (d) {console.log("INSIDE", viz.layerValues[1]);
          if (viz.layerValues[1]) {
              // Set up the tooltip to show the inputs and their sum
              var rows = '<table class="softmax-calculation">', total = 0,
                  inputValue = roundPrecision(viz.layerValues[1][0], 5);
              rows = rows + "<tr>" +
                  "<th>sigmoid input</th>" +
                  "<th>Calculation</th>" +
                  "<th>sigmoid output</th>" +
                  "</tr>";
  
              var sum_term = viz.layerValues[1].map(function (d) {
                  return "e^{" + numberFormatter(d, 2) + "}"
              }).join(" + ");
              var exponential_terms = [];
  
              // The following line generates the html, I then copy it here hardcoded to highlight the terms I need to highlight
              //var eq = katex.renderToString("\\frac{1}{1 + e^{-(" + inputValue + ")}}");
              var eq = '<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mfrac><mrow><mn>1</mn></mrow><mrow><mn>1</mn><mo>+</mo><msup><mi>e</mi><mrow><mo>−</mo><mo>(</mo><mo>−</mo><mn>0</mn><mi mathvariant="normal">.</mi><mn>5</mn><mn>6</mn><mn>4</mn><mn>3</mn><mn>3</mn><mo>)</mo></mrow></msup></mrow></mfrac></mrow><annotation encoding="application/x-tex">\frac{1}{1 + e^{-(-0.56433)}}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="strut" style="height:0.845108em;"></span><span class="strut bottom" style="height:1.2907389999999999em;vertical-align:-0.4456309999999999em;"></span><span class="base textstyle uncramped"><span class="mord reset-textstyle textstyle uncramped"><span class="sizing reset-size5 size5 reset-textstyle textstyle uncramped nulldelimiter"></span><span class="mfrac"><span class="vlist"><span style="top:0.38729999999999987em;"><span class="fontsize-ensurer reset-size5 size5"><span style="font-size:0em;">​</span></span><span class="reset-textstyle scriptstyle cramped"><span class="mord scriptstyle cramped"><span class="mord mathrm">1</span><span class="mbin">+</span><span class="mord"><span class="mord mathit">e</span><span class="vlist"><span style="top:-0.289em;margin-right:0.07142857142857144em;"><span class="fontsize-ensurer reset-size5 size5"><span style="font-size:0em;">​</span></span><span class="reset-scriptstyle scriptscriptstyle cramped"><span class="mord scriptscriptstyle cramped"><span class="mord">−</span><span class="mopen">(</span><span class="mord"></span>         <span class="mord mathrm input">' + inputValue + '</span>         <span class="mclose">)</span></span></span></span><span class="baseline-fix"><span class="fontsize-ensurer reset-size5 size5"><span style="font-size:0em;">​</span></span>​</span></span></span></span></span></span><span style="top:-0.22999999999999998em;"><span class="fontsize-ensurer reset-size5 size5"><span style="font-size:0em;">​</span></span><span class="reset-textstyle textstyle uncramped frac-line"></span></span><span style="top:-0.394em;"><span class="fontsize-ensurer reset-size5 size5"><span style="font-size:0em;">​</span></span><span class="reset-textstyle scriptstyle uncramped"><span class="mord scriptstyle uncramped"><span class="mord mathrm">1</span></span></span></span><span class="baseline-fix"><span class="fontsize-ensurer reset-size5 size5"><span style="font-size:0em;">​</span></span>​</span></span></span><span class="sizing reset-size5 size5 reset-textstyle textstyle uncramped nulldelimiter"></span></span></span></span></span>';
  
              var node_row = "<tr>" +
                  "<td class='input'>" + inputValue + "</td>" +
                  "<td class='calculation'> " + eq + "</td>" +
                  "<td>" + numberFormatter(viz.sigmoidOutput, 2) + "</td>" +
                  "</tr>";
  
              rows = rows + node_row;
  
  
              viz.tooltipDiv.transition()
                  .duration(100)
                  .style("opacity", 1);
              viz.tooltipDiv.html(rows)
                  .style("left", (d3.event.pageX - 250) + "px")
                  .style("top", (d3.event.pageY) + "px");
  
          }
          else {
              var html = "Hover-over or tap one of the row in the table, then you'll see the sigmoid calculation here.";
  
              viz.tooltipDiv.transition()
                  .duration(100)
                  .style("opacity", 1);
              viz.tooltipDiv.html(html)
                  .style("left", (d3.event.pageX - 250) + "px")
                  .style("top", (d3.event.pageY) + "px");
          }
      };
  
  // Define function to close tooltip
      var deactivate = function (d) {
          viz.tooltipDiv.transition()
              .duration(200)
              .style("opacity", 0);
      };
  
      sigmoidGroup
          .append("rect")
          .attr("class", "outlined-sigmoid-node nn-node")
          .attr("width", width)
          .attr("height", height)
          .attr("rx", this.nodeRadius / 4)
          .attr("ry", this.nodeRadius / 4)
          .attr("x", -width / 2)
          .attr("y", yDisplacement)
          .on("mouseover", activate)
          .on("touchenter", activate)
          .on("touchmove", function (d) {
              d3.event.preventDefault();
          })
          .on("mouseout", deactivate)
          .on("touchleave", deactivate);
  
  
      sigmoidGroup.append("text")
          .attr("id", "sigmoid-label")
          .attr("text-anchor", "middle")
          .attr("x", 0)
          .attr("y", -2)
          //.attr("transform", "rotate(90)")
          .text("σ");
  
  
  }
  
  
  function graphSigmoidOutput(sigmoidOutput) {
      var grapher = this;
      var data = [{i: 0, value: sigmoidOutput}];
  
      var maxValueIndex = 0;
  
      var sigmoidOutputGroups = this.neuralNetworkG.selectAll(this.svgElement + " .sigmoid-output-group")
          .data(data);
  
      sigmoidOutputGroups.select("text")
          .attr("class", "sigmoid-output")
          .text(function (d) {
              return numberFormatter(d.value * 100, 2) + "%"
          });
  
      var sigmoidOutputElements = sigmoidOutputGroups.enter()
          .append("g")
          .attrs({
              class: "sigmoid-output-group",
              transform: function (d) {
                  return "translate(" + (grapher.softmaxOutputsX - 8) + ", " + (grapher.getYCoordinateOfNodeInOutputLayer(d.index) + 5) + ")";
              }
          });
  
      sigmoidOutputElements.append("text")
          .attr("class", "sigmoid-output")
          .text(function (d) {
              return numberFormatter(d.value * 100, 2) + "%"
          });
  }
  
  
  function calculateSigmoid(data) {
      return 1 / ( 1 + Math.exp(-data));
  }
  
  
  function updateSigmoid(input) {
      this.sigmoidOutput = calculateSigmoid(input);
  
      // Display sigmoid and final output
      this.graphSigmoidOutput(this.sigmoidOutput);
  
      this.highlightTopSoftmaxOutput();
  }
  
  /**
   * Created by alammar on 3/10/17.
   */
  
  
  
  function getInputToBiasLinesData(inputLayerNodeCount, outputNodeCount,
      inputLayerCoordinates, outputLayerCoordinates){
      var inputToBiasLines = [];
  
      // Calculate the coordiantes of the lines from input to bias
      for (var i = 0; i < inputLayerNodeCount; i++)
          for (var j = 0; j < outputNodeCount; j++) {
              inputToBiasLines.push({
                  x1: inputLayerCoordinates[i].x,
                  y1: inputLayerCoordinates[i].y,
                  x2: outputLayerCoordinates[j].x,
                  y2: outputLayerCoordinates[j].y,
                  inputIndex: i,
                  outputIndex: j
              });
          }
  
      return inputToBiasLines;
  }
  
  
  function getBiasToSoftmaxLinesData(outputNodeCount, outputLayerCoordinates, activationLayerX, nodeRadius){
  
      var BiasToSoftmaxLines = [];
      // Calculate the coordiantes of the lines from input to bias
      for (var i = 0; i < outputNodeCount; i++)
          BiasToSoftmaxLines.push({
              x1: outputLayerCoordinates[i].x,
              y1: outputLayerCoordinates[i].y,
              x2: activationLayerX - nodeRadius,
              y2: outputLayerCoordinates[i].y
          });
  
      console.log("BiasToSoftmaxLines", BiasToSoftmaxLines);
      return BiasToSoftmaxLines;
  }
  
  
  function graphInputToBiasLines(data) {
  
      var inputToBiasLines = this.neuralNetworkG.selectAll(this.svgElement + " .input-to-bias-line")
          .data(data);
  
      inputToBiasLines.exit()
          .remove();
  
      var classes = "nn-arrow input-to-bias-line";
  
      inputToBiasLines
          .attrs({
              class: function (d) {
                  return classes + " input-" + d.inputIndex + " output-" + d.outputIndex
              },
              x1: function (d) {
                  return d.x1
              }, // From the center of the input node
              y1: function (d) {
                  return d.y1
              },
              x2: function (d) {
                  return d.x2
              },    // To the center of the bias node
              y2: function (d) {
                  return d.y2
              }
          });
  
      inputToBiasLines.enter()
          .append('line')
          .attrs({
              "class": function (d) {
                  return classes + " input-" + d.inputIndex + " output-" + d.outputIndex
              },
              "x1": function (d) {
                  return d.x1
              }, // From the center of the input node
              "y1": function (d) {
                  return d.y1
              },
              "x2": function (d) {
                  return d.x2
              },    // To the center of the bias node
              "y2": function (d) {
                  return d.y2
              }
          });
  
  }
  
  
  
  
  function getSoftmaxtoOutputLinesData(outputNodeCount, activationLayerX, nodeRadius, outputLayerCoordinates,
                                              outputLayerX, classes ){
  
      var softmaxtoOutputLines = [];
      // Calculate the coordiantes of the lines from input to bias
      for (var i = 0; i < outputNodeCount; i++)
          softmaxtoOutputLines.push({
              x1: activationLayerX + nodeRadius + 6,
              y1: outputLayerCoordinates[i].y,
              x2: outputLayerX - nodeRadius,
              y2: outputLayerCoordinates[i].y,
              outputNode: i,
              class_name: classes[i]
          });
  
      return softmaxtoOutputLines;
  
  }
  
  
  function graphBiasToSoftmaxArrows(data) {
  
      var inputToBiasLines = this.neuralNetworkG.selectAll(this.svgElement + " .bias-to-softmax-line")
          .data(data);
  
      inputToBiasLines.exit()
          .remove();
  
      inputToBiasLines
          .attrs({
              class: "nn-arrow bias-to-softmax-line",
              "marker-end": "url(#arrow)",
              x1: function (d) {
                  return d.x1
              }, // From the center of the input node
              y1: function (d) {
                  return d.y1
              },
              x2: function (d) {
                  return d.x2
              },    // To the center of the bias node
              y2: function (d) {
                  return d.y2
              }
          });
  
      inputToBiasLines.enter()
          .append('line')
          .attrs({
              "class": "nn-arrow bias-to-softmax-line",
              "marker-end": "url(#arrow)",
              "x1": function (d) {
                  return d.x1
              }, // From the center of the input node
              "y1": function (d) {
                  return d.y1
              },
              "x2": function (d) {
                  return d.x2
              },    // To the center of the bias node
              "y2": function (d) {
                  return d.y2
              }
          });
  
  }
  
  
  function graphSoftmaxToOutputArrows(data) {
  
      var inputToBiasLines = this.neuralNetworkG.selectAll(this.svgElement + " .softmax-to-output-line")
          .data(data);
  
      inputToBiasLines.exit()
          .remove();
  
      inputToBiasLines
          .attrs({
              class: "nn-arrow softmax-to-output-line",
              "marker-end": "url(#arrow)",
              x1: function (d) {
                  return d.x1
              }, // From the center of the input node
              y1: function (d) {
                  return d.y1
              },
              x2: function (d) {
                  return d.x2
              },    // To the center of the bias node
              y2: function (d) {
                  return d.y2
              }
          });
  
      inputToBiasLines.enter()
          .append('line')
          .attrs({
              "class": "nn-arrow softmax-to-output-line",
              "marker-end": "url(#arrow)",
              "x1": function (d) {
                  return d.x1
              }, // From the center of the input node
              "y1": function (d) {
                  return d.y1
              },
              "x2": function (d) {
                  return d.x2
              },    // To the center of the bias node
              "y2": function (d) {
                  return d.y2
              }
          });
  
  
  
  
      var softmaxOutputClassName = this.neuralNetworkG.selectAll(this.svgElement + " .softmax-output-class-name")
          .data(data);
  
      softmaxOutputClassName.exit()
          .remove();
  
      softmaxOutputClassName
          .append('text')
          .attrs({
              "class": function(d){
                  return "softmax-output-class-name "+  d.highlightedOrDim
              },
              "x": function (d) {
                  return d.x1
              }, // From the center of the input node
              "y": function (d) {
                  return d.y1 + 25
              }
          })
          .text(function(d, i){
              return d.class_name
          });
  
      softmaxOutputClassName.enter()
          .append('text')
          .attrs({
              "class": "softmax-output-class-name",
              "x": function (d) {
                  return d.x1
              }, // From the center of the input node
              "y": function (d) {
                  return d.y1 + 25
              }
          })
          .text(function(d, i){
              return d.class_name
          });
  
  
  }
  
  /**
   * Created by alammar on 3/12/17.
   */
  
  // Linear scale between this.neuralNetworkMargin.left and this.neuralNetworkWidth + this.neuralNetworkMargin.left + this.properties.outputRegionWidth
  function getLayerXScale (numberOfLayers, layersRegionWidth, outputRegionWidth, marginLeft, nodeRadius){
      var returnFunction;
      if( numberOfLayers == 1){ // If only one node, draw it in the center
          returnFunction = function(){return  (layersRegionWidth /2) + marginLeft};
      }
      else{ // Otherwise, separate the vertical space linearly
          returnFunction = d3.scaleLinear()
              .domain([0, numberOfLayers -1 ])
              .range([marginLeft + nodeRadius,
                  layersRegionWidth - nodeRadius - outputRegionWidth]);
      }
      return returnFunction;
  }
  
  
  function  getNodeYCoordinate (numberOfNodesInLayer, chartAreaHeight, marginTop, nodeRadius){
      var returnFunction;
      if( numberOfNodesInLayer == 1){ // If only one node, draw it in the center
          returnFunction = function(){return  (chartAreaHeight /2) + marginTop};
      }
      else{ // Otherwise, separate the vertical space linearly
          returnFunction = d3.scaleLinear()
              .domain([0, numberOfNodesInLayer -1 ])
              .range([marginTop + nodeRadius,
                  chartAreaHeight - nodeRadius]);
      }
      return returnFunction;
  }
  
  /**
   * Created by alammar on 3/12/17.
   */
  var input = {
      getNodeRenderingDetails: function(layer, properties){
  
          var inputAndBiasNodeCount = layer.nodes;
          if( layer.bias == true)
              inputAndBiasNodeCount++;
          console.log("inputAndBiasNodeCount", inputAndBiasNodeCount);
  
          // Create a scale to determine the Y coordinate of each node
          var layerNodesYScale = getNodeYCoordinate(inputAndBiasNodeCount, properties.activeRegionHeight,
              properties.margins.top, properties.nodeRadius);
  
  
          var layerCoordinates = getInputNodeCoordinates$1(layer.nodes, layer.x, layerNodesYScale);
          return layerCoordinates;
      },
  
      draw: function(layer, properties, activeRegionSVGGroup){
          var data = layer.nodeRenderingDetails,
              containerElement = properties.containerElement;
          var inputGroups = activeRegionSVGGroup.selectAll(properties.containerElement + " .input-group")
              .data(data);
  
          // EXIT old elements not present in new data.
          inputGroups.exit()
              .attr("class", "exit")
              .remove();
  
          // UPDATE old elements present in new data.
          inputGroups.attr("class", "input-group")
              .attr("transform", function (d) {
                  return "translate(" +
                      (d.x)
                      + ","
                      + (d.y) + ")";
              }.bind(this));
  
          inputGroups.select("#input-name")
              .text(function (d) {
                  return "X" + (d.index + 1)
              });
  
  
          // ENTER new elements present in new data.
          var inputs = inputGroups.enter()
              .append("g")
              .attr("class", "input-group")
              .attr("transform", function (d) {
                  return "translate(" +
                      (d.x)
                      + ","
                      + (d.y) + ")";
              }.bind(this))
              .on("mouseover", function (d) {
  
                  // Highlight the lines connected to this node
                  d3.selectAll(properties.containerElement + " line.input-" + d.index)
                      .classed("nn-arrow-highlighted", true);
  
                  // Highlight the weights connected to the node
                  var weightGroups = d3.selectAll(properties.containerElement + " .input-" + d.index + "-weight");
                  weightGroups.selectAll("ellipse")
                      .classed("nn-node-highlighted", true);
                  var layerInputs = d3.selectAll(properties.containerElement + " .layer-1-input-groups .input-" + d.index)
                      .classed("layer-input-value-highlighted", true);
              })
              .on("mouseout", function (d) {
  
                  // Stop highlighting the lines connected to this node
                  d3.selectAll(properties.containerElement + " .input-" + d.index)
                      .classed("nn-arrow-highlighted", false);
  
                  // Highlight the weights connected to the node
                  var weightGroups = d3.selectAll(properties.containerElement + " .input-" + d.index + "-weight");
                  weightGroups.selectAll("ellipse")
                      .classed("nn-node-highlighted", false);
  
                  var layerInputs = d3.selectAll(properties.containerElement + " .layer-1-input-groups .input-" + d.index)
                      .classed("layer-input-value-highlighted", false);
              });
  
          inputs
              .append("circle")
              .attr("class", function (d) {
                  if (d.type == "input") return "outlined-input-node nn-node";
                  else if (d.type == "bias") return "outlined-bias-node nn-node";
              })
              .attr("r", properties.nodeRadius)
              .attr("cx", 0)
              .attr("cy", 0);
  
          inputs.append("text")
              .attr("id", "input-name")
              .attr("class", "node-text")
              .attr("text-anchor", "middle")
              .attr("x", 0)
              .attr("y", 5)
              .text(function (d) {
                  if (d.type == "input")
                      if(d.index == 0)
                          return "Age";
                      else
                          return "Sex";
                  else if (d.type == "bias") return "Bias";
              });
  
          inputGroups.moveUp();
      }
  };
  
  
  function getInputNodeCoordinates$1(inputNodeCount, inputLayerX, getYCoordinateOfNodeInInputLayer) {
  
      var inputLayerCoordinates = d3.range(inputNodeCount).map(function (i) {
          var y = getYCoordinateOfNodeInInputLayer(i);
          return {x: inputLayerX, y: y, index: i, type: "input"};
      });
  
      //  Add the bias node to the end of the input layer
      inputLayerCoordinates.push({
          x: inputLayerX, y: getYCoordinateOfNodeInInputLayer(inputNodeCount),
          index: inputNodeCount, type: "bias"
      });
  
      return inputLayerCoordinates;
  }
  
  /**
   * Created by alammar on 3/13/17.
   */
  var block = {
      getNodeRenderingDetails: function(layer, properties){
          var blockRenderingDetails ={
              x: layer.x,
              y: (properties.activeRegionHeight / 2) + properties.margins.top,
              index: 0,
              type: "block",
              activation: layer.activation
          };
          return blockRenderingDetails;
      },
  
      draw: function(layer, properties, activeRegionSVGGroup){
  
          // JOIN
          // Each node has an SVG group of its own. In that group is an SVG circle and SVG text label
          var nodeGroups = activeRegionSVGGroup.selectAll(properties.containerElement + " .layer-" + layer.index)
              .data([layer.nodeRenderingDetails]);
          console.log(layer.nodeRenderingDetails);
  
  
          // ENTER new elements present in new data.
          var nodes = nodeGroups.enter()
              .append("g")
              .attr("class", "layer-" + layer.index + " layer-group")
              .attr("transform", function (d) {
                  return "translate(" +
                      (d.x)
                      + ","
                      + (d.y) + ")";
              });
          console.log(nodes);
  
  
  
          nodes
              .append("rect")
              .attr("class", "outlined-sigmoid-node nn-node")
              .attr("rx", properties.nodeRadius)
              .attr("width", properties.nodeRadius * 2)
              .attr("height", properties.activeRegionHeight)
              .attr("x", -properties.nodeRadius)
              .attr("y", -properties.activeRegionHeight/2);
  
  
  
          nodes.append("text")
              //.attr("id", "sigmoid-label")
              //.attr("class", "node-text")
              .attr("text-anchor", "middle")
              .attr("x", 0)
              .attr("y", properties.activeRegionHeight/2 - 12)
              .text(layer.nodes);
  
  
          nodes.append("svg:image")
              .attr('x', -15 )
              .attr('y', -properties.activeRegionHeight/2 + 12)
              .attr('width', 30)
              .attr('height', 10)
              .attr("xlink:href","../../images/relu-symbol.png");
      }
  };
  
  /**
   * Created by alammar on 3/12/17.
   */
  var nodes = {
          getNodeRenderingDetails: function (layer, properties) {
  
              var layerNodesYScale = getNodeYCoordinate(layer.nodes, properties.activeRegionHeight,
                  properties.margins.top, properties.nodeRadius);
  
              var layerCoordinates = getNodeCoordinates(layer.nodes, layer.x, layerNodesYScale);
              return layerCoordinates;
          },
  
          draw: function (layer, properties, activeRegionSVGGroup, layerInputs, tooltipDiv) {
              console.log("layer", layer);
              var data = layer.nodeRenderingDetails,
                  containerElement = properties.containerElement;
  
              // JOIN
              // Each node has an SVG group of its own. In that group is an SVG circle and SVG text label
              var nodeGroups = activeRegionSVGGroup.selectAll(containerElement + " .layer-" + layer.index)
                  .data(data);
  
              // EXIT old elements not present in new data.
              nodeGroups.exit()
                  .attr("class", "exit")
                  .remove();
  
              // UPDATE old elements present in new data.
              nodeGroups.attr("class", "layer-" + layer.index + " layer-group")
                  // Change the location in case the number of nodes has changed
                  .attr("transform", function (d) {
                      return "translate(" +
                          (d.x)
                          + ","
                          + (d.y) + ")";
                  }.bind(this));
  
              nodeGroups.select("#output-name")
                  .text(function (d) {
                      return "Y" + (d.index + 1)
                  });
  
  
              // ENTER new elements present in new data.
              var inputs = nodeGroups.enter()
                  .append("g")
                  .attr("class", ".layer-" + layer.index + " layer-group")
                  .attr("transform", function (d) {
                      return "translate(" +
                          (d.x)
                          + ","
                          + (d.y) + ")";
                  })
                  .on("mouseover", function (d) {
  
                          if (layerInputs != '') {
  
                              // Highlight the lines connected to this node
                              d3.selectAll(activeRegionSVGGroup + " line.output-" + d.index)
                                  .classed("nn-arrow-highlighted", true);
  
                              // Highlight the weights connected to the node
                              var weightGroups = d3.selectAll(activeRegionSVGGroup + " .output-" + d.index + "-weight");
                              weightGroups.selectAll("ellipse")
                                  .classed("nn-node-highlighted", true);
  
  
                              var layerInputs = d3.selectAll(activeRegionSVGGroup + " .layer-1-input-groups .output-" + d.index)
                                  .classed("layer-input-value-highlighted", true);
                              var xValue = d.x;
  
                              // Set up the tooltip to show the inputs and their sum
                              var rows = '<table class="input-calculation">', total = 0,
                                  classToColorNumber = 'weight-layer-input';
                              rows = rows + "<tr><th style='text-align:center'>#</th><th>input value</th></tr> ";
                              for (var i = 0; i < layerInputs[1].length; i++) {
  
                                  if (i + 1 == layerInputs[1].length)
                                      classToColorNumber = 'bias-layer-input';
  
                                  rows = rows + "<tr><td class='importance-10'>" + (i + 1) + "</td><td class='number " +
                                      classToColorNumber + "'>" +
                                      roundPrecision(layerInputs[1][i][d.index], 5) + "</td></tr>";
  
                                  total = total + layerInputs[1][i][d.index];
                              }
                              rows = rows + "<tr class='total-row'><td class='importance-10'>Sum:</td><td class='sum-value number'>"
                                  + roundPrecision(total, 5) + "</td></tr>";
                              rows = rows + "</table>";
  
  
                              var matrix = this.getScreenCTM()
                                  .translate(+this.getAttribute("cx"), +this.getAttribute("cy"));
                              //tooltip.html(d)
                              //    .style("left", (window.pageXOffset + matrix.e + 15) + "px")
                              //    .style("top", (window.pageYOffset + matrix.f - 30) + "px");
  
                              tooltipDiv.transition()
                                  .duration(100)
                                  .style("opacity", 1);
                              tooltipDiv.html(rows)
                                  .style("left", (d3.event.pageX) + "px")
                                  .style("top", (d3.event.pageY) + "px");
                          }
  
  
                      }
                  )
                  .
                  on("mouseout", function (d) {
  
                      if (layerInputs != '') {
                          // Stop highlighting the lines connected to this node
                          d3.selectAll(activeRegionSVGGroup + " .output-" + d.index)
                              .classed("nn-arrow-highlighted", false);
  
  
                          // Stop highlighting the weights connected to the node
                          var weightGroups = d3.selectAll(activeRegionSVGGroup + " .output-" + d.index + "-weight");
                          weightGroups.selectAll("ellipse")
                              .classed("nn-node-highlighted", false);
  
  
                          // Stop highlighting the weights connected to the node
                          var layerInputs = d3.selectAll(activeRegionSVGGroup + " .layer-1-input-groups .output-" + d.index)
                              .classed("layer-input-value-highlighted", false);
                          //weightGroups.selectAll("ellipse")
                          //    .classed("nn-node-highlighted", false);
  
  
                          tooltipDiv.transition()
                              .duration(200)
                              .style("opacity", 0);
                      }
                  });
  
  
              inputs
                  .append("circle")
                  .attr("class", "outlined-output-node nn-node")
                  .attr("r", properties.nodeRadius)
                  .attr("cx", 0)
                  .attr("cy", 0);
  
              inputs.append("text")
                  .attr("id", "output-name")
                  .attr("class", "node-text")
                  .attr("text-anchor", "middle")
                  .attr("x", 0)
                  .attr("y", 5);
  
  
          }
      };
  
  
  function getNodeCoordinates(nodeCount, layerXCoordinate, getYCoordinateOfNodeLayer) {
  
  
      var nodeLayerCoordinates = d3.range(nodeCount).map(function (i) {
          var y = getYCoordinateOfNodeLayer(i);
          return {x: layerXCoordinate, y: y, index: i, type: "input"};
      });
  
      return nodeLayerCoordinates;
  
  }
  
  /**
   * Created by alammar on 3/13/17.
   */
  var sigmoid = {
      getNodeRenderingDetails: function(layer, properties){
  
  
      },
  
      draw: function(layer, properties, activeRegionSVGGroup){
  
      }
  };
  
  /**
   * Created by alammar on 12/23/16.
   */
  
  var renderers = {
      input: input,
      block: block,
      defaultLayer: nodes,
      sigmoid: sigmoid
  };
  
  
  
  var neuralNetworkCalculationViz = function (inputs, inputNodeCount, outputNodeCount, activation, containerElement, weights, biases, tableElement,
                                              trainingSet, classes) {
  
  
      this.properties = {
          graphWidth: 500,
          graphHeight: 300,
          margins: {top: 10, right: 10, bottom: 10, left: 10},
          outputRegionWidth: 50,
          nodeRadius: 25,
          containerElement: inputs.containerElement,
          nodeThreashold: 7, // If more than this, display as block rather than nodes
          get activeRegionWidth(){ return this.graphWidth - this.margins.left - this.margins.right;},
          get activeRegionHeight(){return this.graphHeight - this.margins.top - this.margins.bottom;}
  
      };
  
      if (inputs){ // New abstraction
          //containerElement, graphWidth, graphHeight, margins
          this.initializeGraph(inputs.containerElement, this.properties);
          this.drawGraph(inputs);
  
      }
      else{ // Old ugly code
  
          this.inputNodeCount = inputNodeCount;
          this.inputLayerNodeCount = inputNodeCount + 1;
          this.outputNodeCount = outputNodeCount;
          this.activation = activation;
          this.svgElement = containerElement;
          this.graphWidth = 500;
          this.graphHeight = 300;
          this.nodeRadius = 25;
          this.weightNodeSizeScale = 0.4;
          this.weightNodeWidthRatio = 2.2;
          this.weights = weights;
          this.biases = biases;
          weights.push(biases);
          this.weightsAndBiasWeights = weights; // We will treat biases as an input node with value 1, with the bias value as its weight
          this.layerInputs = [];
          this.layerValues = [];
          this.tableElement = tableElement;
          this.trainingSet = trainingSet;
          this.classes = classes;
  
          this.initializeGraphOld();
          this.drawGraphOld();
          this.initializeTable(trainingSet);
      }
  };
  
  
  // Input
  neuralNetworkCalculationViz.prototype.graphInputNodes = graphInputNodes;
  neuralNetworkCalculationViz.prototype.updateInputNodesText = updateInputNodesText;
  neuralNetworkCalculationViz.prototype.getInputNodeCoordinates = getInputNodeCoordinates;
  // Weight
  neuralNetworkCalculationViz.prototype.graphWeightNodes = graphWeightNodes;
  neuralNetworkCalculationViz.prototype.getWeightNodeCoordinates = getWeightNodeCoordinates;
  // Node / output
  neuralNetworkCalculationViz.prototype.graphBiasNodes = graphNode;
  neuralNetworkCalculationViz.prototype.updateOutputNodesText = updateOutputNodesText;
  neuralNetworkCalculationViz.prototype.getOutputNodeCoordinates = getOutputNodeCoordinates;
  // Layer inputs
  neuralNetworkCalculationViz.prototype.graphLayerInputs = graphLayerInputs;
  neuralNetworkCalculationViz.prototype.updateLayerInputs = updateLayerInputs;
  // Softmax
  neuralNetworkCalculationViz.prototype.graphSoftmax = graphSoftmax;
  neuralNetworkCalculationViz.prototype.graphSoftmaxOutputs = graphSoftmaxOutputs;
  neuralNetworkCalculationViz.prototype.highlightTopSoftmaxOutput = highlightTopSoftmaxOutput;
  // Sigmoid
  neuralNetworkCalculationViz.prototype.graphSigmoid = graphSigmoid;
  neuralNetworkCalculationViz.prototype.graphSigmoidOutput = graphSigmoidOutput;
  neuralNetworkCalculationViz.prototype.updateSigmoid = updateSigmoid;
  // Connection lines
  neuralNetworkCalculationViz.prototype.getInputToBiasLinesData = getInputToBiasLinesData;
  neuralNetworkCalculationViz.prototype.graphInputToBiasLines = graphInputToBiasLines;
  neuralNetworkCalculationViz.prototype.getBiasToSoftmaxLinesData = getBiasToSoftmaxLinesData;
  neuralNetworkCalculationViz.prototype.graphBiasToSoftmaxArrows = graphBiasToSoftmaxArrows;
  neuralNetworkCalculationViz.prototype.getSoftmaxtoOutputLinesData = getSoftmaxtoOutputLinesData;
  neuralNetworkCalculationViz.prototype.graphSoftmaxToOutputArrows = graphSoftmaxToOutputArrows;
  
  
  neuralNetworkCalculationViz.prototype.updateNodeCount = function (nodeType, newValue) {
      if (nodeType == "input") this.inputNodeCount = newValue;
      else if (nodeType == "output") this.outputNodeCount = newValue;
  
      this.drawGraph();
  };
  
  
  
  neuralNetworkCalculationViz.prototype.initializeGraph = function (containerElement, properties) {
      var nnGraphHolder = d3.select(containerElement) // select the 'body' element
          .append("svg")           // append an SVG element to the body
          .attr("width", properties.graphWidth)      // make the SVG element 449 pixels wide
          .attr("height", properties.graphHeight);    // make the SVG element 249 pixels high
  
  
          this.neuralNetworkWidth = + nnGraphHolder.attr("width") - this.properties.margins.left -
              this.properties.margins.right,
          this.neuralNetworkHeight = + nnGraphHolder.attr("height") - this.properties.margins.top -
              this.properties.margins.bottom,
  
          this.neuralNetworkG = nnGraphHolder.append("g");
          this.activeRegionSVGGroup = this.neuralNetworkG;
  
      // Define arrow head
      // http://bl.ocks.org/tomgp/d59de83f771ca2b6f1d4
      var defs = nnGraphHolder.append("defs");
      defs.append("marker")
          .attrs({
              "id": "arrow",
              "viewBox": "0 -5 10 10",
              "refX": 5,
              "refY": 0,
              "markerWidth": 4,
              "markerHeight": 4,
              "orient": "auto"
          })
          .append("path")
          .attr("d", "M0,-5L10,0L0,5")
          .attr("class", "arrowHead");
  
      this.tooltipDiv = d3.select(".nn-tooltip");
      //.append("div")
      //.attr("class", "nn-tooltip")
      //.style("opacity", 0);
  };
  
  
  neuralNetworkCalculationViz.prototype.initializeGraphOld = function () {
      this.nnGraphHolder = d3.select(this.svgElement) // select the 'body' element
          .append("svg")           // append an SVG element to the body
          .attr("width", this.graphWidth)      // make the SVG element 449 pixels wide
          .attr("height", this.graphHeight);    // make the SVG element 249 pixels high
      this.neuralNetworkMargin = {top: 10, right: 10, bottom: 10, left: 10},
          this.neuralNetworkWidth = +this.nnGraphHolder.attr("width") - this.neuralNetworkMargin.left - this.neuralNetworkMargin.right,
          this.neuralNetworkHeight = +this.nnGraphHolder.attr("height") - this.neuralNetworkMargin.top - this.neuralNetworkMargin.bottom,
          this.neuralNetworkG = this.nnGraphHolder.append("g");
  
      // Define arrow head
      // http://bl.ocks.org/tomgp/d59de83f771ca2b6f1d4
      var defs = this.nnGraphHolder.append("defs");
      defs.append("marker")
          .attrs({
              "id": "arrow",
              "viewBox": "0 -5 10 10",
              "refX": 5,
              "refY": 0,
              "markerWidth": 4,
              "markerHeight": 4,
              "orient": "auto"
          })
          .append("path")
          .attr("d", "M0,-5L10,0L0,5")
          .attr("class", "arrowHead");
  
      this.tooltipDiv = d3.select(".nn-tooltip");
          //.append("div")
          //.attr("class", "nn-tooltip")
          //.style("opacity", 0);
  };
  
  
  neuralNetworkCalculationViz.prototype.drawGraph = function (inputs) {
  
      // How many layers?
      var layers = inputs.layers,
          numberOfLayers = inputs.layers.length,
          // Linear scale between this.neuralNetworkMargin.left and this.neuralNetworkWidth + this.neuralNetworkMargin.left + this.properties.outputRegionWidth
          layerXScale = getLayerXScale(numberOfLayers, this.properties.activeRegionWidth, this.properties.margins.left,
              this.properties.outputRegionWidth, this.properties.nodeRadius);
  
  
      for( var i = 0; i < numberOfLayers; i ++){
  
          layers[i].index = i;
  
          // Use scale to set the X coordinates of each layer
          layers[i].x = layerXScale(i);
  
          // Assign a layer-renderer that knows how to draw
          layers[i].renderer =  (typeof layers[i].type !== 'undefined') ? renderers[layers[i].type] : renderers["defaultLayer"];
          //layers[i].renderer =  (layers[i].type == 'input') ? renderers["input"] : renderers["defaultLayer"];
          console.log("renderer: ", layers[i].renderer);
      }
  
  //
  //var graphData = {
  //    layers: [
  //        {
  //            nodes: 3,
  //            type: "input",
  //            bias: true
  //        },
  //        {
  //            nodes: 1
  //        },
  //        {
  //            type: "sigmoid",
  //            nodes: 1
  //        }
  //    ],
  //    containerElement: "#neural-network-architecture-viz",
  //    weights: true,
  //    weightValues: false
  //};
  
      // Calculate Y coordinates of nodes in each layer
      for( var j = 0; j < numberOfLayers; j ++){
  
          layers[j].nodeRenderingDetails = layers[j].renderer.getNodeRenderingDetails(layers[j], this.properties);
      }
  
      // Connection regions
  
  
      // Draw all the things
      for( var m = 0; m < numberOfLayers; m++){
  
          layers[m].renderer.draw(layers[m], this.properties, this.activeRegionSVGGroup, this.layerInputs, this.tooltipDiv);
      }
  
  };
  
  
  neuralNetworkCalculationViz.prototype.getNodeRenderingDetails = function (layer, properties) {
      //if( )
      switch (layer.type){
          case "input": // Special consideration for bias nodes
              layer.nodeRenderingDetails = input.getNodeRenderingDetails(layer, properties);
  
  
              break;
  
          default:
              //console.log("DEFAULT");
              //
              //var layerNodesYScale = getNodeYCoordinate(layer.nodes, properties.activeRegionHeight,
              //    properties.margins.top, properties.nodeRadius);
              //
              //var layerCoordinates = this.getInputNodeCoordinates(layer.nodes, layer.x, layerNodesYScale);
      }
  };
  
  
  
  
  neuralNetworkCalculationViz.prototype.drawGraphOld = function () {
  
      var grapher = this;
      // Let's calculate our coordinates for all the nodes
      // Let's start with the X coordiantes for each layer
      var inputLayerX = this.neuralNetworkMargin.left + this.nodeRadius, // X value of input layer
          biasLayerX = this.neuralNetworkWidth * 2 / 3 - 20, //
          outputLayerX = this.neuralNetworkWidth - this.nodeRadius + this.neuralNetworkMargin.left,
          activationLayerX = (outputLayerX + biasLayerX) / 2;
  
      this.inputLayerX = inputLayerX;
      this.softmaxOutputsX = outputLayerX;
  
  
  
      // Calculate the Y coordinate for each node in each layer
      // 1- Set a scale for each layer
      this.getYCoordinateOfNodeInInputLayer = getNodeYCoordinate(this.inputNodeCount + 1, this.neuralNetworkHeight,
          this.neuralNetworkMargin.top, this.nodeRadius);
  
      this.getYCoordinateOfNodeInOutputLayer = getNodeYCoordinate(this.outputNodeCount, this.neuralNetworkHeight,
          this.neuralNetworkMargin.top, this.nodeRadius);
  
  
      // 2- Primary layers (Y depends only on how many nodes in the layer)
      //  2.1 - Input layergetLayerXScale
      //  Calculate Y coordinates for each node
      this.inputLayerCoordinates = this.getInputNodeCoordinates(this.inputNodeCount, inputLayerX,
          this.getYCoordinateOfNodeInInputLayer);
  
      //  2.2 - Output layer
      this.outputLayerCoordinates = this.getOutputNodeCoordinates(this.outputNodeCount, biasLayerX,
          this.getYCoordinateOfNodeInOutputLayer);
  
  
  
      // ======================================================================
      // Weights
  
      this.weightLayerXCoordinates =  this.getWeightLayerXCoordinates(grapher.nodeRadius, grapher.weightNodeWidthRatio,
          this.outputNodeCount, inputLayerX);
  
  
      // Weight Coordinates
      var weightNodeData = this.getWeightNodeCoordinates(this.inputLayerNodeCount,
          this.inputNodeCount, this.outputNodeCount, this.inputLayerCoordinates, this.weightLayerXCoordinates,
          this.outputLayerCoordinates,biasLayerX, inputLayerX );
      this.lineAngles = weightNodeData.lineAngles;
      this.weightLayerCoordinates = weightNodeData.weightLayerCoordinates;
  
  
  
  
      this.inputToBiasLines = this.getInputToBiasLinesData(this.inputLayerNodeCount, this.outputNodeCount,
          this.inputLayerCoordinates, this.outputLayerCoordinates);
  
  
      //var connectionsArea1Data = [
      //    {x: this.inputLayerCoordinates[0].x + grapher.nodeRadius, y: this.inputLayerCoordinates[0].y},
      //    {x: this.inputLayerCoordinates[this.inputLayerNodeCount-1].x + grapher.nodeRadius, y: this.inputLayerCoordinates[this.inputLayerNodeCount-1].y},
      //    {x: this.outputLayerCoordinates[this.outputNodeCount-1].x, y: this.outputLayerCoordinates[this.outputNodeCount-1].y},
      //    {x: this.outputLayerCoordinates[0].x, y: this.outputLayerCoordinates[0].y}
      //];
      //
      //
      //var line = d3.line()
      //    .x(function(d) { return d.x; })
      //    .y(function(d) { return d.y; })
      //    .curve(d3.curveLinearClosed);
      //
      //var connections = this.neuralNetworkG
      //    .append("path")
      //    .attr("d", line(connectionsArea1Data))
      //    //.datum(connectionsArea1Data)
      //    .attr("class", "input-to-bias-line")
      //    .attr("fill", "#f0f0f0")
      //    .attr("stroke", "#f0f0f0");
  
  
  
  
      this.BiasToSoftmaxLines = this.getBiasToSoftmaxLinesData(this.outputNodeCount, this.outputLayerCoordinates,
          activationLayerX, grapher.nodeRadius);
  
  
      this.softmaxtoOutputLines = this.getSoftmaxtoOutputLinesData(this.outputNodeCount, activationLayerX,
          grapher.nodeRadius, this.outputLayerCoordinates, outputLayerX,  this.classes );
  
  
  
      // Graph all the things
      this.graphSoftmaxToOutputArrows(this.softmaxtoOutputLines);
      this.graphBiasToSoftmaxArrows(this.BiasToSoftmaxLines);
  
      this.graphInputToBiasLines(this.inputToBiasLines);
  
      this.graphInputNodes(this.inputLayerCoordinates);
      this.graphWeightNodes(this.weightLayerCoordinates);
      //this.graphOutputNodes(this.outputLayerCoordinates);
  
      this.graphBiasNodes(this.outputLayerCoordinates);
      this.graphActivation( this.activation, activationLayerX);
  
  
  
      var biasNodeY = this.neuralNetworkMargin.top + this.neuralNetworkHeight / 2 - this.nodeRadius;
  
  
  };
  
  
  neuralNetworkCalculationViz.prototype.graphActivation = function (activationFunction, activationLayerXCoordinate) {
      if( activationFunction == "softmax" ){
          var softmaxCoordinates = {x:activationLayerXCoordinate, y: this.neuralNetworkMargin.top};
          this.graphSoftmax(softmaxCoordinates);
  
      }
      else if ( activationFunction == "sigmoid" ){
          var sigmoidCoordinates = {x:activationLayerXCoordinate,
              y: (this.neuralNetworkHeight / 2) + this.neuralNetworkMargin.top};
          this.graphSigmoid(sigmoidCoordinates);
      }
  };
  
  
  neuralNetworkCalculationViz.prototype.feedforward = function (layerNumber) {
      // Calculate the values scaled by the weights (the input to the next layer)
      for (var i = 0; i < this.weightsAndBiasWeights.length; i++)
          for (var j = 0; j < this.weightsAndBiasWeights[i].length; j++) {
  
              if (j == 0) this.layerInputs[layerNumber][i] = [];
              this.layerInputs[layerNumber][i][j] = this.weightsAndBiasWeights[i][j] * this.inputs[i];
  
              // Calculate the output nodes
              if (i == 0) this.layerValues[1][j] = 0;
  
              this.layerValues[layerNumber][j] = this.layerValues[1][j] + this.layerInputs[layerNumber][i][j];
          }
  };
  
  
  
  
  
  neuralNetworkCalculationViz.prototype.initializeTable = function (trainingSet) {
  
  
      var viz = this;
      var table = d3.select(this.tableElement)
          .append('table')
          .attr("class", "collapsed-style");
  
      this.selectedPerson = false;
      var columns = [
          {head: ' ', cl: 'title', html: function(d, i){
              return '<input type="radio" name="person"  ' +
                  'class="radio_'+i+'">';
          }},
          {head: 'Age', cl: 'title', html: ƒ('age')},
          {head: 'Sex', cl: 'center', html: ƒ('sex')},
          {head: 'Survived', cl: 'center', html: ƒ('survived')}
      ];
  
      // Table header  onclick="function(){ viz.selectedPerson = viz.trainingSet['+i+'] ; }"
      table.append('thead').append('tr')
          .selectAll('th')
          .data(columns).enter()
          .append('th')
          .attr('class', ƒ('cl'))
          .text(ƒ('head'));
  
      // Table body
      table.append('tbody')
          .selectAll('tr')
          .data(trainingSet).enter()
          .append('tr')
          .on("mouseover", function (d) {
              viz.setInput([d.age, d.sex]);
          })
          .on("mouseout", function (d) {
              if( viz.selectedPerson )
              viz.setInput(viz.selectedPerson);
  
          })
          .on("click", function(d){
              d3.select('radio_'+d.age+'_'+d.sex)
              .attr('checked', "true");
          })
          .selectAll('td')
          .data(function (row, i) {
              // evaluate column objects against the current row
              return columns.map(function (c) {
                  var cell = {};
                  d3.keys(c).forEach(function (k) {
                      cell[k] = typeof c[k] == 'function' ? c[k](row, i) : c[k];
                  });
                  return cell;
              });
          })
          .enter()
          .append('td')
          .html(ƒ('html'))
          .attr('class', ƒ('cl'));
  
  
  };
  
  
  
  
  
  neuralNetworkCalculationViz.prototype.getWeightLayerXCoordinates = function (nodeRadius, weightNodeWidthRatio,
                                                                               outputNodeCount, inputLayerX) {
  
  
      var weightMarginLeft = nodeRadius + nodeRadius * weightNodeWidthRatio - 15;
  
      // calculate weight layer x coordinate (one "layer" per output)   // Scales for the axes
      var weightNodeX = d3.scaleLinear()
          .domain([0, outputNodeCount])
          .range([inputLayerX + weightMarginLeft, inputLayerX + weightMarginLeft]); //biasLayerX - weightMarginRight ]);
  
      var weightLayerXCoordinates = d3.range(outputNodeCount).map(function (i) {
          return weightNodeX(i);
      });
  
      return weightLayerXCoordinates;
  };
  
  neuralNetworkCalculationViz.prototype.updateActivation = function (activationInput, activation) {
  
      if( activation == "softmax"){
          // Calculate the softmax values and final output
          this.softmaxOutputs = softmax(activationInput);
  
          // Display softmax and final output
          this.graphSoftmaxOutputs(this.softmaxOutputs);
      }
      else if( activation == "sigmoid" ){
          this.updateSigmoid(activationInput);
      }
  
  };
  
  
  neuralNetworkCalculationViz.prototype.setInput = function (input$$1) {
  
  
      this.inputs = input$$1;
      this.layerInputs[1] = [];
      this.layerValues[1] = [];
      this.inputs.push(1); // This will be our input bias node
  
      this.updateInputNodesText(input$$1);
  
      // Multiply inputs by weights and calculate the next layer value
      this.feedforward(1);
  
      // Display layer 1 input values above their lines
      this.graphLayerInputs(this.layerInputs, 1);
  
      // Display the text on the output nodes
      this.updateOutputNodesText();
  
  
      // Update activation layer
      this.updateActivation(this.layerValues[1], this.activation);
  
      // Highlight the class name with the highest softmax score
      this.highlightTopSoftmaxOutput();
  
  };
  
  
  var weights = [[-0.016852],
      [0.704039]];
  var biases = [-0.116309];
  //
  //var weights = [[-0.020234],
  //    [1.023300]];
  //var biases = [-0.119178];
  
  
  //var weights = [[-0.11510573],
  //    [0.19847003]];
  //var biases = [-0.07722114];
  //
  //var weights = [[-0.11510573, -0.08358583],
  //    [0.19847003, -1.00007236]];
  //var biases = [-0.07722114, 0.07722116];
  
  //var classes=["Survived", "Didn't survive"];
  
  var trainingSet = [
      {age: 22, sex: 0, survived: 0},
      {age: 38, sex: 1, survived: 1},
      {age: 26, sex: 1, survived: 1},
      {age: 35, sex: 1, survived: 1},
      {age: 35, sex: 0, survived: 0},
      {age: 14, sex: 1, survived: 0},
      {age: 25, sex: 0, survived: 0},
      {age: 54, sex: 0, survived: 0}
  
  ];
  var classes=["Survived"];
  
  //var weights = [[2.14149564e-04, -2.14149914e-04],
  //    [5.12748193e-05, -5.12747974e-05]];
  //var biases = [1.19155184e-05, -1.19155284e-05];
  
  var nnCalculationViz = new neuralNetworkCalculationViz(false, 2, 1, "sigmoid", "#neural-network-calculation-viz", weights, biases,
      "#neural-network-calculation-table", trainingSet, classes);
  
  
  //var input = [22,  0];
  //nnCalculationViz.setInput(input);
  
  var graphData = {
      layers: [
          {
              nodes: 2,
              type: "input",
              bias: true
          },
          {
              nodes: 8,
              activation: "relu",
              type:"block"
          },
          {
              nodes: 1
          }
          //{
          //    type: "sigmoid",
          //    nodes: 1
          //}
      ],
      containerElement: "#neural-network-architecture-viz",
      weights: true,
      weightValues: false
  };
  
  var nnArchitectureViz = new neuralNetworkCalculationViz(graphData);
  
  })));
  