function clockTick() {
    if(!simulationArea.clockEnabled)return;
    if (errorDetected) return;
    updateCanvas = true;
    globalScope.clockTick();
    play();
    scheduleUpdate(0,20);

}

function changeClockEnable(val){
    simulationArea.clockEnabled = val
}

function runTest(n=10){
    var t=new Date().getTime();
    for(var i=0;i<n;i++)
        clockTick();
    //console.log((new Date().getTime()-t)/n);
    updateCanvas = true;
    scheduleUpdate();
}


function TflipFlop(x, y, scope = globalScope, dir = "RIGHT") {
    CircuitElement.call(this, x, y, scope, dir, 1);
    this.directionFixed = true;
    this.fixedBitWidth = true;
    this.setDimensions(20, 20);
    this.rectangleObject = true;
    this.clockInp = new Node(-20, +10, 0, this, 1, "Clock");
    this.dInp = new Node(-20, -10, 0, this, this.bitWidth, "T");
    this.qOutput = new Node(20, -10, 1, this, this.bitWidth, "Q");
    this.qInvOutput = new Node(20, 10, 1, this, this.bitWidth, "Q Inverse");
    this.reset = new Node(10, 20, 0, this, 1, "Asynchronous Reset");
    this.preset = new Node(0, 20, 0, this, this.bitWidth, "Preset");
    this.en = new Node(-10, 20, 0, this, 1, "Enable");
    this.masterState = 0;
    this.slaveState = 0;
    this.prevClockState = 0;

    // this.wasClicked = false;

}
TflipFlop.prototype = Object.create(CircuitElement.prototype);
TflipFlop.prototype.constructor = TflipFlop;
TflipFlop.prototype.tooltipText = "T FlipFlop ToolTip :  Changes state / Toggles whenever the clock input is strobed.";
TflipFlop.prototype.helplink = "https://docs.circuitverse.org/#/Sequential?id=t-flip-flop";
TflipFlop.prototype.isResolvable = function() {
    if (this.reset.value == 1) return true;
    if (this.clockInp.value != undefined && this.dInp.value != undefined) return true;
    return false;
}

//add this to output the module
TflipFlop.moduleVerilog = function(){
return `
module TflipFlop(q, q_inv, clk, t, a_rst, pre, en);
  parameter WIDTH = 1;
  output reg [WIDTH-1:0] q, q_inv;
  input clk, a_rst, pre, en;
  input [WIDTH-1:0] t;

  always @ (posedge clk or posedge a_rst)
    if (a_rst) begin
      q <= 'b0;
      q_inv <= 'b1;
    end else if (en == 0) ;
    else if (t) begin
      q <= q ^ t;
      q_inv <= ~q ^ t;
    end
endmodule
`
}

TflipFlop.prototype.newBitWidth = function(bitWidth) {
    this.bitWidth = bitWidth;
    this.dInp.bitWidth = bitWidth;
    this.qOutput.bitWidth = bitWidth;
    this.qInvOutput.bitWidth = bitWidth;
    this.preset.bitWidth = bitWidth;
}
TflipFlop.prototype.resolve = function() {
    if (this.reset.value == 1) {

        this.masterState = this.slaveState = this.preset.value || 0;

    } else if (this.en.value == 0) {

        this.prevClockState = this.clockInp.value;

    } else if (this.en.value == 1 || this.en.connections.length == 0) {
        if (this.clockInp.value == this.prevClockState) {
            if (this.clockInp.value == 0 && this.dInp.value != undefined) {
                this.masterState = this.dInp.value ^ this.slaveState;
            }
        } else if (this.clockInp.value != undefined) {
            if (this.clockInp.value == 1) {
                this.slaveState = this.masterState;
            } else if (this.clockInp.value == 0 && this.dInp.value != undefined) {
                this.masterState = this.dInp.value ^ this.slaveState;
            }
            this.prevClockState = this.clockInp.value;
        }
    }

    if (this.qOutput.value != this.slaveState) {
        this.qOutput.value = this.slaveState;
        this.qInvOutput.value = this.flipBits(this.slaveState);
        simulationArea.simulationQueue.add(this.qOutput);
        simulationArea.simulationQueue.add(this.qInvOutput);
    }
}
TflipFlop.prototype.customSave = function() {
    var data = {
        nodes: {
            clockInp: findNode(this.clockInp),
            dInp: findNode(this.dInp),
            qOutput: findNode(this.qOutput),
            qInvOutput: findNode(this.qInvOutput),
            reset: findNode(this.reset),
            preset: findNode(this.preset),
            en: findNode(this.en),
        },
        constructorParamaters: [this.direction, this.bitWidth]

    }
    return data;
}
TflipFlop.prototype.customDraw = function() {

    ctx = simulationArea.context;
    ctx.beginPath();
    ctx.strokeStyle = ("rgba(0,0,0,1)");
    ctx.fillStyle = "white";
    ctx.lineWidth = correctWidth(3);
    var xx = this.x;
    var yy = this.y;
    // rect(ctx, xx - 20, yy - 20, 40, 40);
    moveTo(ctx, -20, 5, xx, yy, this.direction);
    lineTo(ctx, -15, 10, xx, yy, this.direction);
    lineTo(ctx, -20, 15, xx, yy, this.direction);


    // if ((this.b.hover&&!simulationArea.shiftDown)|| simulationArea.lastSelected == this || simulationArea.multipleObjectSelections.contains(this)) ctx.fillStyle = "rgba(255, 255, 32,0.8)";ctx.fill();
    ctx.stroke();

    ctx.beginPath();
    ctx.font = "20px Georgia";
    ctx.fillStyle = "green";
    ctx.textAlign = "center";
    fillText(ctx, this.slaveState.toString(16), xx, yy + 5);
    ctx.fill();

}

function DflipFlop(x, y, scope = globalScope, dir = "RIGHT", bitWidth = 1) {
    CircuitElement.call(this, x, y, scope, dir, bitWidth);
    this.directionFixed = true;
    this.setDimensions(20, 20);
    this.rectangleObject = true;
    this.clockInp = new Node(-20, +10, 0, this, 1, "Clock");
    this.dInp = new Node(-20, -10, 0, this, this.bitWidth, "D");
    this.qOutput = new Node(20, -10, 1, this, this.bitWidth, "Q");
    this.qInvOutput = new Node(20, 10, 1, this, this.bitWidth, "Q Inverse");
    this.reset = new Node(10, 20, 0, this, 1, "Asynchronous Reset");
    this.preset = new Node(0, 20, 0, this, this.bitWidth, "Preset");
    this.en = new Node(-10, 20, 0, this, 1, "Enable");
    this.masterState = 0;
    this.slaveState = 0;
    this.prevClockState = 0;

    this.wasClicked = false;

}
DflipFlop.prototype = Object.create(CircuitElement.prototype);
DflipFlop.prototype.constructor = DflipFlop;
DflipFlop.prototype.tooltipText = "D FlipFlop ToolTip : Introduces delay in timing circuit.";
DflipFlop.prototype.helplink = "https://docs.circuitverse.org/#/Sequential?id=d-flip-flop";
DflipFlop.prototype.isResolvable = function() {
	return true;
    if (this.reset.value == 1) return true;
    if (this.clockInp.value != undefined && this.dInp.value != undefined ) return true;
    return false;
}
DflipFlop.prototype.newBitWidth = function(bitWidth) {
    this.bitWidth = bitWidth;
    this.dInp.bitWidth = bitWidth;
    this.qOutput.bitWidth = bitWidth;
    this.qInvOutput.bitWidth = bitWidth;
    this.preset.bitWidth = bitWidth;
}
DflipFlop.prototype.resolve = function() {
    if (this.reset.value == 1) {
        this.masterState = this.slaveState = (this.preset.value||0);
    }
    else if (this.en.value == 0) {

        this.prevClockState = this.clockInp.value;

    }
    else if (this.en.value == 1 || this.en.connections.length == 0) { // if(this.en.value==1) // Creating Infinite Loop, WHY ??

        if (this.clockInp.value == this.prevClockState) {
            if (this.clockInp.value == 0 && this.dInp.value != undefined) {
                this.masterState = this.dInp.value;
            }
        } else if (this.clockInp.value != undefined) {
            if (this.clockInp.value == 1) {
                this.slaveState = this.masterState;
            } else if (this.clockInp.value == 0 && this.dInp.value != undefined) {
                this.masterState = this.dInp.value;
            }
            this.prevClockState = this.clockInp.value;
        }
    }

    if (this.qOutput.value != this.slaveState) {
        this.qOutput.value = this.slaveState;
        this.qInvOutput.value = this.flipBits(this.slaveState);
        simulationArea.simulationQueue.add(this.qOutput);
        simulationArea.simulationQueue.add(this.qInvOutput);
    }
}

//add this to output the module
DflipFlop.moduleVerilog = function(){
    return `
module DflipFlop(q, q_inv, clk, d, a_rst, pre, en);
  parameter WIDTH = 1;
  output reg [WIDTH-1:0] q, q_inv;
  input clk, a_rst, pre, en;
  input [WIDTH-1:0] d;

  always @ (posedge clk or posedge a_rst)
    if (a_rst) begin
      q <= 'b0;
      q_inv <= 'b1;
    end else if (en == 0) ;
    else begin
      q <= d;
      q_inv <= ~d;
    end
endmodule
`
}
DflipFlop.prototype.customSave = function() {
    var data = {
        nodes: {
            clockInp: findNode(this.clockInp),
            dInp: findNode(this.dInp),
            qOutput: findNode(this.qOutput),
            qInvOutput: findNode(this.qInvOutput),
            reset: findNode(this.reset),
            preset: findNode(this.preset),
            en: findNode(this.en),
        },
        constructorParamaters: [this.direction, this.bitWidth]

    }
    return data;
}
DflipFlop.prototype.customDraw = function() {

    ctx = simulationArea.context;
    ctx.beginPath();
    ctx.strokeStyle = ("rgba(0,0,0,1)");
    ctx.fillStyle = "white";
    ctx.lineWidth = correctWidth(3);
    var xx = this.x;
    var yy = this.y;
    // rect(ctx, xx - 20, yy - 20, 40, 40);
    moveTo(ctx, -20, 5, xx, yy, this.direction);
    lineTo(ctx, -15, 10, xx, yy, this.direction);
    lineTo(ctx, -20, 15, xx, yy, this.direction);


    // if ((this.b.hover&&!simulationArea.shiftDown)|| simulationArea.lastSelected == this || simulationArea.multipleObjectSelections.contains(this)) ctx.fillStyle = "rgba(255, 255, 32,0.8)";ctx.fill();
    ctx.stroke();

    ctx.beginPath();
    ctx.font = "20px Georgia";
    ctx.fillStyle = "green";
    ctx.textAlign = "center";
    fillText(ctx, this.slaveState.toString(16), xx, yy + 5);
    ctx.fill();

}

function Dlatch(x, y, scope = globalScope, dir = "RIGHT", bitWidth = 1) {
    CircuitElement.call(this, x, y, scope, dir, bitWidth);
    this.directionFixed = true;
    this.setDimensions(20, 20);
    this.rectangleObject = true;
    this.clockInp = new Node(-20, +10, 0, this, 1, "Clock");
    this.dInp = new Node(-20, -10, 0, this, this.bitWidth, "D");
    this.qOutput = new Node(20, -10, 1, this, this.bitWidth, "Q");
    this.qInvOutput = new Node(20, 10, 1, this, this.bitWidth, "Q Inverse");
    // this.reset = new Node(10, 20, 0, this, 1, "Asynchronous Reset");
    // this.preset = new Node(0, 20, 0, this, this.bitWidth, "Preset");
    // this.en = new Node(-10, 20, 0, this, 1, "Enable");
    this.state = 0;
    this.prevClockState = 0;
    this.wasClicked = false;

}
Dlatch.prototype = Object.create(CircuitElement.prototype);
Dlatch.prototype.constructor = Dlatch;
Dlatch.prototype.tooltipText = "D Latch : Single input Flip flop or D FlipFlop";
Dlatch.prototype.helplink = "https://docs.circuitverse.org/#/Sequential?id=d-latch";
Dlatch.prototype.isResolvable = function() {
    if (this.clockInp.value != undefined && this.dInp.value != undefined ) return true;
    return false;
}
Dlatch.prototype.newBitWidth = function(bitWidth) {
    this.bitWidth = bitWidth;
    this.dInp.bitWidth = bitWidth;
    this.qOutput.bitWidth = bitWidth;
    this.qInvOutput.bitWidth = bitWidth;
    // this.preset.bitWidth = bitWidth;
}
Dlatch.prototype.resolve = function() {


    if (this.clockInp.value == 1 && this.dInp.value!=undefined) {
        this.state = this.dInp.value
    }

    if (this.qOutput.value != this.state) {
        this.qOutput.value = this.state;
        this.qInvOutput.value = this.flipBits(this.state);
        simulationArea.simulationQueue.add(this.qOutput);
        simulationArea.simulationQueue.add(this.qInvOutput);
    }
}
Dlatch.prototype.customSave = function() {
    var data = {
        nodes: {
            clockInp: findNode(this.clockInp),
            dInp: findNode(this.dInp),
            qOutput: findNode(this.qOutput),
            qInvOutput: findNode(this.qInvOutput),
            // reset: findNode(this.reset),
            // preset: findNode(this.preset),
            // en: findNode(this.en),
        },
        constructorParamaters: [this.direction, this.bitWidth]

    }
    return data;
}
Dlatch.prototype.customDraw = function() {

    ctx = simulationArea.context;
    ctx.beginPath();
    ctx.strokeStyle = ("rgba(0,0,0,1)");
    ctx.fillStyle = "white";
    ctx.lineWidth = correctWidth(3);
    var xx = this.x;
    var yy = this.y;
    // rect(ctx, xx - 20, yy - 20, 40, 40);
    moveTo(ctx, -20, 5, xx, yy, this.direction);
    lineTo(ctx, -15, 10, xx, yy, this.direction);
    lineTo(ctx, -20, 15, xx, yy, this.direction);


    // if ((this.b.hover&&!simulationArea.shiftDown)|| simulationArea.lastSelected == this || simulationArea.multipleObjectSelections.contains(this)) ctx.fillStyle = "rgba(255, 255, 32,0.8)";ctx.fill();
    ctx.stroke();

    ctx.beginPath();
    ctx.font = "20px Georgia";
    ctx.fillStyle = "green";
    ctx.textAlign = "center";
    fillText(ctx, this.state.toString(16), xx, yy + 5);
    ctx.fill();

}


function Random(x, y, scope = globalScope, dir = "RIGHT", bitWidth = 1) {
    CircuitElement.call(this, x, y, scope, dir, bitWidth);
    this.directionFixed = true;
    this.setDimensions(20, 20);
    this.rectangleObject = true;

    this.currentRandomNo = 0;
    this.clockInp = new Node(-20, +10, 0, this, 1, "Clock");
    this.maxValue = new Node(-20, -10, 0, this, this.bitWidth, "MaxValue");
    this.output = new Node(20, -10, 1, this, this.bitWidth, "RandomValue");
    this.prevClockState = 0;

    this.wasClicked = false;

}
Random.prototype = Object.create(CircuitElement.prototype);
Random.prototype.constructor = Random;
Random.prototype.tooltipText = "Random ToolTip : Random Selected.";
Random.prototype.helplink = "https://docs.circuitverse.org/#/inputElements?id=random";
Random.prototype.isResolvable = function() {
    if (this.clockInp.value != undefined && ( this.maxValue.value != undefined || this.maxValue.connections.length == 0 ) )
        return true;
    return false;
};
Random.prototype.newBitWidth = function(bitWidth) {
    this.bitWidth = bitWidth;
    this.maxValue.bitWidth = bitWidth;
    this.output.bitWidth = bitWidth;
};
Random.prototype.resolve = function() {
    //console.log("HIT")
    var maxValue = this.maxValue.connections.length ? this.maxValue.value + 1 : (2<<(this.bitWidth-1)) ;


    if (this.clockInp.value != undefined) {
        if(this.clockInp.value!=this.prevClockState) {
            if (this.clockInp.value == 1) {
                this.currentRandomNo = Math.floor(Math.random() * maxValue);
            }
            this.prevClockState = this.clockInp.value;
        }
    }


    if (this.output.value != this.currentRandomNo) {
        this.output.value = this.currentRandomNo;
        simulationArea.simulationQueue.add(this.output);
    }
}
Random.prototype.customSave = function() {
    var data = {
        nodes: {
            clockInp: findNode(this.clockInp),
            maxValue: findNode(this.maxValue),
            output: findNode(this.output),
        },
        constructorParamaters: [this.direction, this.bitWidth]

    }
    return data;
}
Random.prototype.customDraw = function() {
    ctx = simulationArea.context;
    ctx.beginPath();
    xx = this.x;
    yy = this.y;
    ctx.font = "20px Georgia";
    ctx.fillStyle = "green";
    ctx.textAlign = "center";
    fillText(ctx, this.currentRandomNo.toString(10), this.x, this.y + 5);
    ctx.fill();

    ctx.beginPath();
    moveTo(ctx, -20, 5, xx, yy, this.direction);
    lineTo(ctx, -15, 10, xx, yy, this.direction);
    lineTo(ctx, -20, 15, xx, yy, this.direction);
    ctx.stroke();


}


Random.moduleVerilog = function () {
  return `
module Random(val, clk, max);
  parameter WIDTH = 1;
  output reg [WIDTH-1:0] val;
  input clk;
  input [WIDTH-1:0] max;

  always @ (posedge clk)
    if (^max === 1'bX)
      val = $urandom_range(0, {WIDTH{1'b1}});
    else
      val = $urandom_range(0, max);
endmodule
`;
}

function SRflipFlop(x, y, scope = globalScope, dir = "RIGHT") {
    CircuitElement.call(this, x, y, scope, dir, 1);
    this.directionFixed = true;
    this.fixedBitWidth = true;
    this.setDimensions(20, 20);
    this.rectangleObject = true;
    this.R = new Node(-20, +10, 0, this, 1, "R");
    this.S = new Node(-20, -10, 0, this, 1, "S");
    this.qOutput = new Node(20, -10, 1, this, 1, "Q");
    this.qInvOutput = new Node(20, 10, 1, this, 1, "Q Inverse");
    this.reset = new Node(10, 20, 0, this, 1, "Asynchronous Reset");
    this.preset = new Node(0, 20, 0, this, 1, "Preset");
    this.en = new Node(-10, 20, 0, this, 1, "Enable");
    this.state = 0;
    // this.slaveState = 0;
    // this.prevClockState = 0;

    // this.wasClicked = false;

}
SRflipFlop.prototype = Object.create(CircuitElement.prototype);
SRflipFlop.prototype.constructor = SRflipFlop;
SRflipFlop.prototype.tooltipText = "SR FlipFlop ToolTip : SR FlipFlop Selected.";
SRflipFlop.prototype.helplink = "https://docs.circuitverse.org/#/Sequential?id=sr-flip-flop";
SRflipFlop.prototype.newBitWidth = function(bitWidth) {
    this.bitWidth = bitWidth;
    this.dInp.bitWidth = bitWidth;
    this.qOutput.bitWidth = bitWidth;
    this.qInvOutput.bitWidth = bitWidth;
    this.preset.bitWidth = bitWidth;
}
SRflipFlop.prototype.isResolvable = function() {
    return true;
    if (this.reset.value == 1) return true;
    if (this.S.value != undefined && this.R.value != undefined) return true;
    return false;
}
SRflipFlop.prototype.resolve = function() {

    if (this.reset.value == 1) {

        this.state = this.preset.value || 0;


    }

    else if ((this.en.value ==1||this.en.connections==0) && this.S.value ^ this.R.value) {
        this.state = this.S.value;
    }

    //console.log(this.reset.value != 1 && this.en.value && this.S.value && this.R.value && this.S.value ^ this.R.value);
    if (this.qOutput.value != this.state) {
        this.qOutput.value = this.state;
        this.qInvOutput.value = this.flipBits(this.state);
        simulationArea.simulationQueue.add(this.qOutput);
        simulationArea.simulationQueue.add(this.qInvOutput);
    }
}
SRflipFlop.prototype.customSave = function() {
    var data = {
        nodes: {
            S: findNode(this.S),
            R: findNode(this.R),
            qOutput: findNode(this.qOutput),
            qInvOutput: findNode(this.qInvOutput),
            reset: findNode(this.reset),
            preset: findNode(this.preset),
            en: findNode(this.en),
        },
        constructorParamaters: [this.direction]

    }
    return data;
}
SRflipFlop.prototype.customDraw = function() {

    ctx = simulationArea.context;
    ctx.beginPath();
    ctx.strokeStyle = ("rgba(0,0,0,1)");
    ctx.fillStyle = "white";
    ctx.lineWidth = correctWidth(3);
    var xx = this.x;
    var yy = this.y;

    // rect(ctx, xx - 20, yy - 20, 40, 40);
    // moveTo(ctx, -20, 5, xx, yy, this.direction);
    // lineTo(ctx, -15, 10, xx, yy, this.direction);
    // lineTo(ctx, -20, 15, xx, yy, this.direction);


    // if ((this.b.hover&&!simulationArea.shiftDown)|| simulationArea.lastSelected == this || simulationArea.multipleObjectSelections.contains(this)) ctx.fillStyle = "rgba(255, 255, 32,0.8)";ctx.fill();
    ctx.stroke();

    ctx.beginPath();
    ctx.font = "20px Georgia";
    ctx.fillStyle = "green";
    ctx.textAlign = "center";
    fillText(ctx, this.state.toString(16), xx, yy + 5);
    ctx.fill();

}

function JKflipFlop(x, y, scope = globalScope, dir = "RIGHT") {
    CircuitElement.call(this, x, y, scope, dir, 1);
    this.directionFixed = true;
    this.fixedBitWidth = true;
    this.setDimensions(20, 20);
    this.rectangleObject = true;
    this.J = new Node(-20, -10, 0, this, 1, "J");
    this.K = new Node(-20, 0, 0, this, 1, "K");
    this.clockInp = new Node(-20, 10, 0, this, 1, "Clock");
    this.qOutput = new Node(20, -10, 1, this, 1, "Q");
    this.qInvOutput = new Node(20, 10, 1, this, 1, "Q Inverse");
    this.reset = new Node(10, 20, 0, this, 1, "Asynchronous Reset");
    this.preset = new Node(0, 20, 0, this, 1, "Preset");
    this.en = new Node(-10, 20, 0, this, 1, "Enable");
    this.state = 0;
    this.slaveState = 0;
    this.masterState = 0;
    this.prevClockState = 0;

    // this.wasClicked = false;

}
JKflipFlop.prototype = Object.create(CircuitElement.prototype);
JKflipFlop.prototype.constructor = JKflipFlop;
JKflipFlop.prototype.tooltipText = "JK FlipFlop ToolTip : gated SR flip-flop with the addition of a clock input.";
JKflipFlop.prototype.helplink = "https://docs.circuitverse.org/#/Sequential?id=jk-flip-flop";
JKflipFlop.prototype.isResolvable = function() {
    if (this.reset.value == 1) return true;
    if (this.clockInp.value != undefined && this.J.value != undefined && this.K.value != undefined) return true;
    return false;
}
JKflipFlop.prototype.newBitWidth = function(bitWidth) {
    this.bitWidth = bitWidth;
    this.dInp.bitWidth = bitWidth;
    this.qOutput.bitWidth = bitWidth;
    this.qInvOutput.bitWidth = bitWidth;
    this.preset.bitWidth = bitWidth;
}
JKflipFlop.prototype.resolve = function() {
    if (this.reset.value == 1) {

        this.masterState = this.slaveState = this.preset.value || 0;

    } else if (this.en.value == 0) {

        this.prevClockState = this.clockInp.value;

    } else if (this.en.value == 1 || this.en.connections.length == 0) {
        if (this.clockInp.value == this.prevClockState) {
            if (this.clockInp.value == 0 && this.J.value != undefined && this.K.value != undefined) {
                if (this.J.value && this.K.value)
                    this.masterState = 1 ^ this.slaveState;
                else if (this.J.value ^ this.K.value)
                    this.masterState = this.J.value;
            }
        } else if (this.clockInp.value != undefined) {
            if (this.clockInp.value == 1) {
                this.slaveState = this.masterState;
            } else if (this.clockInp.value == 0 && this.J.value != undefined && this.K.value != undefined) {
                if (this.J.value && this.K.value)
                    this.masterState = 1 ^ this.slaveState;
                else if (this.J.value ^ this.K.value)
                    this.masterState = this.J.value;
            }
            this.prevClockState = this.clockInp.value;
        }

    }

    if (this.qOutput.value != this.slaveState) {
        this.qOutput.value = this.slaveState;
        this.qInvOutput.value = this.flipBits(this.slaveState);
        simulationArea.simulationQueue.add(this.qOutput);
        simulationArea.simulationQueue.add(this.qInvOutput);
    }


}
JKflipFlop.prototype.customSave = function() {
    var data = {
        nodes: {
            J: findNode(this.J),
            K: findNode(this.K),
            clockInp: findNode(this.clockInp),
            qOutput: findNode(this.qOutput),
            qInvOutput: findNode(this.qInvOutput),
            reset: findNode(this.reset),
            preset: findNode(this.preset),
            en: findNode(this.en),
        },
        constructorParamaters: [this.direction]

    }
    return data;
}
JKflipFlop.prototype.customDraw = function() {

    ctx = simulationArea.context;
    ctx.beginPath();
    ctx.strokeStyle = ("rgba(0,0,0,1)");
    ctx.fillStyle = "white";
    ctx.lineWidth = correctWidth(3);
    var xx = this.x;
    var yy = this.y;

    // rect(ctx, xx - 20, yy - 20, 40, 40);
    moveTo(ctx, -20, 5, xx, yy, this.direction);
    lineTo(ctx, -15, 10, xx, yy, this.direction);
    lineTo(ctx, -20, 15, xx, yy, this.direction);


    // if ((this.b.hover&&!simulationArea.shiftDown)|| simulationArea.lastSelected == this || simulationArea.multipleObjectSelections.contains(this)) ctx.fillStyle = "rgba(255, 255, 32,0.8)";ctx.fill();
    ctx.stroke();

    ctx.beginPath();
    ctx.font = "20px Georgia";
    ctx.fillStyle = "green";
    ctx.textAlign = "center";
    fillText(ctx, this.slaveState.toString(16), xx, yy + 5);
    ctx.fill();

}

function TTY(x, y, scope = globalScope, rows = 3, cols = 32) {
    CircuitElement.call(this, x, y, scope, "RIGHT", 1);
    this.directionFixed = true;
    this.fixedBitWidth = true;
    this.cols = cols || parseInt(prompt("Enter cols:"));
    this.rows = rows || parseInt(prompt("Enter rows:"));

    this.elementWidth = Math.max(40, Math.ceil(this.cols / 2) * 20);
    this.elementHeight = Math.max(40, Math.ceil(this.rows * 15 / 20) * 20);
    this.setWidth(this.elementWidth / 2);
    this.setHeight(this.elementHeight / 2);
    // this.element = new Element(x, y, "TTY",this.elementWidth/2, this,this.elementHeight/2);

    this.clockInp = new Node(-this.elementWidth / 2, this.elementHeight / 2 - 10, 0, this, 1,"Clock");
    this.asciiInp = new Node(-this.elementWidth / 2, this.elementHeight / 2 - 30, 0, this, 7,"Ascii Input");
    // this.qOutput = new Node(20, -10, 1, this);
    this.reset = new Node(30 - this.elementWidth / 2, this.elementHeight / 2, 0, this, 1,"Reset");
    this.en = new Node(10 - this.elementWidth / 2, this.elementHeight / 2, 0, this, 1,"Enable");
    // this.masterState = 0;
    // this.slaveState = 0;
    this.prevClockState = 0;

    this.data = "";
    this.buffer = "";


}
TTY.prototype = Object.create(CircuitElement.prototype);
TTY.prototype.constructor = TTY;
TTY.prototype.tooltipText = "TTY ToolTip : Tele typewriter selected.";
TTY.prototype.helplink = "https://docs.circuitverse.org/#/Sequential?id=tty";
TTY.prototype.changeRowSize = function(size) {
    if (size == undefined || size < 1 || size > 10) return;
    if (this.rows == size) return;
    var obj = new window[this.objectType](this.x, this.y, this.scope, size, this.cols);
    this.delete();
    simulationArea.lastSelected = obj;
    return obj;
}
TTY.prototype.changeColSize = function(size) {
    if (size == undefined || size < 20 || size > 100) return;
    if (this.cols == size) return;
    var obj = new window[this.objectType](this.x, this.y, this.scope, this.rows, size);
    this.delete();
    simulationArea.lastSelected = obj;
    return obj;
}
TTY.prototype.mutableProperties = {
    "cols": {
        name: "Columns",
        type: "number",
        max: "100",
        min: "20",
        func: "changeColSize",
    },
    "rows": {
        name: "Rows",
        type: "number",
        max: "10",
        min: "1",
        func: "changeRowSize",
    }
}
TTY.prototype.isResolvable = function() {
    if (this.reset.value == 1) return true;
    else if (this.en.value == 0||(this.en.connections.length&&this.en.value==undefined)) return false;
    else if (this.clockInp.value == undefined) return false;
    else if (this.asciiInp.value == undefined) return false;
    return true;
}
TTY.prototype.resolve = function() {

    if (this.reset.value == 1) {
        this.data = "";
        return;
    }
    if (this.en.value == 0) {
        this.buffer = "";
        return;
    }

    if (this.clockInp.value == this.prevClockState) {
        if (this.clockInp.value == 0) {
            this.buffer = String.fromCharCode(this.asciiInp.value);
        }
    } else if (this.clockInp.value != undefined) {
        if (this.clockInp.value == 1) {
            this.data = this.data + this.buffer;
            if (this.data.length > this.cols * this.rows)
                this.data = this.data.slice(1);
        } else if (this.clockInp.value == 0) {
            this.buffer = String.fromCharCode(this.asciiInp.value);
        }
        this.prevClockState = this.clockInp.value;
    }

}
TTY.prototype.customSave = function() {
    var data = {
        nodes: {
            clockInp: findNode(this.clockInp),
            asciiInp: findNode(this.asciiInp),
            reset: findNode(this.reset),
            en: findNode(this.en)
        },
        constructorParamaters: [this.rows, this.cols],
    }
    return data;
}
TTY.prototype.customDraw = function() {

    ctx = simulationArea.context;
    ctx.beginPath();
    ctx.strokeStyle = ("rgba(0,0,0,1)");
    ctx.fillStyle = "white";
    ctx.lineWidth = correctWidth(3);
    var xx = this.x;
    var yy = this.y;
    // rect(ctx, xx - this.elementWidth/2, yy - this.elementHeight/2, this.elementWidth, this.elementHeight);

    moveTo(ctx, -this.elementWidth / 2, this.elementHeight / 2 - 15, xx, yy, this.direction);
    lineTo(ctx, 5 - this.elementWidth / 2, this.elementHeight / 2 - 10, xx, yy, this.direction);
    lineTo(ctx, -this.elementWidth / 2, this.elementHeight / 2 - 5, xx, yy, this.direction);


    // if ((this.b.hover&&!simulationArea.shiftDown)|| simulationArea.lastSelected == this || simulationArea.multipleObjectSelections.contains(this))
    //     ctx.fillStyle = "rgba(255, 255, 32,0.8)";
    // ctx.fill();
    ctx.stroke();

    ctx.beginPath();
    ctx.fillStyle = "green";
    ctx.textAlign = "center";
    var startY = -7.5 * this.rows + 3;
    for (var i = 0; i < this.data.length; i += this.cols) {

        var lineData = this.data.slice(i, i + this.cols);
        lineData += ' '.repeat(this.cols - lineData.length);
        fillText3(ctx, lineData, 0, startY + (i / this.cols) * 15 + 9, xx, yy, fontSize = 15, font = "Courier New", textAlign = "center");
    }
    ctx.fill();

}

function Keyboard(x, y, scope = globalScope, bufferSize = 32) {

    CircuitElement.call(this, x, y, scope, "RIGHT", 1);
    this.directionFixed = true;
    this.fixedBitWidth = true;

    this.bufferSize = bufferSize || parseInt(prompt("Enter buffer size:"));
    this.elementWidth = Math.max(80, Math.ceil(this.bufferSize / 2) * 20);
    this.elementHeight = 40; //Math.max(40,Math.ceil(this.rows*15/20)*20);
    this.setWidth(this.elementWidth / 2);
    this.setHeight(this.elementHeight / 2);

    this.clockInp = new Node(-this.elementWidth / 2, this.elementHeight / 2 - 10, 0, this, 1,"Clock");
    this.asciiOutput = new Node(30, this.elementHeight / 2, 1, this, 7,"Ascii Output");
    this.available = new Node(10, this.elementHeight / 2, 1, this, 1,"Available");
    this.reset = new Node(-10, this.elementHeight / 2, 0, this, 1,"Reset");
    this.en = new Node(-30, this.elementHeight / 2, 0, this, 1,"Enable");
    this.prevClockState = 0;
    this.buffer = "";
    this.bufferOutValue = undefined;


}
Keyboard.prototype = Object.create(CircuitElement.prototype);
Keyboard.prototype.constructor = Keyboard;
Keyboard.prototype.tooltipText = "Keyboard";
Keyboard.prototype.helplink = "https://docs.circuitverse.org/#/Sequential?id=keyboard";
Keyboard.prototype.changeBufferSize = function(size) {
    if (size == undefined || size < 20 || size > 100) return;
    if (this.bufferSize == size) return;
    var obj = new window[this.objectType](this.x, this.y, this.scope, size);
    this.delete();
    simulationArea.lastSelected = obj;
    return obj;
}
Keyboard.prototype.mutableProperties = {
    "bufferSize": {
        name: "Buffer Size",
        type: "number",
        max: "100",
        min: "20",
        func: "changeBufferSize",
    }
}
Keyboard.prototype.keyDown = function(key) {
    if (key.length != 1) return;
    this.buffer += key;
    if (this.buffer.length > this.bufferSize)
        this.buffer = this.buffer.slice(1);
    //console.log(key)

}
Keyboard.prototype.isResolvable = function() {
    if (this.reset.value == 1) return true;
    else if (this.en.value == 0||(this.en.connections.length&&this.en.value==undefined)) return false;
    else if (this.clockInp.value == undefined) return false;
    return true;
}
Keyboard.prototype.resolve = function() {

    if (this.reset.value == 1) {
        this.buffer = "";
        return;
    }
    if (this.en.value == 0) {
        return;
    }

    if (this.available.value != 0) {
        this.available.value = 0; //this.bufferOutValue;
        simulationArea.simulationQueue.add(this.available);
    }

    if (this.clockInp.value == this.prevClockState) {
        if (this.clockInp.value == 0) {
            if (this.buffer.length) {
                this.bufferOutValue = this.buffer[0].charCodeAt(0);
            } else {
                this.bufferOutValue = undefined;
            }
        }
    } else if (this.clockInp.value != undefined) {

        if (this.clockInp.value == 1 && this.buffer.length) {
            if (this.bufferOutValue == this.buffer[0].charCodeAt(0)) { // WHY IS THIS REQUIRED ??
                this.buffer = this.buffer.slice(1);
            }
        } else {
            if (this.buffer.length) {
                this.bufferOutValue = this.buffer[0].charCodeAt(0);
            } else {
                this.bufferOutValue = undefined;
            }
        }
        this.prevClockState = this.clockInp.value;
    }

    if (this.asciiOutput.value != this.bufferOutValue) {
        this.asciiOutput.value = this.bufferOutValue;
        simulationArea.simulationQueue.add(this.asciiOutput);
    }

    if (this.bufferOutValue !== undefined && this.available.value != 1) {
        this.available.value = 1; //this.bufferOutValue;
        simulationArea.simulationQueue.add(this.available);
    }

}
Keyboard.prototype.customSave = function() {
    var data = {
        nodes: {
            clockInp: findNode(this.clockInp),
            asciiOutput: findNode(this.asciiOutput),
            available: findNode(this.available),
            reset: findNode(this.reset),
            en: findNode(this.en)
        },
        constructorParamaters: [this.bufferSize]
    }
    return data;
}
Keyboard.prototype.customDraw = function() {

    ctx = simulationArea.context;
    ctx.beginPath();
    ctx.strokeStyle = ("rgba(0,0,0,1)");
    ctx.fillStyle = "white";
    ctx.lineWidth = correctWidth(3);
    var xx = this.x;
    var yy = this.y;
    moveTo(ctx, -this.elementWidth / 2, this.elementHeight / 2 - 15, xx, yy, this.direction);
    lineTo(ctx, 5 - this.elementWidth / 2, this.elementHeight / 2 - 10, xx, yy, this.direction);
    lineTo(ctx, -this.elementWidth / 2, this.elementHeight / 2 - 5, xx, yy, this.direction);

    ctx.stroke();

    ctx.beginPath();
    ctx.fillStyle = "green";
    ctx.textAlign = "center";
    var lineData = this.buffer + ' '.repeat(this.bufferSize - this.buffer.length);
    fillText3(ctx, lineData, 0, +5, xx, yy, fontSize = 15, font = "Courier New", textAlign = "center");
    ctx.fill();
}

function Clock(x, y, scope = globalScope, dir = "RIGHT") {
    CircuitElement.call(this, x, y, scope, dir, 1);
    this.fixedBitWidth = true;
    this.output1 = new Node(10, 0, 1, this, 1);
    this.state = 0;
    this.output1.value = this.state;
    this.wasClicked = false;
    this.interval = null;

}
Clock.prototype = Object.create(CircuitElement.prototype);
Clock.prototype.constructor = Clock;
Clock.prototype.tooltipText = "Clock";
Clock.prototype.customSave = function() {
    var data = {
        nodes: {
            output1: findNode(this.output1)
        },
        constructorParamaters: [this.direction],
    }
    return data;
}
Clock.prototype.resolve = function() {
    this.output1.value = this.state;
    simulationArea.simulationQueue.add(this.output1);
}
Clock.prototype.toggleState = function() { //toggleState
    this.state = (this.state + 1) % 2;
    this.output1.value = this.state;
}
Clock.prototype.click = Clock.prototype.toggleState;
Clock.prototype.customDraw = function() {

    ctx = simulationArea.context;
    ctx.strokeStyle = ("rgba(0,0,0,1)");
    ctx.fillStyle = "white";
    ctx.lineWidth = correctWidth(3);
    var xx = this.x;
    var yy = this.y;

    ctx.beginPath();
    ctx.strokeStyle = ["DarkGreen", "Lime"][this.state];
    ctx.lineWidth = correctWidth(2);
    if (this.state == 0) {
        moveTo(ctx, -6, 0, xx, yy, "RIGHT");
        lineTo(ctx, -6, 5, xx, yy, "RIGHT");
        lineTo(ctx, 0, 5, xx, yy, "RIGHT");
        lineTo(ctx, 0, -5, xx, yy, "RIGHT");
        lineTo(ctx, 6, -5, xx, yy, "RIGHT");
        lineTo(ctx, 6, 0, xx, yy, "RIGHT");

    } else {
        moveTo(ctx, -6, 0, xx, yy, "RIGHT");
        lineTo(ctx, -6, -5, xx, yy, "RIGHT");
        lineTo(ctx, 0, -5, xx, yy, "RIGHT");
        lineTo(ctx, 0, 5, xx, yy, "RIGHT");
        lineTo(ctx, 6, 5, xx, yy, "RIGHT");
        lineTo(ctx, 6, 0, xx, yy, "RIGHT");
    }
    ctx.stroke();

}
Clock.verilogInstructions = function() {
    return "Clock - Use a single global clock\n";
}
/*
Clock.moduleVerilog = function() {
    return `
module Clock(clk);
  output reg clk;
  always begin
    #10
    clk=1'b0;
    #10
    clk=1'b0;
  end
endmodule
`
}
*/
