<commit_before>import _ from 'underscore'
import q from 'q'

import { BOOL_OFF, BOOL_ON, BOOL_TRANSITION_OFF, BOOL_TRANSITION_ON } from '../constants/boolStates'
import { boolInvert }  from '../lib/bool'
import initialState from '../constants/initialState'
import { SWITCH, WIRE, AND_GATE, OR_GATE } from '../constants/nodeTypes'

const SWITCH_TOGGLE_ACTION = 'SWITCH_TOGGLE_ACTION'
const PROPOGATE_CIRCUIT = 'PROPOGATE_CIRCUIT'

// exported action creator
export let switchToggled = function(circuitId, nodeId) {
  return function(dispatch) {
    dispatch({ type: SWITCH_TOGGLE_ACTION, circuitId, nodeId })
    dispatch({ type: PROPOGATE_CIRCUIT})
  }
}
 
// For below functions, use variable named appState to refer to the
// redux state object. Use variables named state to refer to a node's
// state, e.g. an AND gate's boolean state.

// handle user changes to switch and circuit propogation
export let circuitReducer = function(appState=initialState, action) {
  switch (action.type) {
    case SWITCH_TOGGLE_ACTION:
      let nodeId = action.nodeId
      let newAppState = Object.assign({}, appState)
      newAppState.allNodes = appState.allNodes.slice(0) // shallow copy array
      newAppState.allNodes[nodeId] = Object.assign({}, appState.allNodes[nodeId])

      let toggled = boolInvert(newAppState.allNodes[nodeId].state)
      newAppState.allNodes[nodeId].state = toggled
      return addNodeToChangedNodes(newAppState, newAppState.allNodes[nodeId])
    case PROPOGATE_CIRCUIT:
      return propogateCircuit(appState)      
    default:
      return appState
  }
}

// does not mutate app state, returns new app state with node pushed 
let addNodeToChangedNodes = function(appState, node) {
  let newChangedNodes = appState.changedNodes.slice(0)
  newChangedNodes.push(node)
  let newAppState = Object.assign({}, appState)
  newAppState.changedNodes = newChangedNodes
  return newAppState
}

// propogates changes in a circuit until none are left
let propogateCircuit = function(appState) {
  let changedNodes = appState.changedNodes
  let newAppState = Object.assign({}, appState)
  newAppState.changedNodes = []
  _.each(changedNodes, function(changedNode) {
    newAppState = propogateChangedNode(newAppState, changedNode)
  })

  if (newAppState.changedNodes.length > 0) {
    return propogateCircuit(newAppState)
  } else {
    return newAppState
  }
}

// propogates state of node to downstream nodes, changes downstream node state, and
// adds downstream node to changed node list. Does not mutate app state. Returns
// new app state.
let propogateChangedNode = function(appState, node) {  
  let outputs = node.outputs
  if (outputs.length === 0) return appState

  let newAppState = Object.assign({}, appState)

  // most nodes have 1 output, wires can have multiple outputs
  _.each(outputs, function(outputObj) {
    // id of the node that this output connects to
    let outputNodeId = outputObj.nodeId
    // gates have more than one input, index of the downstream node's input
    let outputNodeInputIndex = outputObj.nodeInput

    // update the downstream node's state
    newAppState = updateNodeStateFromInputs(newAppState, outputNodeId, outputNodeInputIndex, node.state)
  })

  return newAppState
}

// updates state of node given new input value. If node state has changed, adds it to
// changedNodes list. Does not mutate app state, returns new app state.
let updateNodeStateFromInputs = function(appState, nodeId, inputIndex, nodeInputState) {
  // here is where we have different logic based on gate type.
  let newNode = Object.assign({}, appState.allNodes[nodeId])
  if (inputIndex >= newNode.inputs.length ) throw ("inputIndex higher than node input expected")
  newNode.inputs[inputIndex] = nodeInputState
  newNode.state = computeState(newNode.type, newNode.inputs)
  let newAppState = Object.assign({}, appState)
  let newAllNodes = newAppState.allNodes.slice(0)
  newAllNodes[nodeId] = newNode
  newAppState.allNodes = newAllNodes

  // if node's state changed, add it to list of changed nodes
  if (newNode.state !== appState.allNodes[nodeId]) {
    return addNodeToChangedNodes(newAppState, newNode)
  }
<commit_msg>added comments<commit_after>import _ from 'underscore'
import q from 'q'

import { BOOL_OFF, BOOL_ON, BOOL_TRANSITION_OFF, BOOL_TRANSITION_ON } from '../constants/boolStates'
import { boolInvert }  from '../lib/bool'
import initialState from '../constants/initialState'
import { SWITCH, WIRE, AND_GATE, OR_GATE } from '../constants/nodeTypes'

const SWITCH_TOGGLE_ACTION = 'SWITCH_TOGGLE_ACTION'
const PROPOGATE_CIRCUIT = 'PROPOGATE_CIRCUIT'

// exported action creator
export let switchToggled = function(circuitId, nodeId) {
  return function(dispatch) {
    dispatch({ type: SWITCH_TOGGLE_ACTION, circuitId, nodeId })
    dispatch({ type: PROPOGATE_CIRCUIT})
  }
}
 
// For below functions, use variable named appState to refer to the
// redux state object. Use variables named state to refer to a node's
// state, e.g. an AND gate's boolean state.

// handle user changes to switch and circuit propogation
export let circuitReducer = function(appState=initialState, action) {
  switch (action.type) {
    case SWITCH_TOGGLE_ACTION:
      let nodeId = action.nodeId
      let newAppState = Object.assign({}, appState)
      newAppState.allNodes = appState.allNodes.slice(0) // shallow copy array
      newAppState.allNodes[nodeId] = Object.assign({}, appState.allNodes[nodeId])

      let toggled = boolInvert(newAppState.allNodes[nodeId].state)
      newAppState.allNodes[nodeId].state = toggled
      return addNodeToChangedNodes(newAppState, newAppState.allNodes[nodeId])
    case PROPOGATE_CIRCUIT:
      return propogateCircuit(appState)      
    default:
      return appState
  }
}

// adds node to nodeChangedNodes list
let addNodeToChangedNodes = function(appState, node) {
  return Object.assign({}, appState, {
    changedNodes: [...appState.changedNodes, node]
  })
}

let propogateCircuit = function(appState) {
  // nodes who's state has not been propogated to downstream node
  let changedNodes = appState.changedNodes

  // empty changed nodes list in appState
  let newAppState = Object.assign({}, appState, {
    changedNodes: []
  })

  // propogate node state for each node
  _.each(changedNodes, function(changedNode) {
    newAppState = propogateChangedNode(newAppState, changedNode)
  })

  if (newAppState.changedNodes.length > 0) {
    // recurse if there are new nodes in changedNodes
    return propogateCircuit(newAppState)
  } else {
    return newAppState
  }
}

// propogates state of node to downstream nodes, changes downstream node state, and
// adds downstream node to changed node list. Does not mutate app state. Returns
// new app state.
let propogateChangedNode = function(appState, node) {  
  let outputs = node.outputs
  if (outputs.length === 0) return appState

  let newAppState = Object.assign({}, appState)

  // most nodes have 1 output, wires can have multiple outputs
  _.each(outputs, function(outputObj) {
    // id of the node that this output connects to
    let outputNodeId = outputObj.nodeId
    // gates have more than one input, index of the downstream node's input
    let outputNodeInputIndex = outputObj.nodeInput

    // update the downstream node's state
    newAppState = updateNodeStateFromInputs(newAppState, outputNodeId, outputNodeInputIndex, node.state)
  })

  return newAppState
}

// updates state of node given new input value. If node state has changed, adds it to
// changedNodes list. Does not mutate app state, returns new app state.
let updateNodeStateFromInputs = function(appState, nodeId, inputIndex, nodeInputState) {
  // here is where we have different logic based on gate type.

  // duplicate node, because we may change it
  let newNode = Object.assign({}, appState.allNodes[nodeId])
  if (inputIndex >= newNode.inputs.length ) throw ("inputIndex higher than node input expected")

  // change node's input state   
  newNode.inputs[inputIndex] = nodeInputState

  // update node's state from inputs. Where not, and, or, etc logic happens
  newNode.state = computeState(newNode.type, newNode.inputs)

  // copy allNodes and add new node
  let newAllNodes = appState.allNodes.slice(0)
  newAllNodes[nodeId] = newNode

  // copy appstate
  let newAppState = Object.assign({}, appState, {
    allNodes: newAllNodes
  })

  // if node's state changed, add it to list of changed nodes
  if (newNode.state !== appState.allNodes[nodeId]) {
    return addNodeToChangedNodes(newAppState, newNode)
  }

{'fraction_non_alphanumeric': 0.08996256434253627, 'fraction_numerical': 0.0012868507253158633, 'mean_word_length': 4.076603325415677, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 7, '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': '3846998', 'n_tokens_mistral': 2616, 'n_tokens_neox': 2527, 'n_words': 794}