diff --git a/.gitignore b/.gitignore
index 2c15fd5..b90121d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
notes.txt
-.DS_Store
\ No newline at end of file
+.DS_Store
+node_modules
diff --git a/Q/Q-BlochSphere.js b/Q/Q-BlochSphere.js
deleted file mode 100644
index af0d3bd..0000000
--- a/Q/Q-BlochSphere.js
+++ /dev/null
@@ -1,582 +0,0 @@
-
-// Copyright © 2019–2020, Stewart Smith. See LICENSE for details.
-
-
-
-
-Q.BlochSphere = function( onValueChange ){
-
- Object.assign( this, {
-
- isRotating: false,
- radius: 1,
- radiusSafe: 1.01,
- axesLineWidth: 0.01,
- arcLineWidth: 0.015,
- state: Q.Qubit.LEFT_HAND_CIRCULAR_POLARIZED.toBlochSphere(),
- target: Q.Qubit.HORIZONTAL.toBlochSphere(),
- group: new THREE.Group(),
- onValueChange
- })
-
-
- // Create the surface of the Bloch sphere.
-
- const surface = new THREE.Mesh(
-
- new THREE.SphereGeometry( this.radius, 64, 64 ),
- new THREE.MeshPhongMaterial({
-
- side: THREE.FrontSide,
- map: Q.BlochSphere.makeSurface(),
- transparent: true,
- opacity: 0.97
- })
- )
- surface.receiveShadow = true
- this.group.add( surface )
-
-
-
-
- // Create the X, Y, and Z axis lines.
-
- const
- xAxis = new THREE.Mesh(
-
- new THREE.BoxGeometry( this.axesLineWidth, this.axesLineWidth, this.radius * 2.5 ),
- new THREE.MeshBasicMaterial({ color: Q.BlochSphere.xAxisColor })
- ),
- yAxis = new THREE.Mesh(
-
- new THREE.BoxGeometry( this.radius * 2.5, this.axesLineWidth, this.axesLineWidth ),
- new THREE.MeshBasicMaterial({ color: Q.BlochSphere.yAxisColor })
- ),
- zAxis = new THREE.Mesh(
-
- new THREE.BoxGeometry( this.axesLineWidth, this.radius * 2.5, this.axesLineWidth ),
- new THREE.MeshBasicMaterial({ color: Q.BlochSphere.zAxisColor })
- )
-
- this.group.add( xAxis, yAxis, zAxis )
-
-
- // Create X, Y, and Z arrow heads,
- // indicating positive directions for all three.
-
- const
- arrowLength = 0.101,// I know, weird, right?
- arrowHeadLength = 0.1,
- arrowHeadWidth = 0.1
-
- this.group.add( new THREE.ArrowHelper(
-
- new THREE.Vector3( 0, 0, 1.00 ),
- new THREE.Vector3( 0, 0, 1.25 ),
- arrowLength,
- Q.BlochSphere.xAxisColor,// Red
- arrowHeadLength,
- arrowHeadWidth
- ))
- this.group.add( new THREE.ArrowHelper(
-
- new THREE.Vector3( 1.00, 0, 0 ),
- new THREE.Vector3( 1.25, 0, 0 ),
- arrowLength,
- Q.BlochSphere.yAxisColor,// Green
- arrowHeadLength,
- arrowHeadWidth
- ))
- this.group.add( new THREE.ArrowHelper(
-
- new THREE.Vector3( 0, 1.00, 0 ),
- new THREE.Vector3( 0, 1.25, 0 ),
- arrowLength,
- Q.BlochSphere.zAxisColor,// Blue
- arrowHeadLength,
- arrowHeadWidth
- ))
-
-
- // Create the X, Y, and Z axis labels.
-
- const
- axesLabelStyle = {
-
- width: 128,
- height: 128,
- fillStyle: Q.BlochSphere.vectorColor,//'#505962',
- font: 'bold italic 64px Georgia, "Times New Roman", serif'
- },
- xAxisLabel = new THREE.Sprite(
-
- new THREE.SpriteMaterial({
-
- map: Object.assign( SurfaceText( axesLabelStyle ))
- })
- ),
- yAxisLabel = new THREE.Sprite(
-
- new THREE.SpriteMaterial({
-
- map: Object.assign( SurfaceText( axesLabelStyle ))
- })
- ),
- zAxisLabel = new THREE.Sprite(
-
- new THREE.SpriteMaterial({
-
- map: Object.assign( SurfaceText( axesLabelStyle ))
- })
- )
-
- xAxisLabel.material.map.print( 'x' )
- xAxisLabel.position.set( 0, 0, 1.45 )
- xAxisLabel.scale.set( 0.25, 0.25, 0.25 )
- xAxis.add( xAxisLabel )
-
- yAxisLabel.material.map.print( 'y' )
- yAxisLabel.position.set( 1.45, 0, 0 )
- yAxisLabel.scale.set( 0.25, 0.25, 0.25 )
- yAxis.add( yAxisLabel )
-
- zAxisLabel.material.map.print( 'z' )
- zAxisLabel.position.set( 0, 1.45, 0 )
- zAxisLabel.scale.set( 0.25, 0.25, 0.25 )
- zAxis.add( zAxisLabel )
-
-
- this.blochColor = new THREE.Color()
-
-
- // Create the line from the sphere’s origin
- // out to where the Bloch vector intersects
- // with the sphere’s surface.
-
- this.blochVector = new THREE.Mesh(
-
- new THREE.BoxGeometry( 0.04, 0.04, this.radius ),
- new THREE.MeshBasicMaterial({ color: Q.BlochSphere.vectorColor })
- )
- this.blochVector.geometry.translate( 0, 0, 0.5 )
- this.group.add( this.blochVector )
-
-
- // Create the cone that indicates the Bloch vector
- // and points to where that vectors
- // intersects with the surface of the sphere.
-
- this.blochPointer = new THREE.Mesh(
-
- new THREE.CylinderBufferGeometry( 0, 0.5, 1, 32, 1 ),
- new THREE.MeshPhongMaterial({ color: Q.BlochSphere.vectorColor })
- )
- this.blochPointer.geometry.translate( 0, -0.5, 0 )
- this.blochPointer.geometry.rotateX( Math.PI / 2 )
- this.blochPointer.geometry.scale( 0.2, 0.2, 0.2 )
- this.blochPointer.lookAt( new THREE.Vector3() )
- this.blochPointer.receiveShadow = true
- this.blochPointer.castShadow = true
- this.group.add( this.blochPointer )
-
-
- // Create the Theta ring that will belt the sphere.
-
- const
- arcR = this.radiusSafe * Math.sin( Math.PI / 2 ),
- arcH = this.radiusSafe * Math.cos( Math.PI / 2 ),
- thetaGeometry = Q.BlochSphere.createLatitudeArc( arcR, 128, Math.PI / 2, Math.PI * 2 ),
- thetaLine = new MeshLine(),
- thetaPhiMaterial = new MeshLineMaterial({
-
- color: Q.BlochSphere.thetaPhiColor,//0x505962,
- lineWidth: this.arcLineWidth * 3,
- sizeAttenuation: true
- })
-
- thetaGeometry.rotateX( Math.PI / 2 )
- thetaGeometry.rotateY( Math.PI / 2 )
- thetaGeometry.translate( 0, arcH, 0 )
- thetaLine.setGeometry( thetaGeometry )
-
- this.thetaMesh = new THREE.Mesh(
-
- thetaLine.geometry,
- thetaPhiMaterial
- )
- this.group.add( this.thetaMesh )
-
-
- // Create the Phi arc that will draw from the north pole
- // down to wherever the Theta arc rests.
-
- this.phiGeometry = Q.BlochSphere.createLongitudeArc( this.radiusSafe, 64, 0, Math.PI * 2 ),
- this.phiLine = new MeshLine()
- this.phiLine.setGeometry( this.phiGeometry )
- this.phiMesh = new THREE.Mesh(
-
- this.phiLine.geometry,
- thetaPhiMaterial
- )
- this.group.add( this.phiMesh )
-
-
-
-
- // Time to put plans to action.
-
- Q.BlochSphere.prototype.setTargetState.call( this )
-}
-
-
-
-
-
-
- ////////////////
- // //
- // Static //
- // //
-////////////////
-
-
-Object.assign( Q.BlochSphere, {
-
- xAxisColor: 0x333333,// Was 0xCF1717 (red)
- yAxisColor: 0x333333,// Was 0x59A112 (green)
- zAxisColor: 0x333333,// Was 0x0F66BD (blue)
- vectorColor: 0xFFFFFF,// Was 0xF2B90D (yellow)
- thetaPhiColor: 0x333333,// Was 0xF2B90D (yellow)
-
-
- // It’s important that we build the texture
- // right here and now, rather than load an image.
- // Why? Because if we load a pre-existing image
- // we run into CORS problems using file:/// !
-
- makeSurface: function(){
-
- const
- width = 2048,
- height = width / 2
-
- const canvas = document.createElement( 'canvas' )
- canvas.width = width
- canvas.height = height
-
- const context = canvas.getContext( '2d' )
- context.fillStyle = 'hsl( 210, 20%, 100% )'
- context.fillRect( 0, 0, width, height )
-
-
- // Create the base hue gradient for our texture.
-
- const
- hueGradient = context.createLinearGradient( 0, height / 2, width, height / 2 ),
- hueSteps = 180,
- huesPerStep = 360 / hueSteps
-
- for( let i = 0; i <= hueSteps; i ++ ){
-
- hueGradient.addColorStop( i / hueSteps, 'hsl( '+ ( i * huesPerStep - 90 ) +', 100%, 50% )' )
- }
- context.fillStyle = hueGradient
- context.fillRect( 0, 0, width, height )
-
-
- // For both the northern gradient (to white)
- // and the southern gradient (to black)
- // we’ll leave a thin band of full saturation
- // near the equator.
-
- const whiteGradient = context.createLinearGradient( width / 2, 0, width / 2, height / 2 )
- whiteGradient.addColorStop( 0.000, 'hsla( 0, 0%, 100%, 1 )' )
- whiteGradient.addColorStop( 0.125, 'hsla( 0, 0%, 100%, 1 )' )
- whiteGradient.addColorStop( 0.875, 'hsla( 0, 0%, 100%, 0 )' )
- context.fillStyle = whiteGradient
- context.fillRect( 0, 0, width, height / 2 )
-
- const blackGradient = context.createLinearGradient( width / 2, height / 2, width / 2, height )
- blackGradient.addColorStop( 0.125, 'hsla( 0, 0%, 0%, 0 )' )
- blackGradient.addColorStop( 0.875, 'hsla( 0, 0%, 0%, 1 )' )
- blackGradient.addColorStop( 1.000, 'hsla( 0, 0%, 0%, 1 )' )
- context.fillStyle = blackGradient
- context.fillRect( 0, height / 2, width, height )
-
-
- // Create lines of latitude and longitude.
- // Note this is an inverse Mercatur projection ;)
-
- context.fillStyle = 'hsla( 0, 0%, 0%, 0.2 )'
- const yStep = height / 16
- for( let y = 0; y <= height; y += yStep ){
-
- context.fillRect( 0, y, width, 1 )
- }
- const xStep = width / 16
- for( let x = 0; x <= width; x += xStep ){
-
- context.fillRect( x, 0, 1, height )
- }
-
-
- // Prepare the THREE texture and return it
- // so we can use it as a material map.
-
- const texture = new THREE.CanvasTexture( canvas )
- texture.needsUpdate = true
- return texture
- },
-
-
-
-
- createLongitudeArc: function( radius, segments, thetaStart, thetaLength ){
-
- const geometry = new THREE.CircleGeometry( radius, segments, thetaStart, thetaLength )
- geometry.vertices.shift()
-
-
- // This is NOT NORMALLY necessary
- // because we expect this to only be
- // between PI/2 and PI*2
- // (so the length is only Math.PI instead of PI*2).
-
- if( thetaLength >= Math.PI * 2 ){
-
- geometry.vertices.push( geometry.vertices[ 0 ].clone() )
- }
- return geometry
- },
- createLatitudeArc: function( radius, segments, phiStart, phiLength ){
-
- const geometry = new THREE.CircleGeometry( radius, segments, phiStart, phiLength )
- geometry.vertices.shift()
- if( phiLength >= 2 * Math.PI ){
-
- geometry.vertices.push( geometry.vertices[ 0 ].clone() )
- }
- return geometry
- },
- createQuadSphere: function( options ){
-
- let {
-
- radius,
- phiStart,
- phiLength,
- thetaStart,
- thetaLength,
- latitudeLinesTotal,
- longitudeLinesTotal,
- latitudeLineSegments,
- longitudeLineSegments,
- latitudeLinesAttributes,
- longitudeLinesAttributes
-
- } = options
-
- if( typeof radius !== 'number' ) radius = 1
- if( typeof phiStart !== 'number' ) phiStart = Math.PI / 2
- if( typeof phiLength !== 'number' ) phiLength = Math.PI * 2
- if( typeof thetaStart !== 'number' ) thetaStart = 0
- if( typeof thetaLength !== 'number' ) thetaLength = Math.PI
- if( typeof latitudeLinesTotal !== 'number' ) latitudeLinesTotal = 16
- if( typeof longitudeLinesTotal !== 'number' ) longitudeLinesTotal = 16
- if( typeof latitudeLineSegments !== 'number' ) latitudeLineSegments = 64
- if( typeof longitudeLineSegments !== 'number' ) longitudeLineSegments = 64
- if( typeof latitudeLinesAttributes === 'undefined' ) latitudeLinesAttributes = { color: 0xCCCCCC }
- if( typeof longitudeLinesAttributes === 'undefined' ) longitudeLinesAttributes = { color: 0xCCCCCC }
-
- const
- sphere = new THREE.Group(),
- latitudeLinesMaterial = new THREE.LineBasicMaterial( latitudeLinesAttributes ),
- longitudeLinesMaterial = new THREE.LineBasicMaterial( longitudeLinesAttributes )
-
-
- // Lines of longitude.
- // https://en.wikipedia.org/wiki/Longitude
-
- for(
-
- let
- phiDelta = phiLength / longitudeLinesTotal,
- phi = phiStart,
- arc = Q.BlochSphere.createLongitudeArc( radius, longitudeLineSegments, thetaStart + Math.PI / 2, thetaLength );
- phi < phiStart + phiLength + phiDelta;
- phi += phiDelta ){
-
- const geometry = arc.clone()
- geometry.rotateY( phi )
- sphere.add( new THREE.Line( geometry, longitudeLinesMaterial ))
- }
-
-
- // Lines of latitude.
- // https://en.wikipedia.org/wiki/Latitude
-
- for (
-
- let
- thetaDelta = thetaLength / latitudeLinesTotal,
- theta = thetaStart;
- theta < thetaStart + thetaLength;
- theta += thetaDelta ){
-
- if( theta === 0 ) continue
-
- const
- arcR = radius * Math.sin( theta ),
- arcH = radius * Math.cos( theta ),
- geometry = Q.BlochSphere.createLatitudeArc( arcR, latitudeLineSegments, phiStart, phiLength )
-
- geometry.rotateX( Math.PI / 2 )
- geometry.rotateY( Math.PI / 2 )
- geometry.translate( 0, arcH, 0 )
- sphere.add( new THREE.Line( geometry, latitudeLinesMaterial ))
- }
-
-
- return sphere
- }
-})
-
-
-
-
-
-
- ///////////////
- // //
- // Proto //
- // //
-///////////////
-
-
-Object.assign( Q.BlochSphere.prototype, {
-
- update: function(){
-
- if( this.isRotating ) this.group.rotation.y += Math.PI / 4096
- },
- setTargetState: function( target ){
-
- if( target === undefined ) target = Q.Qubit.HORIZONTAL.toBlochSphere()
-
-
- // Always take the shortest path around
- // even if it crosses the 0˚ / 360˚ boundary,
- // ie. between Anti-Diagonal (-90˚) and
- // Right0-and circular polarized (180˚).
-
- const
- rangeHalf = Math.PI,
- distance = this.state.phi - target.phi
-
- if( Math.abs( distance ) > rangeHalf ){
-
- this.state.phi += Math.sign( distance ) * rangeHalf * -2
- }
-
-
- // Cheap hack to test if we need to update values
- // from within the updateBlochVector method.
-
- Object.assign( this.target, target )
-
-
- // Create the tween.
-
- window.tween = new TWEEN.Tween( this.state )
- .to( target, 1000 )
- .easing( TWEEN.Easing.Quadratic.InOut )
- .onUpdate( this.updateBlochVector.bind( this ))
- .start()
- },
- updateBlochVector: function( state ){
-
-
- // Move the big-ass surface pointer.
-
- if( state.theta !== this.target.theta ||
- state.phi !== this.target.phi ){
-
- this.blochPointer.position.set(
-
- Math.sin( state.theta ) * Math.sin( state.phi ),
- Math.cos( state.theta ),
- Math.sin( state.theta ) * Math.cos( state.phi )
- )
- this.blochPointer.lookAt( new THREE.Vector3() )
- this.blochVector.lookAt( this.blochPointer.getWorldPosition( new THREE.Vector3() ))
-
-
- // Determine the correct HSL color
- // based on Phi and Theta.
-
- let hue = state.phi * THREE.Math.RAD2DEG
- if( hue < 0 ) hue = 360 + hue
- this.blochColor.setHSL(
-
- hue / 360,
- 1,
- 1 - ( state.theta / Math.PI )
- )
- this.blochPointer.material.color = this.blochColor
- this.blochVector.material.color = this.blochColor
-
- if( state.theta !== this.target.theta ){
-
-
- // Slide the Theta ring from the north pole
- // down as far south as it needs to go
- // and scale its radius so it belts the sphere.
-
- const thetaScaleSafe = Math.max( state.theta, 0.01 )
- this.thetaMesh.scale.set(
-
- Math.sin( thetaScaleSafe ),
- 1,
- Math.sin( thetaScaleSafe )
- )
- this.thetaMesh.position.y = Math.cos( state.theta )
-
-
- // Redraw the Phi arc to extend from the north pole
- // down to only as far as the Theta ring sits.
- // Then rotate the whole Phi arc about the poles.
-
- for(
-
- let
- i = 0,
- limit = this.phiGeometry.vertices.length;
-
- i < limit;
- i ++ ){
-
- const gain = i / ( limit - 1 )
- this.phiGeometry.vertices[ i ].set(
-
- Math.sin( state.theta * gain ) * this.radiusSafe,
- Math.cos( state.theta * gain ) * this.radiusSafe,
- 0
- )
- }
- this.phiLine.setGeometry( this.phiGeometry )
- }
- if( state.phi !== this.target.phi ){
-
- this.phiMesh.rotation.y = state.phi - Math.PI / 2
- }
- if( typeof this.onValueChange === 'function' ) this.onValueChange.call( this )
- }
- }
-})
-
-
-
-
-
-
-
diff --git a/Q/Q-Circuit-Editor.js b/Q/Q-Circuit-Editor.js
deleted file mode 100644
index 4b8549a..0000000
--- a/Q/Q-Circuit-Editor.js
+++ /dev/null
@@ -1,2176 +0,0 @@
-
-// Copyright © 2019–2020, Stewart Smith. See LICENSE for details.
-
-
-
-
-Q.Circuit.Editor = function( circuit, targetEl ){
-
-
- // First order of business,
- // we require a valid circuit.
-
- if( circuit instanceof Q.Circuit !== true ) circuit = new Q.Circuit()
- this.circuit = circuit
- this.index = Q.Circuit.Editor.index ++
-
-
- // Q.Circuit.Editor is all about the DOM
- // so we’re going to get some use out of this
- // stupid (but convenient) shorthand here.
-
- const createDiv = function(){
-
- return document.createElement( 'div' )
- }
-
-
-
-
- // We want to “name” our circuit editor instance
- // but more importantly we want to give it a unique DOM ID.
- // Keep in mind we can have MULTIPLE editors
- // for the SAME circuit!
- // This is a verbose way to do it,
- // but each step is clear and I needed clarity today! ;)
-
- this.name = typeof circuit.name === 'string' ?
- circuit.name :
- 'Q Editor '+ this.index
-
-
- // If we’ve been passed a target DOM element
- // we should use that as our circuit element.
-
- if( typeof targetEl === 'string' ) targetEl = document.getElementById( targetEl )
- const circuitEl = targetEl instanceof HTMLElement ? targetEl : createDiv()
- circuitEl.classList.add( 'Q-circuit' )
-
-
- // If the target element already has an ID
- // then we want to use that as our domID.
-
- if( typeof circuitEl.getAttribute( 'id' ) === 'string' ){
-
- this.domId = circuitEl.getAttribute( 'id' )
- }
-
-
- // Otherwise let’s transform our name value
- // into a usable domId.
-
- else {
-
- let domIdBase = this.name
- .replace( /^[^a-z]+|[^\w:.-]+/gi, '-' ),
- domId = domIdBase,
- domIdAttempt = 1
-
- while( document.getElementById( domId ) !== null ){
-
- domIdAttempt ++
- domId = domIdBase +'-'+ domIdAttempt
- }
- this.domId = domId
- circuitEl.setAttribute( 'id', this.domId )
- }
-
-
-
-
- // We want a way to easily get to the circuit
- // from this interface’s DOM element.
- // (But we don’t need a way to reference this DOM element
- // from the circuit. A circuit can have many DOM elements!)
- // And we also want an easy way to reference this DOM element
- // from this Editor instance.
-
- circuitEl.circuit = circuit
- this.domElement = circuitEl
-
-
- // Create a toolbar for containing buttons.
-
- const toolbarEl = createDiv()
- circuitEl.appendChild( toolbarEl )
- toolbarEl.classList.add( 'Q-circuit-toolbar' )
-
-
- // Create a toggle switch for locking the circuit.
-
- const lockToggle = createDiv()
- toolbarEl.appendChild( lockToggle )
- lockToggle.classList.add( 'Q-circuit-button', 'Q-circuit-toggle', 'Q-circuit-toggle-lock' )
- lockToggle.setAttribute( 'title', 'Lock / unlock' )
- lockToggle.innerText = '🔓'
-
-
- // Create an “Undo” button
- // that enables and disables
- // based on available undo history.
-
- const undoButton = createDiv()
- toolbarEl.appendChild( undoButton )
- undoButton.classList.add( 'Q-circuit-button', 'Q-circuit-button-undo' )
- undoButton.setAttribute( 'title', 'Undo' )
- undoButton.setAttribute( 'Q-disabled', 'Q-disabled' )
- undoButton.innerHTML = '⟲'
- window.addEventListener( 'Q.History undo is depleted', function( event ){
-
- if( event.detail.instance === circuit )
- undoButton.setAttribute( 'Q-disabled', 'Q-disabled' )
- })
- window.addEventListener( 'Q.History undo is capable', function( event ){
-
- if( event.detail.instance === circuit )
- undoButton.removeAttribute( 'Q-disabled' )
- })
-
-
- // Create an “Redo” button
- // that enables and disables
- // based on available redo history.
-
- const redoButton = createDiv()
- toolbarEl.appendChild( redoButton )
- redoButton.classList.add( 'Q-circuit-button', 'Q-circuit-button-redo' )
- redoButton.setAttribute( 'title', 'Redo' )
- redoButton.setAttribute( 'Q-disabled', 'Q-disabled' )
- redoButton.innerHTML = '⟳'
- window.addEventListener( 'Q.History redo is depleted', function( event ){
-
- if( event.detail.instance === circuit )
- redoButton.setAttribute( 'Q-disabled', 'Q-disabled' )
- })
- window.addEventListener( 'Q.History redo is capable', function( event ){
-
- if( event.detail.instance === circuit )
- redoButton.removeAttribute( 'Q-disabled' )
- })
-
-
- // Create a button for joining
- // an “identity cursor”
- // and one or more same-gate operations
- // into a controlled operation.
- // (Will be enabled / disabled from elsewhere.)
-
- const controlButton = createDiv()
- toolbarEl.appendChild( controlButton )
- controlButton.classList.add( 'Q-circuit-button', 'Q-circuit-toggle', 'Q-circuit-toggle-control' )
- controlButton.setAttribute( 'title', 'Create controlled operation' )
- controlButton.setAttribute( 'Q-disabled', 'Q-disabled' )
- controlButton.innerText = 'C'
-
-
- // Create a button for joining
- // two “identity cursors”
- // into a swap operation.
- // (Will be enabled / disabled from elsewhere.)
-
- const swapButton = createDiv()
- toolbarEl.appendChild( swapButton )
- swapButton.classList.add( 'Q-circuit-button', 'Q-circuit-toggle-swap' )
- swapButton.setAttribute( 'title', 'Create swap operation' )
- swapButton.setAttribute( 'Q-disabled', 'Q-disabled' )
- swapButton.innerText = 'S'
-
-
- // Create a circuit board container
- // so we can house a scrollable circuit board.
-
- const boardContainerEl = createDiv()
- circuitEl.appendChild( boardContainerEl )
- boardContainerEl.classList.add( 'Q-circuit-board-container' )
- //boardContainerEl.addEventListener( 'touchstart', Q.Circuit.Editor.onPointerPress )
- boardContainerEl.addEventListener( 'mouseleave', function(){
-
- Q.Circuit.Editor.unhighlightAll( circuitEl )
- })
-
- const boardEl = createDiv()
- boardContainerEl.appendChild( boardEl )
- boardEl.classList.add( 'Q-circuit-board' )
-
- const backgroundEl = createDiv()
- boardEl.appendChild( backgroundEl )
- backgroundEl.classList.add( 'Q-circuit-board-background' )
-
-
- // Create background highlight bars
- // for each row.
-
- for( let i = 0; i < circuit.bandwidth; i ++ ){
-
- const rowEl = createDiv()
- backgroundEl.appendChild( rowEl )
- rowEl.style.position = 'relative'
- rowEl.style.gridRowStart = i + 2
- rowEl.style.gridColumnStart = 1
- rowEl.style.gridColumnEnd = Q.Circuit.Editor.momentIndexToGridColumn( circuit.timewidth ) + 1
- rowEl.setAttribute( 'register-index', i + 1 )
-
- const wireEl = createDiv()
- rowEl.appendChild( wireEl )
- wireEl.classList.add( 'Q-circuit-register-wire' )
- }
-
-
- // Create background highlight bars
- // for each column.
-
- for( let i = 0; i < circuit.timewidth; i ++ ){
-
- const columnEl = createDiv()
- backgroundEl.appendChild( columnEl )
- columnEl.style.gridRowStart = 2
- columnEl.style.gridRowEnd = Q.Circuit.Editor.registerIndexToGridRow( circuit.bandwidth ) + 1
- columnEl.style.gridColumnStart = i + 3
- columnEl.setAttribute( 'moment-index', i + 1 )
- }
-
-
- // Create the circuit board foreground
- // for all interactive elements.
-
- const foregroundEl = createDiv()
- boardEl.appendChild( foregroundEl )
- foregroundEl.classList.add( 'Q-circuit-board-foreground' )
-
-
- // Add “Select All” toggle button to upper-left corner.
-
- const selectallEl = createDiv()
- foregroundEl.appendChild( selectallEl )
- selectallEl.classList.add( 'Q-circuit-header', 'Q-circuit-selectall' )
- selectallEl.setAttribute( 'title', 'Select all' )
- selectallEl.setAttribute( 'moment-index', '0' )
- selectallEl.setAttribute( 'register-index', '0' )
- selectallEl.innerHTML = '↘'
-
-
- // Add register index symbols to left-hand column.
-
- for( let i = 0; i < circuit.bandwidth; i ++ ){
-
- const
- registerIndex = i + 1,
- registersymbolEl = createDiv()
-
- foregroundEl.appendChild( registersymbolEl )
- registersymbolEl.classList.add( 'Q-circuit-header', 'Q-circuit-register-label' )
- registersymbolEl.setAttribute( 'title', 'Register '+ registerIndex +' of '+ circuit.bandwidth )
- registersymbolEl.setAttribute( 'register-index', registerIndex )
- registersymbolEl.style.gridRowStart = Q.Circuit.Editor.registerIndexToGridRow( registerIndex )
- registersymbolEl.innerText = registerIndex
- }
-
-
- // Add “Add register” button.
-
- const addRegisterEl = createDiv()
- foregroundEl.appendChild( addRegisterEl )
- addRegisterEl.classList.add( 'Q-circuit-header', 'Q-circuit-register-add' )
- addRegisterEl.setAttribute( 'title', 'Add register' )
- addRegisterEl.style.gridRowStart = Q.Circuit.Editor.registerIndexToGridRow( circuit.bandwidth + 1 )
- addRegisterEl.innerText = '+'
-
-
- // Add moment index symbols to top row.
-
- for( let i = 0; i < circuit.timewidth; i ++ ){
-
- const
- momentIndex = i + 1,
- momentsymbolEl = createDiv()
-
- foregroundEl.appendChild( momentsymbolEl )
- momentsymbolEl.classList.add( 'Q-circuit-header', 'Q-circuit-moment-label' )
- momentsymbolEl.setAttribute( 'title', 'Moment '+ momentIndex +' of '+ circuit.timewidth )
- momentsymbolEl.setAttribute( 'moment-index', momentIndex )
- momentsymbolEl.style.gridColumnStart = Q.Circuit.Editor.momentIndexToGridColumn( momentIndex )
- momentsymbolEl.innerText = momentIndex
- }
-
-
- // Add “Add moment” button.
-
- const addMomentEl = createDiv()
- foregroundEl.appendChild( addMomentEl )
- addMomentEl.classList.add( 'Q-circuit-header', 'Q-circuit-moment-add' )
- addMomentEl.setAttribute( 'title', 'Add moment' )
- addMomentEl.style.gridColumnStart = Q.Circuit.Editor.momentIndexToGridColumn( circuit.timewidth + 1 )
- addMomentEl.innerText = '+'
-
-
- // Add input values.
-
- circuit.qubits.forEach( function( qubit, i ){
-
- const
- rowIndex = i + 1,
- inputEl = createDiv()
-
- inputEl.classList.add( 'Q-circuit-header', 'Q-circuit-input' )
- inputEl.setAttribute( 'title', `Qubit #${ rowIndex } starting value` )
- inputEl.setAttribute( 'register-index', rowIndex )
- inputEl.style.gridRowStart = Q.Circuit.Editor.registerIndexToGridRow( rowIndex )
- inputEl.innerText = qubit.beta.toText()
- foregroundEl.appendChild( inputEl )
- })
-
-
- // Add operations.
-
- circuit.operations.forEach( function( operation ){
-
- Q.Circuit.Editor.set( circuitEl, operation )
- })
-
-
- // Add event listeners.
-
- circuitEl.addEventListener( 'mousedown', Q.Circuit.Editor.onPointerPress )
- circuitEl.addEventListener( 'touchstart', Q.Circuit.Editor.onPointerPress )
- window.addEventListener(
-
- 'Q.Circuit.set$',
- Q.Circuit.Editor.prototype.onExternalSet.bind( this )
- )
- window.addEventListener(
-
- 'Q.Circuit.clear$',
- Q.Circuit.Editor.prototype.onExternalClear.bind( this )
- )
-
-
- // How can we interact with this circuit
- // through code? (How cool is this?!)
-
- const referenceEl = document.createElement( 'p' )
- circuitEl.appendChild( referenceEl )
- referenceEl.innerHTML = `
- This circuit is accessible in your
- JavaScript console
- as document.getElementById('${ this.domId }').circuit
`
- //document.getElementById('Q-Editor-0').circuit
- //$('#${ this.domId }')
-
-
- // Put a note in the JavaScript console
- // that includes how to reference the circuit via code
- // and an ASCII diagram for reference.
-
- Q.log( 0.5,
-
- `\n\nCreated a DOM interface for $('#${ this.domId }').circuit\n\n`,
- circuit.toDiagram(),
- '\n\n\n'
- )
-}
-
-
-// Augment Q.Circuit to have this functionality.
-
-Q.Circuit.toDom = function( circuit, targetEl ){
-
- return new Q.Circuit.Editor( circuit, targetEl ).domElement
-}
-Q.Circuit.prototype.toDom = function( targetEl ){
-
- return new Q.Circuit.Editor( this, targetEl ).domElement
-}
-
-
-
-
-
-
-
-
-Object.assign( Q.Circuit.Editor, {
-
- index: 0,
- help: function(){ return Q.help( this )},
- dragEl: null,
- gridColumnToMomentIndex: function( gridColumn ){ return +gridColumn - 2 },
- momentIndexToGridColumn: function( momentIndex ){ return momentIndex + 2 },
- gridRowToRegisterIndex: function( gridRow ){ return +gridRow - 1 },
- registerIndexToGridRow: function( registerIndex ){ return registerIndex + 1 },
- gridSize: 4,// CSS: grid-auto-columns = grid-auto-rows = 4rem.
- pointToGrid: function( p ){
-
-
- // Take a 1-dimensional point value
- // (so either an X or a Y but not both)
- // and return what CSS grid cell contains it
- // based on our 4rem × 4rem grid setup.
-
- const rem = parseFloat( getComputedStyle( document.documentElement ).fontSize )
- return 1 + Math.floor( p / ( rem * Q.Circuit.Editor.gridSize ))
- },
- gridToPoint: function( g ){
-
-
- // Take a 1-dimensional grid cell value
- // (so either a row or a column but not both)
- // and return the minimum point value it contains.
-
- const rem = parseFloat( getComputedStyle( document.documentElement ).fontSize )
- return rem * Q.Circuit.Editor.gridSize * ( g - 1 )
- },
- getInteractionCoordinates: function( event, pageOrClient ){
-
- if( typeof pageOrClient !== 'string' ) pageOrClient = 'client'//page
- if( event.changedTouches &&
- event.changedTouches.length ) return {
-
- x: event.changedTouches[ 0 ][ pageOrClient +'X' ],
- y: event.changedTouches[ 0 ][ pageOrClient +'Y' ]
- }
- return {
-
- x: event[ pageOrClient +'X' ],
- y: event[ pageOrClient +'Y' ]
- }
- },
- createPalette: function( targetEl ){
-
- if( typeof targetEl === 'string' ) targetEl = document.getElementById( targetEl )
-
- const
- paletteEl = targetEl instanceof HTMLElement ? targetEl : document.createElement( 'div' ),
- randomRangeAndSign = function( min, max ){
-
- const r = min + Math.random() * ( max - min )
- return Math.floor( Math.random() * 2 ) ? r : -r
- }
-
- paletteEl.classList.add( 'Q-circuit-palette' )
-
- 'HXYZPT*'
- .split( '' )
- .forEach( function( symbol ){
-
- const gate = Q.Gate.findBySymbol( symbol )
-
- const operationEl = document.createElement( 'div' )
- paletteEl.appendChild( operationEl )
- operationEl.classList.add( 'Q-circuit-operation' )
- operationEl.classList.add( 'Q-circuit-operation-'+ gate.nameCss )
- operationEl.setAttribute( 'gate-symbol', symbol )
- operationEl.setAttribute( 'title', gate.name )
-
- const tileEl = document.createElement( 'div' )
- operationEl.appendChild( tileEl )
- tileEl.classList.add( 'Q-circuit-operation-tile' )
- if( symbol !== Q.Gate.CURSOR.symbol ) tileEl.innerText = symbol
-
- ;[ 'before', 'after' ].forEach( function( layer ){
-
- tileEl.style.setProperty( '--Q-'+ layer +'-rotation', randomRangeAndSign( 0.5, 4 ) +'deg' )
- tileEl.style.setProperty( '--Q-'+ layer +'-x', randomRangeAndSign( 1, 4 ) +'px' )
- tileEl.style.setProperty( '--Q-'+ layer +'-y', randomRangeAndSign( 1, 3 ) +'px' )
- })
- })
-
- paletteEl.addEventListener( 'mousedown', Q.Circuit.Editor.onPointerPress )
- paletteEl.addEventListener( 'touchstart', Q.Circuit.Editor.onPointerPress )
- return paletteEl
- }
-})
-
-
-
-
-
-
- /////////////////////////
- // //
- // Operation CLEAR //
- // //
-/////////////////////////
-
-
-Q.Circuit.Editor.prototype.onExternalClear = function( event ){
-
- if( event.detail.circuit === this.circuit ){
-
- Q.Circuit.Editor.clear( this.domElement, {
-
- momentIndex: event.detail.momentIndex,
- registerIndices: event.detail.registerIndices
- })
- }
-}
-Q.Circuit.Editor.clear = function( circuitEl, operation ){
-
- const momentIndex = operation.momentIndex
- operation.registerIndices.forEach( function( registerIndex ){
-
- Array
- .from( circuitEl.querySelectorAll(
-
- `[moment-index="${ momentIndex }"]`+
- `[register-index="${ registerIndex }"]`
-
- ))
- .forEach( function( op ){
-
- op.parentNode.removeChild( op )
- })
- })
-}
-
-
-
-
-
-
- ///////////////////////
- // //
- // Operation SET //
- // //
-///////////////////////
-
-
-Q.Circuit.Editor.prototype.onExternalSet = function( event ){
-
- if( event.detail.circuit === this.circuit ){
-
- Q.Circuit.Editor.set( this.domElement, event.detail.operation )
- }
-}
-Q.Circuit.Editor.set = function( circuitEl, operation ){
-
- const
- backgroundEl = circuitEl.querySelector( '.Q-circuit-board-background' ),
- foregroundEl = circuitEl.querySelector( '.Q-circuit-board-foreground' ),
- circuit = circuitEl.circuit,
- operationIndex = circuitEl.circuit.operations.indexOf( operation )
-
- operation.registerIndices.forEach( function( registerIndex, i ){
-
- const operationEl = document.createElement( 'div' )
- foregroundEl.appendChild( operationEl )
- operationEl.classList.add( 'Q-circuit-operation', 'Q-circuit-operation-'+ operation.gate.nameCss )
- // operationEl.setAttribute( 'operation-index', operationIndex )
- operationEl.setAttribute( 'gate-symbol', operation.gate.symbol )
- operationEl.setAttribute( 'gate-index', operation.gate.index )// Used as an application-wide unique ID!
- operationEl.setAttribute( 'moment-index', operation.momentIndex )
- operationEl.setAttribute( 'register-index', registerIndex )
- operationEl.setAttribute( 'register-array-index', i )// Where within the registerIndices array is this operations fragment located?
- operationEl.setAttribute( 'is-controlled', operation.isControlled )
- operationEl.setAttribute( 'title', operation.gate.name )
- operationEl.style.gridColumnStart = Q.Circuit.Editor.momentIndexToGridColumn( operation.momentIndex )
- operationEl.style.gridRowStart = Q.Circuit.Editor.registerIndexToGridRow( registerIndex )
-
- const tileEl = document.createElement( 'div' )
- operationEl.appendChild( tileEl )
- tileEl.classList.add( 'Q-circuit-operation-tile' )
- if( operation.gate.symbol !== Q.Gate.CURSOR.symbol ) tileEl.innerText = operation.gate.symbol
-
-
- // Add operation link wires
- // for multi-qubit operations.
-
- if( operation.registerIndices.length > 1 ){
-
- operationEl.setAttribute( 'register-indices', operation.registerIndices )
- operationEl.setAttribute( 'register-indices-index', i )
- operationEl.setAttribute(
-
- 'sibling-indices',
- operation.registerIndices
- .filter( function( siblingRegisterIndex ){
-
- return registerIndex !== siblingRegisterIndex
- })
- )
- operation.registerIndices.forEach( function( registerIndex, i ){
-
- if( i < operation.registerIndices.length - 1 ){
-
- const
- siblingRegisterIndex = operation.registerIndices[ i + 1 ],
- registerDelta = Math.abs( siblingRegisterIndex - registerIndex ),
- start = Math.min( registerIndex, siblingRegisterIndex ),
- end = Math.max( registerIndex, siblingRegisterIndex ),
- containerEl = document.createElement( 'div' ),
- linkEl = document.createElement( 'div' )
-
- backgroundEl.appendChild( containerEl )
- containerEl.setAttribute( 'moment-index', operation.momentIndex )
- containerEl.setAttribute( 'register-index', registerIndex )
- containerEl.classList.add( 'Q-circuit-operation-link-container' )
- containerEl.style.gridRowStart = Q.Circuit.Editor.registerIndexToGridRow( start )
- containerEl.style.gridRowEnd = Q.Circuit.Editor.registerIndexToGridRow( end + 1 )
- containerEl.style.gridColumn = Q.Circuit.Editor.momentIndexToGridColumn( operation.momentIndex )
-
- containerEl.appendChild( linkEl )
- linkEl.classList.add( 'Q-circuit-operation-link' )
- if( registerDelta > 1 ) linkEl.classList.add( 'Q-circuit-operation-link-curved' )
- }
- })
- if( operation.isControlled && i === 0 ){
-
- operationEl.classList.add( 'Q-circuit-operation-control' )
- operationEl.setAttribute( 'title', 'Control' )
- tileEl.innerText = ''
- }
- else operationEl.classList.add( 'Q-circuit-operation-target' )
- }
- })
-}
-
-
-
-
-Q.Circuit.Editor.isValidControlCandidate = function( circuitEl ){
-
- const
- selectedOperations = Array
- .from( circuitEl.querySelectorAll( '.Q-circuit-cell-selected' ))
-
-
- // We must have at least two operations selected,
- // hopefully a control and something else,
- // in order to attempt a join.
-
- if( selectedOperations.length < 2 ) return false
-
-
- // Note the different moment indices present
- // among the selected operations.
-
- const moments = selectedOperations.reduce( function( moments, operationEl ){
-
- moments[ operationEl.getAttribute( 'moment-index' )] = true
- return moments
-
- }, {} )
-
-
- // All selected operations must be in the same moment.
-
- if( Object.keys( moments ).length > 1 ) return false
-
-
- // If there are multi-register operations present,
- // regardless of whether those are controls or swaps,
- // all siblings must be present
- // in order to join a new gate to this selection.
-
- // I’m sure we can make this whole routine much more efficient
- // but its results are correct and boy am I tired ;)
-
- const allSiblingsPresent = selectedOperations
- .reduce( function( status, operationEl ){
-
- const registerIndicesString = operationEl.getAttribute( 'register-indices' )
-
-
- // If it’s a single-register operation
- // there’s no need to search further.
-
- if( !registerIndicesString ) return status
-
-
- // How many registers are in use
- // by this operation?
-
- const
- registerIndicesLength = registerIndicesString
- .split( ',' )
- .map( function( registerIndex ){
-
- return +registerIndex
- })
- .length,
-
-
- // How many of this operation’s siblings
- // (including itself) can we find?
-
- allSiblingsLength = selectedOperations
- .reduce( function( siblings, operationEl ){
-
- if( operationEl.getAttribute( 'register-indices' ) === registerIndicesString ){
-
- siblings.push( operationEl )
- }
- return siblings
-
- }, [])
- .length
-
-
- // Did we find all of the siblings for this operation?
- // Square that with previous searches.
-
- return status && allSiblingsLength === registerIndicesLength
-
- }, true )
-
-
- // If we’re missing some siblings
- // then we cannot modify whatever we have selected here.
-
- if( allSiblingsPresent !== true ) return false
-
-
- // Note the different gate types present
- // among the selected operations.
-
- const gates = selectedOperations.reduce( function( gates, operationEl ){
-
- const gateSymbol = operationEl.getAttribute( 'gate-symbol' )
- if( !Q.isUsefulInteger( gates[ gateSymbol ])) gates[ gateSymbol ] = 1
- else gates[ gateSymbol ] ++
- return gates
-
- }, {} )
-
-
- // Note if each operation is already controlled or not.
-
- const {
-
- totalControlled,
- totalNotControlled
-
- } = selectedOperations
- .reduce( function( stats, operationEl ){
-
- if( operationEl.getAttribute( 'is-controlled' ) === 'true' )
- stats.totalControlled ++
- else stats.totalNotControlled ++
- return stats
-
- }, {
-
- totalControlled: 0,
- totalNotControlled: 0
- })
-
-
- // This could be ONE “identity cursor”
- // and one or more of a regular single gate
- // that is NOT already controlled.
-
- if( gates[ Q.Gate.CURSOR.symbol ] === 1 &&
- Object.keys( gates ).length === 2 &&
- totalNotControlled === selectedOperations.length ){
-
- return true
- }
-
-
- // There’s NO “identity cursor”
- // but there is one or more of specific gate type
- // and at least one of those is already controlled.
-
- if( gates[ Q.Gate.CURSOR.symbol ] === undefined &&
- Object.keys( gates ).length === 1 &&
- totalControlled > 0 &&
- totalNotControlled > 0 ){
-
- return true
- }
-
-
- // Any other combination allowed? Nope!
-
- return false
-}
-Q.Circuit.Editor.createControl = function( circuitEl ){
-
- if( Q.Circuit.Editor.isValidControlCandidate( circuitEl ) !== true ) return this
-
-
- const
- circuit = circuitEl.circuit,
- selectedOperations = Array
- .from( circuitEl.querySelectorAll( '.Q-circuit-cell-selected' )),
-
-
- // Are any of these controlled operations??
- // If so, we need to find its control component
- // and re-use it.
-
- existingControlEl = selectedOperations.find( function( operationEl ){
-
- return (
-
- operationEl.getAttribute( 'is-controlled' ) === 'true' &&
- operationEl.getAttribute( 'register-array-index' ) === '0'
- )
- }),
-
-
- // One control. One or more targets.
-
- control = existingControlEl || selectedOperations
- .find( function( el ){
-
- return el.getAttribute( 'gate-symbol' ) === Q.Gate.CURSOR.symbol
- }),
- targets = selectedOperations
- .reduce( function( targets, el ){
-
- //if( el.getAttribute( 'gate-symbol' ) !== '!' ) targets.push( el )
- if( el !== control ) targets.push( el )
- return targets
-
- }, [] )
-
-
- // Ready to roll.
-
- circuit.history.createEntry$()
- selectedOperations.forEach( function( operationEl ){
-
- circuit.clear$(
-
- +operationEl.getAttribute( 'moment-index' ),
- +operationEl.getAttribute( 'register-index' )
- )
- })
- circuit.set$(
-
- targets[ 0 ].getAttribute( 'gate-symbol' ),
- +control.getAttribute( 'moment-index' ),
- [ +control.getAttribute( 'register-index' )].concat(
-
- targets.reduce( function( registers, operationEl ){
-
- registers.push( +operationEl.getAttribute( 'register-index' ))
- return registers
-
- }, [] )
- )
- )
-
-
- // Update our toolbar button states.
-
- Q.Circuit.Editor.onSelectionChanged( circuitEl )
- Q.Circuit.Editor.onCircuitChanged( circuitEl )
-
- return this
-}
-
-
-
-
-Q.Circuit.Editor.isValidSwapCandidate = function( circuitEl ){
-
- const
- selectedOperations = Array
- .from( circuitEl.querySelectorAll( '.Q-circuit-cell-selected' ))
-
-
- // We can only swap between two registers.
- // No crazy rotation-swap bullshit. (Yet.)
-
- if( selectedOperations.length !== 2 ) return false
-
-
- // Both operations must be “identity cursors.”
- // If so, we are good to go.
-
- areBothCursors = selectedOperations.every( function( operationEl ){
-
- return operationEl.getAttribute( 'gate-symbol' ) === Q.Gate.CURSOR.symbol
- })
- if( areBothCursors ) return true
-
-
- // Otherwise this is not a valid swap candidate.
-
- return false
-}
-Q.Circuit.Editor.createSwap = function( circuitEl ){
-
- if( Q.Circuit.Editor.isValidSwapCandidate( circuitEl ) !== true ) return this
-
- const
- selectedOperations = Array
- .from( circuitEl.querySelectorAll( '.Q-circuit-cell-selected' )),
- momentIndex = +selectedOperations[ 0 ].getAttribute( 'moment-index' )
- registerIndices = selectedOperations
- .reduce( function( registerIndices, operationEl ){
-
- registerIndices.push( +operationEl.getAttribute( 'register-index' ))
- return registerIndices
-
- }, [] ),
- circuit = circuitEl.circuit
-
-
- // Create the swap operation.
-
- circuit.history.createEntry$()
- selectedOperations.forEach( function( operation ){
-
- circuit.clear$(
-
- +operation.getAttribute( 'moment-index' ),
- +operation.getAttribute( 'register-index' )
- )
- })
- circuit.set$(
-
- Q.Gate.SWAP,
- momentIndex,
- registerIndices
- )
-
-
- // Update our toolbar button states.
-
- Q.Circuit.Editor.onSelectionChanged( circuitEl )
- Q.Circuit.Editor.onCircuitChanged( circuitEl )
-
- return this
-}
-
-
-
-
-Q.Circuit.Editor.onSelectionChanged = function( circuitEl ){
-
- const controlButtonEl = circuitEl.querySelector( '.Q-circuit-toggle-control' )
- if( Q.Circuit.Editor.isValidControlCandidate( circuitEl )){
-
- controlButtonEl.removeAttribute( 'Q-disabled' )
- }
- else controlButtonEl.setAttribute( 'Q-disabled', true )
-
- const swapButtonEl = circuitEl.querySelector( '.Q-circuit-toggle-swap' )
- if( Q.Circuit.Editor.isValidSwapCandidate( circuitEl )){
-
- swapButtonEl.removeAttribute( 'Q-disabled' )
- }
- else swapButtonEl.setAttribute( 'Q-disabled', true )
-}
-Q.Circuit.Editor.onCircuitChanged = function( circuitEl ){
-
- const circuit = circuitEl.circuit
- window.dispatchEvent( new CustomEvent(
-
- 'Q gui altered circuit',
- { detail: { circuit: circuit }}
- ))
-
- // Should we trigger a circuit.evaluate$() here?
- // Particularly when we move all that to a new thread??
- // console.log( originCircuit.report$() ) ??
-}
-
-
-
-
-
-Q.Circuit.Editor.unhighlightAll = function( circuitEl ){
-
- Array.from( circuitEl.querySelectorAll(
-
- '.Q-circuit-board-background > div,'+
- '.Q-circuit-board-foreground > div'
- ))
- .forEach( function( el ){
-
- el.classList.remove( 'Q-circuit-cell-highlighted' )
- })
-}
-
-
-
-
-
-
- //////////////////////
- // //
- // Pointer MOVE //
- // //
-//////////////////////
-
-
-Q.Circuit.Editor.onPointerMove = function( event ){
-
-
- // We need our cursor coordinates straight away.
- // We’ll use that both for dragging (immediately below)
- // and for hover highlighting (further below).
- // Let’s also hold on to a list of all DOM elements
- // that contain this X, Y point
- // and also see if one of those is a circuit board container.
-
- const
- { x, y } = Q.Circuit.Editor.getInteractionCoordinates( event ),
- foundEls = document.elementsFromPoint( x, y ),
- boardContainerEl = foundEls.find( function( el ){
-
- return el.classList.contains( 'Q-circuit-board-container' )
- })
-
-
- // Are we in the middle of a circuit clipboard drag?
- // If so we need to move that thing!
-
- if( Q.Circuit.Editor.dragEl !== null ){
-
-
- // ex. Don’t scroll on touch devices!
-
- event.preventDefault()
-
-
- // This was a very useful resource
- // for a reality check on DOM coordinates:
- // https://javascript.info/coordinates
-
- Q.Circuit.Editor.dragEl.style.left = ( x + window.pageXOffset + Q.Circuit.Editor.dragEl.offsetX ) +'px'
- Q.Circuit.Editor.dragEl.style.top = ( y + window.pageYOffset + Q.Circuit.Editor.dragEl.offsetY ) +'px'
-
- if( !boardContainerEl && Q.Circuit.Editor.dragEl.circuitEl ) Q.Circuit.Editor.dragEl.classList.add( 'Q-circuit-clipboard-danger' )
- else Q.Circuit.Editor.dragEl.classList.remove( 'Q-circuit-clipboard-danger' )
- }
-
-
- // If we’re not over a circuit board container
- // then there’s no highlighting work to do
- // so let’s bail now.
-
- if( !boardContainerEl ) return
-
-
- // Now we know we have a circuit board
- // so we must have a circuit
- // and if that’s locked then highlighting changes allowed!
-
- const circuitEl = boardContainerEl.closest( '.Q-circuit' )
- if( circuitEl.classList.contains( 'Q-circuit-locked' )) return
-
-
- // Ok, we’ve found a circuit board.
- // First, un-highlight everything.
-
- Array.from( boardContainerEl.querySelectorAll(`
-
- .Q-circuit-board-background > div,
- .Q-circuit-board-foreground > div
-
- `)).forEach( function( el ){
-
- el.classList.remove( 'Q-circuit-cell-highlighted' )
- })
-
-
- // Let’s prioritize any element that is “sticky”
- // which means it can appear OVER another grid cell.
-
- const
- cellEl = foundEls.find( function( el ){
-
- const style = window.getComputedStyle( el )
- return (
-
- style.position === 'sticky' && (
-
- el.getAttribute( 'moment-index' ) !== null ||
- el.getAttribute( 'register-index' ) !== null
- )
- )
- }),
- highlightByQuery = function( query ){
-
- Array.from( boardContainerEl.querySelectorAll( query ))
- .forEach( function( el ){
-
- el.classList.add( 'Q-circuit-cell-highlighted' )
- })
- }
-
-
- // If we’ve found one of these “sticky” cells
- // let’s use its moment and/or register data
- // to highlight moments or registers (or all).
-
- if( cellEl ){
-
- const
- momentIndex = cellEl.getAttribute( 'moment-index' ),
- registerIndex = cellEl.getAttribute( 'register-index' )
-
- if( momentIndex === null ){
-
- highlightByQuery( `div[register-index="${ registerIndex }"]` )
- return
- }
- if( registerIndex === null ){
-
- highlightByQuery( `div[moment-index="${ momentIndex }"]` )
- return
- }
- highlightByQuery(`
-
- .Q-circuit-board-background > div[moment-index],
- .Q-circuit-board-foreground > .Q-circuit-operation
-
- `)
- return
- }
-
-
- // Ok, we know we’re hovering over the circuit board
- // but we’re not on a “sticky” cell.
- // We might be over an operation, but we might not.
- // No matter -- we’ll infer the moment and register indices
- // from the cursor position.
-
- const
- boardElBounds = boardContainerEl.getBoundingClientRect(),
- xLocal = x - boardElBounds.left + boardContainerEl.scrollLeft + 1,
- yLocal = y - boardElBounds.top + boardContainerEl.scrollTop + 1,
- columnIndex = Q.Circuit.Editor.pointToGrid( xLocal ),
- rowIndex = Q.Circuit.Editor.pointToGrid( yLocal ),
- momentIndex = Q.Circuit.Editor.gridColumnToMomentIndex( columnIndex ),
- registerIndex = Q.Circuit.Editor.gridRowToRegisterIndex( rowIndex )
-
-
- // If this hover is “out of bounds”
- // ie. on the same row or column as an “Add register” or “Add moment” button
- // then let’s not highlight anything.
-
- if( momentIndex > circuitEl.circuit.timewidth ||
- registerIndex > circuitEl.circuit.bandwidth ) return
-
-
- // If we’re at 0, 0 or below that either means
- // we’re over the “Select all” button (already taken care of above)
- // or over the lock toggle button.
- // Either way, it’s time to bail.
-
- if( momentIndex < 1 || registerIndex < 1 ) return
-
-
- // If we’ve made it this far that means
- // we have valid moment and register indices.
- // Highlight them!
-
- highlightByQuery(`
-
- div[moment-index="${ momentIndex }"],
- div[register-index="${ registerIndex }"]
- `)
- return
-}
-
-
-
-
-
-
- ///////////////////////
- // //
- // Pointer PRESS //
- // //
-///////////////////////
-
-
-Q.Circuit.Editor.onPointerPress = function( event ){
-
-
- // This is just a safety net
- // in case something terrible has ocurred.
- // (ex. Did the user click and then their mouse ran
- // outside the window but browser didn’t catch it?)
-
- if( Q.Circuit.Editor.dragEl !== null ){
-
- Q.Circuit.Editor.onPointerRelease( event )
- return
- }
-
-
- const
- targetEl = event.target,
- circuitEl = targetEl.closest( '.Q-circuit' ),
- paletteEl = targetEl.closest( '.Q-circuit-palette' )
-
-
- // If we can’t find a circuit that’s a really bad sign
- // considering this event should be fired when a circuit
- // is clicked on. So... bail!
-
- if( !circuitEl && !paletteEl ) return
-
-
- // This is a bit of a gamble.
- // There’s a possibility we’re not going to drag anything,
- // but we’ll prep these variables here anyway
- // because both branches of if( circuitEl ) and if( paletteEl )
- // below will have access to this scope.
-
- dragEl = document.createElement( 'div' )
- dragEl.classList.add( 'Q-circuit-clipboard' )
- const { x, y } = Q.Circuit.Editor.getInteractionCoordinates( event )
-
-
- // Are we dealing with a circuit interface?
- // ie. NOT a palette interface.
-
- if( circuitEl ){
-
-
- // Shall we toggle the circuit lock?
-
- const
- circuit = circuitEl.circuit,
- circuitIsLocked = circuitEl.classList.contains( 'Q-circuit-locked' ),
- lockEl = targetEl.closest( '.Q-circuit-toggle-lock' )
-
- if( lockEl ){
-
- // const toolbarEl = Array.from( circuitEl.querySelectorAll( '.Q-circuit-button' ))
- if( circuitIsLocked ){
-
- circuitEl.classList.remove( 'Q-circuit-locked' )
- lockEl.innerText = '🔓'
- }
- else {
-
- circuitEl.classList.add( 'Q-circuit-locked' )
- lockEl.innerText = '🔒'
- Q.Circuit.Editor.unhighlightAll( circuitEl )
- }
-
-
- // We’ve toggled the circuit lock button
- // so we should prevent further propagation
- // before proceeding further.
- // That includes running all this code again
- // if it was originally fired by a mouse event
- // and about to be fired by a touch event!
-
- event.preventDefault()
- event.stopPropagation()
- return
- }
-
-
- // If our circuit is already “locked”
- // then there’s nothing more to do here.
-
- if( circuitIsLocked ) {
-
- Q.warn( `User attempted to interact with a circuit editor but it was locked.` )
- return
- }
-
-
- const
- cellEl = targetEl.closest(`
-
- .Q-circuit-board-foreground > div,
- .Q-circuit-palette > div
- `),
- undoEl = targetEl.closest( '.Q-circuit-button-undo' ),
- redoEl = targetEl.closest( '.Q-circuit-button-redo' ),
- controlEl = targetEl.closest( '.Q-circuit-toggle-control' ),
- swapEl = targetEl.closest( '.Q-circuit-toggle-swap' ),
- addMomentEl = targetEl.closest( '.Q-circuit-moment-add' ),
- addRegisterEl = targetEl.closest( '.Q-circuit-register-add' )
-
- if( !cellEl &&
- !undoEl &&
- !redoEl &&
- !controlEl &&
- !swapEl &&
- !addMomentEl &&
- !addRegisterEl ) return
-
-
- // By this point we know that the circuit is unlocked
- // and that we’ll activate a button / drag event / etc.
- // So we need to hault futher event propagation
- // including running this exact code again if this was
- // fired by a touch event and about to again by mouse.
- // This may SEEM redundant because we did this above
- // within the lock-toggle button code
- // but we needed to NOT stop propagation if the circuit
- // was already locked -- for scrolling and such.
-
- event.preventDefault()
- event.stopPropagation()
-
-
- if( undoEl && circuit.history.undo$() ){
-
- Q.Circuit.Editor.onSelectionChanged( circuitEl )
- Q.Circuit.Editor.onCircuitChanged( circuitEl )
- }
- if( redoEl && circuit.history.redo$() ){
-
- Q.Circuit.Editor.onSelectionChanged( circuitEl )
- Q.Circuit.Editor.onCircuitChanged( circuitEl )
- }
- if( controlEl ) Q.Circuit.Editor.createControl( circuitEl )
- if( swapEl ) Q.Circuit.Editor.createSwap( circuitEl )
- if( addMomentEl ) console.log( '→ Add moment' )
- if( addRegisterEl ) console.log( '→ Add register' )
-
-
- // We’re done dealing with external buttons.
- // So if we can’t find a circuit CELL
- // then there’s nothing more to do here.
-
- if( !cellEl ) return
-
-
- // Once we know what cell we’ve pressed on
- // we can get the momentIndex and registerIndex
- // from its pre-defined attributes.
- // NOTE that we are getting CSS grid column and row
- // from our own conversion function and NOT from
- // asking its styles. Why? Because browsers convert
- // grid commands to a shorthand less easily parsable
- // and therefore makes our code and reasoning
- // more prone to quirks / errors. Trust me!
-
- const
- momentIndex = +cellEl.getAttribute( 'moment-index' ),
- registerIndex = +cellEl.getAttribute( 'register-index' ),
- columnIndex = Q.Circuit.Editor.momentIndexToGridColumn( momentIndex ),
- rowIndex = Q.Circuit.Editor.registerIndexToGridRow( registerIndex )
-
-
- // Looks like our circuit is NOT locked
- // and we have a valid circuit CELL
- // so let’s find everything else we could need.
-
- const
- selectallEl = targetEl.closest( '.Q-circuit-selectall' ),
- registersymbolEl = targetEl.closest( '.Q-circuit-register-label' ),
- momentsymbolEl = targetEl.closest( '.Q-circuit-moment-label' ),
- inputEl = targetEl.closest( '.Q-circuit-input' ),
- operationEl = targetEl.closest( '.Q-circuit-operation' )
-
-
- // +++++++++++++++
- // We’ll have to add some input editing capability later...
- // Of course you can already do this in code!
- // For now though most quantum code assumes all qubits
- // begin with a value of zero so this is mostly ok ;)
-
- if( inputEl ){
-
- console.log( '→ Edit input Qubit value at', registerIndex )
- return
- }
-
-
- // Let’s inspect a group of items via a CSS query.
- // If any of them are NOT “selected” (highlighted)
- // then select them all.
- // But if ALL of them are already selected
- // then UNSELECT them all.
-
- function toggleSelection( query ){
-
- const
- operations = Array.from( circuitEl.querySelectorAll( query )),
- operationsSelectedLength = operations.reduce( function( sum, element ){
-
- sum += +element.classList.contains( 'Q-circuit-cell-selected' )
- return sum
-
- }, 0 )
-
- if( operationsSelectedLength === operations.length ){
-
- operations.forEach( function( el ){
-
- el.classList.remove( 'Q-circuit-cell-selected' )
- })
- }
- else {
-
- operations.forEach( function( el ){
-
- el.classList.add( 'Q-circuit-cell-selected' )
- })
- }
- Q.Circuit.Editor.onSelectionChanged( circuitEl )
- }
-
-
- // Clicking on the “selectAll” button
- // or any of the Moment symbols / Register symbols
- // causes a selection toggle.
- // In the future we may want to add
- // dragging of entire Moment columns / Register rows
- // to splice them out / insert them elsewhere
- // when a user clicks and drags them.
-
- if( selectallEl ){
-
- toggleSelection( '.Q-circuit-operation' )
- return
- }
- if( momentsymbolEl ){
-
- toggleSelection( `.Q-circuit-operation[moment-index="${ momentIndex }"]` )
- return
- }
- if( registersymbolEl ){
-
- toggleSelection( `.Q-circuit-operation[register-index="${ registerIndex }"]` )
- return
- }
-
-
- // Right here we can made a big decision:
- // If you’re not pressing on an operation
- // then GO HOME.
-
- if( !operationEl ) return
-
-
- // Ok now we know we are dealing with an operation.
- // This preserved selection state information
- // will be useful for when onPointerRelease is fired.
-
- if( operationEl.classList.contains( 'Q-circuit-cell-selected' )){
-
- operationEl.wasSelected = true
- }
- else operationEl.wasSelected = false
-
-
- // And now we can proceed knowing that
- // we need to select this operation
- // and possibly drag it
- // as well as any other selected operations.
-
- operationEl.classList.add( 'Q-circuit-cell-selected' )
- const selectedOperations = Array.from( circuitEl.querySelectorAll( '.Q-circuit-cell-selected' ))
- dragEl.circuitEl = circuitEl
- dragEl.originEl = circuitEl.querySelector( '.Q-circuit-board-foreground' )
-
-
- // These are the default values;
- // will be used if we’re only dragging one operation around.
- // But if dragging more than one operation
- // and we’re dragging the clipboard by an operation
- // that is NOT in the upper-left corner of the clipboard
- // then we need to know what the offset is.
- // (Will be calculated below.)
-
- dragEl.columnIndexOffset = 1
- dragEl.rowIndexOffset = 1
-
-
- // Now collect all of the selected operations,
- // rip them from the circuit board’s foreground layer
- // and place them on the clipboard.
-
- let
- columnIndexMin = Infinity,
- rowIndexMin = Infinity
-
- selectedOperations.forEach( function( el ){
-
-
- // WORTH REPEATING:
- // Once we know what cell we’ve pressed on
- // we can get the momentIndex and registerIndex
- // from its pre-defined attributes.
- // NOTE that we are getting CSS grid column and row
- // from our own conversion function and NOT from
- // asking its styles. Why? Because browsers convert
- // grid commands to a shorthand less easily parsable
- // and therefore makes our code and reasoning
- // more prone to quirks / errors. Trust me!
-
- const
- momentIndex = +el.getAttribute( 'moment-index' ),
- registerIndex = +el.getAttribute( 'register-index' ),
- columnIndex = Q.Circuit.Editor.momentIndexToGridColumn( momentIndex ),
- rowIndex = Q.Circuit.Editor.registerIndexToGridRow( registerIndex )
-
- columnIndexMin = Math.min( columnIndexMin, columnIndex )
- rowIndexMin = Math.min( rowIndexMin, rowIndex )
- el.classList.remove( 'Q-circuit-cell-selected' )
- el.origin = { momentIndex, registerIndex, columnIndex, rowIndex }
- dragEl.appendChild( el )
- })
- selectedOperations.forEach( function( el ){
-
- const
- columnIndexForClipboard = 1 + el.origin.columnIndex - columnIndexMin,
- rowIndexForClipboard = 1 + el.origin.rowIndex - rowIndexMin
-
- el.style.gridColumn = columnIndexForClipboard
- el.style.gridRow = rowIndexForClipboard
-
-
- // If this operation element is the one we grabbed
- // (mostly relevant if we’re moving multiple operations at once)
- // we need to know what the “offset” so everything can be
- // placed correctly relative to this drag-and-dropped item.
-
- if( el.origin.columnIndex === columnIndex &&
- el.origin.rowIndex === rowIndex ){
-
- dragEl.columnIndexOffset = columnIndexForClipboard
- dragEl.rowIndexOffset = rowIndexForClipboard
- }
- })
-
-
- // We need an XY offset that describes the difference
- // between the mouse / finger press position
- // and the clipboard’s intended upper-left position.
- // To do that we need to know the press position (obviously!),
- // the upper-left bounds of the circuit board’s foreground,
- // and the intended upper-left bound of clipboard.
-
- const
- boardEl = circuitEl.querySelector( '.Q-circuit-board-foreground' ),
- bounds = boardEl.getBoundingClientRect(),
- minX = Q.Circuit.Editor.gridToPoint( columnIndexMin ),
- minY = Q.Circuit.Editor.gridToPoint( rowIndexMin )
-
- dragEl.offsetX = bounds.left + minX - x
- dragEl.offsetY = bounds.top + minY - y
- dragEl.momentIndex = momentIndex
- dragEl.registerIndex = registerIndex
- }
- else if( paletteEl ){
-
- const operationEl = targetEl.closest( '.Q-circuit-operation' )
-
- if( !operationEl ) return
-
- const
- bounds = operationEl.getBoundingClientRect(),
- { x, y } = Q.Circuit.Editor.getInteractionCoordinates( event )
-
- dragEl.appendChild( operationEl.cloneNode( true ))
- dragEl.originEl = paletteEl
- dragEl.offsetX = bounds.left - x
- dragEl.offsetY = bounds.top - y
- }
- dragEl.timestamp = Date.now()
-
-
- // Append the clipboard to the document,
- // establish a global reference to it,
- // and trigger a draw of it in the correct spot.
-
- document.body.appendChild( dragEl )
- Q.Circuit.Editor.dragEl = dragEl
- Q.Circuit.Editor.onPointerMove( event )
-}
-
-
-
-
-
-
- /////////////////////////
- // //
- // Pointer RELEASE //
- // //
-/////////////////////////
-
-
-Q.Circuit.Editor.onPointerRelease = function( event ){
-
-
- // If there’s no dragEl then bail immediately.
-
- if( Q.Circuit.Editor.dragEl === null ) return
-
-
- // Looks like we’re moving forward with this plan,
- // so we’ll take control of the input now.
-
- event.preventDefault()
- event.stopPropagation()
-
-
- // We can’t get the drop target from the event.
- // Think about it: What was under the mouse / finger
- // when this drop event was fired? THE CLIPBOARD !
- // So instead we need to peek at what elements are
- // under the mouse / finger, skipping element [0]
- // because that will be the clipboard.
-
- const
- { x, y } = Q.Circuit.Editor.getInteractionCoordinates( event ),
- boardContainerEl = document.elementsFromPoint( x, y )
- .find( function( el ){
-
- return el.classList.contains( 'Q-circuit-board-container' )
- }),
- returnToOrigin = function(){
-
-
- // We can only do a “true” return to origin
- // if we were dragging from a circuit.
- // If we were dragging from a palette
- // we can just stop dragging.
-
- if( Q.Circuit.Editor.dragEl.circuitEl ){
-
- Array.from( Q.Circuit.Editor.dragEl.children ).forEach( function( el ){
-
- Q.Circuit.Editor.dragEl.originEl.appendChild( el )
- el.style.gridColumn = el.origin.columnIndex
- el.style.gridRow = el.origin.rowIndex
- if( el.wasSelected === true ) el.classList.remove( 'Q-circuit-cell-selected' )
- else el.classList.add( 'Q-circuit-cell-selected' )
- })
- Q.Circuit.Editor.onSelectionChanged( Q.Circuit.Editor.dragEl.circuitEl )
- }
- document.body.removeChild( Q.Circuit.Editor.dragEl )
- Q.Circuit.Editor.dragEl = null
- }
-
-
- // If we have not dragged on to a circuit board
- // then we’re throwing away this operation.
-
- if( !boardContainerEl ){
-
- if( Q.Circuit.Editor.dragEl.circuitEl ){
-
- const
- originCircuitEl = Q.Circuit.Editor.dragEl.circuitEl
- originCircuit = originCircuitEl.circuit
-
- originCircuit.history.createEntry$()
- Array
- .from( Q.Circuit.Editor.dragEl.children )
- .forEach( function( child ){
-
- originCircuit.clear$(
-
- child.origin.momentIndex,
- child.origin.registerIndex
- )
- })
- Q.Circuit.Editor.onSelectionChanged( originCircuitEl )
- Q.Circuit.Editor.onCircuitChanged( originCircuitEl )
- }
-
-
- // TIME TO DIE.
- // Let’s keep a private reference to
- // the current clipboard.
-
- let clipboardToDestroy = Q.Circuit.Editor.dragEl
-
-
- // Now we can remove our dragging reference.
-
- Q.Circuit.Editor.dragEl = null
-
-
- // Add our CSS animation routine
- // which will run for 1 second.
- // If we were SUPER AWESOME
- // we would have also calculated drag momentum
- // and we’d let this glide away!
-
- clipboardToDestroy.classList.add( 'Q-circuit-clipboard-destroy' )
-
-
- // And around the time that animation is completing
- // we can go ahead and remove our clipboard from the DOM
- // and kill the reference.
-
- setTimeout( function(){
-
- document.body.removeChild( clipboardToDestroy )
- clipboardToDestroy = null
-
- }, 500 )
-
-
- // No more to do here. Goodbye.
-
- return
- }
-
-
- // If we couldn’t determine a circuitEl
- // from the drop target,
- // or if there is a target circuit but it’s locked,
- // then we need to return these dragged items
- // to their original circuit.
-
- const circuitEl = boardContainerEl.closest( '.Q-circuit' )
- if( circuitEl.classList.contains( 'Q-circuit-locked' )){
-
- returnToOrigin()
- return
- }
-
-
- // Time to get serious.
- // Where exactly are we dropping on to this circuit??
-
- const
- circuit = circuitEl.circuit,
- bounds = boardContainerEl.getBoundingClientRect(),
- droppedAtX = x - bounds.left + boardContainerEl.scrollLeft,
- droppedAtY = y - bounds.top + boardContainerEl.scrollTop,
- droppedAtMomentIndex = Q.Circuit.Editor.gridColumnToMomentIndex(
-
- Q.Circuit.Editor.pointToGrid( droppedAtX )
- ),
- droppedAtRegisterIndex = Q.Circuit.Editor.gridRowToRegisterIndex(
-
- Q.Circuit.Editor.pointToGrid( droppedAtY )
- ),
- foregroundEl = circuitEl.querySelector( '.Q-circuit-board-foreground' )
-
-
- // If this is a self-drop
- // we can also just return to origin and bail.
-
- if( Q.Circuit.Editor.dragEl.circuitEl === circuitEl &&
- Q.Circuit.Editor.dragEl.momentIndex === droppedAtMomentIndex &&
- Q.Circuit.Editor.dragEl.registerIndex === droppedAtRegisterIndex ){
-
- returnToOrigin()
- return
- }
-
-
- // Is this a valid drop target within this circuit?
-
- if(
- droppedAtMomentIndex < 1 ||
- droppedAtMomentIndex > circuit.timewidth ||
- droppedAtRegisterIndex < 1 ||
- droppedAtRegisterIndex > circuit.bandwidth
- ){
-
- returnToOrigin()
- return
- }
-
-
- // Finally! Work is about to be done!
- // All we need to do is tell the circuit itself
- // where we need to place these dragged items.
- // It will do all the validation for us
- // and then fire events that will place new elements
- // where they need to go!
-
- const
- draggedOperations = Array.from( Q.Circuit.Editor.dragEl.children ),
- draggedMomentDelta = droppedAtMomentIndex - Q.Circuit.Editor.dragEl.momentIndex,
- draggedRegisterDelta = droppedAtRegisterIndex - Q.Circuit.Editor.dragEl.registerIndex,
- setCommands = []
-
-
- // Whatever the next action is that we perform on the circuit,
- // this was user-initiated via the graphic user interface (GUI).
-
- circuit.history.createEntry$()
-
-
- // Now let’s work our way through each of the dragged operations.
- // If some of these are components of a multi-register operation
- // the sibling components will get spliced out of the array
- // to avoid processing any specific operation more than once.
-
- draggedOperations.forEach( function( childEl, i ){
-
- let
- momentIndexTarget = droppedAtMomentIndex,
- registerIndexTarget = droppedAtRegisterIndex
-
- if( Q.Circuit.Editor.dragEl.circuitEl ){
-
- momentIndexTarget += childEl.origin.momentIndex - Q.Circuit.Editor.dragEl.momentIndex
- registerIndexTarget += childEl.origin.registerIndex - Q.Circuit.Editor.dragEl.registerIndex
- }
-
-
- // Is this a multi-register operation?
- // If so, this is also a from-circuit drop
- // rather than a from-palette drop.
-
- const registerIndicesString = childEl.getAttribute( 'register-indices' )
- if( registerIndicesString ){
-
-
- // What are ALL of the registerIndices
- // associated with this multi-register operation?
- // (We may use them later as a checklist.)
-
- const
- registerIndices = registerIndicesString
- .split( ',' )
- .map( function( str ){ return +str }),
-
-
- // Lets look for ALL of the sibling components of this operation.
- // Later we’ll check and see if the length of this array
- // is equal to the total number of components for this operation.
- // If they’re equal then we know we’re dragging the WHOLE thing.
- // Otherwise we need to determine if it needs to break apart
- // and if so, what that nature of that break might be.
-
- foundComponents = Array.from(
-
- Q.Circuit.Editor.dragEl.querySelectorAll(
-
- `[moment-index="${ childEl.origin.momentIndex }"]`+
- `[register-indices="${ registerIndicesString }"]`
- )
- )
- .sort( function( a, b ){
-
- const
- aRegisterIndicesIndex = +a.getAttribute( 'register-indices-index' ),
- bRegisterIndicesIndex = +b.getAttribute( 'register-indices-index' )
-
- return aRegisterIndicesIndex - bRegisterIndicesIndex
- }),
- allComponents = Array.from( Q.Circuit.Editor.dragEl.circuitEl.querySelectorAll(
-
- `[moment-index="${ childEl.origin.momentIndex }"]`+
- `[register-indices="${ registerIndicesString }"]`
- )),
- remainingComponents = allComponents.filter( function( componentEl, i ){
-
- return !foundComponents.includes( componentEl )
- }),
-
-
- // We can’t pick the gate symbol
- // off the 0th gate in the register indices array
- // because that will be an identity / control / null gate.
- // We need to look at slot 1.
-
- component1 = Q.Circuit.Editor.dragEl.querySelector(
-
- `[moment-index="${ childEl.origin.momentIndex }"]`+
- `[register-index="${ registerIndices[ 1 ] }"]`
- ),
- gatesymbol = component1 ?
- component1.getAttribute( 'gate-symbol' ) :
- childEl.getAttribute( 'gate-symbol' )
-
-
- // We needed to grab the above gatesymbol information
- // before we sent any clear$ commands
- // which would in turn delete those componentEls.
- // We’ve just completed that,
- // so now’s the time to send a clear$ command
- // before we do any set$ commands.
-
- draggedOperations.forEach( function( childEl ){
-
- Q.Circuit.Editor.dragEl.circuitEl.circuit.clear$(
-
- childEl.origin.momentIndex,
- childEl.origin.registerIndex
- )
- })
-
-
- // FULL MULTI-REGISTER DRAG (TO ANY POSITION ON ANY CIRCUIT).
- // If we are dragging all of the components
- // of a multi-register operation
- // then we are good to go.
-
- if( registerIndices.length === foundComponents.length ){
-
- //circuit.set$(
- setCommands.push([
-
- gatesymbol,
- momentIndexTarget,
-
-
- // We need to remap EACH register index here
- // according to the drop position.
- // Let’s let set$ do all the validation on this.
-
- registerIndices.map( function( registerIndex ){
-
- const siblingDelta = registerIndex - childEl.origin.registerIndex
- registerIndexTarget = droppedAtRegisterIndex
- if( Q.Circuit.Editor.dragEl.circuitEl ){
-
- registerIndexTarget += childEl.origin.registerIndex - Q.Circuit.Editor.dragEl.registerIndex + siblingDelta
- }
- return registerIndexTarget
- })
- // )
- ])
- }
-
-
- // IN-MOMENT (IN-CIRCUIT) PARTIAL MULTI-REGISTER DRAG.
- // It appears we are NOT dragging all components
- // of a multi-register operation.
- // But if we’re dragging within the same circuit
- // and we’re staying within the same moment index
- // that might be ok!
-
- else if( Q.Circuit.Editor.dragEl.circuitEl === circuitEl &&
- momentIndexTarget === childEl.origin.momentIndex ){
-
-
- // We must ensure that only one component
- // can sit at each register index.
- // This copies registerIndices,
- // but inverts the key : property relationship.
-
- const registerMap = registerIndices
- .reduce( function( registerMap, registerIndex, r ){
-
- registerMap[ registerIndex ] = r
- return registerMap
-
- }, {} )
-
-
- // First, we must remove each dragged component
- // from the register it was sitting at.
-
- foundComponents.forEach( function( component ){
-
- const componentRegisterIndex = +component.getAttribute( 'register-index' )
-
-
- // Remove this component from
- // where this component used to be.
-
- delete registerMap[ componentRegisterIndex ]
- })
-
-
- // Now we can seat it at its new position.
- // Note: This may OVERWRITE one of its siblings!
- // And that’s ok.
-
- foundComponents.forEach( function( component ){
-
- const
- componentRegisterIndex = +component.getAttribute( 'register-index' ),
- registerGrabDelta = componentRegisterIndex - Q.Circuit.Editor.dragEl.registerIndex
-
-
- // Now put it where it wants to go,
- // possibly overwriting a sibling component!
-
- registerMap[
-
- componentRegisterIndex + draggedRegisterDelta
-
- ] = +component.getAttribute( 'register-indices-index' )
- })
-
-
- // Now let’s flip that registerMap
- // back into an array of register indices.
-
- const fixedRegistersIndices = Object.entries( registerMap )
- .reduce( function( registers, entry, i ){
-
- registers[ +entry[ 1 ]] = +entry[ 0 ]
- return registers
-
- }, [] )
-
-
- // This will remove any blank entries in the array
- // ie. if a dragged sibling overwrote a seated one.
-
- .filter( function( entry ){
-
- return Q.isUsefulInteger( entry )
- })
-
-
- // Finally, we’re ready to set.
-
- // circuit.set$(
- setCommands.push([
-
- childEl.getAttribute( 'gate-symbol' ),
- momentIndexTarget,
- fixedRegistersIndices
- // )
- ])
- }
- else {
-
- remainingComponents.forEach( function( componentEl, i ){
-
- //circuit.set$(
- setCommands.push([
-
- +componentEl.getAttribute( 'register-indices-index' ) ?
- gatesymbol :
- Q.Gate.NOOP,
- +componentEl.getAttribute( 'moment-index' ),
- +componentEl.getAttribute( 'register-index' )
- // )
- ])
- })
-
-
- // Finally, let’s separate and update
- // all the components that were part of the drag.
-
- foundComponents.forEach( function( componentEl ){
-
- // circuit.set$(
- setCommands.push([
-
- +componentEl.getAttribute( 'register-indices-index' ) ?
- gatesymbol :
- Q.Gate.NOOP,
- +componentEl.getAttribute( 'moment-index' ) + draggedMomentDelta,
- +componentEl.getAttribute( 'register-index' ) + draggedRegisterDelta,
- // )
- ])
- })
- }
-
-
- // We’ve just completed the movement
- // of a multi-register operation.
- // But all of the sibling components
- // will also trigger this process
- // unless we remove them
- // from the draggd operations array.
-
- let j = i + 1
- while( j < draggedOperations.length ){
-
- const possibleSibling = draggedOperations[ j ]
- if( possibleSibling.getAttribute( 'gate-symbol' ) === gatesymbol &&
- possibleSibling.getAttribute( 'register-indices' ) === registerIndicesString ){
-
- draggedOperations.splice( j, 1 )
- }
- else j ++
- }
- }
-
-
- // This is just a single-register operation.
- // How simple this looks
- // compared to all the gibberish above.
-
- else {
-
-
- // First, if this operation comes from a circuit
- // (and not a circuit palette)
- // make sure the old positions are cleared away.
-
- if( Q.Circuit.Editor.dragEl.circuitEl ){
-
- draggedOperations.forEach( function( childEl ){
-
- Q.Circuit.Editor.dragEl.circuitEl.circuit.clear$(
-
- childEl.origin.momentIndex,
- childEl.origin.registerIndex
- )
- })
- }
-
-
- // And now set$ the operation
- // in its new home.
-
- // circuit.set$(
- setCommands.push([
-
- childEl.getAttribute( 'gate-symbol' ),
- momentIndexTarget,
- [ registerIndexTarget ]
- // )
- ])
- }
- })
-
-
- // DO IT DO IT DO IT
-
- setCommands.forEach( function( setCommand ){
-
- circuit.set$.apply( circuit, setCommand )
- })
-
-
- // Are we capable of making controls? Swaps?
-
- Q.Circuit.Editor.onSelectionChanged( circuitEl )
- Q.Circuit.Editor.onCircuitChanged( circuitEl )
-
-
- // If the original circuit and destination circuit
- // are not the same thing
- // then we need to also eval the original circuit.
-
- if( Q.Circuit.Editor.dragEl.circuitEl &&
- Q.Circuit.Editor.dragEl.circuitEl !== circuitEl ){
-
- const originCircuitEl = Q.Circuit.Editor.dragEl.circuitEl
- Q.Circuit.Editor.onSelectionChanged( originCircuitEl )
- Q.Circuit.Editor.onCircuitChanged( originCircuitEl )
- }
-
-
- // We’re finally done here.
- // Clean up and go home.
- // It’s been a long journey.
- // I love you all.
-
- document.body.removeChild( Q.Circuit.Editor.dragEl )
- Q.Circuit.Editor.dragEl = null
-}
-
-
-
-
-
-
- ///////////////////
- // //
- // Listeners //
- // //
-///////////////////
-
-
-// These listeners must be applied
-// to the entire WINDOW (and not just document.body!)
-
-window.addEventListener( 'mousemove', Q.Circuit.Editor.onPointerMove )
-window.addEventListener( 'touchmove', Q.Circuit.Editor.onPointerMove )
-window.addEventListener( 'mouseup', Q.Circuit.Editor.onPointerRelease )
-window.addEventListener( 'touchend', Q.Circuit.Editor.onPointerRelease )
-
-
-
-
-
-
-
diff --git a/Q/Q-ComplexNumber.js b/Q/Q-ComplexNumber.js
deleted file mode 100644
index 36ccb35..0000000
--- a/Q/Q-ComplexNumber.js
+++ /dev/null
@@ -1,722 +0,0 @@
-
-// Copyright © 2019–2020, Stewart Smith. See LICENSE for details.
-
-
-
-
-Q.ComplexNumber = function( real, imaginary ){
-
- `
- The set of “real numbers” (ℝ) contains any number that can be expressed
- along an infinite timeline. https://en.wikipedia.org/wiki/Real_number
-
- … -3 -2 -1 0 +1 +2 +3 …
- ┄───┴───┴───┴───┴───┴─┬─┴──┬┴┬──┄
- √2 𝒆 π
-
-
- Meanwhile, “imaginary numbers” (𝕀) consist of a real (ℝ) multiplier and
- the symbol 𝒊, which is the impossible solution to the equation 𝒙² = −1.
- Note that no number when multiplied by itself can ever result in a
- negative product, but the concept of 𝒊 gives us a way to reason around
- this imaginary scenario nonetheless.
- https://en.wikipedia.org/wiki/Imaginary_number
-
- … -3𝒊 -2𝒊 -1𝒊 0𝒊 +1𝒊 +2𝒊 +3𝒊 …
- ┄───┴───┴───┴───┴───┴───┴───┴───┄
-
-
- A “complex number“ (ℂ) is a number that can be expressed in the form
- 𝒂 + 𝒃𝒊, where 𝒂 is the real component (ℝ) and 𝒃𝒊 is the imaginary
- component (𝕀). https://en.wikipedia.org/wiki/Complex_number
-
-
- Operation functions on Q.ComplexNumber instances generally accept as
- arguments both sibling instances and pure Number instances, though the
- value returned is always an instance of Q.ComplexNumber.
-
- `
-
- if( real instanceof Q.ComplexNumber ){
-
- imaginary = real.imaginary
- real = real.real
- Q.warn( 'Q.ComplexNumber tried to create a new instance with an argument that is already a Q.ComplexNumber — and that’s weird!' )
- }
- else if( real === undefined ) real = 0
- if( imaginary === undefined ) imaginary = 0
- if(( Q.ComplexNumber.isNumberLike( real ) !== true && isNaN( real ) !== true ) ||
- ( Q.ComplexNumber.isNumberLike( imaginary ) !== true && isNaN( imaginary ) !== true ))
- return Q.error( 'Q.ComplexNumber attempted to create a new instance but the arguments provided were not actual numbers.' )
-
- this.real = real
- this.imaginary = imaginary
- this.index = Q.ComplexNumber.index ++
-}
-
-
-
-
-Object.assign( Q.ComplexNumber, {
-
- index: 0,
- help: function(){ return Q.help( this )},
- constants: {},
- createConstant: Q.createConstant,
- createConstants: Q.createConstants,
-
-
-
-
- toText: function( rNumber, iNumber, roundToDecimal, padPositive ){
-
-
- // Should we round these numbers?
- // Our default is yes: to 3 digits.
- // Otherwise round to specified decimal.
-
- if( typeof roundToDecimal !== 'number' ) roundToDecimal = 3
- const factor = Math.pow( 10, roundToDecimal )
- rNumber = Math.round( rNumber * factor ) / factor
- iNumber = Math.round( iNumber * factor ) / factor
-
-
- // Convert padPositive
- // from a potential Boolean
- // to a String.
- // If we don’t receive a FALSE
- // then we’ll pad the positive numbers.
-
- padPositive = padPositive === false ? '' : ' '
-
-
- // We need the absolute values of each.
-
- let
- rAbsolute = Math.abs( rNumber ),
- iAbsolute = Math.abs( iNumber )
-
-
- // And an absolute value string.
-
- let
- rText = rAbsolute.toString(),
- iText = iAbsolute.toString()
-
-
- // Is this an IMAGINARY-ONLY number?
- // Don’t worry: -0 === 0.
-
- if( rNumber === 0 ){
-
- if( iNumber === Infinity ) return padPositive +'∞i'
- if( iNumber === -Infinity ) return '-∞i'
- if( iNumber === 0 ) return padPositive +'0'
- if( iNumber === -1 ) return '-i'
- if( iNumber === 1 ) return padPositive +'i'
- if( iNumber >= 0 ) return padPositive + iText +'i'
- if( iNumber < 0 ) return '-'+ iText +'i'
- return iText +'i'// NaN
- }
-
-
- // This number contains a real component
- // and may also contain an imaginary one as well.
-
- if( rNumber === Infinity ) rText = padPositive +'∞'
- else if( rNumber === -Infinity ) rText = '-∞'
- else if( rNumber >= 0 ) rText = padPositive + rText
- else if( rNumber < 0 ) rText = '-'+ rText
-
- if( iNumber === Infinity ) return rText +' + ∞i'
- if( iNumber === -Infinity ) return rText +' - ∞i'
- if( iNumber === 0 ) return rText
- if( iNumber === -1 ) return rText +' - i'
- if( iNumber === 1 ) return rText +' + i'
- if( iNumber > 0 ) return rText +' + '+ iText +'i'
- if( iNumber < 0 ) return rText +' - '+ iText +'i'
- return rText +' + '+ iText +'i'// NaN
- },
-
-
-
-
- isNumberLike: function( n ){
-
- return isNaN( n ) === false && ( typeof n === 'number' || n instanceof Number )
- },
- isNaN: function( n ){
-
- return isNaN( n.real ) || isNaN( n.imaginary )
- },
- isZero: function( n ){
-
- return ( n.real === 0 || n.real === -0 ) &&
- ( n.imaginary === 0 || n.imaginary === -0 )
- },
- isFinite: function( n ){
-
- return isFinite( n.real ) && isFinite( n.imaginary )
- },
- isInfinite: function( n ){
-
- return !( this.isNaN( n ) || this.isFinite( n ))
- },
- areEqual: function( a, b ){
-
- return Q.ComplexNumber.operate(
-
- 'areEqual', a, b,
- function( a, b ){
-
- return Math.abs( a - b ) < Q.EPSILON
- },
- function( a, b ){
-
- return (
-
- Math.abs( a - b.real ) < Q.EPSILON &&
- Math.abs( b.imaginary ) < Q.EPSILON
- )
- },
- function( a, b ){
-
- return (
-
- Math.abs( a.real - b ) < Q.EPSILON &&
- Math.abs( a.imaginary ) < Q.EPSILON
- )
- },
- function( a, b ){
-
- return (
-
- Math.abs( a.real - b.real ) < Q.EPSILON &&
- Math.abs( a.imaginary - b.imaginary ) < Q.EPSILON
- )
- }
- )
- },
-
-
-
-
- absolute: function( n ){
-
- return Q.hypotenuse( n.real, n.imaginary )
- },
- conjugate: function( n ){
-
- return new Q.ComplexNumber( n.real, n.imaginary * -1 )
- },
- operate: function(
-
- name,
- a,
- b,
- numberAndNumber,
- numberAndComplex,
- complexAndNumber,
- complexAndComplex ){
-
- if( Q.ComplexNumber.isNumberLike( a )){
-
- if( Q.ComplexNumber.isNumberLike( b )) return numberAndNumber( a, b )
- else if( b instanceof Q.ComplexNumber ) return numberAndComplex( a, b )
- else return Q.error( 'Q.ComplexNumber attempted to', name, 'with the number', a, 'and something that is neither a Number or Q.ComplexNumber:', b )
- }
- else if( a instanceof Q.ComplexNumber ){
-
- if( Q.ComplexNumber.isNumberLike( b )) return complexAndNumber( a, b )
- else if( b instanceof Q.ComplexNumber ) return complexAndComplex( a, b )
- else return Q.error( 'Q.ComplexNumber attempted to', name, 'with the complex number', a, 'and something that is neither a Number or Q.ComplexNumber:', b )
- }
- else return Q.error( 'Q.ComplexNumber attempted to', name, 'with something that is neither a Number or Q.ComplexNumber:', a )
- },
-
-
-
-
- sine: function( n ){
-
- const
- a = n.real,
- b = n.imaginary
-
- return new Q.ComplexNumber(
-
- Math.sin( a ) * Q.hyperbolicCosine( b ),
- Math.cos( a ) * Q.hyperbolicSine( b )
- )
- },
- cosine: function( n ){
-
- const
- a = n.real,
- b = n.imaginary
-
- return new Q.ComplexNumber(
-
- Math.cos( a ) * Q.hyperbolicCosine( b ),
- -Math.sin( a ) * Q.hyperbolicSine( b )
- )
- },
- arcCosine: function( n ){
-
- const
- a = n.real,
- b = n.imaginary,
- t1 = Q.ComplexNumber.squareRoot( new Q.ComplexNumber(
-
- b * b - a * a + 1,
- a * b * -2
-
- )),
- t2 = Q.ComplexNumber.log( new Q.ComplexNumber(
-
- t1.real - b,
- t1.imaginary + a
- ))
- return new Q.ComplexNumber( Math.PI / 2 - t2.imaginary, t2.real )
- },
- arcTangent: function( n ){
-
- const
- a = n.real,
- b = n.imaginary
-
- if( a === 0 ){
-
- if( b === 1 ) return new Q.ComplexNumber( 0, Infinity )
- if( b === -1 ) return new Q.ComplexNumber( 0, -Infinity )
- }
-
- const
- d = a * a + ( 1 - b ) * ( 1 - b ),
- t = Q.ComplexNumber.log( new Q.ComplexNumber(
-
- ( 1 - b * b - a * a ) / d,
- a / d * -2
-
- ))
- return new Q.ComplexNumber( t.imaginary / 2, t.real / 2 )
- },
-
-
-
-
- power: function( a, b ){
-
- if( Q.ComplexNumber.isNumberLike( a )) a = new Q.ComplexNumber( a )
- if( Q.ComplexNumber.isNumberLike( b )) b = new Q.ComplexNumber( b )
-
-
- // Anything raised to the Zero power is 1.
-
- if( b.isZero() ) return Q.ComplexNumber.ONE
-
-
- // Zero raised to any power is 0.
- // Note: What happens if b.real is zero or negative?
- // What happens if b.imaginary is negative?
- // Do we really need those conditionals??
-
- if( a.isZero() &&
- b.real > 0 &&
- b.imaginary >= 0 ){
-
- return Q.ComplexNumber.ZERO
- }
-
-
- // If our exponent is Real (has no Imaginary component)
- // then we’re really just raising to a power.
-
- if( b.imaginary === 0 ){
-
- if( a.real >= 0 && a.imaginary === 0 ){
-
- return new Q.ComplexNumber( Math.pow( a.real, b.real ), 0 )
- }
- else if( a.real === 0 ){// If our base is Imaginary (has no Real component).
-
- switch(( b.real % 4 + 4 ) % 4 ){
-
- case 0:
- return new Q.ComplexNumber( Math.pow( a.imaginary, b.real ), 0 )
- case 1:
- return new Q.ComplexNumber( 0, Math.pow( a.imaginary, b.real ))
- case 2:
- return new Q.ComplexNumber( -Math.pow( a.imaginary, b.real ), 0 )
- case 3:
- return new Q.ComplexNumber( 0, -Math.pow( a.imaginary, b.real ))
- }
- }
- }
-
-
- const
- arctangent2 = Math.atan2( a.imaginary, a.real ),
- logHypotenuse = Q.logHypotenuse( a.real, a.imaginary ),
- x = Math.exp( b.real * logHypotenuse - b.imaginary * arctangent2 ),
- y = b.imaginary * logHypotenuse + b.real * arctangent2
-
- return new Q.ComplexNumber(
-
- x * Math.cos( y ),
- x * Math.sin( y )
- )
- },
- squareRoot: function( a ){
-
- const
- result = new Q.ComplexNumber( 0, 0 ),
- absolute = Q.ComplexNumber.absolute( a )
-
- if( a.real >= 0 ){
-
- if( a.imaginary === 0 ){
-
- result.real = Math.sqrt( a.real )// and imaginary already equals 0.
- }
- else {
-
- result.real = Math.sqrt( 2 * ( absolute + a.real )) / 2
- }
- }
- else {
-
- result.real = Math.abs( a.imaginary ) / Math.sqrt( 2 * ( absolute - a.real ))
- }
- if( a.real <= 0 ){
-
- result.imaginary = Math.sqrt( 2 * ( absolute - a.real )) / 2
- }
- else {
-
- result.imaginary = Math.abs( a.imaginary ) / Math.sqrt( 2 * ( absolute + a.real ))
- }
- if( a.imaginary < 0 ) result.imaginary *= -1
- return result
- },
- log: function( a ){
-
- return new Q.ComplexNumber(
-
- Q.logHypotenuse( a.real, a.imaginary ),
- Math.atan2( a.imaginary, a.real )
- )
- },
- multiply: function( a, b ){
-
- return Q.ComplexNumber.operate(
-
- 'multiply', a, b,
- function( a, b ){
-
- return new Q.ComplexNumber( a * b )
- },
- function( a, b ){
-
- return new Q.ComplexNumber(
-
- a * b.real,
- a * b.imaginary
- )
- },
- function( a, b ){
-
- return new Q.ComplexNumber(
-
- a.real * b,
- a.imaginary * b
- )
- },
- function( a, b ){
-
-
- // FOIL Method that shit.
- // https://en.wikipedia.org/wiki/FOIL_method
-
- const
- firsts = a.real * b.real,
- outers = a.real * b.imaginary,
- inners = a.imaginary * b.real,
- lasts = a.imaginary * b.imaginary * -1// Because i² = -1.
-
- return new Q.ComplexNumber(
-
- firsts + lasts,
- outers + inners
- )
- }
- )
- },
- divide: function( a, b ){
-
- return Q.ComplexNumber.operate(
-
- 'divide', a, b,
- function( a, b ){
-
- return new Q.ComplexNumber( a / b )
- },
- function( a, b ){
-
- return new Q.ComplexNumber( a ).divide( b )
- },
- function( a, b ){
-
- return new Q.ComplexNumber(
-
- a.real / b,
- a.imaginary / b
- )
- },
- function( a, b ){
-
-
- // Ermergerd I had to look this up because it’s been so long.
- // https://www.khanacademy.org/math/precalculus/imaginary-and-complex-numbers/complex-conjugates-and-dividing-complex-numbers/a/dividing-complex-numbers-review
-
- const
- conjugate = b.conjugate(),
- numerator = a.multiply( conjugate ),
-
-
- // The .imaginary will be ZERO for sure,
- // so this forces a ComplexNumber.divide( Number ) ;)
-
- denominator = b.multiply( conjugate ).real
-
- return numerator.divide( denominator )
- }
- )
- },
- add: function( a, b ){
-
- return Q.ComplexNumber.operate(
-
- 'add', a, b,
- function( a, b ){
-
- return new Q.ComplexNumber( a + b )
- },
- function( a, b ){
-
- return new Q.ComplexNumber(
-
- b.real + a,
- b.imaginary
- )
- },
- function( a, b ){
-
- return new Q.ComplexNumber(
-
- a.real + b,
- a.imaginary
- )
- },
- function( a, b ){
-
- return new Q.ComplexNumber(
-
- a.real + b.real,
- a.imaginary + b.imaginary
- )
- }
- )
- },
- subtract: function( a, b ){
-
- return Q.ComplexNumber.operate(
-
- 'subtract', a, b,
- function( a, b ){
-
- return new Q.ComplexNumber( a - b )
- },
- function( a, b ){
-
- return new Q.ComplexNumber(
-
- b.real - a,
- b.imaginary
- )
- },
- function( a, b ){
-
- return new Q.ComplexNumber(
-
- a.real - b,
- a.imaginary
- )
- },
- function( a, b ){
-
- return new Q.ComplexNumber(
-
- a.real - b.real,
- a.imaginary - b.imaginary
- )
- }
- )
- }
-})
-
-
-
-
-Q.ComplexNumber.createConstants(
-
- 'ZERO', new Q.ComplexNumber( 0, 0 ),
- 'ONE', new Q.ComplexNumber( 1, 0 ),
- 'E', new Q.ComplexNumber( Math.E, 0 ),
- 'PI', new Q.ComplexNumber( Math.PI, 0 ),
- 'I', new Q.ComplexNumber( 0, 1 ),
- 'EPSILON', new Q.ComplexNumber( Q.EPSILON, Q.EPSILON ),
- 'INFINITY', new Q.ComplexNumber( Infinity, Infinity ),
- 'NAN', new Q.ComplexNumber( NaN, NaN )
-)
-
-
-
-
-Object.assign( Q.ComplexNumber.prototype, {
-
-
- // NON-destructive operations.
-
- clone: function(){
-
- return new Q.ComplexNumber( this.real, this.imaginary )
- },
- reduce: function(){
-
-
- // Note: this *might* kill function chaining.
-
- if( this.imaginary === 0 ) return this.real
- return this
- },
- toText: function( roundToDecimal, padPositive ){
-
-
- // Note: this will kill function chaining.
-
- return Q.ComplexNumber.toText( this.real, this.imaginary, roundToDecimal, padPositive )
- },
-
-
- isNaN: function( n ){
-
- return Q.ComplexNumber.isNaN( this )// Returned boolean will kill function chaining.
- },
- isZero: function( n ){
-
- return Q.ComplexNumber.isZero( this )// Returned boolean will kill function chaining.
- },
- isFinite: function( n ){
-
- return Q.ComplexNumber.isFinite( this )// Returned boolean will kill function chaining.
- },
- isInfinite: function( n ){
-
- return Q.ComplexNumber.isInfinite( this )// Returned boolean will kill function chaining.
- },
- isEqualTo: function( b ){
-
- return Q.ComplexNumber.areEqual( this, b )// Returned boolean will kill function chaining.
- },
-
-
- absolute: function(){
-
- return Q.ComplexNumber.absolute( this )// Returned number will kill function chaining.
- },
- conjugate: function(){
-
- return Q.ComplexNumber.conjugate( this )
- },
-
-
- power: function( b ){
-
- return Q.ComplexNumber.power( this, b )
- },
- squareRoot: function(){
-
- return Q.ComplexNumber.squareRoot( this )
- },
- log: function(){
-
- return Q.ComplexNumber.log( this )
- },
- multiply: function( b ){
-
- return Q.ComplexNumber.multiply( this, b )
- },
- divide: function( b ){
-
- return Q.ComplexNumber.divide( this, b )
- },
- add: function( b ){
-
- return Q.ComplexNumber.add( this, b )
- },
- subtract: function( b ){
-
- return Q.ComplexNumber.subtract( this, b )
- },
-
-
-
-
- // DESTRUCTIVE operations.
-
- copy$: function( b ){
-
- if( b instanceof Q.ComplexNumber !== true )
- return Q.error( `Q.ComplexNumber attempted to copy something that was not a complex number in to this complex number #${this.index}.`, this )
-
- this.real = b.real
- this.imaginary = b.imaginary
- return this
- },
- conjugate$: function(){
-
- return this.copy$( this.conjugate() )
- },
- power$: function( b ){
-
- return this.copy$( this.power( b ))
- },
- squareRoot$: function(){
-
- return this.copy$( this.squareRoot() )
- },
- log$: function(){
-
- return this.copy$( this.log() )
- },
- multiply$: function( b ){
-
- return this.copy$( this.multiply( b ))
- },
- divide$: function( b ){
-
- return this.copy$( this.divide( b ))
- },
- add$: function( b ){
-
- return this.copy$( this.add( b ))
- },
- subtract$: function( b ){
-
- return this.copy$( this.subtract( b ))
- }
-})
-
-
-
diff --git a/Q/Q-Gate.js b/Q/Q-Gate.js
deleted file mode 100644
index a1352a6..0000000
--- a/Q/Q-Gate.js
+++ /dev/null
@@ -1,300 +0,0 @@
-
-// Copyright © 2019–2020, Stewart Smith. See LICENSE for details.
-
-
-
-
-Q.Gate = function( params ){
-
- Object.assign( this, params )
- this.index = Q.Gate.index ++
-
- if( typeof this.symbol !== 'string' ) this.symbol = '?'
- if( typeof this.symbolAmazonBraket !== 'string' ) this.symbolAmazonBraket = this.symbol.toLowerCase()
-
-
- // We use symbols as unique identifiers
- // among gate CONSTANTS
- // so if you use the same symbol for a non-constant
- // that’s not a deal breaker
- // but it is good to know.
-
- const
- scope = this,
- foundConstant = Object
- .values( Q.Gate.constants )
- .find( function( gate ){
-
- return gate.symbol === scope.symbol
- })
-
- if( foundConstant ){
-
- Q.warn( `Q.Gate is creating a new instance, #${ this.index }, that uses the same symbol as a pre-existing Gate constant:`, foundConstant )
- }
-
- if( typeof this.name !== 'string' ) this.name = 'Unknown'
- if( typeof this.nameCss !== 'string' ) this.nameCss = 'unknown'
-
-
- // If our gate’s matrix is to be
- // dynamically created or updated
- // then we ouoght to do that now.
-
- if( typeof this.updateMatrix$ === 'function' ) this.updateMatrix$()
-
-
- // Every gate must have an applyToQubit method.
- // If it doesn’t exist we’ll create one
- // based on whether a matrix property exists or not.
-
- if( typeof this.applyToQubit !== 'function' ){
-
- if( this.matrix instanceof Q.Matrix === true ){
-
- this.applyToQubit = function( qubit ){
-
- return new Q.Qubit( this.matrix.multiply( qubit ))
- }
- }
- else {
-
- this.applyToQubit = function( qubit ){ return qubit }
- }
- }
-}
-
-
-
-
-Object.assign( Q.Gate, {
-
- index: 0,
- constants: {},
- createConstant: Q.createConstant,
- createConstants: Q.createConstants,
- findBy: function( key, value ){
-
- return (
-
- Object
- .values( Q.Gate.constants )
- .find( function( item ){
-
- if( typeof value === 'string' &&
- typeof item[ key ] === 'string' ){
-
- return value.toLowerCase() === item[ key ].toLowerCase()
- }
- return value === item[ key ]
- })
- )
- },
- findBySymbol: function( symbol ){
-
- return Q.Gate.findBy( 'symbol', symbol )
- },
- findByName: function( name ){
-
- return Q.Gate.findBy( 'name', name )
- }
-})
-
-
-
-
-Object.assign( Q.Gate.prototype, {
-
- clone: function( params ){
-
- return new Q.Gate( Object.assign( {}, this, params ))
- },
- applyToQubits: function(){
-
- return Array.from( arguments ).map( this.applyToQubit.bind( this ))
- },
- set$: function( key, value ){
-
- this[ key ] = value
- return this
- },
- setSymbol$: function( value ){
-
- return this.set$( 'symbol', value )
- }
-})
-
-
-
-
-Q.Gate.createConstants(
-
-
- // Operate on a single qubit.
-
- 'IDENTITY', new Q.Gate({
-
- symbol: 'I',
- symbolAmazonBraket: 'i',
- symbolSvg: '',
- name: 'Identity',
- nameCss: 'identity',
- matrix: Q.Matrix.IDENTITY_2X2
- }),
- 'CURSOR', new Q.Gate({
-
- symbol: '*',
- symbolAmazonBraket: 'i',
- symbolSvg: '',
- name: 'Identity',
- nameCss: 'identity',
- matrix: Q.Matrix.IDENTITY_2X2
- }),
- 'MEASURE', new Q.Gate({
-
- symbol: 'M',
- symbolAmazonBraket: 'm',
- symbolSvg: '',
- name: 'Measure',
- nameCss: 'measure',
- matrix: Q.Matrix.IDENTITY_2X2,
- applyToQubit: function( state ){}
- }),
- 'HADAMARD', new Q.Gate({
-
- symbol: 'H',
- symbolAmazonBraket: 'h',
- symbolSvg: '',
- name: 'Hadamard',
- nameCss: 'hadamard',
- matrix: new Q.Matrix(
- [ Math.SQRT1_2, Math.SQRT1_2 ],
- [ Math.SQRT1_2, -Math.SQRT1_2 ])
- }),
- 'PAULI_X', new Q.Gate({
-
- symbol: 'X',
- symbolAmazonBraket: 'x',
- symbolSvg: '',
- name: 'Pauli X',
- nameCss: 'pauli-x',
- matrix: new Q.Matrix(
- [ 0, 1 ],
- [ 1, 0 ])
- }),
- 'PAULI_Y', new Q.Gate({
-
- symbol: 'Y',
- symbolAmazonBraket: 'y',
- symbolSvg: '',
- name: 'Pauli Y',
- nameCss: 'pauli-y',
- matrix: new Q.Matrix(
- [ 0, new Q.ComplexNumber( 0, -1 )],
- [ new Q.ComplexNumber( 0, 1 ), 0 ])
- }),
- 'PAULI_Z', new Q.Gate({
-
- symbol: 'Z',
- symbolAmazonBraket: 'z',
- symbolSvg: '',
- name: 'Pauli Z',
- nameCss: 'pauli-z',
- matrix: new Q.Matrix(
- [ 1, 0 ],
- [ 0, -1 ])
- }),
- 'PHASE', new Q.Gate({
-
- symbol: 'P',
- symbolAmazonBraket: 'p',// !!! Double check this !!!
- symbolSvg: '',
- name: 'Phase',
- nameCss: 'phase',
- phi: 1,
- updateMatrix$: function( phi ){
-
- if( Q.isUsefulNumber( phi ) === true ) this.phi = phi
- this.matrix = new Q.Matrix(
- [ 1, 0 ],
- [ 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, this.phi ))])
- return this
- },
- applyToQubit: function( qubit, phi ){
-
- if( Q.isUsefulNumber( phi ) !== true ) phi = this.phi
- const matrix = new Q.Matrix(
- [ 1, 0 ],
- [ 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, phi ))])
- return new Q.Qubit( matrix.multiply( qubit ))
- }
- }),
- 'PI_8', new Q.Gate({
-
- symbol: 'T',
- symbolAmazonBraket: 't',// !!! Double check this !!!
- symbolSvg: '',
- name: 'π ÷ 8',
- nameCss: 'pi8',
- matrix: new Q.Matrix(
- [ 1, 0 ],
- [ 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, Math.PI / 4 )) ])
- }),
- 'BLOCH', new Q.Gate({
-
- symbol: 'B',
- //symbolAmazonBraket: Does not exist.
- symbolSvg: '',
- name: 'Bloch sphere',
- nameCss: 'bloch',
- applyToQubit: function( qubit ){
-
- // Create Bloch sphere visualizer instance.
- }
- }),
-
-
- // Operate on 2 qubits.
-
- 'SWAP', new Q.Gate({
-
- symbol: 'S',
- symbolAmazonBraket: 's',// !!! Double check this !!!
- symbolSvg: '',
- name: 'Swap',
- nameCss: 'swap',
- matrix: new Q.Matrix(
- [ 1, 0, 0, 0 ],
- [ 0, 0, 1, 0 ],
- [ 0, 1, 0, 0 ],
- [ 0, 0, 0, 1 ])
- }),
- 'SWAP1_2', new Q.Gate({
-
- symbol: '√S',
- //symbolAmazonBraket: !!! UNKNOWN !!!
- symbolSvg: '',
- name: '√Swap',
- nameCss: 'swap1-2',
- matrix: new Q.Matrix(
- [ 1, 0, 0, 0 ],
- [ 0, new Q.ComplexNumber( 0.5, 0.5 ), new Q.ComplexNumber( 0.5, -0.5 ), 0 ],
- [ 0, new Q.ComplexNumber( 0.5, -0.5 ), new Q.ComplexNumber( 0.5, 0.5 ), 0 ],
- [ 0, 0, 0, 1 ])
- })
- /*
-
-
- All further gates,
- such as Toffoli (CCNOT)
- or Fredkin (CSWAP)
- can be easily constructed
- from the above gates
- using Q conveniences.
-
-
- */
-)
-
-
-
diff --git a/Q/Q-Matrix.js b/Q/Q-Matrix.js
deleted file mode 100644
index a4ebbf9..0000000
--- a/Q/Q-Matrix.js
+++ /dev/null
@@ -1,662 +0,0 @@
-
-// Copyright © 2019–2020, Stewart Smith. See LICENSE for details.
-
-
-
-
-Q.Matrix = function(){
-
-
- // We’re keeping track of how many matrices are
- // actually being generated. Just curiosity.
-
- this.index = Q.Matrix.index ++
-
-
- let matrixWidth = null
-
-
- // Has Matrix been called with two numerical arguments?
- // If so, we need to create an empty Matrix
- // with dimensions of those values.
-
- if( arguments.length == 1 &&
- Q.ComplexNumber.isNumberLike( arguments[ 0 ])){
-
- matrixWidth = arguments[ 0 ]
- this.rows = new Array( matrixWidth ).fill( 0 ).map( function(){
-
- return new Array( matrixWidth ).fill( 0 )
- })
- }
- else if( arguments.length == 2 &&
- Q.ComplexNumber.isNumberLike( arguments[ 0 ]) &&
- Q.ComplexNumber.isNumberLike( arguments[ 1 ])){
-
- matrixWidth = arguments[ 0 ]
- this.rows = new Array( arguments[ 1 ]).fill( 0 ).map( function(){
-
- return new Array( matrixWidth ).fill( 0 )
- })
- }
- else {
-
- // Matrices’ primary organization is by rows,
- // which is more congruent with our written langauge;
- // primarily organizated by horizontally juxtaposed glyphs.
- // That means it’s easier to write an instance invocation in code
- // and easier to read when inspecting properties in the console.
-
- let matrixWidthIsBroken = false
- this.rows = Array.from( arguments )
- this.rows.forEach( function( row ){
-
- if( row instanceof Array !== true ) row = [ row ]
- if( matrixWidth === null ) matrixWidth = row.length
- else if( matrixWidth !== row.length ) matrixWidthIsBroken = true
- })
- if( matrixWidthIsBroken )
- return Q.error( `Q.Matrix found upon initialization that matrix#${this.index} row lengths were not equal. You are going to have a bad time.`, this )
- }
-
-
-
-
-
-
- // But for convenience we can also organize by columns.
- // Note this represents the transposed version of itself!
-
- const matrix = this
- this.columns = []
- for( let x = 0; x < matrixWidth; x ++ ){
-
- const column = []
- for( let y = 0; y < this.rows.length; y ++ ){
-
-
- // Since we’re combing through here
- // this is a good time to convert Number to ComplexNumber!
-
- const value = matrix.rows[ y ][ x ]
- if( typeof value === 'number' ){
-
- // console.log('Created a complex number!')
- matrix.rows[ y ][ x ] = new Q.ComplexNumber( value )
- }
- else if( value instanceof Q.ComplexNumber === false ){
- return Q.error( `Q.Matrix found upon initialization that matrix#${this.index} contained non-quantitative values. A+ for creativity, but F for functionality.`, this )
- }
-
- // console.log( x, y, matrix.rows[ y ][ x ])
-
-
- Object.defineProperty( column, y, {
-
- get: function(){ return matrix.rows[ y ][ x ]},
- set: function( n ){ matrix.rows[ y ][ x ] = n }
- })
- }
- this.columns.push( column )
- }
-}
-
-
-
-
-
-
- ///////////////////////////
- // //
- // Static properties //
- // //
-///////////////////////////
-
-
-Object.assign( Q.Matrix, {
-
- index: 0,
- help: function(){ return Q.help( this )},
- constants: {},// Only holds references; an easy way to look up what constants exist.
- createConstant: Q.createConstant,
- createConstants: Q.createConstants,
-
-
- isMatrixLike: function( obj ){
-
- //return obj instanceof Q.Matrix || Q.Matrix.prototype.isPrototypeOf( obj )
- return obj instanceof this || this.prototype.isPrototypeOf( obj )
- },
- isWithinRange: function( n, minimum, maximum ){
-
- return typeof n === 'number' &&
- n >= minimum &&
- n <= maximum &&
- n == parseInt( n )
- },
- getWidth: function( matrix ){
-
- return matrix.columns.length
- },
- getHeight: function( matrix ){
-
- return matrix.rows.length
- },
- haveEqualDimensions: function( matrix0, matrix1 ){
-
- return (
-
- matrix0.rows.length === matrix1.rows.length &&
- matrix0.columns.length === matrix1.columns.length
- )
- },
- areEqual: function( matrix0, matrix1 ){
-
- if( matrix0 instanceof Q.Matrix !== true ) return false
- if( matrix1 instanceof Q.Matrix !== true ) return false
- if( Q.Matrix.haveEqualDimensions( matrix0, matrix1 ) !== true ) return false
- return matrix0.rows.reduce( function( state, row, r ){
-
- return state && row.reduce( function( state, cellValue, c ){
-
- return state && cellValue.isEqualTo( matrix1.rows[ r ][ c ])
-
- }, true )
-
- }, true )
- },
-
-
-
-
- createSquare: function( size, f ){
-
- if( typeof size !== 'number' ) size = 2
- if( typeof f !== 'function' ) f = function(){ return 0 }
- const data = []
- for( let y = 0; y < size; y ++ ){
-
- const row = []
- for( let x = 0; x < size; x ++ ){
-
- row.push( f( x, y ))
- }
- data.push( row )
- }
- return new Q.Matrix( ...data )
- },
- createZero: function( size ){
-
- return new Q.Matrix.createSquare( size )
- },
- createOne: function( size ){
-
- return new Q.Matrix.createSquare( size, function(){ return 1 })
- },
- createIdentity: function( size ){
-
- return new Q.Matrix.createSquare( size, function( x, y ){ return x === y ? 1 : 0 })
- },
-
-
-
-
- // Import FROM a format.
-
- from: function( format ){
-
- if( typeof format !== 'string' ) format = 'Array'
- const f = Q.Matrix[ 'from'+ format ]
- format = format.toLowerCase()
- if( typeof f !== 'function' )
- return Q.error( `Q.Matrix could not find an importer for “${format}” data.` )
- return f
- },
- fromArray: function( array ){
-
- return new Q.Matrix( ...array )
- },
- fromXsv: function( input, rowSeparator, valueSeparator ){
-
- `
- Ingest string data organized by row, then by column
- where rows are separated by one token (default: \n)
- and column values are separated by another token
- (default: \t).
-
- `
-
- if( typeof rowSeparator !== 'string' ) rowSeparator = '\n'
- if( typeof valueSeparator !== 'string' ) valueSeparator = '\t'
-
- const
- inputRows = input.split( rowSeparator ),
- outputRows = []
-
- inputRows.forEach( function( inputRow ){
-
- inputRow = inputRow.trim()
- if( inputRow === '' ) return
-
- const outputRow = []
- inputRow.split( valueSeparator ).forEach( function( cellValue ){
-
- outputRow.push( parseFloat( cellValue ))
- })
- outputRows.push( outputRow )
- })
- return new Q.Matrix( ...outputRows )
- },
- fromCsv: function( csv ){
-
- return Q.Matrix.fromXsv( csv.replace( /\r/g, '\n' ), '\n', ',' )
- },
- fromTsv: function( tsv ){
-
- return Q.Matrix.fromXsv( tsv, '\n', '\t' )
- },
- fromHtml: function( html ){
-
- return Q.Matrix.fromXsv(
-
- html
- .replace( /\r?\n|\r|
|/g, '' )
- .replace( /<\/td>(\s*)<\/tr>/g, ' |
' )
- .match( /(.*)<\/table>/i )[ 1 ],
- '',
- ''
- )
- },
-
-
-
-
- // Export TO a format.
-
- toXsv: function( matrix, rowSeparator, valueSeparator ){
-
- return matrix.rows.reduce( function( xsv, row ){
-
- return xsv + rowSeparator + row.reduce( function( xsv, cell, c ){
-
- return xsv + ( c > 0 ? valueSeparator : '' ) + cell.toText()
-
- }, '' )
-
- }, '' )
- },
- toCsv: function( matrix ){
-
- return Q.Matrix.toXsv( matrix, '\n', ',' )
- },
- toTsv: function( matrix ){
-
- return Q.Matrix.toXsv( matrix, '\n', '\t' )
- },
-
-
-
-
- // Operate NON-destructive.
-
- add: function( matrix0, matrix1 ){
-
- if( Q.Matrix.isMatrixLike( matrix0 ) !== true ||
- Q.Matrix.isMatrixLike( matrix1 ) !== true ){
-
- return Q.error( `Q.Matrix attempted to add something that was not a matrix.` )
- }
- if( Q.Matrix.haveEqualDimensions( matrix0, matrix1 ) !== true )
- return Q.error( `Q.Matrix cannot add matrix#${matrix0.index} of dimensions ${matrix0.columns.length}x${matrix0.rows.length} to matrix#${matrix1.index} of dimensions ${matrix1.columns.length}x${matrix1.rows.length}.`)
-
- return new Q.Matrix( ...matrix0.rows.reduce( function( resultMatrixRow, row, r ){
-
- resultMatrixRow.push( row.reduce( function( resultMatrixColumn, cellValue, c ){
-
- // resultMatrixColumn.push( cellValue + matrix1.rows[ r ][ c ])
- resultMatrixColumn.push( cellValue.add( matrix1.rows[ r ][ c ]))
- return resultMatrixColumn
-
- }, [] ))
- return resultMatrixRow
-
- }, [] ))
- },
- multiplyScalar: function( matrix, scalar ){
-
- if( Q.Matrix.isMatrixLike( matrix ) !== true ){
-
- return Q.error( `Q.Matrix attempted to scale something that was not a matrix.` )
- }
- if( typeof scalar !== 'number' ){
-
- return Q.error( `Q.Matrix attempted to scale this matrix#${matrix.index} by an invalid scalar: ${scalar}.` )
- }
- return new Q.Matrix( ...matrix.rows.reduce( function( resultMatrixRow, row ){
-
- resultMatrixRow.push( row.reduce( function( resultMatrixColumn, cellValue ){
-
- // resultMatrixColumn.push( cellValue * scalar )
- resultMatrixColumn.push( cellValue.multiply( scalar ))
- return resultMatrixColumn
-
- }, [] ))
- return resultMatrixRow
-
- }, [] ))
- },
- multiply: function( matrix0, matrix1 ){
-
- `
- Two matrices can be multiplied only when
- the number of columns in the first matrix
- equals the number of rows in the second matrix.
- Reminder: Matrix multiplication is not commutative
- so the order in which you multiply matters.
-
-
- SEE ALSO
-
- https://en.wikipedia.org/wiki/Matrix_multiplication
- `
-
- if( Q.Matrix.isMatrixLike( matrix0 ) !== true ||
- Q.Matrix.isMatrixLike( matrix1 ) !== true ){
-
- return Q.error( `Q.Matrix attempted to multiply something that was not a matrix.` )
- }
- if( matrix0.columns.length !== matrix1.rows.length ){
-
- return Q.error( `Q.Matrix attempted to multiply Matrix#${matrix0.index}(cols==${matrix0.columns.length}) by Matrix#${matrix1.index}(rows==${matrix1.rows.length}) but their dimensions were not compatible for this.` )
- }
- const resultMatrix = []
- matrix0.rows.forEach( function( matrix0Row ){// Each row of THIS matrix
-
- const resultMatrixRow = []
- matrix1.columns.forEach( function( matrix1Column ){// Each column of OTHER matrix
-
- const sum = new Q.ComplexNumber()
- matrix1Column.forEach( function( matrix1CellValue, index ){// Work down the column of OTHER matrix
-
- sum.add$( matrix0Row[ index ].multiply( matrix1CellValue ))
- })
- resultMatrixRow.push( sum )
- })
- resultMatrix.push( resultMatrixRow )
- })
- //return new Q.Matrix( ...resultMatrix )
- return new this( ...resultMatrix )
- },
- multiplyTensor: function( matrix0, matrix1 ){
-
- `
- https://en.wikipedia.org/wiki/Kronecker_product
- https://en.wikipedia.org/wiki/Tensor_product
- `
-
- if( Q.Matrix.isMatrixLike( matrix0 ) !== true ||
- Q.Matrix.isMatrixLike( matrix1 ) !== true ){
-
- return Q.error( `Q.Matrix attempted to tensor something that was not a matrix.` )
- }
-
- const
- resultMatrix = [],
- resultMatrixWidth = matrix0.columns.length * matrix1.columns.length,
- resultMatrixHeight = matrix0.rows.length * matrix1.rows.length
-
- for( let y = 0; y < resultMatrixHeight; y ++ ){
-
- const resultMatrixRow = []
- for( let x = 0; x < resultMatrixWidth; x ++ ){
-
- const
- matrix0X = Math.floor( x / matrix0.columns.length ),
- matrix0Y = Math.floor( y / matrix0.rows.length ),
- matrix1X = x % matrix1.columns.length,
- matrix1Y = y % matrix1.rows.length
-
- resultMatrixRow.push(
-
- //matrix0.rows[ matrix0Y ][ matrix0X ] * matrix1.rows[ matrix1Y ][ matrix1X ]
- matrix0.rows[ matrix0Y ][ matrix0X ].multiply( matrix1.rows[ matrix1Y ][ matrix1X ])
- )
- }
- resultMatrix.push( resultMatrixRow )
- }
- return new Q.Matrix( ...resultMatrix )
- }
-})
-
-
-
-
-
-
- //////////////////////////////
- // //
- // Prototype properties //
- // //
-//////////////////////////////
-
-
-Object.assign( Q.Matrix.prototype, {
-
- isValidRow: function( r ){
-
- return Q.Matrix.isWithinRange( r, 0, this.rows.length - 1 )
- },
- isValidColumn: function( c ){
-
- return Q.Matrix.isWithinRange( c, 0, this.columns.length - 1 )
- },
- isValidAddress: function( x, y ){
-
- return this.isValidRow( y ) && this.isValidColumn( x )
- },
- getWidth: function(){
-
- return Q.Matrix.getWidth( this )
- },
- getHeight: function(){
-
- return Q.Matrix.getHeight( this )
- },
-
-
-
-
- // Read NON-destructive by nature. (Except quantum reads of course! ROFL!!)
-
- read: function( x, y ){
-
- `
- Equivalent to
- this.columns[ x ][ y ]
- or
- this.rows[ y ][ x ]
- but with safety checks.
- `
-
- if( this.isValidAddress( x, y )) return this.rows[ y ][ x ]
- return Q.error( `Q.Matrix could not read from cell address (x=${x}, y=${y}) in matrix#${this.index}.`, this )
- },
- clone: function(){
-
- return new Q.Matrix( ...this.rows )
- },
- isEqualTo: function( otherMatrix ){
-
- return Q.Matrix.areEqual( this, otherMatrix )
- },
-
-
- toArray: function(){
-
- return this.rows
- },
- toXsv: function( rowSeparator, valueSeparator ){
-
- return Q.Matrix.toXsv( this, rowSeparator, valueSeparator )
- },
- toCsv: function(){
-
- return Q.Matrix.toXsv( this, '\n', ',' )
- },
- toTsv: function(){
-
- return Q.Matrix.toXsv( this, '\n', '\t' )
- },
- toHtml: function(){
-
- return this.rows.reduce( function( html, row ){
-
- return html + row.reduce( function( html, cell ){
-
- return html +'\n\t\t'+ cell.toText() +' | '
-
- }, '\n\t' ) + '\n\t
'
-
- }, '\n'
- },
-
-
-
-
- // Write is DESTRUCTIVE by nature. Not cuz I hate ya.
-
- write$: function( x, y, n ){
-
- `
- Equivalent to
- this.columns[ x ][ y ] = n
- or
- this.rows[ y ][ x ] = n
- but with safety checks.
- `
-
- if( this.isValidAddress( x, y )){
-
- if( Q.ComplexNumber.isNumberLike( n )) n = new Q.ComplexNumber( n )
- if( n instanceof Q.ComplexNumber !== true ) return Q.error( `Attempted to write an invalid value (${n}) to matrix#${this.index} at x=${x}, y=${y}`, this )
- this.rows[ y ][ x ] = n
- return this
- }
- return Q.error( `Invalid cell address for Matrix#${this.index}: x=${x}, y=${y}`, this )
- },
- copy$: function( matrix ){
-
- if( Q.Matrix.isMatrixLike( matrix ) !== true )
- return Q.error( `Q.Matrix attempted to copy something that was not a matrix in to this matrix#${matrix.index}.`, this )
-
- if( Q.Matrix.haveEqualDimensions( matrix, this ) !== true )
- return Q.error( `Q.Matrix cannot copy matrix#${matrix.index} of dimensions ${matrix.columns.length}x${matrix.rows.length} in to this matrix#${this.index} of dimensions ${this.columns.length}x${this.rows.length} because their dimensions do not match.`, this )
-
- const that = this
- matrix.rows.forEach( function( row, r ){
-
- row.forEach( function( n, c ){
-
- that.rows[ r ][ c ] = n
- })
- })
- return this
- },
- fromArray$: function( array ){
-
- return this.copy$( Q.Matrix.fromArray( array ))
- },
- fromCsv$: function( csv ){
-
- return this.copy$( Q.Matrix.fromCsv( csv ))
- },
- fromTsv$: function( tsv ){
-
- return this.copy$( Q.Matrix.fromTsv( tsv ))
- },
- fromHtml$: function( html ){
-
- return this.copy$( Q.Matrix.fromHtml( html ))
- },
-
-
-
-
- // Operate NON-destructive.
-
- add: function( otherMatrix ){
-
- return Q.Matrix.add( this, otherMatrix )
- },
- multiplyScalar: function( scalar ){
-
- return Q.Matrix.multiplyScalar( this, scalar )
- },
- multiply: function( otherMatrix ){
-
- return Q.Matrix.multiply( this, otherMatrix )
- },
- multiplyTensor: function( otherMatrix ){
-
- return Q.Matrix.multiplyTensor( this, otherMatrix )
- },
-
-
-
-
- // Operate DESTRUCTIVE.
-
- add$: function( otherMatrix ){
-
- return this.copy$( this.add( otherMatrix ))
- },
- multiplyScalar$: function( scalar ){
-
- return this.copy$( this.multiplyScalar( scalar ))
- }
-})
-
-
-
-
-
-
- //////////////////////////
- // //
- // Static constants //
- // //
-//////////////////////////
-
-
-Q.Matrix.createConstants(
-
- 'IDENTITY_2X2', Q.Matrix.createIdentity( 2 ),
- 'IDENTITY_3X3', Q.Matrix.createIdentity( 3 ),
- 'IDENTITY_4X4', Q.Matrix.createIdentity( 4 ),
-
- 'CONSTANT0_2X2', new Q.Matrix(
- [ 1, 1 ],
- [ 0, 0 ]),
-
- 'CONSTANT1_2X2', new Q.Matrix(
- [ 0, 0 ],
- [ 1, 1 ]),
-
- 'NEGATION_2X2', new Q.Matrix(
- [ 0, 1 ],
- [ 1, 0 ]),
-
- 'TEST_MAP_9X9', new Q.Matrix(
- [ 11, 21, 31, 41, 51, 61, 71, 81, 91 ],
- [ 12, 22, 32, 42, 52, 62, 72, 82, 92 ],
- [ 13, 23, 33, 43, 53, 63, 73, 83, 93 ],
- [ 14, 24, 34, 44, 54, 64, 74, 84, 94 ],
- [ 15, 25, 35, 45, 55, 65, 75, 85, 95 ],
- [ 16, 26, 36, 46, 56, 66, 76, 86, 96 ],
- [ 17, 27, 37, 47, 57, 67, 77, 87, 97 ],
- [ 18, 28, 38, 48, 58, 68, 78, 88, 98 ],
- [ 19, 29, 39, 49, 59, 69, 79, 89, 99 ])
-)
-
-
-
diff --git a/Q/Q-Qubit.js b/Q/Q-Qubit.js
deleted file mode 100644
index 2b33355..0000000
--- a/Q/Q-Qubit.js
+++ /dev/null
@@ -1,381 +0,0 @@
-
-// Copyright © 2019–2020, Stewart Smith. See LICENSE for details.
-
-
-
-
-Q.Qubit = function( a, b, symbol, name ){
-
-
- // If we’ve received an instance of Q.Matrix as our first argument
- // then we’ll assume there are no further arguments
- // and just use that matrix as our new Q.Qubit instance.
-
- if( Q.Matrix.isMatrixLike( a ) && b === undefined ){
-
- b = a.rows[ 1 ][ 0 ]
- a = a.rows[ 0 ][ 0 ]
- }
- else {
-
-
- // All of our internal math now uses complex numbers
- // rather than Number literals
- // so we’d better convert!
-
- if( typeof a === 'number' ) a = new Q.ComplexNumber( a, 0 )
- if( typeof b === 'number' ) b = new Q.ComplexNumber( b, 0 )
-
-
- // If we receive undefined (or garbage inputs)
- // let’s try to make it useable.
- // This way we can always call Q.Qubit with no arguments
- // to make a new qubit available for computing with.
-
- if( a instanceof Q.ComplexNumber !== true ) a = new Q.ComplexNumber( 1, 0 )
- if( b instanceof Q.ComplexNumber !== true ){
-
-
- // 1 - |𝒂|² = |𝒃|²
- // So this does NOT account for if 𝒃 ought to be imaginary or not.
- // Perhaps for completeness we could randomly decide
- // to flip the real and imaginary components of 𝒃 after this line?
-
- b = Q.ComplexNumber.ONE.subtract( Math.pow( a.absolute(), 2 )).squareRoot()
- }
- }
-
-
- // Sanity check!
- // Does this constraint hold true? |𝒂|² + |𝒃|² = 1
-
- if( Math.pow( a.absolute(), 2 ) + Math.pow( b.absolute(), 2 ) - 1 > Q.EPSILON )
- return Q.error( `Q.Qubit could not accept the initialization values of a=${a} and b=${b} because their squares do not add up to 1.` )
-
- Q.Matrix.call( this, [ a ],[ b ])
- this.index = Q.Qubit.index ++
-
-
- // Convenience getters and setters for this qubit’s
- // controll bit and target bit.
-
- Object.defineProperty( this, 'alpha', {
-
- get: function(){ return this.rows[ 0 ][ 0 ]},
- set: function( n ){ this.rows[ 0 ][ 0 ] = n }
- })
- Object.defineProperty( this, 'beta', {
-
- get: function(){ return this.rows[ 1 ][ 0 ]},
- set: function( n ){ this.rows[ 1 ][ 0 ] = n }
- })
-
-
- // Used for Dirac notation: |?⟩
-
- if( typeof symbol === 'string' ) this.symbol = symbol
- if( typeof name === 'string' ) this.name = name
- if( this.symbol === undefined || this.name === undefined ){
-
- const found = Object.values( Q.Qubit.constants ).find( function( qubit ){
-
- return (
-
- a.isEqualTo( qubit.alpha ) &&
- b.isEqualTo( qubit.beta )
- )
- })
- if( found === undefined ){
-
- this.symbol = '?'
- this.name = 'Unnamed'
- }
- else {
-
- if( this.symbol === undefined ) this.symbol = found.symbol
- if( this.name === undefined ) this.name = found.name
- }
- }
-}
-Q.Qubit.prototype = Object.create( Q.Matrix.prototype )
-Q.Qubit.prototype.constructor = Q.Qubit
-
-
-
-
-Object.assign( Q.Qubit, {
-
- index: 0,
- help: function(){ return Q.help( this )},
- constants: {},
- createConstant: Q.createConstant,
- createConstants: Q.createConstants,
-
-
-
-
- findBy: function( key, value ){
-
- return (
-
- Object
- .values( Q.Qubit.constants )
- .find( function( item ){
-
- if( typeof value === 'string' &&
- typeof item[ key ] === 'string' ){
-
- return value.toLowerCase() === item[ key ].toLowerCase()
- }
- return value === item[ key ]
- })
- )
- },
- findBySymbol: function( symbol ){
-
- return Q.Qubit.findBy( 'symbol', symbol )
- },
- findByName: function( name ){
-
- return Q.Qubit.findBy( 'name', name )
- },
- findByBeta: function( beta ){
-
- if( beta instanceof Q.ComplexNumber === false ){
-
- beta = new Q.ComplexNumber( beta )
- }
- return Object.values( Q.Qubit.constants ).find( function( qubit ){
-
- return qubit.beta.isEqualTo( beta )
- })
- },
- areEqual: function( qubit0, qubit1 ){
-
- return (
-
- qubit0.alpha.isEqualTo( qubit1.alpha ) &&
- qubit0.beta.isEqualTo( qubit1.beta )
- )
- },
- collapse: function( qubit ){
-
- const
- alpha2 = Math.pow( qubit.alpha.absolute(), 2 ),
- beta2 = Math.pow( qubit.beta.absolute(), 2 ),
- randomNumberRange = Math.pow( 2, 32 ) - 1,
- randomNumber = new Uint32Array( 1 )
-
- // console.log( 'alpha^2', alpha2 )
- // console.log( 'beta^2', beta2 )
- window.crypto.getRandomValues( randomNumber )
- const randomNumberNormalized = randomNumber / randomNumberRange
- if( randomNumberNormalized <= alpha2 ){
-
- return new Q.Qubit( 1, 0 )
- }
- else return new Q.Qubit( 0, 1 )
- },
- applyGate: function( qubit, gate, ...args ){
-
- `
- This is means of inverting what comes first:
- the Gate or the Qubit?
- If the Gate only operates on a single qubit,
- then it doesn’t matter and we can do this:
- `
-
- if( gate instanceof Q.Gate === false ) return Q.error( `Q.Qubit attempted to apply something that was not a gate to this qubit #${ qubit.index }.` )
- else return gate.applyToQubit( qubit, ...args )
- },
- toText: function( qubit ){
-
- //return `|${qubit.beta.toText()}⟩`
- return qubit.alpha.toText() +'\n'+ qubit.beta.toText()
- },
- toStateVectorText: function( qubit ){
-
- return `|${ qubit.beta.toText() }⟩`
- },
- toStateVectorHtml: function( qubit ){
-
- return `${ qubit.beta.toText() }`
- },
-
-
-
- // This code was a pain in the ass to figure out.
- // I’m not fluent in trigonometry
- // and none of the quantum primers actually lay out
- // how to convert arbitrary qubit states
- // to Bloch Sphere representation.
- // Oh, they provide equivalencies for specific states, sure.
- // I hope this is useful to you
- // unless you are porting this to a terrible language
- // like C# or Java or something ;)
-
- toBlochSphere: function( qubit ){
-
- `
- Based on this qubit’s state return the
- Polar angle θ (theta),
- azimuth angle ϕ (phi),
- Bloch vector,
- corrected surface coordinate.
-
- https://en.wikipedia.org/wiki/Bloch_sphere
- `
-
-
- // Polar angle θ (theta).
-
- const theta = Q.ComplexNumber.arcCosine( qubit.alpha ).multiply( 2 )
- if( isNaN( theta.real )) theta.real = 0
- if( isNaN( theta.imaginary )) theta.imaginary = 0
-
-
- // Azimuth angle ϕ (phi).
-
- const phi = Q.ComplexNumber.log(
-
- qubit.beta.divide( Q.ComplexNumber.sine( theta.divide( 2 )))
- )
- .divide( Q.ComplexNumber.I )
- if( isNaN( phi.real )) phi.real = 0
- if( isNaN( phi.imaginary )) phi.imaginary = 0
-
-
- // Bloch vector.
-
- const vector = {
-
- x: Q.ComplexNumber.sine( theta ).multiply( Q.ComplexNumber.cosine( phi )).real,
- y: Q.ComplexNumber.sine( theta ).multiply( Q.ComplexNumber.sine( phi )).real,
- z: Q.ComplexNumber.cosine( theta ).real
- }
-
-
- // Bloch vector’s axes are wonked.
- // Let’s “correct” them for use with Three.js, etc.
-
- const position = {
-
- x: vector.y,
- y: vector.z,
- z: vector.x
- }
-
- return {
-
-
- // Wow does this make tweening easier down the road.
-
- alphaReal: qubit.alpha.real,
- alphaImaginary: qubit.alpha.imaginary,
- betaReal: qubit.beta.real,
- betaImaginary: qubit.beta.imaginary,
-
-
- // Ummm... I’m only returnig the REAL portions. Please forgive me!
-
- theta: theta.real,
- phi: phi.real,
- vector, // Wonked YZX vector for maths because maths.
- position// Un-wonked XYZ for use by actual 3D engines.
- }
- },
- fromBlochVector: function( x, y, z ){
-
-
- //basically from a Pauli Rotation
- }
-
-})
-
-
-
-
-Q.Qubit.createConstants(
-
-
- // Opposing pairs:
- // |H⟩ and |V⟩
- // |D⟩ and |A⟩
- // |R⟩ and |L⟩
-
- 'HORIZONTAL', new Q.Qubit( 1, 0, 'H', 'Horizontal' ),// ZERO.
- 'VERTICAL', new Q.Qubit( 0, 1, 'V', 'Vertical' ),// ONE.
- 'DIAGONAL', new Q.Qubit( Math.SQRT1_2, Math.SQRT1_2, 'D', 'Diagonal' ),
- 'ANTI_DIAGONAL', new Q.Qubit( Math.SQRT1_2, -Math.SQRT1_2, 'A', 'Anti-diagonal' ),
- 'RIGHT_HAND_CIRCULAR_POLARIZED', new Q.Qubit( Math.SQRT1_2, new Q.ComplexNumber( 0, -Math.SQRT1_2 ), 'R', 'Right-hand Circular Polarized' ),// RHCP
- 'LEFT_HAND_CIRCULAR_POLARIZED', new Q.Qubit( Math.SQRT1_2, new Q.ComplexNumber( 0, Math.SQRT1_2 ), 'L', 'Left-hand Circular Polarized' ) // LHCP
-)
-
-
-
-
-Object.assign( Q.Qubit.prototype, {
-
- copy$: function( matrix ){
-
- if( Q.Matrix.isMatrixLike( matrix ) !== true )
- return Q.error( `Q.Qubit attempted to copy something that was not a matrix in this qubit #${qubit.index}.`, this )
-
- if( Q.Matrix.haveEqualDimensions( matrix, this ) !== true )
- return Q.error( `Q.Qubit cannot copy matrix#${matrix.index} of dimensions ${matrix.columns.length}x${matrix.rows.length} in to this qubit #${this.index} of dimensions ${this.columns.length}x${this.rows.length} because their dimensions do not match.`, this )
-
- const that = this
- matrix.rows.forEach( function( row, r ){
-
- row.forEach( function( n, c ){
-
- that.rows[ r ][ c ] = n
- })
- })
- this.dirac = matrix.dirac
- return this
- },
- clone: function(){
-
- return new Q.Qubit( this.alpha, this.beta )
- },
- isEqualTo: function( otherQubit ){
-
- return Q.Qubit.areEqual( this, otherQubit )// Returns a Boolean, breaks function chaining!
- },
- collapse: function(){
-
- return Q.Qubit.collapse( this )
- },
- applyGate: function( gate, ...args ){
-
- return Q.Qubit.applyGate( this, gate, ...args )
- },
- toText: function(){
-
- return Q.Qubit.toText( this )// Returns a String, breaks function chaining!
- },
- toStateVectorText: function(){
-
- return Q.Qubit.toStateVectorText( this )// Returns a String, breaks function chaining!
- },
- toStateVectorHtml: function(){
-
- return Q.Qubit.toStateVectorHtml( this )// Returns a String, breaks function chaining!
- },
- toBlochSphere: function(){
-
- return Q.Qubit.toBlochSphere( this )// Returns an Object, breaks function chaining!
- },
- collapse$: function(){
-
- return this.copy$( Q.Qubit.collapse( this ))
- },
- applyGate$: function( gate ){
-
- return this.copy$( Q.Qubit.applyGate( this, gate ))
- },
-})
-
-
-
diff --git a/Q/Q.css b/Q/Q.css
deleted file mode 100644
index 3f28f02..0000000
--- a/Q/Q.css
+++ /dev/null
@@ -1,475 +0,0 @@
-/*
-
- Copyright © 2019–2020, Stewart Smith. See LICENSE for details.
-
-*/
-@charset "utf-8";
-
-
-
-
-/*
-
- This file is in the process of being separated
- in to “essential global Q.js styles” which will
- remain here in Q.css, and “documentation-specific”
- styles which will be removed from here and placed
- within the /other/documentation.css file instead.
-
- The goal is for a developer to be able to place
- this Q.css file into their own web app with little
- to no interference with their app. All variables
- and styles here will ultimately be prefaced with
- a capital Q, eg. --Q-color-base, or .Q-text-input
- and so on.
-
-
-*/
-
-
-
-
-svg, :root {
-
-
-
- /**************/
- /* */
- /* Colors */
- /* */
- /**************/
-
-
- /* Base color (blue) */
-
- --Q-color-base-hue: 210;
- --Q-color-base-saturation: 85%;
- --Q-color-base-lightness: 40%;
-
-
- /* Red */
-
- --Q-color-red-hue: calc( var( --Q-color-base-hue ) + 180 - 30 );
- --Q-color-red-saturation: 85%;
- --Q-color-red-lightness: 45%;
- --Q-color-red: hsl(
-
- var( --Q-color-red-hue ),
- var( --Q-color-red-saturation ),
- var( --Q-color-red-lightness )
- );
-
-
- /* Orange */
-
- --Q-color-orange-hue: calc( var( --Q-color-base-hue ) + 180 - 15 );
- --Q-color-orange-saturation: 85%;
- --Q-color-orange-lightness: 50%;
- --Q-color-orange: hsl(
-
- var( --Q-color-orange-hue ),
- var( --Q-color-orange-saturation ),
- var( --Q-color-orange-lightness )
- );
-
-
- /* Yellow */
-
- --Q-color-yellow-hue: calc( var( --Q-color-base-hue ) + 180 + 15 );
- --Q-color-yellow-saturation: 90%;
- --Q-color-yellow-lightness: 50%;
- --Q-color-yellow: hsl(
-
- var( --Q-color-yellow-hue ),
- var( --Q-color-yellow-saturation ),
- var( --Q-color-yellow-lightness )
- );
-
-
- /* Green */
-
- --Q-color-green-hue: calc( var( --Q-color-base-hue ) + 180 + 60 );
- --Q-color-green-saturation: 80%;
- --Q-color-green-lightness: 35%;
- --Q-color-green: hsl(
-
- var( --Q-color-green-hue ),
- var( --Q-color-green-saturation ),
- var( --Q-color-green-lightness )
- );
-
-
- /* Blue */
-
- --Q-color-blue-hue: var( --Q-color-base-hue );
- --Q-color-blue-saturation: var( --Q-color-base-saturation );
- --Q-color-blue-lightness: var( --Q-color-base-lightness );
- --Q-color-blue: hsl(
-
- var( --Q-color-blue-hue ),
- var( --Q-color-blue-saturation ),
- var( --Q-color-blue-lightness )
- );
-
-
- /* Grayscale */
-
- --Q-color-white: #FFFFFF;
- --Q-color-chalk: #F9F9F9;
- --Q-color-newsprint: #F3F3F3;
- --Q-color-titanium: #CCCCCC;
- --Q-color-slate: #777777;
- --Q-color-charcoal: #333333;
- --Q-color-black: #000000;
-
-
- /* Background */
-
- --Q-color-background-hue: var( --Q-color-base-hue );
- --Q-color-background-saturation: 15%;
- --Q-color-background-lightness: 98%;
- --Q-color-background: hsl(
-
- var( --Q-color-background-hue ),
- var( --Q-color-background-saturation ),
- var( --Q-color-background-lightness )
- );
- /*--Q-color-background: white;*/
-
-
- /* Misc */
-
- --Q-text-color: hsl(
-
- var( --Q-color-base-hue ),
- 5%,
- 35%
- );
- --Q-text-code-comment-color: rgba( 0, 0, 0, 0.4 );
- --Q-text-code-output-color: rgba( 0, 0, 0, 1 );
-
- --Q-selection-color: var( --Q-color-black );
- --Q-selection-background-color: var( --Q-color-yellow );
-
- --Q-hyperlink-internal-color: var( --Q-color-blue );
- --Q-hyperlink-external-color: var( --Q-text-color );
-
- --Q-background-callout-color: var( --Q-color-white );
-
- --Q-svg-fill-color: var( --Q-text-color );
-
-
-
-
- /* Fonts */
-
- --Q-font-family-serif: 'Source Serif Pro', 'Roboto Slab', 'Georgia', serif;
- --Q-font-family-sans: 'SF Pro Text', system-ui, -apple-system, 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif;
- --Q-font-family-mono: 'Roboto Mono', 'Source Code Pro', 'Menlo', 'Courier New', monospace;
- --Q-font-family-symbols: 'Georgia', serif;
-}
-
-
-
-
- /*******************/
- /* */
- /* Interactive */
- /* */
-/*******************/
-
-
-.Q-input,
-.Q-circuit-text-input {
-
- margin: 1.5rem 0 0 0 !important;
- outline: none !important;
- border: none !important;
- border-radius: 1.2rem !important;
- box-shadow:
- 0.2rem 0.2rem 0.2rem rgba( 0, 0, 0, 0.15 ) inset,
- -0.2rem -0.2rem 0.2rem rgba( 255, 255, 255, 1 ) inset;
-
- background: linear-gradient(
-
- 0.375turn,
- rgba( 255, 255, 255, 1.0 ),
- rgba( 255, 255, 255, 0.2 )
- ) !important;
-}
-.Q-input,
-.Q-circuit-text-input {
-
- padding: 1.5rem !important;
- color: #555 !important;
- font-size: 0.9rem !important;
- line-height: 1.2rem !important;
-}
-
-
-
-.Q-circuit-text-input {
-
- /*min-width: 18rem;*/
- width: 100%;
- min-height: 8rem;
- /*margin: 1rem 0 2rem 0;*/
- margin: 1rem 0 0 0;
- border: 1px solid var( --Q-color-blue );
- border-radius: 0.5rem;
- background-color: var( --Q-color-chalk );
- padding: 1rem 0 0 2rem;
- color: var( --Q-color-blue );
- font-family: var( --Q-font-family-mono );
- font-size: 1.0rem;
- line-height: 1.2rem;
- white-space: pre;
- word-wrap: normal;/* OMFG, iOS you make me sad. */
-}
-
-
-
-
-
-
-.Q-button {
-
- position: relative;
- text-align: right;
- margin: 0.5rem 1rem 0 0;
- border-radius: 3rem;
- box-shadow:
- -0.1rem -0.1rem 0 rgba( 255, 255, 255, 1 ),
- 0.1rem 0.1rem 0.2rem rgba( 0, 0, 0, 0.3 );
- height: 3rem;
- background:
- var( --Q-color-blue )
- linear-gradient(
-
- 0.4turn,
- rgba( 255, 255, 255, 0.2 ),
- rgba( 0, 0, 0, 0.08 )
- );
- padding: 0.8rem 1.8rem;
- color: var( --Q-color-white );
- font-family: var( --Q-font-family-sans );
- font-size: 1rem;
- line-height: 1rem;
- font-weight: 500;
- letter-spacing: 0;
- text-shadow: -1px -1px 0 rgba( 0, 0, 0, 0.1 );
- cursor: pointer;
-}
-.Q-button:hover {
-
- background:
- hsl(
-
- var( --Q-color-blue-hue ),
- var( --Q-color-blue-saturation ),
- calc( var( --Q-color-blue-lightness ) * 1.2 )
- )
- linear-gradient(
-
- 0.4turn,
- rgba( 255, 255, 255, 0.2 ),
- rgba( 0, 0, 0, 0.08 )
- );
-}
-.Q-button:focus {
-
- margin-top: 0.7rem;
- margin-bottom: -0.2rem;
- margin-right: 0.9rem;
- outline: none;
- box-shadow:
- -0.1rem -0.1rem 0 rgba( 255, 255, 255, 1 ) inset,
- 0.1rem 0.1rem 0.2rem rgba( 0, 0, 0, 0.3 ) inset;
- background:
- var( --Q-color-blue )
- linear-gradient(
-
- 0.4turn,
- rgba( 0, 0, 0, 0.08 ),
- rgba( 255, 255, 255, 0.2 )
- );
-}
-.Q-button[disabled] {
-
- box-shadow:
- -0.1rem -0.1rem 0 rgba( 255, 255, 255, 1 ),
- 0.1rem 0.1rem 0.2rem rgba( 0, 0, 0, 0.3 );
- background:
- var( --Q-color-background )
- linear-gradient(
-
- 0.45turn,
- rgba( 255, 255, 255, 0.1 ),
- rgba( 0, 0, 0, 0.05 )
- );
- color: rgba( 0, 0, 0, 0.3 );
- text-shadow: 1px 1px 0 rgba( 255, 255, 255, 1 );
- cursor: default;
-}
-
-
-
-
-
-
-/*
-
- The below still need to be prefaced with “Q-”
- and for the HTML pages to be updated accordingly.
-
-*/
-
-
-
-
-
-
- /*************/
- /* */
- /* Maths */
- /* */
-/*************/
-
-
-.maths {
-
- max-width: 100%;
- overflow-x: auto;
- font-family: var( --Q-font-family-sans );
- /*letter-spacing: 0.03em;*/
- word-spacing: 0.2em;
-}
-dd .maths {
-
- margin-top: 0;
- margin-left: 0;
-}
-
-
-
-
-.symbol {
-
- font-size: 1.1em;
- padding: 0 0.1em;
- font-family: var( --Q-font-family-symbols );
- font-style: italic;
- font-weight: 900;
- letter-spacing: 0.05em;
-}
-
-
-
-
-.division {
-
- display: inline-block;
- vertical-align: middle;
- margin: 10px;
-}
-.division td {
-
- padding: 5px;
-}
-.dividend {
-
- border-bottom: 1px solid #CCC;
- text-align: center;
-}
-.divisor {
-
- text-align: center;
-}
-
-
-
-
-.matrix {
-
- display: inline-block;
- vertical-align: middle;
- position: relative;
- align: middle;
- margin: 1em 0.5em;
- padding: 1em;
- /*font-family: var( --Q-font-family-mono );*/
- font-weight: 300;
- line-height: 1em;
- /*text-align: right;*/
- text-align: center;
-}
-.matrix td {
-
- padding: 0.25em 0.5em;
-}
-.matrix-bracket-left, .matrix-bracket-right {
-
- position: absolute;
- top: 0;
- width: 0.5em;
- height: 100%;
- border: 1px solid #CCC;
-}
-.matrix-bracket-left {
-
- left: 0;
- border-right: none;
-}
-.matrix-bracket-right {
-
- right: 0;
- border-left: none;
-}
-/*.matrix.qubit tr:first-child td {
-
- color: #BBB;
-}*/
-
-
-
-.Q-state-vector,
-.complex-vector {
-
- font-family: var( --Q-font-family-mono );
-}
-.Q-state-vector.bra::before,
-.complex-vector.bra::before {
-
- content: '⟨';
- color: #BBB;
-}
-.Q-state-vector.bra::after,
-.complex-vector.bra::after {
-
- content: '|';
- color: #BBB;
-}
-.Q-state-vector.ket::before,
-.complex-vector.ket::before {
-
- content: '|';
- color: #BBB;
-}
-.Q-state-vector.ket::after,
-.complex-vector.ket::after {
-
- content: '⟩';
- color: #BBB;
-}
-.Q-state-vector.bra + .Q-state-vector.ket::before,
-.complex-vector.bra + .complex-vector.ket::before {
-
- content: '';
-}
-
-
-
-
-
-
-
diff --git a/Q/Q.js b/Q/Q.js
deleted file mode 100644
index 8ef8ae4..0000000
--- a/Q/Q.js
+++ /dev/null
@@ -1,623 +0,0 @@
-
-// Copyright © 2019–2020, Stewart Smith. See LICENSE for details.
-
-
-
-
-const Q = function(){
-
-
- // Did we send arguments of the form
- // ( bandwidth, timewidth )?
-
- if( arguments.length === 2 &&
- Array.from( arguments ).every( function( argument ){
-
- return Q.isUsefulInteger( argument )
-
- })){
-
- return new Q.Circuit( arguments[ 0 ], arguments[ 1 ])
- }
-
-
- // Otherwise assume we are creating a circuit
- // from a text block.
-
- return Q.Circuit.fromText( arguments[ 0 ])
-}
-
-
-
-
-Object.assign( Q, {
-
- verbosity: 0.5,
- log: function( verbosityThreshold, ...remainingArguments ){
-
- if( Q.verbosity >= verbosityThreshold ) console.log( ...remainingArguments )
- return '(log)'
- },
- warn: function(){
-
- console.warn( ...arguments )
- return '(warn)'
- },
- error: function(){
-
- console.error( ...arguments )
- return '(error)'
- },
- extractDocumentation: function( f ){
-
- `
- I wanted a way to document code
- that was cleaner, more legible, and more elegant
- than the bullshit we put up with today.
- Also wanted it to print nicely in the console.
- `
-
- f = f.toString()
-
- const
- begin = f.indexOf( '`' ) + 1,
- end = f.indexOf( '`', begin ),
- lines = f.substring( begin, end ).split( '\n' )
-
-
- function countPrefixTabs( text ){
-
-
- // Is counting tabs “manually”
- // actually more performant than regex?
-
- let count = index = 0
- while( text.charAt( index ++ ) === '\t' ) count ++
- return count
- }
-
-
- //------------------- TO DO!
- // we should check that there is ONLY whitespace between the function opening and the tick mark!
- // otherwise it’s not documentation.
-
- let
- tabs = Number.MAX_SAFE_INTEGER
-
- lines.forEach( function( line ){
-
- if( line ){
-
- const lineTabs = countPrefixTabs( line )
- if( tabs > lineTabs ) tabs = lineTabs
- }
- })
- lines.forEach( function( line, i ){
-
- if( line.trim() === '' ) line = '\n\n'
- lines[ i ] = line.substring( tabs ).replace( / {2}$/, '\n' )
- })
- return lines.join( '' )
- },
- help: function( f ){
-
- if( f === undefined ) f = Q
- return Q.extractDocumentation( f )
- },
- constants: {},
- createConstant: function( key, value ){
-
- //Object.freeze( value )
- this[ key ] = value
- // Object.defineProperty( this, key, {
-
- // value,
- // writable: false
- // })
- // Object.defineProperty( this.constants, key, {
-
- // value,
- // writable: false
- // })
- this.constants[ key ] = this[ key ]
- Object.freeze( this[ key ])
- },
- createConstants: function(){
-
- if( arguments.length % 2 !== 0 ){
-
- return Q.error( 'Q attempted to create constants with invalid (KEY, VALUE) pairs.' )
- }
- for( let i = 0; i < arguments.length; i += 2 ){
-
- this.createConstant( arguments[ i ], arguments[ i + 1 ])
- }
- },
-
-
-
-
- isUsefulNumber: function( n ){
-
- return isNaN( n ) === false &&
- ( typeof n === 'number' || n instanceof Number ) &&
- n !== Infinity &&
- n !== -Infinity
- },
- isUsefulInteger: function( n ){
-
- return Q.isUsefulNumber( n ) && Number.isInteger( n )
- },
- loop: function(){},
-
-
-
-
- hypotenuse: function( x, y ){
-
- let
- a = Math.abs( x ),
- b = Math.abs( y )
-
- if( a < 2048 && b < 2048 ){
-
- return Math.sqrt( a * a + b * b )
- }
- if( a < b ){
-
- a = b
- b = x / y
- }
- else b = y / x
- return a * Math.sqrt( 1 + b * b )
- },
- logHypotenuse: function( x, y ){
-
- const
- a = Math.abs( x ),
- b = Math.abs( y )
-
- if( x === 0 ) return Math.log( b )
- if( y === 0 ) return Math.log( a )
- if( a < 2048 && b < 2048 ){
-
- return Math.log( x * x + y * y ) / 2
- }
- return Math.log( x / Math.cos( Math.atan2( y, x )))
- },
- hyperbolicSine: function( n ){
-
- return ( Math.exp( n ) - Math.exp( -n )) / 2
- },
- hyperbolicCosine: function( n ){
-
- return ( Math.exp( n ) + Math.exp( -n )) / 2
- },
- round: function( n, d ){
-
- if( typeof d !== 'number' ) d = 0
- const f = Math.pow( 10, d )
- return Math.round( n * f ) / f
- },
- toTitleCase: function( text ){
-
- text = text.replace( /_/g, ' ' )
- return text.toLowerCase().split( ' ' ).map( function( word ){
-
- return word.replace( word[ 0 ], word[ 0 ].toUpperCase() )
-
- }).join(' ')
- },
- centerText: function( text, length, filler ){
-
- if( length > text.length ){
-
- if( typeof filler !== 'string' ) filler = ' '
-
- const
- padLengthLeft = Math.floor(( length - text.length ) / 2 ),
- padLengthRight = length - text.length - padLengthLeft
-
- return text
- .padStart( padLengthLeft + text.length, filler )
- .padEnd( length, filler )
- }
- else return text
- },
-
-
-
-
-
-
-
-
- namesIndex: 0,
- shuffledNames: [],
- shuffleNames$: function(){
-
- let m = []
- for( let c = 0; c < Q.COLORS.length; c ++ ){
-
- for( let a = 0; a < Q.ANIMALS.length; a ++ ){
-
- m.push([ c, a, Math.random() ])
- }
- }
- Q.shuffledNames = m.sort( function( a, b ){
-
- return a[ 2 ] - b[ 2 ]
- })
- },
- getRandomName$: function(){
-
- if( Q.shuffledNames.length === 0 ) Q.shuffleNames$()
-
- const
- pair = Q.shuffledNames[ Q.namesIndex ],
- name = Q.COLORS[ pair[ 0 ]] +' '+ Q.ANIMALS[ pair[ 1 ]]
-
- Q.namesIndex = ( Q.namesIndex + 1 ) % Q.shuffledNames.length
- return name
- },
- hueToColorName: function( hue ){
-
- hue = hue % 360
- hue = Math.floor( hue / 10 )
- return Q.COLORS[ hue ]
- },
- colorIndexToHue: function( i ){
-
- return i * 10
- }
-
-
-
-
-})
-
-
-
-
-Q.createConstants(
-
- 'REVISION', 19,
-
-
- // Yeah... F’ing floating point numbers, Man!
- // Here’s the issue:
- // var a = new Q.ComplexNumber( 1, 2 )
- // a.multiply(a).isEqualTo( a.power( new Q.ComplexNumber( 2, 0 )))
- // That’s only true if Q.EPSILON >= Number.EPSILON * 6
-
- 'EPSILON', Number.EPSILON * 6,
-
- 'RADIANS_TO_DEGREES', 180 / Math.PI,
-
- 'ANIMALS', [
-
- 'Aardvark',
- 'Albatross',
- 'Alligator',
- 'Alpaca',
- 'Ant',
- 'Anteater',
- 'Antelope',
- 'Ape',
- 'Armadillo',
- 'Baboon',
- 'Badger',
- 'Barracuda',
- 'Bat',
- 'Bear',
- 'Beaver',
- 'Bee',
- 'Bison',
- 'Boar',
- 'Buffalo',
- 'Butterfly',
- 'Camel',
- 'Caribou',
- 'Cat',
- 'Caterpillar',
- 'Cattle',
- 'Chamois',
- 'Cheetah',
- 'Chicken',
- 'Chimpanzee',
- 'Chinchilla',
- 'Chough',
- 'Clam',
- 'Cobra',
- 'Cod',
- 'Cormorant',
- 'Coyote',
- 'Crab',
- 'Crane',
- 'Crocodile',
- 'Crow',
- 'Curlew',
- 'Deer',
- 'Dinosaur',
- 'Dog',
- 'Dogfish',
- 'Dolphin',
- 'Donkey',
- 'Dotterel',
- 'Dove',
- 'Dragonfly',
- 'Duck',
- 'Dugong',
- 'Dunlin',
- 'Eagle',
- 'Echidna',
- 'Eel',
- 'Eland',
- 'Elephant',
- 'Elephant seal',
- 'Elk',
- 'Emu',
- 'Falcon',
- 'Ferret',
- 'Finch',
- 'Fish',
- 'Flamingo',
- 'Fly',
- 'Fox',
- 'Frog',
- 'Galago',
- 'Gaur',
- 'Gazelle',
- 'Gerbil',
- 'Giant Panda',
- 'Giraffe',
- 'Gnat',
- 'Gnu',
- 'Goat',
- 'Goose',
- 'Goldfinch',
- 'Goldfish',
- 'Gorilla',
- 'Goshawk',
- 'Grasshopper',
- 'Grouse',
- 'Guanaco',
- 'Guinea fowl',
- 'Guinea pig',
- 'Gull',
- 'Guppy',
- 'Hamster',
- 'Hare',
- 'Hawk',
- 'Hedgehog',
- 'Hen',
- 'Heron',
- 'Herring',
- 'Hippopotamus',
- 'Hornet',
- 'Horse',
- 'Human',
- 'Hummingbird',
- 'Hyena',
- 'Ide',
- 'Jackal',
- 'Jaguar',
- 'Jay',
- 'Jellyfish',
- 'Kangaroo',
- 'Koala',
- 'Koi',
- 'Komodo dragon',
- 'Kouprey',
- 'Kudu',
- 'Lapwing',
- 'Lark',
- 'Lemur',
- 'Leopard',
- 'Lion',
- 'Llama',
- 'Lobster',
- 'Locust',
- 'Loris',
- 'Louse',
- 'Lyrebird',
- 'Magpie',
- 'Mallard',
- 'Manatee',
- 'Marten',
- 'Meerkat',
- 'Mink',
- 'Mole',
- 'Monkey',
- 'Moose',
- 'Mouse',
- 'Mosquito',
- 'Mule',
- 'Narwhal',
- 'Newt',
- 'Nightingale',
- 'Octopus',
- 'Okapi',
- 'Opossum',
- 'Oryx',
- 'Ostrich',
- 'Otter',
- 'Owl',
- 'Ox',
- 'Oyster',
- 'Panther',
- 'Parrot',
- 'Partridge',
- 'Peafowl',
- 'Pelican',
- 'Penguin',
- 'Pheasant',
- 'Pig',
- 'Pigeon',
- 'Pony',
- 'Porcupine',
- 'Porpoise',
- 'Prairie Dog',
- 'Quail',
- 'Quelea',
- 'Rabbit',
- 'Raccoon',
- 'Rail',
- 'Ram',
- 'Raven',
- 'Reindeer',
- 'Rhinoceros',
- 'Rook',
- 'Ruff',
- 'Salamander',
- 'Salmon',
- 'Sand Dollar',
- 'Sandpiper',
- 'Sardine',
- 'Scorpion',
- 'Sea lion',
- 'Sea Urchin',
- 'Seahorse',
- 'Seal',
- 'Shark',
- 'Sheep',
- 'Shrew',
- 'Shrimp',
- 'Skunk',
- 'Snail',
- 'Snake',
- 'Sow',
- 'Spider',
- 'Squid',
- 'Squirrel',
- 'Starling',
- 'Stingray',
- 'Stinkbug',
- 'Stork',
- 'Swallow',
- 'Swan',
- 'Tapir',
- 'Tarsier',
- 'Termite',
- 'Tiger',
- 'Toad',
- 'Trout',
- 'Tui',
- 'Turkey',
- 'Turtle',
- // U
- 'Vicuña',
- 'Viper',
- 'Vulture',
- 'Wallaby',
- 'Walrus',
- 'Wasp',
- 'Water buffalo',
- 'Weasel',
- 'Whale',
- 'Wolf',
- 'Wolverine',
- 'Wombat',
- 'Woodcock',
- 'Woodpecker',
- 'Worm',
- 'Wren',
- // X
- 'Yak',
- 'Zebra'
-
- ],
- 'ANIMALS3', [
-
- 'ape',
- 'bee',
- 'cat',
- 'dog',
- 'elk',
- 'fox',
- 'gup',
- 'hen',
- 'ide',
- 'jay',
- 'koi',
- 'leo',
- 'moo',
- 'nit',
- 'owl',
- 'pig',
- // Q ?
- 'ram',
- 'sow',
- 'tui',
- // U ?
- // V ?
- // W ?
- // X ?
- 'yak',
- 'zeb'
- ],
- 'COLORS', [
-
- 'Red', // 0 RED
- 'Scarlet', // 10
- 'Tawny', // 20
- 'Carrot', // 30
- 'Pumpkin', // 40
- 'Mustard', // 50
- 'Lemon', // 60 Yellow
- 'Lime', // 70
- 'Spring bud', // 80
- 'Spring grass',// 90
- 'Pear', // 100
- 'Kelly', // 110
- 'Green', // 120 GREEN
- 'Malachite', // 130
- 'Sea green', // 140
- 'Sea foam', // 150
- 'Aquamarine', // 160
- 'Turquoise', // 170
- 'Cyan', // 180 Cyan
- 'Pacific blue',// 190
- 'Baby blue', // 200
- 'Ocean blue', // 210
- 'Sapphire', // 220
- 'Azure', // 230
- 'Blue', // 240 BLUE
- 'Cobalt', // 250
- 'Indigo', // 260
- 'Violet', // 270
- 'Lavender', // 280
- 'Purple', // 290
- 'Magenta', // 300 Magenta
- 'Hot pink', // 310
- 'Fuschia', // 320
- 'Ruby', // 330
- 'Crimson', // 340
- 'Carmine' // 350
- ]
-)
-
-
-
-
-console.log( `
-
-
- QQQQQQ
-QQ QQ
-QQ QQ
-QQ QQ
-QQ QQ QQ
-QQ QQ
- QQQQ ${Q.REVISION}
-
-
-
-https://quantumjavascript.app
-
-
-
-` )
-
-
-
diff --git a/Q/Q-Circuit-Editor.css b/build/q-old.css
similarity index 61%
rename from Q/Q-Circuit-Editor.css
rename to build/q-old.css
index af97203..e4a6620 100644
--- a/Q/Q-Circuit-Editor.css
+++ b/build/q-old.css
@@ -1,6 +1,6 @@
/*
- Copyright © 2019–2020, Stewart Smith. See LICENSE for details.
+ Copyright © 2019–2020, Stewart Smith. See LICENSE for details.
*/
@charset "utf-8";
@@ -8,6 +8,480 @@
+/*
+
+ This file is in the process of being separated
+ in to “essential global Q.js styles†which will
+ remain here in Q.css, and “documentation-specificâ€
+ styles which will be removed from here and placed
+ within the /other/documentation.css file instead.
+
+ The goal is for a developer to be able to place
+ this Q.css file into their own web app with little
+ to no interference with their app. All variables
+ and styles here will ultimately be prefaced with
+ a capital Q, eg. --Q-color-base, or .Q-text-input
+ and so on.
+
+
+*/
+
+
+
+
+svg, :root {
+
+
+
+ /**************/
+ /* */
+ /* Colors */
+ /* */
+ /**************/
+
+
+ /* Base color (blue) */
+
+ --Q-color-base-hue: 210;
+ --Q-color-base-saturation: 85%;
+ --Q-color-base-lightness: 40%;
+
+
+ /* Red */
+
+ --Q-color-red-hue: calc( var( --Q-color-base-hue ) + 180 - 30 );
+ --Q-color-red-saturation: 85%;
+ --Q-color-red-lightness: 45%;
+ --Q-color-red: hsl(
+
+ var( --Q-color-red-hue ),
+ var( --Q-color-red-saturation ),
+ var( --Q-color-red-lightness )
+ );
+
+
+ /* Orange */
+
+ --Q-color-orange-hue: calc( var( --Q-color-base-hue ) + 180 - 15 );
+ --Q-color-orange-saturation: 85%;
+ --Q-color-orange-lightness: 50%;
+ --Q-color-orange: hsl(
+
+ var( --Q-color-orange-hue ),
+ var( --Q-color-orange-saturation ),
+ var( --Q-color-orange-lightness )
+ );
+
+
+ /* Yellow */
+
+ --Q-color-yellow-hue: calc( var( --Q-color-base-hue ) + 180 + 15 );
+ --Q-color-yellow-saturation: 90%;
+ --Q-color-yellow-lightness: 50%;
+ --Q-color-yellow: hsl(
+
+ var( --Q-color-yellow-hue ),
+ var( --Q-color-yellow-saturation ),
+ var( --Q-color-yellow-lightness )
+ );
+
+
+ /* Green */
+
+ --Q-color-green-hue: calc( var( --Q-color-base-hue ) + 180 + 60 );
+ --Q-color-green-saturation: 80%;
+ --Q-color-green-lightness: 35%;
+ --Q-color-green: hsl(
+
+ var( --Q-color-green-hue ),
+ var( --Q-color-green-saturation ),
+ var( --Q-color-green-lightness )
+ );
+
+
+ /* Blue */
+
+ --Q-color-blue-hue: var( --Q-color-base-hue );
+ --Q-color-blue-saturation: var( --Q-color-base-saturation );
+ --Q-color-blue-lightness: var( --Q-color-base-lightness );
+ --Q-color-blue: hsl(
+
+ var( --Q-color-blue-hue ),
+ var( --Q-color-blue-saturation ),
+ var( --Q-color-blue-lightness )
+ );
+
+
+ /* Grayscale */
+
+ --Q-color-white: #FFFFFF;
+ --Q-color-chalk: #F9F9F9;
+ --Q-color-newsprint: #F3F3F3;
+ --Q-color-titanium: #CCCCCC;
+ --Q-color-slate: #777777;
+ --Q-color-charcoal: #333333;
+ --Q-color-black: #000000;
+
+
+ /* Background */
+
+ --Q-color-background-hue: var( --Q-color-base-hue );
+ --Q-color-background-saturation: 15%;
+ --Q-color-background-lightness: 98%;
+ --Q-color-background: hsl(
+
+ var( --Q-color-background-hue ),
+ var( --Q-color-background-saturation ),
+ var( --Q-color-background-lightness )
+ );
+ /*--Q-color-background: white;*/
+
+
+ /* Misc */
+
+ --Q-text-color: hsl(
+
+ var( --Q-color-base-hue ),
+ 5%,
+ 35%
+ );
+ --Q-text-code-comment-color: rgba( 0, 0, 0, 0.4 );
+ --Q-text-code-output-color: rgba( 0, 0, 0, 1 );
+
+ --Q-selection-color: var( --Q-color-black );
+ --Q-selection-background-color: var( --Q-color-yellow );
+
+ --Q-hyperlink-internal-color: var( --Q-color-blue );
+ --Q-hyperlink-external-color: var( --Q-text-color );
+
+ --Q-background-callout-color: var( --Q-color-white );
+
+ --Q-svg-fill-color: var( --Q-text-color );
+
+
+
+
+ /* Fonts */
+
+ --Q-font-family-serif: 'Source Serif Pro', 'Roboto Slab', 'Georgia', serif;
+ --Q-font-family-sans: 'SF Pro Text', system-ui, -apple-system, 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif;
+ --Q-font-family-mono: 'Roboto Mono', 'Source Code Pro', 'Menlo', 'Courier New', monospace;
+ --Q-font-family-symbols: 'Georgia', serif;
+}
+
+
+
+
+ /*******************/
+ /* */
+ /* Interactive */
+ /* */
+/*******************/
+
+
+.Q-input,
+.Q-circuit-text-input {
+
+ margin: 1.5rem 0 0 0 !important;
+ outline: none !important;
+ border: none !important;
+ border-radius: 1.2rem !important;
+ box-shadow:
+ 0.2rem 0.2rem 0.2rem rgba( 0, 0, 0, 0.15 ) inset,
+ -0.2rem -0.2rem 0.2rem rgba( 255, 255, 255, 1 ) inset;
+
+ background: linear-gradient(
+
+ 0.375turn,
+ rgba( 255, 255, 255, 1.0 ),
+ rgba( 255, 255, 255, 0.2 )
+ ) !important;
+}
+.Q-input,
+.Q-circuit-text-input {
+
+ padding: 1.5rem !important;
+ color: #555 !important;
+ font-size: 0.9rem !important;
+ line-height: 1.2rem !important;
+}
+
+
+
+.Q-circuit-text-input {
+
+ /*min-width: 18rem;*/
+ width: 100%;
+ min-height: 8rem;
+ /*margin: 1rem 0 2rem 0;*/
+ margin: 1rem 0 0 0;
+ border: 1px solid var( --Q-color-blue );
+ border-radius: 0.5rem;
+ background-color: var( --Q-color-chalk );
+ padding: 1rem 0 0 2rem;
+ color: var( --Q-color-blue );
+ font-family: var( --Q-font-family-mono );
+ font-size: 1.0rem;
+ line-height: 1.2rem;
+ white-space: pre;
+ word-wrap: normal;/* OMFG, iOS you make me sad. */
+}
+
+
+
+
+
+
+.Q-button {
+
+ position: relative;
+ text-align: right;
+ margin: 0.5rem 1rem 0 0;
+ border-radius: 3rem;
+ box-shadow:
+ -0.1rem -0.1rem 0 rgba( 255, 255, 255, 1 ),
+ 0.1rem 0.1rem 0.2rem rgba( 0, 0, 0, 0.3 );
+ height: 3rem;
+ background:
+ var( --Q-color-blue )
+ linear-gradient(
+
+ 0.4turn,
+ rgba( 255, 255, 255, 0.2 ),
+ rgba( 0, 0, 0, 0.08 )
+ );
+ padding: 0.8rem 1.8rem;
+ color: var( --Q-color-white );
+ font-family: var( --Q-font-family-sans );
+ font-size: 1rem;
+ line-height: 1rem;
+ font-weight: 500;
+ letter-spacing: 0;
+ text-shadow: -1px -1px 0 rgba( 0, 0, 0, 0.1 );
+ cursor: pointer;
+}
+.Q-button:hover {
+
+ background:
+ hsl(
+
+ var( --Q-color-blue-hue ),
+ var( --Q-color-blue-saturation ),
+ calc( var( --Q-color-blue-lightness ) * 1.2 )
+ )
+ linear-gradient(
+
+ 0.4turn,
+ rgba( 255, 255, 255, 0.2 ),
+ rgba( 0, 0, 0, 0.08 )
+ );
+}
+.Q-button:focus {
+
+ margin-top: 0.7rem;
+ margin-bottom: -0.2rem;
+ margin-right: 0.9rem;
+ outline: none;
+ box-shadow:
+ -0.1rem -0.1rem 0 rgba( 255, 255, 255, 1 ) inset,
+ 0.1rem 0.1rem 0.2rem rgba( 0, 0, 0, 0.3 ) inset;
+ background:
+ var( --Q-color-blue )
+ linear-gradient(
+
+ 0.4turn,
+ rgba( 0, 0, 0, 0.08 ),
+ rgba( 255, 255, 255, 0.2 )
+ );
+}
+.Q-button[disabled] {
+
+ box-shadow:
+ -0.1rem -0.1rem 0 rgba( 255, 255, 255, 1 ),
+ 0.1rem 0.1rem 0.2rem rgba( 0, 0, 0, 0.3 );
+ background:
+ var( --Q-color-background )
+ linear-gradient(
+
+ 0.45turn,
+ rgba( 255, 255, 255, 0.1 ),
+ rgba( 0, 0, 0, 0.05 )
+ );
+ color: rgba( 0, 0, 0, 0.3 );
+ text-shadow: 1px 1px 0 rgba( 255, 255, 255, 1 );
+ cursor: default;
+}
+
+
+
+
+
+
+/*
+
+ The below still need to be prefaced with “Q-â€
+ and for the HTML pages to be updated accordingly.
+
+*/
+
+
+
+
+
+
+ /*************/
+ /* */
+ /* Maths */
+ /* */
+/*************/
+
+
+.maths {
+
+ max-width: 100%;
+ overflow-x: auto;
+ font-family: var( --Q-font-family-sans );
+ /*letter-spacing: 0.03em;*/
+ word-spacing: 0.2em;
+}
+dd .maths {
+
+ margin-top: 0;
+ margin-left: 0;
+}
+
+
+
+
+.symbol {
+
+ font-size: 1.1em;
+ padding: 0 0.1em;
+ font-family: var( --Q-font-family-symbols );
+ font-style: italic;
+ font-weight: 900;
+ letter-spacing: 0.05em;
+}
+
+
+
+
+.division {
+
+ display: inline-block;
+ vertical-align: middle;
+ margin: 10px;
+}
+.division td {
+
+ padding: 5px;
+}
+.dividend {
+
+ border-bottom: 1px solid #CCC;
+ text-align: center;
+}
+.divisor {
+
+ text-align: center;
+}
+
+
+
+
+.matrix {
+
+ display: inline-block;
+ vertical-align: middle;
+ position: relative;
+ align: middle;
+ margin: 1em 0.5em;
+ padding: 1em;
+ /*font-family: var( --Q-font-family-mono );*/
+ font-weight: 300;
+ line-height: 1em;
+ /*text-align: right;*/
+ text-align: center;
+}
+.matrix td {
+
+ padding: 0.25em 0.5em;
+}
+.matrix-bracket-left, .matrix-bracket-right {
+
+ position: absolute;
+ top: 0;
+ width: 0.5em;
+ height: 100%;
+ border: 1px solid #CCC;
+}
+.matrix-bracket-left {
+
+ left: 0;
+ border-right: none;
+}
+.matrix-bracket-right {
+
+ right: 0;
+ border-left: none;
+}
+/*.matrix.qubit tr:first-child td {
+
+ color: #BBB;
+}*/
+
+
+
+.Q-state-vector,
+.complex-vector {
+
+ font-family: var( --Q-font-family-mono );
+}
+.Q-state-vector.bra::before,
+.complex-vector.bra::before {
+
+ content: '⟨';
+ color: #BBB;
+}
+.Q-state-vector.bra::after,
+.complex-vector.bra::after {
+
+ content: '|';
+ color: #BBB;
+}
+.Q-state-vector.ket::before,
+.complex-vector.ket::before {
+
+ content: '|';
+ color: #BBB;
+}
+.Q-state-vector.ket::after,
+.complex-vector.ket::after {
+
+ content: '⟩';
+ color: #BBB;
+}
+.Q-state-vector.bra + .Q-state-vector.ket::before,
+.complex-vector.bra + .complex-vector.ket::before {
+
+ content: '';
+}
+
+
+
+
+
+
+
+/*
+
+ Copyright © 2019–2020, Stewart Smith. See LICENSE for details.
+
+*/
+@charset "utf-8";
+
+
+
@@ -95,11 +569,12 @@
+
.Q-circuit,
.Q-circuit-palette {
position: relative;
- width: 100%;
+ width: 50%;
}
.Q-circuit-palette {
@@ -123,6 +598,7 @@
margin: 1rem 0 2rem 0;
/*border-top: 2px solid hsl( 0, 0%, 50% );*/
}
+.Q-parameters-box,
.Q-circuit-board-foreground {
line-height: 3.85rem;
@@ -275,6 +751,19 @@
grid-auto-columns: 4rem;
grid-auto-flow: column;
}
+
+.Q-parameters-box {
+
+ position: absolute;
+ display: none;
+ z-index: 100;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background-color: whitesmoke;
+}
+
/*.Q-circuit-palette,*/
.Q-circuit-board-foreground,
.Q-circuit-board-background {
@@ -364,8 +853,17 @@
);
}
+.Q-parameter-box-exit {
+ position: relative;
+ right: 0;
+ left: 0;
+ width: 5rem;
+ height: 2.5rem;
+ background-color: whitesmoke;
+ border-radius: 0;
+}
-
+.Q-parameters-box > div,
.Q-circuit-palette > div,
.Q-circuit-clipboard > div,
.Q-circuit-board-foreground > div {
@@ -607,7 +1105,7 @@
rgba( 0, 0, 0, 0.05 )
)*/;
}
-.Q-circuit-palette .Q-circuit-operation:hover {
+.Q-parameter-box-exit .Q-circuit-palette .Q-circuit-operation:hover {
/*background-color: rgba( 255, 255, 255, 0.6 );*/
background-color: white;
@@ -720,6 +1218,25 @@
}
+.Q-parameter-box-input-container {
+ position: relative;
+ text-align: center;
+ grid-auto-columns: 4rem;
+ grid-auto-flow: column;
+}
+
+.Q-parameter-box-input {
+ position: relative;
+ border-radius: .2rem;
+ margin-left: 10px;
+ font-family: var( --Q-font-family-mono );
+}
+
+.Q-parameter-input-label {
+ position: relative;
+ color: var( --Q-color-blue );
+ font-family: var( --Q-font-family-mono );
+}
@@ -857,4 +1374,3 @@
}
-
diff --git a/build/q-old.js b/build/q-old.js
new file mode 100644
index 0000000..ddcf90e
--- /dev/null
+++ b/build/q-old.js
@@ -0,0 +1,7874 @@
+
+// Copyright © 2019–2020, Stewart Smith. See LICENSE for details.
+const Q = function(){
+
+
+ // Did we send arguments of the form
+ // ( bandwidth, timewidth )?
+ if( arguments.length === 2 &&
+ Array.from( arguments ).every( function( argument ){
+
+ return Q.isUsefulInteger( argument )
+
+ })){
+
+ return new Q.Circuit( arguments[ 0 ], arguments[ 1 ])
+ }
+
+
+ // Otherwise assume we are creating a circuit
+ // from a text block.
+
+ return Q.Circuit.fromText( arguments[ 0 ])
+}
+
+
+
+
+Object.assign( Q, {
+
+ verbosity: 0.5,
+ log: function( verbosityThreshold, ...remainingArguments ){
+
+ if( Q.verbosity >= verbosityThreshold ) console.log( ...remainingArguments )
+ return '(log)'
+ },
+ warn: function(){
+
+ console.warn( ...arguments )
+ return '(warn)'
+ },
+ error: function(){
+
+ console.error( ...arguments )
+ return '(error)'
+ },
+ extractDocumentation: function( f ){
+
+ `
+ I wanted a way to document code
+ that was cleaner, more legible, and more elegant
+ than the bullshit we put up with today.
+ Also wanted it to print nicely in the console.
+ `
+
+ f = f.toString()
+
+ const
+ begin = f.indexOf( '`' ) + 1,
+ end = f.indexOf( '`', begin ),
+ lines = f.substring( begin, end ).split( '\n' )
+
+
+ function countPrefixTabs( text ){
+
+
+ // Is counting tabs “manuallyâ€
+ // actually more performant than regex?
+
+ let count = index = 0
+ while( text.charAt( index ++ ) === '\t' ) count ++
+ return count
+ }
+
+
+ //------------------- TO DO!
+ // we should check that there is ONLY whitespace between the function opening and the tick mark!
+ // otherwise it’s not documentation.
+
+ let
+ tabs = Number.MAX_SAFE_INTEGER
+
+ lines.forEach( function( line ){
+
+ if( line ){
+
+ const lineTabs = countPrefixTabs( line )
+ if( tabs > lineTabs ) tabs = lineTabs
+ }
+ })
+ lines.forEach( function( line, i ){
+
+ if( line.trim() === '' ) line = '\n\n'
+ lines[ i ] = line.substring( tabs ).replace( / {2}$/, '\n' )
+ })
+ return lines.join( '' )
+ },
+ help: function( f ){
+
+ if( f === undefined ) f = Q
+ return Q.extractDocumentation( f )
+ },
+ constants: {},
+ createConstant: function( key, value ){
+
+ //Object.freeze( value )
+ this[ key ] = value
+ // Object.defineProperty( this, key, {
+
+ // value,
+ // writable: false
+ // })
+ // Object.defineProperty( this.constants, key, {
+
+ // value,
+ // writable: false
+ // })
+ this.constants[ key ] = this[ key ]
+ Object.freeze( this[ key ])
+ },
+ createConstants: function(){
+
+ if( arguments.length % 2 !== 0 ){
+
+ return Q.error( 'Q attempted to create constants with invalid (KEY, VALUE) pairs.' )
+ }
+ for( let i = 0; i < arguments.length; i += 2 ){
+
+ this.createConstant( arguments[ i ], arguments[ i + 1 ])
+ }
+ },
+
+
+
+
+ isUsefulNumber: function( n ){
+
+ return isNaN( n ) === false &&
+ ( typeof n === 'number' || n instanceof Number ) &&
+ n !== Infinity &&
+ n !== -Infinity
+ },
+ isUsefulInteger: function( n ){
+
+ return Q.isUsefulNumber( n ) && Number.isInteger( n )
+ },
+ loop: function(){},
+
+
+
+
+ hypotenuse: function( x, y ){
+
+ let
+ a = Math.abs( x ),
+ b = Math.abs( y )
+
+ if( a < 2048 && b < 2048 ){
+
+ return Math.sqrt( a * a + b * b )
+ }
+ if( a < b ){
+
+ a = b
+ b = x / y
+ }
+ else b = y / x
+ return a * Math.sqrt( 1 + b * b )
+ },
+ logHypotenuse: function( x, y ){
+
+ const
+ a = Math.abs( x ),
+ b = Math.abs( y )
+
+ if( x === 0 ) return Math.log( b )
+ if( y === 0 ) return Math.log( a )
+ if( a < 2048 && b < 2048 ){
+
+ return Math.log( x * x + y * y ) / 2
+ }
+ return Math.log( x / Math.cos( Math.atan2( y, x )))
+ },
+ hyperbolicSine: function( n ){
+
+ return ( Math.exp( n ) - Math.exp( -n )) / 2
+ },
+ hyperbolicCosine: function( n ){
+
+ return ( Math.exp( n ) + Math.exp( -n )) / 2
+ },
+ round: function( n, d ){
+
+ if( typeof d !== 'number' ) d = 0
+ const f = Math.pow( 10, d )
+ return Math.round( n * f ) / f
+ },
+ toTitleCase: function( text ){
+
+ text = text.replace( /_/g, ' ' )
+ return text.toLowerCase().split( ' ' ).map( function( word ){
+
+ return word.replace( word[ 0 ], word[ 0 ].toUpperCase() )
+
+ }).join(' ')
+ },
+ centerText: function( text, length, filler ){
+
+ if( length > text.length ){
+
+ if( typeof filler !== 'string' ) filler = ' '
+
+ const
+ padLengthLeft = Math.floor(( length - text.length ) / 2 ),
+ padLengthRight = length - text.length - padLengthLeft
+
+ return text
+ .padStart( padLengthLeft + text.length, filler )
+ .padEnd( length, filler )
+ }
+ else return text
+ },
+
+
+
+
+
+
+
+
+ namesIndex: 0,
+ shuffledNames: [],
+ shuffleNames$: function(){
+
+ let m = []
+ for( let c = 0; c < Q.COLORS.length; c ++ ){
+
+ for( let a = 0; a < Q.ANIMALS.length; a ++ ){
+
+ m.push([ c, a, Math.random() ])
+ }
+ }
+ Q.shuffledNames = m.sort( function( a, b ){
+
+ return a[ 2 ] - b[ 2 ]
+ })
+ },
+ getRandomName$: function(){
+
+ if( Q.shuffledNames.length === 0 ) Q.shuffleNames$()
+
+ const
+ pair = Q.shuffledNames[ Q.namesIndex ],
+ name = Q.COLORS[ pair[ 0 ]] +' '+ Q.ANIMALS[ pair[ 1 ]]
+
+ Q.namesIndex = ( Q.namesIndex + 1 ) % Q.shuffledNames.length
+ return name
+ },
+ hueToColorName: function( hue ){
+
+ hue = hue % 360
+ hue = Math.floor( hue / 10 )
+ return Q.COLORS[ hue ]
+ },
+ colorIndexToHue: function( i ){
+
+ return i * 10
+ }
+
+
+
+
+})
+
+
+
+
+Q.createConstants(
+
+ 'REVISION', 19,
+
+
+ // Yeah... F’ing floating point numbers, Man!
+ // Here’s the issue:
+ // var a = new Q.ComplexNumber( 1, 2 )
+ // a.multiply(a).isEqualTo( a.power( new Q.ComplexNumber( 2, 0 )))
+ // That’s only true if Q.EPSILON >= Number.EPSILON * 6
+
+ 'EPSILON', Number.EPSILON * 6,
+
+ 'RADIANS_TO_DEGREES', 180 / Math.PI,
+
+ 'ANIMALS', [
+
+ 'Aardvark',
+ 'Albatross',
+ 'Alligator',
+ 'Alpaca',
+ 'Ant',
+ 'Anteater',
+ 'Antelope',
+ 'Ape',
+ 'Armadillo',
+ 'Baboon',
+ 'Badger',
+ 'Barracuda',
+ 'Bat',
+ 'Bear',
+ 'Beaver',
+ 'Bee',
+ 'Bison',
+ 'Boar',
+ 'Buffalo',
+ 'Butterfly',
+ 'Camel',
+ 'Caribou',
+ 'Cat',
+ 'Caterpillar',
+ 'Cattle',
+ 'Chamois',
+ 'Cheetah',
+ 'Chicken',
+ 'Chimpanzee',
+ 'Chinchilla',
+ 'Chough',
+ 'Clam',
+ 'Cobra',
+ 'Cod',
+ 'Cormorant',
+ 'Coyote',
+ 'Crab',
+ 'Crane',
+ 'Crocodile',
+ 'Crow',
+ 'Curlew',
+ 'Deer',
+ 'Dinosaur',
+ 'Dog',
+ 'Dogfish',
+ 'Dolphin',
+ 'Donkey',
+ 'Dotterel',
+ 'Dove',
+ 'Dragonfly',
+ 'Duck',
+ 'Dugong',
+ 'Dunlin',
+ 'Eagle',
+ 'Echidna',
+ 'Eel',
+ 'Eland',
+ 'Elephant',
+ 'Elephant seal',
+ 'Elk',
+ 'Emu',
+ 'Falcon',
+ 'Ferret',
+ 'Finch',
+ 'Fish',
+ 'Flamingo',
+ 'Fly',
+ 'Fox',
+ 'Frog',
+ 'Galago',
+ 'Gaur',
+ 'Gazelle',
+ 'Gerbil',
+ 'Giant Panda',
+ 'Giraffe',
+ 'Gnat',
+ 'Gnu',
+ 'Goat',
+ 'Goose',
+ 'Goldfinch',
+ 'Goldfish',
+ 'Gorilla',
+ 'Goshawk',
+ 'Grasshopper',
+ 'Grouse',
+ 'Guanaco',
+ 'Guinea fowl',
+ 'Guinea pig',
+ 'Gull',
+ 'Guppy',
+ 'Hamster',
+ 'Hare',
+ 'Hawk',
+ 'Hedgehog',
+ 'Hen',
+ 'Heron',
+ 'Herring',
+ 'Hippopotamus',
+ 'Hornet',
+ 'Horse',
+ 'Human',
+ 'Hummingbird',
+ 'Hyena',
+ 'Ide',
+ 'Jackal',
+ 'Jaguar',
+ 'Jay',
+ 'Jellyfish',
+ 'Kangaroo',
+ 'Koala',
+ 'Koi',
+ 'Komodo dragon',
+ 'Kouprey',
+ 'Kudu',
+ 'Lapwing',
+ 'Lark',
+ 'Lemur',
+ 'Leopard',
+ 'Lion',
+ 'Llama',
+ 'Lobster',
+ 'Locust',
+ 'Loris',
+ 'Louse',
+ 'Lyrebird',
+ 'Magpie',
+ 'Mallard',
+ 'Manatee',
+ 'Marten',
+ 'Meerkat',
+ 'Mink',
+ 'Mole',
+ 'Monkey',
+ 'Moose',
+ 'Mouse',
+ 'Mosquito',
+ 'Mule',
+ 'Narwhal',
+ 'Newt',
+ 'Nightingale',
+ 'Octopus',
+ 'Okapi',
+ 'Opossum',
+ 'Oryx',
+ 'Ostrich',
+ 'Otter',
+ 'Owl',
+ 'Ox',
+ 'Oyster',
+ 'Panther',
+ 'Parrot',
+ 'Partridge',
+ 'Peafowl',
+ 'Pelican',
+ 'Penguin',
+ 'Pheasant',
+ 'Pig',
+ 'Pigeon',
+ 'Pony',
+ 'Porcupine',
+ 'Porpoise',
+ 'Prairie Dog',
+ 'Quail',
+ 'Quelea',
+ 'Rabbit',
+ 'Raccoon',
+ 'Rail',
+ 'Ram',
+ 'Raven',
+ 'Reindeer',
+ 'Rhinoceros',
+ 'Rook',
+ 'Ruff',
+ 'Salamander',
+ 'Salmon',
+ 'Sand Dollar',
+ 'Sandpiper',
+ 'Sardine',
+ 'Scorpion',
+ 'Sea lion',
+ 'Sea Urchin',
+ 'Seahorse',
+ 'Seal',
+ 'Shark',
+ 'Sheep',
+ 'Shrew',
+ 'Shrimp',
+ 'Skunk',
+ 'Snail',
+ 'Snake',
+ 'Sow',
+ 'Spider',
+ 'Squid',
+ 'Squirrel',
+ 'Starling',
+ 'Stingray',
+ 'Stinkbug',
+ 'Stork',
+ 'Swallow',
+ 'Swan',
+ 'Tapir',
+ 'Tarsier',
+ 'Termite',
+ 'Tiger',
+ 'Toad',
+ 'Trout',
+ 'Tui',
+ 'Turkey',
+ 'Turtle',
+ // U
+ 'Vicuña',
+ 'Viper',
+ 'Vulture',
+ 'Wallaby',
+ 'Walrus',
+ 'Wasp',
+ 'Water buffalo',
+ 'Weasel',
+ 'Whale',
+ 'Wolf',
+ 'Wolverine',
+ 'Wombat',
+ 'Woodcock',
+ 'Woodpecker',
+ 'Worm',
+ 'Wren',
+ // X
+ 'Yak',
+ 'Zebra'
+
+ ],
+ 'ANIMALS3', [
+
+ 'ape',
+ 'bee',
+ 'cat',
+ 'dog',
+ 'elk',
+ 'fox',
+ 'gup',
+ 'hen',
+ 'ide',
+ 'jay',
+ 'koi',
+ 'leo',
+ 'moo',
+ 'nit',
+ 'owl',
+ 'pig',
+ // Q ?
+ 'ram',
+ 'sow',
+ 'tui',
+ // U ?
+ // V ?
+ // W ?
+ // X ?
+ 'yak',
+ 'zeb'
+ ],
+ 'COLORS', [
+
+ 'Red', // 0 RED
+ 'Scarlet', // 10
+ 'Tawny', // 20
+ 'Carrot', // 30
+ 'Pumpkin', // 40
+ 'Mustard', // 50
+ 'Lemon', // 60 Yellow
+ 'Lime', // 70
+ 'Spring bud', // 80
+ 'Spring grass',// 90
+ 'Pear', // 100
+ 'Kelly', // 110
+ 'Green', // 120 GREEN
+ 'Malachite', // 130
+ 'Sea green', // 140
+ 'Sea foam', // 150
+ 'Aquamarine', // 160
+ 'Turquoise', // 170
+ 'Cyan', // 180 Cyan
+ 'Pacific blue',// 190
+ 'Baby blue', // 200
+ 'Ocean blue', // 210
+ 'Sapphire', // 220
+ 'Azure', // 230
+ 'Blue', // 240 BLUE
+ 'Cobalt', // 250
+ 'Indigo', // 260
+ 'Violet', // 270
+ 'Lavender', // 280
+ 'Purple', // 290
+ 'Magenta', // 300 Magenta
+ 'Hot pink', // 310
+ 'Fuschia', // 320
+ 'Ruby', // 330
+ 'Crimson', // 340
+ 'Carmine' // 350
+ ]
+)
+
+
+
+
+console.log( `
+
+
+ QQQQQQ
+QQ QQ
+QQ QQ
+QQ QQ
+QQ QQ QQ
+QQ QQ
+ QQQQ ${Q.REVISION}
+
+
+
+https://quantumjavascript.app
+
+
+
+` )
+
+
+
+
+// Copyright © 2019–2020, Stewart Smith. See LICENSE for details.
+
+
+
+
+Q.ComplexNumber = function( real, imaginary ){
+
+ `
+ The set of “real numbers†(â„) contains any number that can be expressed
+ along an infinite timeline. https://en.wikipedia.org/wiki/Real_number
+
+ … -3 -2 -1 0 +1 +2 +3 …
+ ┄───┴───┴───┴───┴───┴─┬─┴──┬┴┬──┄
+ √2 𒆠π
+
+
+ Meanwhile, “imaginary numbers†(ð•€) consist of a real (â„) multiplier and
+ the symbol ð’Š, which is the impossible solution to the equation ð’™Â² = −1.
+ Note that no number when multiplied by itself can ever result in a
+ negative product, but the concept of ð’Š gives us a way to reason around
+ this imaginary scenario nonetheless.
+ https://en.wikipedia.org/wiki/Imaginary_number
+
+ … -3𒊠-2𒊠-1𒊠0𒊠+1𒊠+2𒊠+3𒊠…
+ ┄───┴───┴───┴───┴───┴───┴───┴───┄
+
+
+ A “complex number“ (ℂ) is a number that can be expressed in the form
+ ð’‚ + ð’ƒð’Š, where ð’‚ is the real component (â„) and ð’ƒð’Š is the imaginary
+ component (ð•€). https://en.wikipedia.org/wiki/Complex_number
+
+
+ Operation functions on Q.ComplexNumber instances generally accept as
+ arguments both sibling instances and pure Number instances, though the
+ value returned is always an instance of Q.ComplexNumber.
+
+ `
+
+ if( real instanceof Q.ComplexNumber ){
+
+ imaginary = real.imaginary
+ real = real.real
+ Q.warn( 'Q.ComplexNumber tried to create a new instance with an argument that is already a Q.ComplexNumber — and that’s weird!' )
+ }
+ else if( real === undefined ) real = 0
+ if( imaginary === undefined ) imaginary = 0
+ if(( Q.ComplexNumber.isNumberLike( real ) !== true && isNaN( real ) !== true ) ||
+ ( Q.ComplexNumber.isNumberLike( imaginary ) !== true && isNaN( imaginary ) !== true ))
+ return Q.error( 'Q.ComplexNumber attempted to create a new instance but the arguments provided were not actual numbers.' )
+
+ this.real = real
+ this.imaginary = imaginary
+ this.index = Q.ComplexNumber.index ++
+}
+
+
+
+
+Object.assign( Q.ComplexNumber, {
+
+ index: 0,
+ help: function(){ return Q.help( this )},
+ constants: {},
+ createConstant: Q.createConstant,
+ createConstants: Q.createConstants,
+
+
+
+
+ toText: function( rNumber, iNumber, roundToDecimal, padPositive ){
+
+ // Should we round these numbers?
+ // Our default is yes: to 3 digits.
+ // Otherwise round to specified decimal.
+
+ if( typeof roundToDecimal !== 'number' ) roundToDecimal = 3
+ const factor = Math.pow( 10, roundToDecimal )
+ rNumber = Math.round( rNumber * factor ) / factor
+ iNumber = Math.round( iNumber * factor ) / factor
+
+
+ // Convert padPositive
+ // from a potential Boolean
+ // to a String.
+ // If we don’t receive a FALSE
+ // then we’ll pad the positive numbers.
+
+ padPositive = padPositive === false ? '' : ' '
+
+
+ // We need the absolute values of each.
+
+ let
+ rAbsolute = Math.abs( rNumber ),
+ iAbsolute = Math.abs( iNumber )
+
+
+ // And an absolute value string.
+
+ let
+ rText = rAbsolute.toString(),
+ iText = iAbsolute.toString()
+
+
+ // Is this an IMAGINARY-ONLY number?
+ // Don’t worry: -0 === 0.
+
+ if( rNumber === 0 ){
+
+ if( iNumber === Infinity ) return padPositive +'∞i'
+ if( iNumber === -Infinity ) return '-∞i'
+ if( iNumber === 0 ) return padPositive +'0'
+ if( iNumber === -1 ) return '-i'
+ if( iNumber === 1 ) return padPositive +'i'
+ if( iNumber >= 0 ) return padPositive + iText +'i'
+ if( iNumber < 0 ) return '-'+ iText +'i'
+ return iText +'i'// NaN
+ }
+
+
+ // This number contains a real component
+ // and may also contain an imaginary one as well.
+
+ if( rNumber === Infinity ) rText = padPositive +'∞'
+ else if( rNumber === -Infinity ) rText = '-∞'
+ else if( rNumber >= 0 ) rText = padPositive + rText
+ else if( rNumber < 0 ) rText = '-'+ rText
+
+ if( iNumber === Infinity ) return rText +' + ∞i'
+ if( iNumber === -Infinity ) return rText +' - ∞i'
+ if( iNumber === 0 ) return rText
+ if( iNumber === -1 ) return rText +' - i'
+ if( iNumber === 1 ) return rText +' + i'
+ if( iNumber > 0 ) return rText +' + '+ iText +'i'
+ if( iNumber < 0 ) return rText +' - '+ iText +'i'
+ return rText +' + '+ iText +'i'// NaN
+ },
+
+
+
+
+ isNumberLike: function( n ){
+
+ return isNaN( n ) === false && ( typeof n === 'number' || n instanceof Number )
+ },
+ isNaN: function( n ){
+
+ return isNaN( n.real ) || isNaN( n.imaginary )
+ },
+ isZero: function( n ){
+
+ return ( n.real === 0 || n.real === -0 ) &&
+ ( n.imaginary === 0 || n.imaginary === -0 )
+ },
+ isFinite: function( n ){
+
+ return isFinite( n.real ) && isFinite( n.imaginary )
+ },
+ isInfinite: function( n ){
+
+ return !( this.isNaN( n ) || this.isFinite( n ))
+ },
+ areEqual: function( a, b ){
+
+ return Q.ComplexNumber.operate(
+
+ 'areEqual', a, b,
+ function( a, b ){
+
+ return Math.abs( a - b ) < Q.EPSILON
+ },
+ function( a, b ){
+
+ return (
+
+ Math.abs( a - b.real ) < Q.EPSILON &&
+ Math.abs( b.imaginary ) < Q.EPSILON
+ )
+ },
+ function( a, b ){
+
+ return (
+
+ Math.abs( a.real - b ) < Q.EPSILON &&
+ Math.abs( a.imaginary ) < Q.EPSILON
+ )
+ },
+ function( a, b ){
+
+ return (
+
+ Math.abs( a.real - b.real ) < Q.EPSILON &&
+ Math.abs( a.imaginary - b.imaginary ) < Q.EPSILON
+ )
+ }
+ )
+ },
+
+
+
+
+ absolute: function( n ){
+
+ return Q.hypotenuse( n.real, n.imaginary )
+ },
+ conjugate: function( n ){
+
+ return new Q.ComplexNumber( n.real, n.imaginary * -1 )
+ },
+ operate: function(
+
+ name,
+ a,
+ b,
+ numberAndNumber,
+ numberAndComplex,
+ complexAndNumber,
+ complexAndComplex ){
+
+ if( Q.ComplexNumber.isNumberLike( a )){
+
+ if( Q.ComplexNumber.isNumberLike( b )) return numberAndNumber( a, b )
+ else if( b instanceof Q.ComplexNumber ) return numberAndComplex( a, b )
+ else return Q.error( 'Q.ComplexNumber attempted to', name, 'with the number', a, 'and something that is neither a Number or Q.ComplexNumber:', b )
+ }
+ else if( a instanceof Q.ComplexNumber ){
+
+ if( Q.ComplexNumber.isNumberLike( b )) return complexAndNumber( a, b )
+ else if( b instanceof Q.ComplexNumber ) return complexAndComplex( a, b )
+ else return Q.error( 'Q.ComplexNumber attempted to', name, 'with the complex number', a, 'and something that is neither a Number or Q.ComplexNumber:', b )
+ }
+ else return Q.error( 'Q.ComplexNumber attempted to', name, 'with something that is neither a Number or Q.ComplexNumber:', a )
+ },
+
+
+
+
+ sine: function( n ){
+
+ const
+ a = n.real,
+ b = n.imaginary
+
+ return new Q.ComplexNumber(
+
+ Math.sin( a ) * Q.hyperbolicCosine( b ),
+ Math.cos( a ) * Q.hyperbolicSine( b )
+ )
+ },
+ cosine: function( n ){
+
+ const
+ a = n.real,
+ b = n.imaginary
+
+ return new Q.ComplexNumber(
+
+ Math.cos( a ) * Q.hyperbolicCosine( b ),
+ -Math.sin( a ) * Q.hyperbolicSine( b )
+ )
+ },
+ arcCosine: function( n ){
+
+ const
+ a = n.real,
+ b = n.imaginary,
+ t1 = Q.ComplexNumber.squareRoot( new Q.ComplexNumber(
+
+ b * b - a * a + 1,
+ a * b * -2
+
+ )),
+ t2 = Q.ComplexNumber.log( new Q.ComplexNumber(
+
+ t1.real - b,
+ t1.imaginary + a
+ ))
+ return new Q.ComplexNumber( Math.PI / 2 - t2.imaginary, t2.real )
+ },
+ arcTangent: function( n ){
+
+ const
+ a = n.real,
+ b = n.imaginary
+
+ if( a === 0 ){
+
+ if( b === 1 ) return new Q.ComplexNumber( 0, Infinity )
+ if( b === -1 ) return new Q.ComplexNumber( 0, -Infinity )
+ }
+
+ const
+ d = a * a + ( 1 - b ) * ( 1 - b ),
+ t = Q.ComplexNumber.log( new Q.ComplexNumber(
+
+ ( 1 - b * b - a * a ) / d,
+ a / d * -2
+
+ ))
+ return new Q.ComplexNumber( t.imaginary / 2, t.real / 2 )
+ },
+
+
+
+
+ power: function( a, b ){
+
+ if( Q.ComplexNumber.isNumberLike( a )) a = new Q.ComplexNumber( a )
+ if( Q.ComplexNumber.isNumberLike( b )) b = new Q.ComplexNumber( b )
+
+
+ // Anything raised to the Zero power is 1.
+
+ if( b.isZero() ) return Q.ComplexNumber.ONE
+
+
+ // Zero raised to any power is 0.
+ // Note: What happens if b.real is zero or negative?
+ // What happens if b.imaginary is negative?
+ // Do we really need those conditionals??
+
+ if( a.isZero() &&
+ b.real > 0 &&
+ b.imaginary >= 0 ){
+
+ return Q.ComplexNumber.ZERO
+ }
+
+
+ // If our exponent is Real (has no Imaginary component)
+ // then we’re really just raising to a power.
+
+ if( b.imaginary === 0 ){
+
+ if( a.real >= 0 && a.imaginary === 0 ){
+
+ return new Q.ComplexNumber( Math.pow( a.real, b.real ), 0 )
+ }
+ else if( a.real === 0 ){// If our base is Imaginary (has no Real component).
+
+ switch(( b.real % 4 + 4 ) % 4 ){
+
+ case 0:
+ return new Q.ComplexNumber( Math.pow( a.imaginary, b.real ), 0 )
+ case 1:
+ return new Q.ComplexNumber( 0, Math.pow( a.imaginary, b.real ))
+ case 2:
+ return new Q.ComplexNumber( -Math.pow( a.imaginary, b.real ), 0 )
+ case 3:
+ return new Q.ComplexNumber( 0, -Math.pow( a.imaginary, b.real ))
+ }
+ }
+ }
+
+
+ const
+ arctangent2 = Math.atan2( a.imaginary, a.real ),
+ logHypotenuse = Q.logHypotenuse( a.real, a.imaginary ),
+ x = Math.exp( b.real * logHypotenuse - b.imaginary * arctangent2 ),
+ y = b.imaginary * logHypotenuse + b.real * arctangent2
+
+ return new Q.ComplexNumber(
+
+ x * Math.cos( y ),
+ x * Math.sin( y )
+ )
+ },
+ squareRoot: function( a ){
+
+ const
+ result = new Q.ComplexNumber( 0, 0 ),
+ absolute = Q.ComplexNumber.absolute( a )
+
+ if( a.real >= 0 ){
+
+ if( a.imaginary === 0 ){
+
+ result.real = Math.sqrt( a.real )// and imaginary already equals 0.
+ }
+ else {
+
+ result.real = Math.sqrt( 2 * ( absolute + a.real )) / 2
+ }
+ }
+ else {
+
+ result.real = Math.abs( a.imaginary ) / Math.sqrt( 2 * ( absolute - a.real ))
+ }
+ if( a.real <= 0 ){
+
+ result.imaginary = Math.sqrt( 2 * ( absolute - a.real )) / 2
+ }
+ else {
+
+ result.imaginary = Math.abs( a.imaginary ) / Math.sqrt( 2 * ( absolute + a.real ))
+ }
+ if( a.imaginary < 0 ) result.imaginary *= -1
+ return result
+ },
+ log: function( a ){
+
+ return new Q.ComplexNumber(
+
+ Q.logHypotenuse( a.real, a.imaginary ),
+ Math.atan2( a.imaginary, a.real )
+ )
+ },
+ multiply: function( a, b ){
+
+ return Q.ComplexNumber.operate(
+
+ 'multiply', a, b,
+ function( a, b ){
+
+ return new Q.ComplexNumber( a * b )
+ },
+ function( a, b ){
+
+ return new Q.ComplexNumber(
+
+ a * b.real,
+ a * b.imaginary
+ )
+ },
+ function( a, b ){
+
+ return new Q.ComplexNumber(
+
+ a.real * b,
+ a.imaginary * b
+ )
+ },
+ function( a, b ){
+
+
+ // FOIL Method that shit.
+ // https://en.wikipedia.org/wiki/FOIL_method
+
+ const
+ firsts = a.real * b.real,
+ outers = a.real * b.imaginary,
+ inners = a.imaginary * b.real,
+ lasts = a.imaginary * b.imaginary * -1// Because i² = -1.
+
+ return new Q.ComplexNumber(
+
+ firsts + lasts,
+ outers + inners
+ )
+ }
+ )
+ },
+ divide: function( a, b ){
+
+ return Q.ComplexNumber.operate(
+
+ 'divide', a, b,
+ function( a, b ){
+
+ return new Q.ComplexNumber( a / b )
+ },
+ function( a, b ){
+
+ return new Q.ComplexNumber( a ).divide( b )
+ },
+ function( a, b ){
+
+ return new Q.ComplexNumber(
+
+ a.real / b,
+ a.imaginary / b
+ )
+ },
+ function( a, b ){
+
+
+ // Ermergerd I had to look this up because it’s been so long.
+ // https://www.khanacademy.org/math/precalculus/imaginary-and-complex-numbers/complex-conjugates-and-dividing-complex-numbers/a/dividing-complex-numbers-review
+
+ const
+ conjugate = b.conjugate(),
+ numerator = a.multiply( conjugate ),
+
+
+ // The .imaginary will be ZERO for sure,
+ // so this forces a ComplexNumber.divide( Number ) ;)
+
+ denominator = b.multiply( conjugate ).real
+
+ return numerator.divide( denominator )
+ }
+ )
+ },
+ add: function( a, b ){
+
+ return Q.ComplexNumber.operate(
+
+ 'add', a, b,
+ function( a, b ){
+
+ return new Q.ComplexNumber( a + b )
+ },
+ function( a, b ){
+
+ return new Q.ComplexNumber(
+
+ b.real + a,
+ b.imaginary
+ )
+ },
+ function( a, b ){
+
+ return new Q.ComplexNumber(
+
+ a.real + b,
+ a.imaginary
+ )
+ },
+ function( a, b ){
+
+ return new Q.ComplexNumber(
+
+ a.real + b.real,
+ a.imaginary + b.imaginary
+ )
+ }
+ )
+ },
+ subtract: function( a, b ){
+
+ return Q.ComplexNumber.operate(
+
+ 'subtract', a, b,
+ function( a, b ){
+
+ return new Q.ComplexNumber( a - b )
+ },
+ function( a, b ){
+
+ return new Q.ComplexNumber(
+
+ b.real - a,
+ b.imaginary
+ )
+ },
+ function( a, b ){
+
+ return new Q.ComplexNumber(
+
+ a.real - b,
+ a.imaginary
+ )
+ },
+ function( a, b ){
+
+ return new Q.ComplexNumber(
+
+ a.real - b.real,
+ a.imaginary - b.imaginary
+ )
+ }
+ )
+ }
+})
+
+
+
+
+Q.ComplexNumber.createConstants(
+
+ 'ZERO', new Q.ComplexNumber( 0, 0 ),
+ 'ONE', new Q.ComplexNumber( 1, 0 ),
+ 'E', new Q.ComplexNumber( Math.E, 0 ),
+ 'PI', new Q.ComplexNumber( Math.PI, 0 ),
+ 'I', new Q.ComplexNumber( 0, 1 ),
+ 'EPSILON', new Q.ComplexNumber( Q.EPSILON, Q.EPSILON ),
+ 'INFINITY', new Q.ComplexNumber( Infinity, Infinity ),
+ 'NAN', new Q.ComplexNumber( NaN, NaN )
+)
+
+
+
+
+Object.assign( Q.ComplexNumber.prototype, {
+
+
+ // NON-destructive operations.
+
+ clone: function(){
+
+ return new Q.ComplexNumber( this.real, this.imaginary )
+ },
+ reduce: function(){
+
+
+ // Note: this *might* kill function chaining.
+
+ if( this.imaginary === 0 ) return this.real
+ return this
+ },
+ toText: function( roundToDecimal, padPositive ){
+
+
+ // Note: this will kill function chaining.
+
+ return Q.ComplexNumber.toText( this.real, this.imaginary, roundToDecimal, padPositive )
+ },
+
+
+ isNaN: function( n ){
+
+ return Q.ComplexNumber.isNaN( this )// Returned boolean will kill function chaining.
+ },
+ isZero: function( n ){
+
+ return Q.ComplexNumber.isZero( this )// Returned boolean will kill function chaining.
+ },
+ isFinite: function( n ){
+
+ return Q.ComplexNumber.isFinite( this )// Returned boolean will kill function chaining.
+ },
+ isInfinite: function( n ){
+
+ return Q.ComplexNumber.isInfinite( this )// Returned boolean will kill function chaining.
+ },
+ isEqualTo: function( b ){
+
+ return Q.ComplexNumber.areEqual( this, b )// Returned boolean will kill function chaining.
+ },
+
+
+ absolute: function(){
+
+ return Q.ComplexNumber.absolute( this )// Returned number will kill function chaining.
+ },
+ conjugate: function(){
+
+ return Q.ComplexNumber.conjugate( this )
+ },
+
+
+ power: function( b ){
+
+ return Q.ComplexNumber.power( this, b )
+ },
+ squareRoot: function(){
+
+ return Q.ComplexNumber.squareRoot( this )
+ },
+ log: function(){
+
+ return Q.ComplexNumber.log( this )
+ },
+ multiply: function( b ){
+
+ return Q.ComplexNumber.multiply( this, b )
+ },
+ divide: function( b ){
+
+ return Q.ComplexNumber.divide( this, b )
+ },
+ add: function( b ){
+
+ return Q.ComplexNumber.add( this, b )
+ },
+ subtract: function( b ){
+
+ return Q.ComplexNumber.subtract( this, b )
+ },
+
+
+
+
+ // DESTRUCTIVE operations.
+
+ copy$: function( b ){
+
+ if( b instanceof Q.ComplexNumber !== true )
+ return Q.error( `Q.ComplexNumber attempted to copy something that was not a complex number in to this complex number #${this.index}.`, this )
+
+ this.real = b.real
+ this.imaginary = b.imaginary
+ return this
+ },
+ conjugate$: function(){
+
+ return this.copy$( this.conjugate() )
+ },
+ power$: function( b ){
+
+ return this.copy$( this.power( b ))
+ },
+ squareRoot$: function(){
+
+ return this.copy$( this.squareRoot() )
+ },
+ log$: function(){
+
+ return this.copy$( this.log() )
+ },
+ multiply$: function( b ){
+
+ return this.copy$( this.multiply( b ))
+ },
+ divide$: function( b ){
+
+ return this.copy$( this.divide( b ))
+ },
+ add$: function( b ){
+
+ return this.copy$( this.add( b ))
+ },
+ subtract$: function( b ){
+
+ return this.copy$( this.subtract( b ))
+ }
+})
+
+
+
+
+// Copyright © 2019–2020, Stewart Smith. See LICENSE for details.
+
+
+
+
+Q.Matrix = function(){
+
+
+ // We’re keeping track of how many matrices are
+ // actually being generated. Just curiosity.
+
+ this.index = Q.Matrix.index ++
+
+
+ let matrixWidth = null
+
+
+ // Has Matrix been called with two numerical arguments?
+ // If so, we need to create an empty Matrix
+ // with dimensions of those values.
+
+ if( arguments.length == 1 &&
+ Q.ComplexNumber.isNumberLike( arguments[ 0 ])){
+
+ matrixWidth = arguments[ 0 ]
+ this.rows = new Array( matrixWidth ).fill( 0 ).map( function(){
+
+ return new Array( matrixWidth ).fill( 0 )
+ })
+ }
+ else if( arguments.length == 2 &&
+ Q.ComplexNumber.isNumberLike( arguments[ 0 ]) &&
+ Q.ComplexNumber.isNumberLike( arguments[ 1 ])){
+
+ matrixWidth = arguments[ 0 ]
+ this.rows = new Array( arguments[ 1 ]).fill( 0 ).map( function(){
+
+ return new Array( matrixWidth ).fill( 0 )
+ })
+ }
+ else {
+
+ // Matrices’ primary organization is by rows,
+ // which is more congruent with our written langauge;
+ // primarily organizated by horizontally juxtaposed glyphs.
+ // That means it’s easier to write an instance invocation in code
+ // and easier to read when inspecting properties in the console.
+
+ let matrixWidthIsBroken = false
+ this.rows = Array.from( arguments )
+ this.rows.forEach( function( row ){
+
+ if( row instanceof Array !== true ) row = [ row ]
+ if( matrixWidth === null ) matrixWidth = row.length
+ else if( matrixWidth !== row.length ) matrixWidthIsBroken = true
+ })
+ if( matrixWidthIsBroken )
+ return Q.error( `Q.Matrix found upon initialization that matrix#${this.index} row lengths were not equal. You are going to have a bad time.`, this )
+ }
+
+
+
+
+
+
+ // But for convenience we can also organize by columns.
+ // Note this represents the transposed version of itself!
+
+ const matrix = this
+ this.columns = []
+ for( let x = 0; x < matrixWidth; x ++ ){
+
+ const column = []
+ for( let y = 0; y < this.rows.length; y ++ ){
+
+
+ // Since we’re combing through here
+ // this is a good time to convert Number to ComplexNumber!
+
+ const value = matrix.rows[ y ][ x ]
+ if( typeof value === 'number' ){
+
+ // console.log('Created a complex number!')
+ matrix.rows[ y ][ x ] = new Q.ComplexNumber( value )
+ }
+ else if( value instanceof Q.ComplexNumber === false ){
+ return Q.error( `Q.Matrix found upon initialization that matrix#${this.index} contained non-quantitative values. A+ for creativity, but F for functionality.`, this )
+ }
+
+ // console.log( x, y, matrix.rows[ y ][ x ])
+
+
+ Object.defineProperty( column, y, {
+
+ get: function(){ return matrix.rows[ y ][ x ]},
+ set: function( n ){ matrix.rows[ y ][ x ] = n }
+ })
+ }
+ this.columns.push( column )
+ }
+}
+
+
+
+
+
+
+ ///////////////////////////
+ // //
+ // Static properties //
+ // //
+///////////////////////////
+
+
+Object.assign( Q.Matrix, {
+
+ index: 0,
+ help: function(){ return Q.help( this )},
+ constants: {},// Only holds references; an easy way to look up what constants exist.
+ createConstant: Q.createConstant,
+ createConstants: Q.createConstants,
+
+
+ isMatrixLike: function( obj ){
+
+ //return obj instanceof Q.Matrix || Q.Matrix.prototype.isPrototypeOf( obj )
+ return obj instanceof this || this.prototype.isPrototypeOf( obj )
+ },
+ isWithinRange: function( n, minimum, maximum ){
+
+ return typeof n === 'number' &&
+ n >= minimum &&
+ n <= maximum &&
+ n == parseInt( n )
+ },
+ getWidth: function( matrix ){
+
+ return matrix.columns.length
+ },
+ getHeight: function( matrix ){
+
+ return matrix.rows.length
+ },
+ haveEqualDimensions: function( matrix0, matrix1 ){
+
+ return (
+
+ matrix0.rows.length === matrix1.rows.length &&
+ matrix0.columns.length === matrix1.columns.length
+ )
+ },
+ areEqual: function( matrix0, matrix1 ){
+
+ if( matrix0 instanceof Q.Matrix !== true ) return false
+ if( matrix1 instanceof Q.Matrix !== true ) return false
+ if( Q.Matrix.haveEqualDimensions( matrix0, matrix1 ) !== true ) return false
+ return matrix0.rows.reduce( function( state, row, r ){
+
+ return state && row.reduce( function( state, cellValue, c ){
+
+ return state && cellValue.isEqualTo( matrix1.rows[ r ][ c ])
+
+ }, true )
+
+ }, true )
+ },
+
+
+
+
+ createSquare: function( size, f ){
+
+ if( typeof size !== 'number' ) size = 2
+ if( typeof f !== 'function' ) f = function(){ return 0 }
+ const data = []
+ for( let y = 0; y < size; y ++ ){
+
+ const row = []
+ for( let x = 0; x < size; x ++ ){
+
+ row.push( f( x, y ))
+ }
+ data.push( row )
+ }
+ return new Q.Matrix( ...data )
+ },
+ createZero: function( size ){
+
+ return new Q.Matrix.createSquare( size )
+ },
+ createOne: function( size ){
+
+ return new Q.Matrix.createSquare( size, function(){ return 1 })
+ },
+ createIdentity: function( size ){
+
+ return new Q.Matrix.createSquare( size, function( x, y ){ return x === y ? 1 : 0 })
+ },
+
+
+
+
+ // Import FROM a format.
+
+ from: function( format ){
+
+ if( typeof format !== 'string' ) format = 'Array'
+ const f = Q.Matrix[ 'from'+ format ]
+ format = format.toLowerCase()
+ if( typeof f !== 'function' )
+ return Q.error( `Q.Matrix could not find an importer for “${format}†data.` )
+ return f
+ },
+ fromArray: function( array ){
+
+ return new Q.Matrix( ...array )
+ },
+ fromXsv: function( input, rowSeparator, valueSeparator ){
+
+ `
+ Ingest string data organized by row, then by column
+ where rows are separated by one token (default: \n)
+ and column values are separated by another token
+ (default: \t).
+
+ `
+
+ if( typeof rowSeparator !== 'string' ) rowSeparator = '\n'
+ if( typeof valueSeparator !== 'string' ) valueSeparator = '\t'
+
+ const
+ inputRows = input.split( rowSeparator ),
+ outputRows = []
+
+ inputRows.forEach( function( inputRow ){
+
+ inputRow = inputRow.trim()
+ if( inputRow === '' ) return
+
+ const outputRow = []
+ inputRow.split( valueSeparator ).forEach( function( cellValue ){
+
+ outputRow.push( parseFloat( cellValue ))
+ })
+ outputRows.push( outputRow )
+ })
+ return new Q.Matrix( ...outputRows )
+ },
+ fromCsv: function( csv ){
+
+ return Q.Matrix.fromXsv( csv.replace( /\r/g, '\n' ), '\n', ',' )
+ },
+ fromTsv: function( tsv ){
+
+ return Q.Matrix.fromXsv( tsv, '\n', '\t' )
+ },
+ fromHtml: function( html ){
+
+ return Q.Matrix.fromXsv(
+
+ html
+ .replace( /\r?\n|\r||/g, '' )
+ .replace( /<\/td>(\s*)<\/tr>/g, ' |
' )
+ .match( /(.*)<\/table>/i )[ 1 ],
+ '',
+ ''
+ )
+ },
+
+
+
+
+ // Export TO a format.
+
+ toXsv: function( matrix, rowSeparator, valueSeparator ){
+
+ return matrix.rows.reduce( function( xsv, row ){
+
+ return xsv + rowSeparator + row.reduce( function( xsv, cell, c ){
+
+ return xsv + ( c > 0 ? valueSeparator : '' ) + cell.toText()
+
+ }, '' )
+
+ }, '' )
+ },
+ toCsv: function( matrix ){
+
+ return Q.Matrix.toXsv( matrix, '\n', ',' )
+ },
+ toTsv: function( matrix ){
+
+ return Q.Matrix.toXsv( matrix, '\n', '\t' )
+ },
+
+
+
+
+ // Operate NON-destructive.
+
+ add: function( matrix0, matrix1 ){
+
+ if( Q.Matrix.isMatrixLike( matrix0 ) !== true ||
+ Q.Matrix.isMatrixLike( matrix1 ) !== true ){
+
+ return Q.error( `Q.Matrix attempted to add something that was not a matrix.` )
+ }
+ if( Q.Matrix.haveEqualDimensions( matrix0, matrix1 ) !== true )
+ return Q.error( `Q.Matrix cannot add matrix#${matrix0.index} of dimensions ${matrix0.columns.length}x${matrix0.rows.length} to matrix#${matrix1.index} of dimensions ${matrix1.columns.length}x${matrix1.rows.length}.`)
+
+ return new Q.Matrix( ...matrix0.rows.reduce( function( resultMatrixRow, row, r ){
+
+ resultMatrixRow.push( row.reduce( function( resultMatrixColumn, cellValue, c ){
+
+ // resultMatrixColumn.push( cellValue + matrix1.rows[ r ][ c ])
+ resultMatrixColumn.push( cellValue.add( matrix1.rows[ r ][ c ]))
+ return resultMatrixColumn
+
+ }, [] ))
+ return resultMatrixRow
+
+ }, [] ))
+ },
+ multiplyScalar: function( matrix, scalar ){
+
+ if( Q.Matrix.isMatrixLike( matrix ) !== true ){
+
+ return Q.error( `Q.Matrix attempted to scale something that was not a matrix.` )
+ }
+ if( typeof scalar !== 'number' ){
+
+ return Q.error( `Q.Matrix attempted to scale this matrix#${matrix.index} by an invalid scalar: ${scalar}.` )
+ }
+ return new Q.Matrix( ...matrix.rows.reduce( function( resultMatrixRow, row ){
+
+ resultMatrixRow.push( row.reduce( function( resultMatrixColumn, cellValue ){
+
+ // resultMatrixColumn.push( cellValue * scalar )
+ resultMatrixColumn.push( cellValue.multiply( scalar ))
+ return resultMatrixColumn
+
+ }, [] ))
+ return resultMatrixRow
+
+ }, [] ))
+ },
+ multiply: function( matrix0, matrix1 ){
+
+ `
+ Two matrices can be multiplied only when
+ the number of columns in the first matrix
+ equals the number of rows in the second matrix.
+ Reminder: Matrix multiplication is not commutative
+ so the order in which you multiply matters.
+
+
+ SEE ALSO
+
+ https://en.wikipedia.org/wiki/Matrix_multiplication
+ `
+
+ if( Q.Matrix.isMatrixLike( matrix0 ) !== true ||
+ Q.Matrix.isMatrixLike( matrix1 ) !== true ){
+
+ return Q.error( `Q.Matrix attempted to multiply something that was not a matrix.` )
+ }
+ if( matrix0.columns.length !== matrix1.rows.length ){
+
+ return Q.error( `Q.Matrix attempted to multiply Matrix#${matrix0.index}(cols==${matrix0.columns.length}) by Matrix#${matrix1.index}(rows==${matrix1.rows.length}) but their dimensions were not compatible for this.` )
+ }
+ const resultMatrix = []
+ matrix0.rows.forEach( function( matrix0Row ){// Each row of THIS matrix
+
+ const resultMatrixRow = []
+ matrix1.columns.forEach( function( matrix1Column ){// Each column of OTHER matrix
+
+ const sum = new Q.ComplexNumber()
+ matrix1Column.forEach( function( matrix1CellValue, index ){// Work down the column of OTHER matrix
+
+ sum.add$( matrix0Row[ index ].multiply( matrix1CellValue ))
+ })
+ resultMatrixRow.push( sum )
+ })
+ resultMatrix.push( resultMatrixRow )
+ })
+ //return new Q.Matrix( ...resultMatrix )
+ return new this( ...resultMatrix )
+ },
+ multiplyTensor: function( matrix0, matrix1 ){
+
+ `
+ https://en.wikipedia.org/wiki/Kronecker_product
+ https://en.wikipedia.org/wiki/Tensor_product
+ `
+
+ if( Q.Matrix.isMatrixLike( matrix0 ) !== true ||
+ Q.Matrix.isMatrixLike( matrix1 ) !== true ){
+
+ return Q.error( `Q.Matrix attempted to tensor something that was not a matrix.` )
+ }
+
+ const
+ resultMatrix = [],
+ resultMatrixWidth = matrix0.columns.length * matrix1.columns.length,
+ resultMatrixHeight = matrix0.rows.length * matrix1.rows.length
+
+ for( let y = 0; y < resultMatrixHeight; y ++ ){
+
+ const resultMatrixRow = []
+ for( let x = 0; x < resultMatrixWidth; x ++ ){
+
+ const
+ matrix0X = Math.floor( x / matrix0.columns.length ),
+ matrix0Y = Math.floor( y / matrix0.rows.length ),
+ matrix1X = x % matrix1.columns.length,
+ matrix1Y = y % matrix1.rows.length
+
+ resultMatrixRow.push(
+
+ //matrix0.rows[ matrix0Y ][ matrix0X ] * matrix1.rows[ matrix1Y ][ matrix1X ]
+ matrix0.rows[ matrix0Y ][ matrix0X ].multiply( matrix1.rows[ matrix1Y ][ matrix1X ])
+ )
+ }
+ resultMatrix.push( resultMatrixRow )
+ }
+ return new Q.Matrix( ...resultMatrix )
+ }
+})
+
+
+
+
+
+
+ //////////////////////////////
+ // //
+ // Prototype properties //
+ // //
+//////////////////////////////
+
+
+Object.assign( Q.Matrix.prototype, {
+
+ isValidRow: function( r ){
+
+ return Q.Matrix.isWithinRange( r, 0, this.rows.length - 1 )
+ },
+ isValidColumn: function( c ){
+
+ return Q.Matrix.isWithinRange( c, 0, this.columns.length - 1 )
+ },
+ isValidAddress: function( x, y ){
+
+ return this.isValidRow( y ) && this.isValidColumn( x )
+ },
+ getWidth: function(){
+
+ return Q.Matrix.getWidth( this )
+ },
+ getHeight: function(){
+
+ return Q.Matrix.getHeight( this )
+ },
+
+
+
+
+ // Read NON-destructive by nature. (Except quantum reads of course! ROFL!!)
+
+ read: function( x, y ){
+
+ `
+ Equivalent to
+ this.columns[ x ][ y ]
+ or
+ this.rows[ y ][ x ]
+ but with safety checks.
+ `
+
+ if( this.isValidAddress( x, y )) return this.rows[ y ][ x ]
+ return Q.error( `Q.Matrix could not read from cell address (x=${x}, y=${y}) in matrix#${this.index}.`, this )
+ },
+ clone: function(){
+
+ return new Q.Matrix( ...this.rows )
+ },
+ isEqualTo: function( otherMatrix ){
+
+ return Q.Matrix.areEqual( this, otherMatrix )
+ },
+
+
+ toArray: function(){
+
+ return this.rows
+ },
+ toXsv: function( rowSeparator, valueSeparator ){
+
+ return Q.Matrix.toXsv( this, rowSeparator, valueSeparator )
+ },
+ toCsv: function(){
+
+ return Q.Matrix.toXsv( this, '\n', ',' )
+ },
+ toTsv: function(){
+
+ return Q.Matrix.toXsv( this, '\n', '\t' )
+ },
+ toHtml: function(){
+
+ return this.rows.reduce( function( html, row ){
+
+ return html + row.reduce( function( html, cell ){
+
+ return html +'\n\t\t'+ cell.toText() +' | '
+
+ }, '\n\t' ) + '\n\t
'
+
+ }, '\n'
+ },
+
+
+
+
+ // Write is DESTRUCTIVE by nature. Not cuz I hate ya.
+
+ write$: function( x, y, n ){
+
+ `
+ Equivalent to
+ this.columns[ x ][ y ] = n
+ or
+ this.rows[ y ][ x ] = n
+ but with safety checks.
+ `
+
+ if( this.isValidAddress( x, y )){
+
+ if( Q.ComplexNumber.isNumberLike( n )) n = new Q.ComplexNumber( n )
+ if( n instanceof Q.ComplexNumber !== true ) return Q.error( `Attempted to write an invalid value (${n}) to matrix#${this.index} at x=${x}, y=${y}`, this )
+ this.rows[ y ][ x ] = n
+ return this
+ }
+ return Q.error( `Invalid cell address for Matrix#${this.index}: x=${x}, y=${y}`, this )
+ },
+ copy$: function( matrix ){
+
+ if( Q.Matrix.isMatrixLike( matrix ) !== true )
+ return Q.error( `Q.Matrix attempted to copy something that was not a matrix in to this matrix#${matrix.index}.`, this )
+
+ if( Q.Matrix.haveEqualDimensions( matrix, this ) !== true )
+ return Q.error( `Q.Matrix cannot copy matrix#${matrix.index} of dimensions ${matrix.columns.length}x${matrix.rows.length} in to this matrix#${this.index} of dimensions ${this.columns.length}x${this.rows.length} because their dimensions do not match.`, this )
+
+ const that = this
+ matrix.rows.forEach( function( row, r ){
+
+ row.forEach( function( n, c ){
+
+ that.rows[ r ][ c ] = n
+ })
+ })
+ return this
+ },
+ fromArray$: function( array ){
+
+ return this.copy$( Q.Matrix.fromArray( array ))
+ },
+ fromCsv$: function( csv ){
+
+ return this.copy$( Q.Matrix.fromCsv( csv ))
+ },
+ fromTsv$: function( tsv ){
+
+ return this.copy$( Q.Matrix.fromTsv( tsv ))
+ },
+ fromHtml$: function( html ){
+
+ return this.copy$( Q.Matrix.fromHtml( html ))
+ },
+
+
+
+
+ // Operate NON-destructive.
+
+ add: function( otherMatrix ){
+
+ return Q.Matrix.add( this, otherMatrix )
+ },
+ multiplyScalar: function( scalar ){
+
+ return Q.Matrix.multiplyScalar( this, scalar )
+ },
+ multiply: function( otherMatrix ){
+
+ return Q.Matrix.multiply( this, otherMatrix )
+ },
+ multiplyTensor: function( otherMatrix ){
+
+ return Q.Matrix.multiplyTensor( this, otherMatrix )
+ },
+
+
+
+
+ // Operate DESTRUCTIVE.
+
+ add$: function( otherMatrix ){
+
+ return this.copy$( this.add( otherMatrix ))
+ },
+ multiplyScalar$: function( scalar ){
+
+ return this.copy$( this.multiplyScalar( scalar ))
+ }
+})
+
+
+
+
+
+
+ //////////////////////////
+ // //
+ // Static constants //
+ // //
+//////////////////////////
+
+
+Q.Matrix.createConstants(
+
+ 'IDENTITY_2X2', Q.Matrix.createIdentity( 2 ),
+ 'IDENTITY_3X3', Q.Matrix.createIdentity( 3 ),
+ 'IDENTITY_4X4', Q.Matrix.createIdentity( 4 ),
+
+ 'CONSTANT0_2X2', new Q.Matrix(
+ [ 1, 1 ],
+ [ 0, 0 ]),
+
+ 'CONSTANT1_2X2', new Q.Matrix(
+ [ 0, 0 ],
+ [ 1, 1 ]),
+
+ 'NEGATION_2X2', new Q.Matrix(
+ [ 0, 1 ],
+ [ 1, 0 ]),
+
+ 'TEST_MAP_9X9', new Q.Matrix(
+ [ 11, 21, 31, 41, 51, 61, 71, 81, 91 ],
+ [ 12, 22, 32, 42, 52, 62, 72, 82, 92 ],
+ [ 13, 23, 33, 43, 53, 63, 73, 83, 93 ],
+ [ 14, 24, 34, 44, 54, 64, 74, 84, 94 ],
+ [ 15, 25, 35, 45, 55, 65, 75, 85, 95 ],
+ [ 16, 26, 36, 46, 56, 66, 76, 86, 96 ],
+ [ 17, 27, 37, 47, 57, 67, 77, 87, 97 ],
+ [ 18, 28, 38, 48, 58, 68, 78, 88, 98 ],
+ [ 19, 29, 39, 49, 59, 69, 79, 89, 99 ])
+)
+
+
+
+
+// Copyright © 2019–2020, Stewart Smith. See LICENSE for details.
+
+
+
+
+Q.Qubit = function( a, b, symbol, name ){
+
+
+ // If we’ve received an instance of Q.Matrix as our first argument
+ // then we’ll assume there are no further arguments
+ // and just use that matrix as our new Q.Qubit instance.
+
+ if( Q.Matrix.isMatrixLike( a ) && b === undefined ){
+
+ b = a.rows[ 1 ][ 0 ]
+ a = a.rows[ 0 ][ 0 ]
+ }
+ else {
+
+
+ // All of our internal math now uses complex numbers
+ // rather than Number literals
+ // so we’d better convert!
+
+ if( typeof a === 'number' ) a = new Q.ComplexNumber( a, 0 )
+ if( typeof b === 'number' ) b = new Q.ComplexNumber( b, 0 )
+
+
+ // If we receive undefined (or garbage inputs)
+ // let’s try to make it useable.
+ // This way we can always call Q.Qubit with no arguments
+ // to make a new qubit available for computing with.
+
+ if( a instanceof Q.ComplexNumber !== true ) a = new Q.ComplexNumber( 1, 0 )
+ if( b instanceof Q.ComplexNumber !== true ){
+
+
+ // 1 - |ð’‚|² = |ð’ƒ|²
+ // So this does NOT account for if ð’ƒ ought to be imaginary or not.
+ // Perhaps for completeness we could randomly decide
+ // to flip the real and imaginary components of ð’ƒ after this line?
+
+ b = Q.ComplexNumber.ONE.subtract( Math.pow( a.absolute(), 2 )).squareRoot()
+ }
+ }
+
+
+ // Sanity check!
+ // Does this constraint hold true? |ð’‚|² + |ð’ƒ|² = 1
+
+ if( Math.pow( a.absolute(), 2 ) + Math.pow( b.absolute(), 2 ) - 1 > Q.EPSILON )
+ return Q.error( `Q.Qubit could not accept the initialization values of a=${a} and b=${b} because their squares do not add up to 1.` )
+
+ Q.Matrix.call( this, [ a ],[ b ])
+ this.index = Q.Qubit.index ++
+
+
+ // Convenience getters and setters for this qubit’s
+ // controll bit and target bit.
+
+ Object.defineProperty( this, 'alpha', {
+
+ get: function(){ return this.rows[ 0 ][ 0 ]},
+ set: function( n ){ this.rows[ 0 ][ 0 ] = n }
+ })
+ Object.defineProperty( this, 'beta', {
+
+ get: function(){ return this.rows[ 1 ][ 0 ]},
+ set: function( n ){ this.rows[ 1 ][ 0 ] = n }
+ })
+
+
+ // Used for Dirac notation: |?⟩
+
+ if( typeof symbol === 'string' ) this.symbol = symbol
+ if( typeof name === 'string' ) this.name = name
+ if( this.symbol === undefined || this.name === undefined ){
+
+ const found = Object.values( Q.Qubit.constants ).find( function( qubit ){
+
+ return (
+
+ a.isEqualTo( qubit.alpha ) &&
+ b.isEqualTo( qubit.beta )
+ )
+ })
+ if( found === undefined ){
+
+ this.symbol = '?'
+ this.name = 'Unnamed'
+ }
+ else {
+
+ if( this.symbol === undefined ) this.symbol = found.symbol
+ if( this.name === undefined ) this.name = found.name
+ }
+ }
+}
+Q.Qubit.prototype = Object.create( Q.Matrix.prototype )
+Q.Qubit.prototype.constructor = Q.Qubit
+
+
+
+
+Object.assign( Q.Qubit, {
+
+ index: 0,
+ help: function(){ return Q.help( this )},
+ constants: {},
+ createConstant: Q.createConstant,
+ createConstants: Q.createConstants,
+
+
+
+
+ findBy: function( key, value ){
+
+ return (
+
+ Object
+ .values( Q.Qubit.constants )
+ .find( function( item ){
+
+ if( typeof value === 'string' &&
+ typeof item[ key ] === 'string' ){
+
+ return value.toLowerCase() === item[ key ].toLowerCase()
+ }
+ return value === item[ key ]
+ })
+ )
+ },
+ findBySymbol: function( symbol ){
+
+ return Q.Qubit.findBy( 'symbol', symbol )
+ },
+ findByName: function( name ){
+
+ return Q.Qubit.findBy( 'name', name )
+ },
+ findByBeta: function( beta ){
+
+ if( beta instanceof Q.ComplexNumber === false ){
+
+ beta = new Q.ComplexNumber( beta )
+ }
+ return Object.values( Q.Qubit.constants ).find( function( qubit ){
+
+ return qubit.beta.isEqualTo( beta )
+ })
+ },
+ areEqual: function( qubit0, qubit1 ){
+
+ return (
+
+ qubit0.alpha.isEqualTo( qubit1.alpha ) &&
+ qubit0.beta.isEqualTo( qubit1.beta )
+ )
+ },
+ collapse: function( qubit ){
+
+ const
+ alpha2 = Math.pow( qubit.alpha.absolute(), 2 ),
+ beta2 = Math.pow( qubit.beta.absolute(), 2 ),
+ randomNumberRange = Math.pow( 2, 32 ) - 1,
+ randomNumber = new Uint32Array( 1 )
+
+ // console.log( 'alpha^2', alpha2 )
+ // console.log( 'beta^2', beta2 )
+ window.crypto.getRandomValues( randomNumber )
+ const randomNumberNormalized = randomNumber / randomNumberRange
+ if( randomNumberNormalized <= alpha2 ){
+
+ return new Q.Qubit( 1, 0 )
+ }
+ else return new Q.Qubit( 0, 1 )
+ },
+ applyGate: function( qubit, gate, ...args ){
+
+ `
+ This is means of inverting what comes first:
+ the Gate or the Qubit?
+ If the Gate only operates on a single qubit,
+ then it doesn’t matter and we can do this:
+ `
+
+ if( gate instanceof Q.Gate === false ) return Q.error( `Q.Qubit attempted to apply something that was not a gate to this qubit #${ qubit.index }.` )
+ else return gate.applyToQubit( qubit, ...args )
+ },
+ toText: function( qubit ){
+
+ //return `|${qubit.beta.toText()}⟩`
+ return qubit.alpha.toText() +'\n'+ qubit.beta.toText()
+ },
+ toStateVectorText: function( qubit ){
+
+ return `|${ qubit.beta.toText() }⟩`
+ },
+ toStateVectorHtml: function( qubit ){
+
+ return `${ qubit.beta.toText() }`
+ },
+
+
+
+ // This code was a pain in the ass to figure out.
+ // I’m not fluent in trigonometry
+ // and none of the quantum primers actually lay out
+ // how to convert arbitrary qubit states
+ // to Bloch Sphere representation.
+ // Oh, they provide equivalencies for specific states, sure.
+ // I hope this is useful to you
+ // unless you are porting this to a terrible language
+ // like C# or Java or something ;)
+
+ toBlochSphere: function( qubit ){
+
+ `
+ Based on this qubit’s state return the
+ Polar angle θ (theta),
+ azimuth angle Ï• (phi),
+ Bloch vector,
+ corrected surface coordinate.
+
+ https://en.wikipedia.org/wiki/Bloch_sphere
+ `
+
+
+ // Polar angle θ (theta).
+
+ const theta = Q.ComplexNumber.arcCosine( qubit.alpha ).multiply( 2 )
+ if( isNaN( theta.real )) theta.real = 0
+ if( isNaN( theta.imaginary )) theta.imaginary = 0
+
+
+ // Azimuth angle Ï• (phi).
+
+ const phi = Q.ComplexNumber.log(
+
+ qubit.beta.divide( Q.ComplexNumber.sine( theta.divide( 2 )))
+ )
+ .divide( Q.ComplexNumber.I )
+ if( isNaN( phi.real )) phi.real = 0
+ if( isNaN( phi.imaginary )) phi.imaginary = 0
+
+
+ // Bloch vector.
+
+ const vector = {
+
+ x: Q.ComplexNumber.sine( theta ).multiply( Q.ComplexNumber.cosine( phi )).real,
+ y: Q.ComplexNumber.sine( theta ).multiply( Q.ComplexNumber.sine( phi )).real,
+ z: Q.ComplexNumber.cosine( theta ).real
+ }
+
+
+ // Bloch vector’s axes are wonked.
+ // Let’s “correct†them for use with Three.js, etc.
+
+ const position = {
+
+ x: vector.y,
+ y: vector.z,
+ z: vector.x
+ }
+
+ return {
+
+
+ // Wow does this make tweening easier down the road.
+
+ alphaReal: qubit.alpha.real,
+ alphaImaginary: qubit.alpha.imaginary,
+ betaReal: qubit.beta.real,
+ betaImaginary: qubit.beta.imaginary,
+
+
+ // Ummm... I’m only returnig the REAL portions. Please forgive me!
+
+ theta: theta.real,
+ phi: phi.real,
+ vector, // Wonked YZX vector for maths because maths.
+ position// Un-wonked XYZ for use by actual 3D engines.
+ }
+ },
+ fromBlochVector: function( x, y, z ){
+
+
+ //basically from a Pauli Rotation
+ }
+
+})
+
+
+
+
+Q.Qubit.createConstants(
+
+
+ // Opposing pairs:
+ // |H⟩ and |V⟩
+ // |D⟩ and |A⟩
+ // |R⟩ and |L⟩
+
+ 'HORIZONTAL', new Q.Qubit( 1, 0, 'H', 'Horizontal' ),// ZERO.
+ 'VERTICAL', new Q.Qubit( 0, 1, 'V', 'Vertical' ),// ONE.
+ 'DIAGONAL', new Q.Qubit( Math.SQRT1_2, Math.SQRT1_2, 'D', 'Diagonal' ),
+ 'ANTI_DIAGONAL', new Q.Qubit( Math.SQRT1_2, -Math.SQRT1_2, 'A', 'Anti-diagonal' ),
+ 'RIGHT_HAND_CIRCULAR_POLARIZED', new Q.Qubit( Math.SQRT1_2, new Q.ComplexNumber( 0, -Math.SQRT1_2 ), 'R', 'Right-hand Circular Polarized' ),// RHCP
+ 'LEFT_HAND_CIRCULAR_POLARIZED', new Q.Qubit( Math.SQRT1_2, new Q.ComplexNumber( 0, Math.SQRT1_2 ), 'L', 'Left-hand Circular Polarized' ) // LHCP
+)
+
+
+
+
+Object.assign( Q.Qubit.prototype, {
+
+ copy$: function( matrix ){
+
+ if( Q.Matrix.isMatrixLike( matrix ) !== true )
+ return Q.error( `Q.Qubit attempted to copy something that was not a matrix in this qubit #${qubit.index}.`, this )
+
+ if( Q.Matrix.haveEqualDimensions( matrix, this ) !== true )
+ return Q.error( `Q.Qubit cannot copy matrix#${matrix.index} of dimensions ${matrix.columns.length}x${matrix.rows.length} in to this qubit #${this.index} of dimensions ${this.columns.length}x${this.rows.length} because their dimensions do not match.`, this )
+
+ const that = this
+ matrix.rows.forEach( function( row, r ){
+
+ row.forEach( function( n, c ){
+
+ that.rows[ r ][ c ] = n
+ })
+ })
+ this.dirac = matrix.dirac
+ return this
+ },
+ clone: function(){
+
+ return new Q.Qubit( this.alpha, this.beta )
+ },
+ isEqualTo: function( otherQubit ){
+
+ return Q.Qubit.areEqual( this, otherQubit )// Returns a Boolean, breaks function chaining!
+ },
+ collapse: function(){
+
+ return Q.Qubit.collapse( this )
+ },
+ applyGate: function( gate, ...args ){
+
+ return Q.Qubit.applyGate( this, gate, ...args )
+ },
+ toText: function(){
+
+ return Q.Qubit.toText( this )// Returns a String, breaks function chaining!
+ },
+ toStateVectorText: function(){
+
+ return Q.Qubit.toStateVectorText( this )// Returns a String, breaks function chaining!
+ },
+ toStateVectorHtml: function(){
+
+ return Q.Qubit.toStateVectorHtml( this )// Returns a String, breaks function chaining!
+ },
+ toBlochSphere: function(){
+
+ return Q.Qubit.toBlochSphere( this )// Returns an Object, breaks function chaining!
+ },
+ collapse$: function(){
+
+ return this.copy$( Q.Qubit.collapse( this ))
+ },
+ applyGate$: function( gate ){
+
+ return this.copy$( Q.Qubit.applyGate( this, gate ))
+ },
+})
+
+
+
+
+// Copyright © 2019–2020, Stewart Smith. See LICENSE for details.
+
+
+
+
+Q.Gate = function( params ){
+
+ Object.assign( this, params )
+ this.index = Q.Gate.index ++
+
+ if( typeof this.symbol !== 'string' ) this.symbol = '?'
+ if( typeof this.symbolAmazonBraket !== 'string' ) this.symbolAmazonBraket = this.symbol.toLowerCase()
+ const parameters = Object.assign( {}, params.parameters )
+ this.parameters = parameters
+
+ // We use symbols as unique identifiers
+ // among gate CONSTANTS
+ // so if you use the same symbol for a non-constant
+ // that’s not a deal breaker
+ // but it is good to know.
+
+ const
+ scope = this,
+ foundConstant = Object
+ .values( Q.Gate.constants )
+ .find( function( gate ){
+
+ return gate.symbol === scope.symbol
+ })
+
+ //Muting this warning in order to have parameterized gates (that don't totally mess with the constants), we need
+ //to make clones of the constants...a lot if you're using a lot of parameterized gates. Warning gets annoying :/.
+ // if( foundConstant ){
+
+ // Q.warn( `Q.Gate is creating a new instance, #${ this.index }, that uses the same symbol as a pre-existing Gate constant:`, foundConstant )
+ // }
+
+ if( typeof this.name !== 'string' ) this.name = 'Unknown'
+ if( typeof this.nameCss !== 'string' ) this.nameCss = 'unknown'
+
+
+ // If our gate’s matrix is to be
+ // dynamically created or updated
+ // then we ouoght to do that now.
+
+ if( typeof this.updateMatrix$ === 'function' ) this.updateMatrix$()
+
+
+ // Every gate must have an applyToQubit method.
+ // If it doesn’t exist we’ll create one
+ // based on whether a matrix property exists or not.
+
+ if( typeof this.applyToQubit !== 'function' ){
+
+ if( this.matrix instanceof Q.Matrix === true ){
+
+ this.applyToQubit = function( qubit ){
+
+ return new Q.Qubit( this.matrix.multiply( qubit ))
+ }
+ }
+ else {
+
+ this.applyToQubit = function( qubit ){ return qubit }
+ }
+ }
+}
+
+
+
+
+Object.assign( Q.Gate, {
+
+ index: 0,
+ constants: {},
+ createConstant: Q.createConstant,
+ createConstants: Q.createConstants,
+ findBy: function( key, value ){
+
+ return (
+
+ Object
+ .values( Q.Gate.constants )
+ .find( function( item ){
+
+ if( typeof value === 'string' &&
+ typeof item[ key ] === 'string' ){
+
+ return value.toLowerCase() === item[ key ].toLowerCase()
+ }
+ return value === item[ key ]
+ })
+ )
+ },
+ findBySymbol: function( symbol ){
+
+ return Q.Gate.findBy( 'symbol', symbol )
+ },
+ findByName: function( name ){
+
+ return Q.Gate.findBy( 'name', name )
+ }
+})
+
+
+
+
+Object.assign( Q.Gate.prototype, {
+
+ clone: function( params ){
+
+ return new Q.Gate( Object.assign( {}, this, params ))
+ },
+ applyToQubits: function(){
+
+ return Array.from( arguments ).map( this.applyToQubit.bind( this ))
+ },
+ set$: function( key, value ){
+
+ this[ key ] = value
+ return this
+ },
+ setSymbol$: function( value ){
+
+ return this.set$( 'symbol', value )
+ }
+})
+
+
+
+
+Q.Gate.createConstants(
+
+
+ // Operate on a single qubit.
+
+ 'IDENTITY', new Q.Gate({
+
+ symbol: 'I',
+ symbolAmazonBraket: 'i',
+ symbolSvg: '',
+ name: 'Identity',
+ nameCss: 'identity',
+ matrix: Q.Matrix.IDENTITY_2X2
+ }),
+ 'CURSOR', new Q.Gate({
+
+ symbol: '*',
+ symbolAmazonBraket: 'i',
+ symbolSvg: '',
+ name: 'Identity',
+ nameCss: 'identity',
+ matrix: Q.Matrix.IDENTITY_2X2
+ }),
+ 'MEASURE', new Q.Gate({
+
+ symbol: 'M',
+ symbolAmazonBraket: 'm',
+ symbolSvg: '',
+ name: 'Measure',
+ nameCss: 'measure',
+ matrix: Q.Matrix.IDENTITY_2X2,
+ applyToQubit: function( state ){}
+ }),
+ 'HADAMARD', new Q.Gate({
+
+ symbol: 'H',
+ symbolAmazonBraket: 'h',
+ symbolSvg: '',
+ name: 'Hadamard',
+ nameCss: 'hadamard',
+ matrix: new Q.Matrix(
+ [ Math.SQRT1_2, Math.SQRT1_2 ],
+ [ Math.SQRT1_2, -Math.SQRT1_2 ])
+ }),
+ 'PAULI_X', new Q.Gate({
+
+ symbol: 'X',
+ symbolAmazonBraket: 'x',
+ symbolSvg: '',
+ name: 'Pauli X',
+ nameCss: 'pauli-x',
+ matrix: new Q.Matrix(
+ [ 0, 1 ],
+ [ 1, 0 ]),
+ //ltnln: NOTE! can_be_controlled refers to whether or not the Braket SDK supports a controlled
+ //application of this gate; if we want Q to be able to support controlled gated regardless of whether
+ //or not Braket can, this must be changed..
+ can_be_controlled: true
+ },
+ ),
+ 'PAULI_Y', new Q.Gate({
+
+ symbol: 'Y',
+ symbolAmazonBraket: 'y',
+ symbolSvg: '',
+ name: 'Pauli Y',
+ nameCss: 'pauli-y',
+ matrix: new Q.Matrix(
+ [ 0, new Q.ComplexNumber( 0, -1 )],
+ [ new Q.ComplexNumber( 0, 1 ), 0 ]),
+ can_be_controlled: true
+ },
+ ),
+ 'PAULI_Z', new Q.Gate({
+
+ symbol: 'Z',
+ symbolAmazonBraket: 'z',
+ symbolSvg: '',
+ name: 'Pauli Z',
+ nameCss: 'pauli-z',
+ matrix: new Q.Matrix(
+ [ 1, 0 ],
+ [ 0, -1 ]),
+ can_be_controlled: true
+ },
+ ),
+ 'PHASE', new Q.Gate({
+
+ symbol: 'P',
+ symbolAmazonBraket: 'phaseshift',// ltnln edit: change from 'p' to 'phaseshift'
+ symbolSvg: '',
+ name: 'Phase',
+ nameCss: 'phase',
+ parameters: { "phi" : 1 },
+ updateMatrix$: function( phi ){
+ if( Q.isUsefulNumber( phi ) === true ) this.parameters[ "phi" ] = phi
+ this.matrix = new Q.Matrix(
+ [ 1, 0 ],
+ [ 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, this.parameters["phi"] ))])
+ return this
+ },
+ applyToQubit: function( qubit, phi ){
+
+ if( Q.isUsefulNumber( phi ) !== true ) phi = this.parameters[ "phi" ]
+ const matrix = new Q.Matrix(
+ [ 1, 0 ],
+ [ 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, phi ))])
+ return new Q.Qubit( matrix.multiply( qubit ))
+ },
+ can_be_controlled: true,
+ has_parameters: true
+ }),
+ 'PI_8', new Q.Gate({
+
+ symbol: 'T',
+ symbolAmazonBraket: 't',// !!! Double check this !!!
+ symbolSvg: '',
+ name: 'π ÷ 8',
+ nameCss: 'pi8',
+ matrix: new Q.Matrix(
+ [ 1, 0 ],
+ [ 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, Math.PI / 4 )) ])
+ }),
+ 'BLOCH', new Q.Gate({
+
+ symbol: 'B',
+ //symbolAmazonBraket: Does not exist.
+ symbolSvg: '',
+ name: 'Bloch sphere',
+ nameCss: 'bloch',
+ applyToQubit: function( qubit ){
+
+ // Create Bloch sphere visualizer instance.
+ }
+ }),
+ 'RX', new Q.Gate({
+
+ symbol: 'Rx',
+ symbolAmazonBraket: 'rx',
+ symbolSvg: '',
+ name: 'X Rotation',
+ nameCss: 'x-rotation',
+ parameters: { "phi" : Math.PI / 2 },
+ updateMatrix$: function( phi ){
+
+ if( Q.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi
+ this.matrix = new Q.Matrix(
+ [ Math.cos( this.parameters[ "phi" ] / 2 ), new Q.ComplexNumber( 0, -Math.sin( this.parameters[ "phi" ] / 2 )) ],
+ [ new Q.ComplexNumber( 0, -Math.sin( this.parameters[ "phi" ] / 2 )), Math.cos( this.parameters[ "phi" ] / 2 )])
+ return this
+ },
+ applyToQubit: function( qubit, phi ){
+
+ if( Q.isUsefulNumber( phi ) !== true ) phi = this.parameters[ "phi" ]
+ const matrix = new Q.Matrix(
+ [ Math.cos( phi / 2 ), new Q.ComplexNumber( 0, -Math.sin( phi / 2 )) ],
+ [ new Q.ComplexNumber( 0, -Math.sin( phi / 2 )), Math.cos( phi / 2 )])
+ return new Q.Qubit( matrix.multiply( qubit ))
+ },
+ has_parameters: true
+ }),
+ 'RY', new Q.Gate({
+
+ symbol: 'Ry',
+ symbolAmazonBraket: 'ry',
+ symbolSvg: '',
+ name: 'Y Rotation',
+ nameCss: 'y-rotation',
+ parameters: { "phi" : Math.PI / 2 },
+ updateMatrix$: function( phi ){
+
+ if( Q.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi
+ this.matrix = new Q.Matrix(
+ [ Math.cos( this.parameters[ "phi" ] / 2 ), -Math.sin( phi / 2 ) ],
+ [ Math.sin( this.parameters[ "phi" ] / 2 ), Math.cos( this.parameters[ "phi" ] / 2 )])
+ return this
+ },
+ applyToQubit: function( qubit, phi ){
+
+ if( Q.isUsefulNumber( phi ) !== true ) phi = this.parameters[ "phi" ]
+ const matrix = new Q.Matrix(
+ [ Math.cos( phi / 2 ), -Math.sin( phi / 2 ) ],
+ [ Math.sin( phi / 2 ), Math.cos( phi / 2 )])
+ return new Q.Qubit( matrix.multiply( qubit ))
+ },
+ has_parameters: true
+ }),
+ 'RZ', new Q.Gate({
+
+ symbol: 'Rz',
+ symbolAmazonBraket: 'rz',
+ symbolSvg: '',
+ name: 'Z Rotation',
+ nameCss: 'z-rotation',
+ parameters: { "phi" : Math.PI / 2 },
+ updateMatrix$: function( phi ){
+
+ if( Q.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi
+ this.matrix = new Q.Matrix(
+ [ Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, -this.parameters[ "phi" ] / 2 )), 0 ],
+ [ 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, this.parameters[ "phi" ] / 2 ))])
+ return this
+ },
+ applyToQubit: function( qubit, phi ){
+
+ if( Q.isUsefulNumber( phi ) !== true ) phi = this.parameters[ "phi" ]
+ const matrix = new Q.Matrix(
+ [ Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, -phi / 2 )), 0 ],
+ [ 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, phi / 2 ))])
+ return new Q.Qubit( matrix.multiply( qubit ))
+ },
+ has_parameters: true
+ }),
+ 'UNITARY', new Q.Gate({
+
+ symbol: 'U',
+ symbolAmazonBraket: 'unitary',
+ symbolSvg: '',
+ name: 'Unitary',
+ nameCss: 'unitary',
+ //toAmazonBraket will have to use the following matrix as an argument for unitary()
+ parameters: { "phi" : Math.PI / 2,
+ "theta" : Math.PI / 2,
+ "lambda" : Math.PI / 2 },
+ updateMatrix$: function( phi, theta, lambda ){
+ //if all are valid, update; otherwise, update none.
+ if( (Q.isUsefulNumber( +phi ) === true) && (Q.isUsefulNumber( +theta ) === true) && (Q.isUsefulNumber( +lambda ) === true) ) {
+ this.parameters[ "phi" ] = +phi;
+ this.parameters[ "theta" ] = +theta;
+ this.parameters[ "lambda" ] = +lambda;
+ }
+ const a = Q.ComplexNumber.multiply(
+ Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, -( this.parameters[ "phi" ] + this.parameters[ "lambda" ] ) / 2 )), Math.cos( this.parameters[ "theta" ] / 2 ))
+ const b = Q.ComplexNumber.multiply(
+ Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, -( this.parameters[ "phi" ] - this.parameters[ "lambda" ] ) / 2 )), -Math.sin( this.parameters[ "theta" ] / 2 ))
+ const c = Q.ComplexNumber.multiply(
+ Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, ( this.parameters[ "phi" ] - this.parameters[ "lambda" ] ) / 2 )), Math.sin( this.parameters[ "theta" ] / 2 ))
+ const d = Q.ComplexNumber.multiply(
+ Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, ( this.parameters[ "phi" ] + this.parameters[ "lambda" ] ) / 2 )), Math.cos( this.parameters[ "theta" ] / 2 ))
+ this.matrix = new Q.Matrix(
+ [ a, b ],
+ [ c, d ])
+ return this
+ },
+ applyToQubit: function( qubit, phi, theta, lambda ){
+ if( Q.isUsefulNumber( phi ) === true ) phi = this.parameters[ "phi" ]
+ if( Q.isUsefulNumber( theta ) === true ) theta = this.parameters[ "theta" ]
+ if( Q.isUsefulNumber( lambda ) === true ) lambda = this.parameters[ "lambda" ]
+ const a = Q.ComplexNumber.multiply(
+ Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, -( phi + lambda ) / 2 )), Math.cos( theta / 2 ));
+ const b = Q.ComplexNumber.multiply(
+ Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, -( phi - lambda ) / 2 )), -Math.sin( theta / 2 ));
+ const c = Q.ComplexNumber.multiply(
+ Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, ( phi - lambda ) / 2 )), Math.sin( theta / 2 ));
+ const d = Q.ComplexNumber.multiply(
+ Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, ( phi + lambda ) / 2 )), Math.cos( theta / 2 ));
+ const matrix = new Q.Matrix(
+ [ a, b ],
+ [ c, d ])
+ return new Q.Qubit( matrix.multiply( qubit ))
+ },
+ has_parameters: true
+ }),
+ 'NOT1_2', new Q.Gate({
+
+ symbol: 'V',
+ symbolAmazonBraket: 'v',
+ symbolSvg: '',
+ name: '√Not',
+ nameCss: 'not1-2',
+ matrix: new Q.Matrix(
+ [ new Q.ComplexNumber( 1, 1 ) / 2, new Q.ComplexNumber( 1, -1 ) / 2 ],
+ [ new Q.ComplexNumber( 1, -1 ) / 2, new Q.ComplexNumber( 1, 1 ) / 2 ])
+ }),
+ 'PI_8_Dagger', new Q.Gate({
+
+ symbol: 'T†',
+ symbolAmazonBraket: 'ti',
+ symbolSvg: '',
+ name: 'PI_8_Dagger',
+ nameCss: 'pi8-dagger',
+ matrix: new Q.Matrix(
+ [ 1, 0 ],
+ [ 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, -Math.PI / 4 )) ])
+ }),
+ 'NOT1_2_Dagger', new Q.Gate({
+
+ symbol: 'V†',
+ symbolAmazonBraket: 'vi',
+ symbolSvg: '',
+ name: '√Not_Dagger',
+ nameCss: 'not1-2-dagger',
+ matrix: new Q.Matrix(
+ [ new Q.ComplexNumber( 1, -1 ) / 2, new Q.ComplexNumber( 1, 1 ) / 2 ],
+ [ new Q.ComplexNumber( 1, 1 ) / 2, new Q.ComplexNumber( 1, -1 ) / 2 ])
+ }),
+ //Note that S, S_Dagger, PI_8, and PI_8_Dagger can all be implemented by applying the PHASE gate
+ //using certain values of phi.
+ //These gates are included for completeness.
+ 'S', new Q.Gate({
+ symbol: 'S*', //Gotta think of a better symbol name...
+ symbolAmazonBraket: 's',
+ symbolSvg: '',
+ name: 'π ÷ 4',
+ nameCss: 'pi4',
+ matrix: new Q.Matrix(
+ [ 1, 0 ],
+ [ 0, new Q.ComplexNumber( 0, 1 ) ])
+ }),
+ 'S_Dagger', new Q.Gate({
+
+ symbol: 'S†',
+ symbolAmazonBraket: 'si',
+ symbolSvg: '',
+ name: 'π ÷ 4 Dagger',
+ nameCss: 'pi4-dagger',
+ matrix: new Q.Matrix(
+ [ 1, 0 ],
+ [ 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, -1 )) ])
+ }),
+ // Operate on 2 qubits.
+ 'SWAP', new Q.Gate({
+
+ symbol: 'S',
+ symbolAmazonBraket: 'swap',
+ symbolSvg: '',
+ name: 'Swap',
+ nameCss: 'swap',
+ parameters: { "phi" : 0.0 },
+ updateMatrix$: function( phi ) {
+
+ if( Q.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi
+ this.matrix = new Q.Matrix(
+ [ 1, 0, 0, 0 ],
+ [ 0, 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, this.parameters[ "phi" ] )), 0 ],
+ [ 0, Q.ComplexNumber.E.power(new Q.ComplexNumber( 0, this.parameters[ "phi" ] )), 0, 0 ],
+ [ 0, 0, 0, 1 ])
+ return this
+ },
+ applyToQubit: function( qubit, phi ) {
+
+ if( Q.isUsefulNumber( phi ) !== true) phi = this.parameters[ "phi" ]
+ const matrix = new Q.Matrix(
+ [ 1, 0, 0, 0 ],
+ [ 0, 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, phi )), 0 ],
+ [ 0, new Q.ComplexNumber( 0, 1 ), 0, 0 ],
+ [ 0, 0, 0, 1 ]
+ )
+ return new Q.Qubit( matrix.multiply( qubit ))
+ },
+ can_be_controlled: true,
+ has_parameters: true,
+ is_multi_qubit: true
+ }),
+ 'SWAP1_2', new Q.Gate({
+
+ symbol: '√S',
+ //symbolAmazonBraket: !!! UNKNOWN !!!
+ symbolSvg: '',
+ name: '√Swap',
+ nameCss: 'swap1-2',
+ matrix: new Q.Matrix(
+ [ 1, 0, 0, 0 ],
+ [ 0, new Q.ComplexNumber( 0.5, 0.5 ), new Q.ComplexNumber( 0.5, -0.5 ), 0 ],
+ [ 0, new Q.ComplexNumber( 0.5, -0.5 ), new Q.ComplexNumber( 0.5, 0.5 ), 0 ],
+ [ 0, 0, 0, 1 ]),
+ is_multi_qubit: true
+ }),
+ 'ISWAP', new Q.Gate({
+
+ symbol: 'iS',
+ symbolAmazonBraket: 'iswap',
+ symbolSvg: '',
+ name: 'Imaginary Swap',
+ nameCss: 'iswap',
+ matrix: new Q.Matrix(
+ [ 1, 0, 0, 0 ],
+ [ 0, 0, new Q.ComplexNumber( 0, 1 ), 0 ],
+ [ 0, new Q.ComplexNumber( 0, 1 ), 0, 0 ],
+ [ 0, 0, 0, 1 ]),
+ is_multi_qubit: true
+ }),
+ 'ISING-XX', new Q.Gate({
+
+ symbol: 'XX',
+ symbolAmazonBraket: 'xx',
+ symbolSvg: '',
+ name: 'Ising XX Coupling',
+ nameCss: 'ising-xx-coupling',
+ parameters: { "phi" : 1 },
+ updateMatrix$: function( phi ) {
+
+ if( Q.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi
+ this.matrix = new Q.Matrix(
+ [ Math.cos( this.parameters[ "phi" ] / 2 ), 0, 0, new Q.ComplexNumber( 0, -Math.sin( this.parameters[ "phi" ] / 2 )) ],
+ [ 0, Math.cos( this.parameters[ "phi" ] / 2 ), new Q.ComplexNumber( 0, -Math.sin( this.parameters[ "phi" ] / 2 )), 0 ],
+ [ 0, new Q.ComplexNumber( 0, -Math.sin( this.parameters[ "phi" ] / 2 )), Math.cos( this.parameters[ "phi" ] / 2 ), 0 ],
+ [ new Q.ComplexNumber( 0, -Math.sin( this.parameters[ "phi" ] / 2 )), 0, 0, Math.cos( this.parameters[ "phi" ] / 2 ) ])
+ return this
+ },
+ applyToQubit: function( qubit, phi ) {
+ if( Q.isUsefulNumber( phi ) !== true) phi = this.parameters[ "phi" ]
+ const matrix = new Q.Matrix(
+ [ Math.cos( phi / 2 ), 0, 0, new Q.ComplexNumber( 0, -Math.sin( phi / 2 )) ],
+ [ 0, Math.cos( phi / 2 ), new Q.ComplexNumber( 0, -Math.sin( phi / 2 )), 0 ],
+ [ 0, new Q.ComplexNumber( 0, -Math.sin( phi / 2 )), Math.cos( phi / 2 ), 0 ],
+ [ new Q.ComplexNumber( 0, -Math.sin( phi / 2 )), 0, 0, Math.cos( phi / 2 ) ]
+ )
+ return new Q.Qubit( matrix.multiply( qubit ))
+ },
+ is_multi_qubit: true,
+ has_parameters: true
+ }),
+ 'ISING-XY', new Q.Gate({
+
+ symbol: 'XY',
+ symbolAmazonBraket: 'xy',
+ symbolSvg: '',
+ name: 'Ising XY Coupling',
+ nameCss: 'ising-xy-coupling',
+ parameters: { "phi" : 1 },
+ updateMatrix$: function( phi ) {
+
+ if( Q.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi
+ this.matrix = new Q.Matrix(
+ [ 1, 0, 0, 0 ],
+ [ 0, Math.cos( this.parameters[ "phi" ] / 2 ), new Q.ComplexNumber( 0, Math.sin( this.parameters[ "phi" ] / 2 )), 0 ],
+ [ 0, new Q.ComplexNumber( 0, Math.sin( this.parameters[ "phi" ] / 2 )), Math.cos( this.parameters[ "phi" ] / 2 ), 0 ],
+ [ 0, 0, 0, 1 ])
+ return this
+ },
+ applyToQubit: function( qubit, phi ) {
+ if( Q.isUsefulNumber( phi ) !== true) phi = this.parameters[ "phi" ]
+ const matrix = new Q.Matrix(
+ [ 1, 0, 0, 0 ],
+ [ 0, Math.cos( phi / 2 ), new Q.ComplexNumber( 0, Math.sin( phi / 2 )), 0 ],
+ [ 0, new Q.ComplexNumber( 0, Math.sin( phi / 2 )), Math.cos( phi / 2 ), 0 ],
+ [ 0, 0, 0, 1 ]
+ )
+ return new Q.Qubit( matrix.multiply( qubit ))
+ },
+ is_multi_qubit: true,
+ has_parameters: true
+ }),
+ 'ISING-YY', new Q.Gate({
+
+ symbol: 'YY',
+ symbolAmazonBraket: 'yy',
+ symbolSvg: '',
+ name: 'Ising YY Coupling',
+ nameCss: 'ising-yy-coupling',
+ parameters: { "phi" : 1 },
+ updateMatrix$: function( phi ) {
+
+ if( Q.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi
+ this.matrix = new Q.Matrix(
+ [ Math.cos( this.parameters[ "phi" ] / 2 ), 0, 0, new Q.ComplexNumber( 0, Math.sin( this.parameters[ "phi" ] / 2 )) ],
+ [ 0, Math.cos( this.parameters[ "phi" ] / 2 ), new Q.ComplexNumber( 0, -Math.sin( this.parameters[ "phi" ] / 2 )), 0 ],
+ [ 0, new Q.ComplexNumber( 0, -Math.sin( this.parameters[ "phi" ] / 2 )), Math.cos( this.parameters[ "phi" ] / 2 ), 0 ],
+ [ new Q.ComplexNumber( 0, -Math.sin( this.parameters[ "phi" ] / 2 )), 0, 0, Math.cos( this.parameters[ "phi" ] / 2 ) ])
+ return this
+ },
+ applyToQubit: function( qubit, phi ) {
+ if( Q.isUsefulNumber( phi ) !== true) phi = this.parameters[ "phi" ]
+ const matrix = new Q.Matrix(
+ [ Math.cos( phi / 2 ), 0, 0, new Q.ComplexNumber( 0, -Math.sin( phi / 2 )) ],
+ [ 0, Math.cos( phi / 2 ), new Q.ComplexNumber( 0, -Math.sin( phi / 2 )), 0 ],
+ [ 0, new Q.ComplexNumber( 0, -Math.sin( phi / 2 )), Math.cos( phi / 2 ), 0 ],
+ [ new Q.ComplexNumber( 0, Math.sin( phi / 2 )), 0, 0, Math.cos( phi / 2 ) ]
+ )
+ return new Q.Qubit( matrix.multiply( qubit ))
+ },
+ is_multi_qubit: true,
+ has_parameters: true
+ }),
+ 'ISING-ZZ', new Q.Gate({
+
+ symbol: 'ZZ',
+ symbolAmazonBraket: 'zz',
+ symbolSvg: '',
+ name: 'Ising ZZ Coupling',
+ nameCss: 'ising-zz-coupling',
+ parameters: { "phi" : 1 },
+ updateMatrix$: function( phi ) {
+
+ if( Q.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi
+ this.matrix = new Q.Matrix(
+ [ Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, this.parameters[ "phi" ] / 2 )), 0, 0, 0 ],
+ [ 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, this.parameters[ "phi" ] / 2 )), 0, 0 ],
+ [ 0, 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, this.parameters[ "phi" ] / 2 )), 0],
+ [ 0, 0, 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, -this.parameters[ "phi" ] / 2 )) ])
+ return this
+ },
+ applyToQubit: function( qubit, phi ) {
+ if( Q.isUsefulNumber( phi ) !== true) phi = this.parameters[ "phi" ]
+ const matrix = new Q.Matrix(
+ [ Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, phi / 2 )), 0, 0, 0 ],
+ [ 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, phi / 2 )), 0, 0 ],
+ [ 0, 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, phi / 2 )), 0],
+ [ 0, 0, 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, -phi / 2 )) ]
+ )
+ return new Q.Qubit( matrix.multiply( qubit ))
+ },
+ is_multi_qubit: true,
+ has_parameters: true
+ }),
+ 'CPhase00', new Q.Gate({
+
+ symbol: '00', //placeholder
+ symbolAmazonBraket: 'cphaseshift00',
+ symbolSvg: '',
+ name: 'Controlled Phase Shift 00',
+ nameCss: 'cphase00',
+ parameters: { "phi" : 1 },
+ updateMatrix$: function( phi ) {
+
+ if( Q.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi
+ this.matrix = new Q.Matrix(
+ [ Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, this.parameters[ "phi" ] )), 0, 0, 0 ],
+ [ 0, 1, 0, 0 ],
+ [ 0, 0, 1, 0 ],
+ [ 0, 0, 0, 1 ])
+ return this
+ },
+ applyToQubit: function( qubit, phi ) {
+ if( Q.isUsefulNumber( phi ) !== true) phi = this.parameters[ "phi" ]
+ const matrix = new Q.Matrix(
+ [ Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, phi )), 0, 0, 0 ],
+ [ 0, 1, 0, 0 ],
+ [ 0, 0, 1, 0 ],
+ [ 0, 0, 0, 1 ]
+ )
+ return new Q.Qubit( matrix.multiply( qubit ))
+ },
+ is_multi_qubit: true,
+ has_parameters: true
+ }),
+ 'CPhase01', new Q.Gate({
+
+ symbol: '01', //placeholder
+ symbolAmazonBraket: 'cphaseshift01',
+ symbolSvg: '',
+ name: 'Controlled Phase Shift 01',
+ nameCss: 'cphase01',
+ parameters: { "phi" : 1 },
+ updateMatrix$: function( phi ) {
+
+ if( Q.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi
+ this.matrix = new Q.Matrix(
+ [ 1, 0, 0, 0 ],
+ [ 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, this.parameters[ "phi" ] )), 0, 0 ],
+ [ 0, 0, 1, 0 ],
+ [ 0, 0, 0, 1 ])
+ return this
+ },
+ applyToQubit: function( qubit, phi ) {
+ if( Q.isUsefulNumber( phi ) !== true) phi = this.parameters[ "phi" ]
+ const matrix = new Q.Matrix(
+ [ 1, 0, 0, 0 ],
+ [ 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, phi)), 0, 0 ],
+ [ 0, 0, 1, 0 ],
+ [ 0, 0, 0, 1 ]
+ )
+ return new Q.Qubit( matrix.multiply( qubit ))
+ },
+ is_multi_qubit: true,
+ has_parameters: true
+ }),
+ 'CPhase10', new Q.Gate({
+
+ symbol: '10', //placeholder
+ symbolAmazonBraket: 'cphaseshift10',
+ symbolSvg: '',
+ name: 'Controlled Phase Shift 10',
+ nameCss: 'cphase01',
+ parameters: { "phi" : 1 },
+ updateMatrix$: function( phi ) {
+
+ if( Q.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi
+ this.matrix = new Q.Matrix(
+ [ 1, 0, 0, 0 ],
+ [ 0, 1, 0, 0 ],
+ [ 0, 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, this.parameters[ "phi" ] )), 0 ],
+ [ 0, 0, 0, 1 ])
+ return this
+ },
+ applyToQubit: function( qubit, phi ) {
+ if( Q.isUsefulNumber( phi ) !== true) phi = this.parameters[ "phi" ]
+ const matrix = new Q.Matrix(
+ [ 1, 0, 0, 0 ],
+ [ 0, 1, 0, 0 ],
+ [ 0, 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, phi)), 0 ],
+ [ 0, 0, 0, 1 ]
+ )
+ return new Q.Qubit( matrix.multiply( qubit ))
+ },
+ is_multi_qubit: true,
+ has_parameters: true
+ }),
+ 'CSWAP', new Q.Gate({
+
+ symbol: 'CSWAP',
+ symbolAmazonBraket: 'cswap',
+ symbolSvg: '',
+ name: 'Controlled Swap',
+ nameCss: 'controlled-swap',
+ matrix: new Q.Matrix(
+ [1, 0, 0, 0, 0, 0, 0, 0],
+ [0, 1, 0, 0, 0, 0, 0, 0],
+ [0, 0, 1, 0, 0, 0, 0, 0],
+ [0, 0, 0, 1, 0, 0, 0, 0],
+ [0, 0, 0, 0, 1, 0, 0, 0],
+ [0, 0, 0, 0, 0, 0, 1, 0],
+ [0, 0, 0, 0, 0, 1, 0, 0],
+ [0, 0, 0, 0, 0, 0, 0, 1]
+ )
+ })
+ /*
+
+
+ All further gates,
+ such as Toffoli (CCNOT)
+ or Fredkin (CSWAP)
+ can be easily constructed
+ from the above gates
+ using Q conveniences.
+
+
+ */
+)
+
+
+
+
+
+
+// Copyright © 2019–2020, Stewart Smith. See LICENSE for details.
+
+
+
+
+Q.History = function( instance ){
+
+ this.instance = instance
+ this.entries = [[{
+
+ redo: {},
+ undo: [{}]
+ }]]
+ this.index = 0
+ this.isRecording = true
+}
+
+
+
+
+Object.assign( Q.History.prototype, {
+
+ assess: function(){
+
+ const instance = this.instance
+ if( this.index > 0 ){
+
+ window.dispatchEvent( new CustomEvent(
+
+ 'Q.History undo is capable', { detail: { instance }}
+ ))
+ }
+ else {
+
+ window.dispatchEvent( new CustomEvent(
+
+ 'Q.History undo is depleted', { detail: { instance }}
+ ))
+ }
+ if( this.index + 1 < this.entries.length ){
+
+ window.dispatchEvent( new CustomEvent(
+
+ 'Q.History redo is capable', { detail: { instance }}
+ ))
+ }
+ else {
+
+ window.dispatchEvent( new CustomEvent(
+
+ 'Q.History redo is depleted', { detail: { instance }}
+ ))
+ }
+ return this
+ },
+ createEntry$: function(){
+
+ this.entries.splice( this.index + 1 )
+ this.entries.push([])
+ this.index = this.entries.length - 1
+ },
+ record$: function( entry ){
+
+
+ // Are we recording this history?
+ // Usually, yes.
+ // But if our history state is “playbackâ€
+ // then we will NOT record this.
+
+ if( this.isRecording ){
+
+ this.entries[ this.index ].push( entry )
+ this.index = this.entries.length - 1
+ this.assess()
+ }
+ return this
+ },
+ step$: function( direction ){
+
+
+ // If we are stepping backward (undo)
+ // we cannot go back further than index === 0
+ // which we would happen if index is already 0
+ // before we subtract 1.
+ // Similarly, if stepping forward (redo)
+ // we cannot go further than index === entries.length - 1
+ // which would happen if the index is already entries.length
+ // before we add 1.
+
+ if(
+ ( direction < 0 && this.index < 1 ) ||
+ ( direction > 0 && this.index > this.entries.length - 2 )
+ ) return false
+
+
+ // Before we step backward (undo) or forward (redo)
+ // we need to turn OFF history recording.
+
+ this.isRecording = false
+
+ const
+ instance = this.instance,
+ command = direction < 0 ? 'undo' : 'redo'
+
+
+ // If we are stepping forward (redo)
+ // then we need to advance the history index
+ // BEFORE we execute.
+
+ if( direction > 0 ) this.index ++
+
+
+ // Take this history entry, which itself is an Array.
+ // It may contain several tasks.
+ // Put my thing down, flip and reverse it.
+ // .ti esrever dna pilf ,nwod gniht ym tuP
+
+ const entry = direction > 0 ?
+ Array.from( this.entries[ this.index ]) :
+ Array.from( this.entries[ this.index ]).reverse()
+
+ entry
+ .reduce( function( tasks, subentry, s ){
+
+ return tasks.concat( subentry[ command ])
+
+ }, [] )
+ .forEach( function( task, i ){
+
+ if( typeof task.func === 'function' ){
+
+ task.func.apply( instance, task.args )
+ }
+ })
+
+
+ // If we are stepping backward (undo)
+ // then we decrement the history index
+ // AFTER the execution above.
+
+ if( direction < 0 ) this.index --
+
+
+ // It’s now safe to turn recording back on.
+
+ this.isRecording = true
+
+
+ // Emit an event so the GUI or anyone else listening
+ // can know if we have available undo or redo commands
+ // based on where or index is.
+
+ this.assess()
+ return true
+ },
+ undo$: function(){ return this.step$( -1 )},
+ redo$: function(){ return this.step$( 1 )},
+ report: function(){
+
+ const argsParse = function( output, entry, i ){
+
+ if( i > 0 ) output += ', '
+ return output + ( typeof entry === 'object' && entry.name ? entry.name : entry )
+ }
+ return this.entries.reduce( function( output, entry, i ){
+
+ output += '\n\n'+ i + ' â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•'+
+ entry.reduce( function( output, entry, i ){
+
+ output += '\n\n '+ i +' ────────────────────────────────────────\n'
+ if( entry.redo ){
+
+ output += '\n ⟳ Redo ── '+ entry.redo.name +' '
+ if( entry.redo.args ) output += entry.redo.args.reduce( argsParse, '' )
+ }
+ output += entry.undo.reduce( function( output, entry, i ){
+
+ output += '\n ⟲ Undo '+ i +' ── '+ entry.name +' '
+ if( entry.args ) output += entry.args.reduce( argsParse, '' )
+ return output
+
+ }, '' )
+
+ return output
+
+ }, '' )
+ return output
+
+ }, 'History entry cursor: '+ this.index )
+ }
+})
+
+
+
+
+// Copyright © 2019–2020, Stewart Smith. See LICENSE for details.
+
+
+
+
+Q.Circuit = function( bandwidth, timewidth ){
+
+
+ // What number Circuit is this
+ // that we’re attempting to make here?
+
+ this.index = Q.Circuit.index ++
+
+
+ // How many qubits (registers) shall we use?
+
+ if( !Q.isUsefulInteger( bandwidth )) bandwidth = 3
+ this.bandwidth = bandwidth
+
+
+ // How many operations can we perform on each qubit?
+ // Each operation counts as one moment; one clock tick.
+
+ if( !Q.isUsefulInteger( timewidth )) timewidth = 5
+ this.timewidth = timewidth
+
+
+ // We’ll start with Horizontal qubits (zeros) as inputs
+ // but we can of course modify this after initialization.
+
+ this.qubits = new Array( bandwidth ).fill( Q.Qubit.HORIZONTAL )
+
+
+ // What operations will we perform on our qubits?
+
+ this.operations = []
+
+
+ // Does our circuit need evaluation?
+ // Certainly, yes!
+ // (And will again each time it is modified.)
+
+ this.needsEvaluation = true
+
+
+ // When our circuit is evaluated
+ // we store those results in this array.
+
+ this.results = []
+ this.matrix = null
+
+
+ // Undo / Redo history.
+
+ this.history = new Q.History( this )
+}
+
+
+
+
+Object.assign( Q.Circuit, {
+
+ index: 0,
+ help: function(){ return Q.help( this )},
+ constants: {},
+ createConstant: Q.createConstant,
+ createConstants: Q.createConstants,
+
+
+ fromText: function( text ){
+
+
+ // This is a quick way to enable `fromText()`
+ // to return a default new Q.Circuit().
+
+ if( text === undefined ) return new Q.Circuit()
+
+ // Is this a String Template -- as opposed to a regular String?
+ // If so, let’s convert it to a regular String.
+ // Yes, this maintains the line breaks.
+
+ if( text.raw !== undefined ) text = ''+text.raw
+ return Q.Circuit.fromTableTransposed(
+
+ text
+ .trim()
+ .split( /\r?\n/ )
+ .filter( function( item ){ return item.length })
+ .map( function( item, r ){
+
+ return item
+ .trim()
+ .split( /[-+\s+=+]/ )
+ .filter( function( item ){ return item.length })
+ .map( function( item, m ){
+
+ //const matches = item.match( /(^\w+)(#(\w+))*(\.(\d+))*/ )
+ const matches = item.match( /(^\w+)(\.(\w+))*(#(\d+))*/ )
+ return {
+
+ gateSymbol: matches[ 1 ],
+ operationMomentId: matches[ 3 ],
+ mappingIndex: +matches[ 5 ]
+ }
+ })
+ })
+ )
+ },
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+// Working out a new syntax here... Patience please!
+
+
+ fromText2: function( text ){
+
+
+ text = `
+ H C C
+ I C1 C1
+ I X1 S1
+ I X1 S1`
+
+
+ // This is a quick way to enable `fromText()`
+ // to return a default new Q.Circuit().
+
+ if( text === undefined ) return new Q.Circuit()
+
+
+ // Is this a String Template -- as opposed to a regular String?
+ // If so, let’s convert it to a regular String.
+ // Yes, this maintains the line breaks.
+
+ if( text.raw !== undefined ) text = ''+text.raw
+
+
+
+ text
+ .trim()
+ .split( /\r?\n/ )
+ .filter( function( item ){ return item.length })
+ .map( function( item, r ){
+
+ return item
+ .trim()
+ .split( /[-+\s+=+]/ )
+ .filter( function( item ){ return item.length })
+ .map( function( item, m ){
+
+ // +++++++++++++++++++++++
+ // need to map LETTER[] optional NUMBER ]
+
+ const matches = item.match( /(^\w+)(\.(\w+))*(#(\d+))*/ )
+
+ //const matches = item.match( /(^\w+)(#(\w+))*(\.(\d+))*/ )
+ // const matches = item.match( /(^\w+)(\.(\w+))*(#(\d+))*/ )
+ // return {
+
+ // gateSymbol: matches[ 1 ],
+ // operationMomentId: matches[ 3 ],
+ // mappingIndex: +matches[ 5 ]
+ // }
+ })
+ })
+
+ },
+
+
+
+//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+
+
+
+
+
+
+
+
+
+
+ fromTableTransposed: function( table ){
+ const
+ bandwidth = table.length,
+ timewidth = table.reduce( function( max, moments ){
+
+ return Math.max( max, moments.length )
+
+ }, 0 ),
+ circuit = new Q.Circuit( bandwidth, timewidth )
+
+ circuit.bandwidth = bandwidth
+ circuit.timewidth = timewidth
+ for( let r = 0; r < bandwidth; r ++ ){
+
+ const registerIndex = r + 1
+ for( let m = 0; m < timewidth; m ++ ){
+
+ const
+ momentIndex = m + 1,
+ operation = table[ r ][ m ]
+ let siblingHasBeenFound = false
+ for( let s = 0; s < r; s ++ ){
+
+ const sibling = table[ s ][ m ]
+ if( operation.gateSymbol === sibling.gateSymbol &&
+ operation.operationMomentId === sibling.operationMomentId &&
+ Q.isUsefulInteger( operation.mappingIndex ) &&
+ Q.isUsefulInteger( sibling.mappingIndex ) &&
+ operation.mappingIndex !== sibling.mappingIndex ){
+
+
+ // We’ve found a sibling !
+ const operationsIndex = circuit.operations.findIndex( function( operation ){
+
+ return (
+
+ operation.momentIndex === momentIndex &&
+ operation.registerIndices.includes( s + 1 )
+ )
+ })
+ // console.log( 'operationsIndex?', operationsIndex )
+ circuit.operations[ operationsIndex ].registerIndices[ operation.mappingIndex ] = registerIndex
+ circuit.operations[ operationsIndex ].isControlled = operation.gateSymbol != '*'// Q.Gate.SWAP.
+ siblingHasBeenFound = true
+ }
+ }
+ if( siblingHasBeenFound === false && operation.gateSymbol !== 'I' ){
+
+ const
+ gate = Q.Gate.findBySymbol( operation.gateSymbol ),
+ registerIndices = []
+
+ if( Q.isUsefulInteger( operation.mappingIndex )){
+
+ registerIndices[ operation.mappingIndex ] = registerIndex
+ }
+ else registerIndices[ 0 ] = registerIndex
+ circuit.operations.push({
+
+ gate,
+ momentIndex,
+ registerIndices,
+ isControlled: false,
+ operationMomentId: operation.operationMomentId
+ })
+ }
+ }
+ }
+ circuit.sort$()
+ return circuit
+ },
+
+
+
+
+ controlled: function( U ){
+
+
+ // we should really just replace this with a nice Matrix.copy({}) command!!!!
+
+ // console.log( 'U?', U )
+
+ const
+ size = U.getWidth(),
+ result = Q.Matrix.createIdentity( size * 2 )
+
+ // console.log( 'U', U.toTsv() )
+ // console.log( 'size', size )
+ // console.log( 'result', result.toTsv() )
+
+ for( let x = 0; x < size; x ++ ){
+
+ for( let y = 0; y < size; y ++ ){
+
+ const v = U.read( x, y )
+ // console.log( `value at ${x}, ${y}`, v )
+ result.write$( x + size, y + size, v )
+ }
+ }
+ return result
+ },
+
+
+
+ // Return transformation over entire nqubit register that applies U to
+ // specified qubits (in order given).
+ // Algorithm from Lee Spector's "Automatic Quantum Computer Programming"
+ // Page 21 in the 2004 PDF?
+ // http://148.206.53.84/tesiuami/S_pdfs/AUTOMATIC%20QUANTUM%20COMPUTER%20PROGRAMMING.pdf
+
+ expandMatrix: function( circuitBandwidth, U, qubitIndices ){
+
+ // console.log( 'EXPANDING THE MATRIX...' )
+ // console.log( 'this one: U', U.toTsv())
+
+ const _qubits = []
+ const n = Math.pow( 2, circuitBandwidth )
+
+
+ // console.log( 'qubitIndices used by this operation:', qubitIndices )
+ // console.log( 'qubits before slice', qubitIndices )
+ // qubitIndices = qubitIndices.slice( 0 )
+ // console.log( 'qubits AFTER slice', qubitIndices )
+
+
+
+
+ for( let i = 0; i < qubitIndices.length; i ++ ){
+
+ //qubitIndices[ i ] = ( circuitBandwidth - 1 ) - qubitIndices[ i ]
+ qubitIndices[ i ] = ( circuitBandwidth - 0 ) - qubitIndices[ i ]
+ }
+ // console.log( 'qubits AFTER manipulation', qubitIndices )
+
+
+ qubitIndices.reverse()
+ for( let i = 0; i < circuitBandwidth; i ++ ){
+
+ if( qubitIndices.indexOf( i ) == -1 ){
+
+ _qubits.push( i )
+ }
+ }
+
+
+ // console.log( 'qubitIndices vs _qubits:' )
+ // console.log( 'qubitIndices', qubitIndices )
+ // console.log( '_qubits', _qubits )
+
+
+
+ const result = new Q.Matrix.createZero( n )
+
+
+ // const X = numeric.rep([n, n], 0);
+ // const Y = numeric.rep([n, n], 0);
+
+
+ let i = n
+ while( i -- ){
+
+ let j = n
+ while( j -- ){
+
+ let
+ bitsEqual = true,
+ k = _qubits.length
+
+ while( k -- ){
+
+ if(( i & ( 1 << _qubits[ k ])) != ( j & ( 1 << _qubits[ k ]))){
+
+ bitsEqual = false
+ break
+ }
+ }
+ if( bitsEqual ){
+
+ // console.log( 'bits ARE equal' )
+ let
+ istar = 0,
+ jstar = 0,
+ k = qubitIndices.length
+
+ while( k -- ){
+
+ const q = qubitIndices[ k ]
+ istar |= (( i & ( 1 << q )) >> q ) << k
+ jstar |= (( j & ( 1 << q )) >> q ) << k
+ }
+ //console.log( 'U.read( istar, jstar )', U.read( istar, jstar ).toText() )
+
+ // console.log( 'before write$', result.toTsv())
+
+ // console.log( 'U.read at ', istar, jstar, '=', U.read( istar, jstar ).toText())
+ result.write$( i, j, U.read( istar, jstar ))
+
+ // console.log( 'after write$', result.toTsv())
+
+ // X[i][j] = U.x[ istar ][ jstar ]
+ // Y[i][j] = U.y[ istar ][ jstar ]
+ }
+ // else console.log('bits NOT equal')
+ }
+ }
+ //return new numeric.T(X, Y);
+
+ // console.log( 'expanded matrix to:', result.toTsv() )
+ return result
+ },
+
+
+
+
+ evaluate: function( circuit ){
+
+
+ // console.log( circuit.toDiagram() )
+
+
+ window.dispatchEvent( new CustomEvent(
+
+ 'Q.Circuit.evaluate began', {
+
+ detail: { circuit }
+ }
+ ))
+
+
+ // Our circuit’s operations must be in the correct order
+ // before we attempt to step through them!
+
+ circuit.sort$()
+
+
+
+ // Create a new matrix (or more precisely, a vector)
+ // that is a 1 followed by all zeros.
+ //
+ // ┌ ┐
+ // │ 1 │
+ // │ 0 │
+ // │ 0 │
+ // │ . │
+ // │ . │
+ // │ . │
+ // └ ┘
+
+ const state = new Q.Matrix( 1, Math.pow( 2, circuit.bandwidth ))
+ state.write$( 0, 0, 1 )
+
+
+
+
+ // Create a state matrix from this circuit’s input qubits.
+
+ // const state2 = circuit.qubits.reduce( function( state, qubit, i ){
+
+ // if( i > 0 ) return state.multiplyTensor( qubit )
+ // else return state
+
+ // }, circuit.qubits[ 0 ])
+ // console.log( 'Initial state', state2.toTsv() )
+ // console.log( 'multiplied', state2.multiplyTensor( state ).toTsv() )
+
+
+
+
+
+ const operationsTotal = circuit.operations.length
+ let operationsCompleted = 0
+ let matrix = circuit.operations.reduce( function( state, operation, i ){
+
+
+
+ let U
+ if( operation.registerIndices.length < Infinity ){
+
+ if( operation.isControlled ){
+ //if( operation.registerIndices.length > 1 ){
+
+ // operation.gate = Q.Gate.PAULI_X
+ // why the F was this hardcoded in there?? what was i thinking?!
+ // OH I KNOW !
+ // that was from back when i represented this as "C" -- its own gate
+ // rather than an X with multiple registers.
+ // so now no need for this "if" block at all.
+ // will remove in a few cycles.
+ }
+ U = operation.gate.matrix
+ }
+ else {
+
+ // This is for Quantum Fourier Transforms (QFT).
+ // Will have to come back to this at a later date!
+ }
+ // console.log( operation.gate.name, U.toTsv() )
+
+
+
+
+
+ // Yikes. May need to separate registerIndices in to controls[] and targets[] ??
+ // Works for now tho.....
+ // Houston we have a problem. Turns out, not every gate with registerIndices.length > 1 is
+ // controlled.
+ // This is a nasty fix, leads to a lot of edge cases. (For instance: hard-coding cswaps...) But just experimenting.
+ if(!operation.gate.is_multi_qubit || (operation.gate.symbol == 'S' && operation.registerIndices.length > 2) && operation.gate.can_be_controlled) {
+ for( let j = 0; j < operation.registerIndices.length - 1; j ++ ){
+
+ U = Q.Circuit.controlled( U )
+ //console.log( 'qubitIndex #', j, 'U = Q.Circuit.controlled( U )', U.toTsv() )
+ }
+ }
+
+
+ // We need to send a COPY of the registerIndices Array
+ // to .expandMatrix()
+ // otherwise it *may* modify the actual registerIndices Array
+ // and wow -- tracking down that bug was painful!
+
+ const registerIndices = operation.registerIndices.slice()
+ state = Q.Circuit.expandMatrix(
+
+ circuit.bandwidth,
+ U,
+ registerIndices
+
+ ).multiply( state )
+
+
+
+ operationsCompleted ++
+ const progress = operationsCompleted / operationsTotal
+
+
+ window.dispatchEvent( new CustomEvent( 'Q.Circuit.evaluate progressed', { detail: {
+
+ circuit,
+ progress,
+ operationsCompleted,
+ operationsTotal,
+ momentIndex: operation.momentIndex,
+ registerIndices: operation.registerIndices,
+ gate: operation.gate.name,
+ state
+
+ }}))
+
+
+ // console.log( `\n\nProgress ... ${ Math.round( operationsCompleted / operationsTotal * 100 )}%`)
+ // console.log( 'Moment .....', operation.momentIndex )
+ // console.log( 'Registers ..', JSON.stringify( operation.registerIndices ))
+ // console.log( 'Gate .......', operation.gate.name )
+ // console.log( 'Intermediate result:', state.toTsv() )
+ // console.log( '\n' )
+
+
+ return state
+
+ }, state )
+
+
+ // console.log( 'result matrix', matrix.toTsv() )
+
+
+
+
+ const outcomes = matrix.rows.reduce( function( outcomes, row, i ){
+
+ outcomes.push({
+
+ state: '|'+ parseInt( i, 10 ).toString( 2 ).padStart( circuit.bandwidth, '0' ) +'⟩',
+ probability: Math.pow( row[ 0 ].absolute(), 2 )
+ })
+ return outcomes
+
+ }, [] )
+
+
+
+ circuit.needsEvaluation = false
+ circuit.matrix = matrix
+ circuit.results = outcomes
+
+
+
+ window.dispatchEvent( new CustomEvent( 'Q.Circuit.evaluate completed', { detail: {
+ // circuit.dispatchEvent( new CustomEvent( 'evaluation complete', { detail: {
+
+ circuit,
+ results: outcomes
+
+ }}))
+
+
+
+
+ return matrix
+ }
+})
+
+
+
+
+
+
+
+Object.assign( Q.Circuit.prototype, {
+
+ clone: function(){
+
+ const
+ original = this,
+ clone = original.copy()
+
+ clone.qubits = original.qubits.slice()
+ clone.results = original.results.slice()
+ clone.needsEvaluation = original.needsEvaluation
+
+ return clone
+ },
+ evaluate$: function(){
+
+ Q.Circuit.evaluate( this )
+ return this
+ },
+ report$: function( length ){
+
+ if( this.needsEvaluation ) this.evaluate$()
+ if( !Q.isUsefulInteger( length )) length = 20
+
+ const
+ circuit = this,
+ text = this.results.reduce( function( text, outcome, i ){
+
+ const
+ probabilityPositive = Math.round( outcome.probability * length ),
+ probabilityNegative = length - probabilityPositive
+
+ return text +'\n'
+ + ( i + 1 ).toString().padStart( Math.ceil( Math.log10( Math.pow( 2, circuit.qubits.length ))), ' ' ) +' '
+ + outcome.state +' '
+ + ''.padStart( probabilityPositive, '█' )
+ + ''.padStart( probabilityNegative, '░' )
+ + Q.round( Math.round( 100 * outcome.probability ), 8 ).toString().padStart( 4, ' ' ) +'% chance'
+
+ }, '' ) + '\n'
+ return text
+ },
+ try$: function(){
+
+ if( this.needsEvaluation ) this.evaluate$()
+
+
+ // We need to “stack” our probabilities from 0..1.
+
+ const outcomesStacked = new Array( this.results.length )
+ this.results.reduce( function( sum, outcome, i ){
+
+ sum += outcome.probability
+ outcomesStacked[ i ] = sum
+ return sum
+
+ }, 0 )
+
+
+ // Now we can pick a random number
+ // and return the first outcome
+ // with a probability equal to or greater than
+ // that random number.
+
+ const
+ randomNumber = Math.random(),
+ randomIndex = outcomesStacked.findIndex( function( index ){
+
+ return randomNumber <= index
+ })
+
+
+ // Output that to the console
+ // but return the random index
+ // so we can pipe that to something else
+ // should we want to :)
+
+ // console.log( this.outcomes[ randomIndex ].state )
+ return randomIndex
+ },
+
+
+
+
+ ////////////////
+ // //
+ // Output //
+ // //
+ ////////////////
+
+
+ // This is absolutely required by toTable.
+
+ sort$: function(){
+
+
+ // Sort this circuit’s operations
+ // primarily by momentIndex,
+ // then by the first registerIndex.
+
+ this.operations.sort( function( a, b ){
+
+ if( a.momentIndex === b.momentIndex ){
+
+
+ // Note that we are NOT sorting registerIndices here!
+ // We are merely asking which set of indices contain
+ // the lowest register index.
+ // If we instead sorted the registerIndices
+ // we could confuse which qubit is the controller
+ // and which is the controlled!
+
+ return Math.min( ...a.registerIndices ) - Math.min( b.registerIndices )
+ }
+ else {
+
+ return a.momentIndex - b.momentIndex
+ }
+ })
+ return this
+ },
+
+
+
+
+
+
+ ///////////////////
+ // //
+ // Exporters //
+ // //
+ ///////////////////
+
+
+ // Many export functions rely on toTable
+ // and toTable itself absolutely relies on
+ // a circuit’s operations to be SORTED correctly.
+ // We could force circuit.sort$() here,
+ // but then toTable would become toTable$
+ // and every exporter that relies on it would
+ // also become destructive.
+
+ toTable: function(){
+
+ const
+ table = new Array( this.timewidth ),
+ circuit = this
+
+
+ // Sure, this is equal to table.length
+ // but isn’t legibility and convenience everything?
+
+ table.timewidth = this.timewidth
+
+
+ // Similarly, this should be equal to table[ 0 ].length
+ // or really table[ i >= 0; i < table.length ].length,
+ // but again, lowest cognitive hurdle is key ;)
+
+ table.bandwidth = this.bandwidth
+
+
+ // First, let’s establish a “blank” table
+ // that contains an identity operation
+ // for each register during each moment.
+
+ table.fill( 0 ).forEach( function( element, index, array ){
+
+ const operations = new Array( circuit.bandwidth )
+ operations.fill( 0 ).forEach( function( element, index, array ){
+
+ array[ index ] = {
+
+ symbol: 'I',
+ symbolDisplay: 'I',
+ name: 'Identity',
+ nameCss: 'identity',
+ gateInputIndex: 0,
+ bandwidth: 0,
+ thisGateAmongMultiQubitGatesIndex: 0,
+ aSiblingIsAbove: false,
+ aSiblingIsBelow: false
+ }
+ })
+ array[ index ] = operations
+ })
+
+
+ // Now iterate through the circuit’s operations list
+ // and note those operations in our table.
+ // NOTE: This relies on operations being pre-sorted with .sort$()
+ // prior to the .toTable() call.
+
+ let
+ momentIndex = 1,
+ multiRegisterOperationIndex = 0,
+ gateTypesUsedThisMoment = {}
+
+ this.operations.forEach( function( operation, operationIndex, operations ){
+
+
+ // We need to keep track of
+ // how many multi-register operations
+ // occur during this moment.
+
+ if( momentIndex !== operation.momentIndex ){
+
+ table[ momentIndex ].gateTypesUsedThisMoment = gateTypesUsedThisMoment
+ momentIndex = operation.momentIndex
+ multiRegisterOperationIndex = 0
+ gateTypesUsedThisMoment = {}
+ }
+ if( operation.registerIndices.length > 1 ){
+
+ table[ momentIndex - 1 ].multiRegisterOperationIndex = multiRegisterOperationIndex
+ multiRegisterOperationIndex ++
+ }
+ if( gateTypesUsedThisMoment[ operation.gate.symbol ] === undefined ){
+
+ gateTypesUsedThisMoment[ operation.gate.symbol ] = 1
+ }
+ else gateTypesUsedThisMoment[ operation.gate.symbol ] ++
+
+
+ // By default, an operation’s CSS name
+ // is its regular name, all lowercase,
+ // with all spaces replaced by hyphens.
+
+ let nameCss = operation.gate.name.toLowerCase().replace( /\s+/g, '-' )
+
+
+ operation.registerIndices.forEach( function( registerIndex, indexAmongSiblings ){
+
+ let isMultiRegisterOperation = false
+ if( operation.registerIndices.length > 1 ){
+
+ isMultiRegisterOperation = true
+ if( indexAmongSiblings === operation.registerIndices.length - 1 ){
+
+ nameCss = 'target'
+ }
+ else {
+
+ nameCss = 'control'
+ }
+
+ // May need to re-visit the code above in consideration of SWAPs.
+
+ }
+ table[ operation.momentIndex - 1 ][ registerIndex - 1 ] = {
+
+ symbol: operation.gate.symbol,
+ symbolDisplay: operation.gate.symbol,
+ name: operation.gate.name,
+ nameCss,
+ operationIndex,
+ momentIndex: operation.momentIndex,
+ registerIndex,
+ isMultiRegisterOperation,
+ multiRegisterOperationIndex,
+ gatesOfThisTypeNow: gateTypesUsedThisMoment[ operation.gate.symbol ],
+ indexAmongSiblings,
+ siblingExistsAbove: Math.min( ...operation.registerIndices ) < registerIndex,
+ siblingExistsBelow: Math.max( ...operation.registerIndices ) > registerIndex
+ }
+ })
+
+/*
+
+
+++++++++++++++++++++++
+
+Non-fatal problem to solve here:
+
+Previously we were concerned with “gates of this type used this moment”
+when we were thinking about CNOT as its own special gate.
+But now that we treat CNOT as just connected X gates,
+we now have situations
+where a moment can have one “CNOT” but also a stand-alone X gate
+and toTable will symbol the “CNOT” as X.0
+(never X.1, because it’s the only multi-register gate that moment)
+but still uses the symbol X.0 instead of just X
+because there’s another stand-alone X there tripping the logic!!!
+
+
+
+
+
+*/
+
+
+ // if( operationIndex === operations.length - 1 ){
+
+ table[ momentIndex - 1 ].gateTypesUsedThisMoment = gateTypesUsedThisMoment
+ // }
+ })
+
+
+
+
+
+
+
+
+
+
+
+ table.forEach( function( moment, m ){
+
+ moment.forEach( function( operation, o ){
+
+ if( operation.isMultiRegisterOperation ){
+
+ if( moment.gateTypesUsedThisMoment[ operation.symbol ] > 1 ){
+
+ operation.symbolDisplay = operation.symbol +'.'+ ( operation.gatesOfThisTypeNow - 1 )
+ }
+ operation.symbolDisplay += '#'+ operation.indexAmongSiblings
+ }
+ })
+ })
+
+
+ // Now we can easily read down each moment
+ // and establish the moment’s character width.
+ // Very useful for text-based diagrams ;)
+
+ table.forEach( function( moment ){
+
+ const maximumWidth = moment.reduce( function( maximumWidth, operation ){
+
+ return Math.max( maximumWidth, operation.symbolDisplay.length )
+
+ }, 1 )
+ moment.maximumCharacterWidth = maximumWidth
+ })
+
+
+ // We can also do this for the table as a whole.
+
+ table.maximumCharacterWidth = table.reduce( function( maximumWidth, moment ){
+
+ return Math.max( maximumWidth, moment.maximumCharacterWidth )
+
+ }, 1 )
+
+
+ // I think we’re done here.
+
+ return table
+ },
+ toText: function( makeAllMomentsEqualWidth ){
+
+ `
+ Create a text representation of this circuit
+ using only common characters,
+ ie. no fancy box-drawing characters.
+ This is the complement of Circuit.fromText()
+ `
+
+ const
+ table = this.toTable(),
+ output = new Array( table.bandwidth ).fill( '' )
+
+ for( let x = 0; x < table.timewidth; x ++ ){
+
+ for( let y = 0; y < table.bandwidth; y ++ ){
+
+ let cellString = table[ x ][ y ].symbolDisplay.padEnd( table[ x ].maximumCharacterWidth, '-' )
+ if( makeAllMomentsEqualWidth && x < table.timewidth - 1 ){
+
+ cellString = table[ x ][ y ].symbolDisplay.padEnd( table.maximumCharacterWidth, '-' )
+ }
+ if( x > 0 ) cellString = '-'+ cellString
+ output[ y ] += cellString
+ }
+ }
+ return '\n'+ output.join( '\n' )
+ // return output.join( '\n' )
+ },
+ toDiagram: function( makeAllMomentsEqualWidth ){
+
+ `
+ Create a text representation of this circuit
+ using fancy box-drawing characters.
+ `
+
+ const
+ scope = this,
+ table = this.toTable(),
+ output = new Array( table.bandwidth * 3 + 1 ).fill( '' )
+
+ output[ 0 ] = ' '
+ scope.qubits.forEach( function( qubit, q ){
+
+ const y3 = q * 3
+ output[ y3 + 1 ] += ' '
+ output[ y3 + 2 ] += 'r'+ ( q + 1 ) +' |'+ qubit.beta.toText().trim() +'⟩─'
+ output[ y3 + 3 ] += ' '
+ })
+ for( let x = 0; x < table.timewidth; x ++ ){
+
+ const padToLength = makeAllMomentsEqualWidth
+ ? table.maximumCharacterWidth
+ : table[ x ].maximumCharacterWidth
+
+ output[ 0 ] += Q.centerText( 'm'+ ( x + 1 ), padToLength + 4 )
+ for( let y = 0; y < table.bandwidth; y ++ ){
+
+ let
+ operation = table[ x ][ y ],
+ first = '',
+ second = '',
+ third = ''
+
+ if( operation.symbol === 'I' ){
+
+ first += ' '
+ second += '──'
+ third += ' '
+
+ first += ' '.padEnd( padToLength )
+ second += Q.centerText( '○', padToLength, '─' )
+ third += ' '.padEnd( padToLength )
+
+ first += ' '
+ if( x < table.timewidth - 1 ) second += '──'
+ else second += ' '
+ third += ' '
+ }
+ else {
+
+ if( operation.isMultiRegisterOperation ){
+
+ first += '╭─'
+ third += '╰─'
+ }
+ else {
+
+ first += '┌─'
+ third += '└─'
+ }
+ second += '┤ '
+
+ first += '─'.padEnd( padToLength, '─' )
+ second += Q.centerText( operation.symbolDisplay, padToLength )
+ third += '─'.padEnd( padToLength, '─' )
+
+
+ if( operation.isMultiRegisterOperation ){
+
+ first += '─╮'
+ third += '─╯'
+ }
+ else {
+
+ first += '─┐'
+ third += '─┘'
+ }
+ second += x < table.timewidth - 1 ? ' ├' : ' │'
+
+ if( operation.isMultiRegisterOperation ){
+
+ let n = ( operation.multiRegisterOperationIndex * 2 ) % ( table[ x ].maximumCharacterWidth + 1 ) + 1
+ if( operation.siblingExistsAbove ){
+
+ first = first.substring( 0, n ) +'┴'+ first.substring( n + 1 )
+ }
+ if( operation.siblingExistsBelow ){
+
+ third = third.substring( 0, n ) +'┬'+ third.substring( n + 1 )
+ }
+ }
+ }
+ const y3 = y * 3
+ output[ y3 + 1 ] += first
+ output[ y3 + 2 ] += second
+ output[ y3 + 3 ] += third
+ }
+ }
+ return '\n'+ output.join( '\n' )
+ },
+
+
+
+
+ // Oh yes my friends... WebGL is coming!
+
+ toShader: function(){
+
+ },
+ toGoogleCirq: function(){
+/*
+
+
+cirq.GridQubit(4,5)
+
+https://cirq.readthedocs.io/en/stable/tutorial.html
+
+*/
+ const header = `import cirq`
+
+ return headers
+ },
+ toAmazonBraket: function(){
+ let is_valid_braket_circuit = true
+ const header = `import boto3
+from braket.aws import AwsDevice
+from braket.circuits import Circuit
+
+my_bucket = f"amazon-braket-Your-Bucket-Name" # the name of the bucket
+my_prefix = "Your-Folder-Name" # the name of the folder in the bucket
+s3_folder = (my_bucket, my_prefix)\n
+device = LocalSimulator()\n\n`
+//TODO (ltnln): Syntax is different for simulators and actual quantum computers. Should there be a default? Should there be a way to change?
+//vs an actual quantum computer? May not be necessary.
+ let variables = ''
+ let num_unitaries = 0
+ //`qjs_circuit = Circuit().h(0).cnot(0,1)`
+ //ltnln change: from gate.AmazonBraketName -> gate.symbolAmazonBraket
+ let circuit = this.operations.reduce( function( string, operation ){
+ let awsGate = operation.gate.symbolAmazonBraket !== undefined ?
+ operation.gate.symbolAmazonBraket :
+ operation.gate.symbol.substr( 0, 1 ).toLowerCase()
+ if( operation.gate.symbolAmazonBraket === undefined ) is_valid_braket_circuit = false
+ if( operation.gate.symbol === 'X' ) {
+ if( operation.registerIndices.length === 1 ) awsGate = operation.gate.symbolAmazonBraket
+ else if( operation.registerIndices.length === 2 ) awsGate = 'cnot'
+ else if( operation.registerIndices.length === 3) awsGate = 'ccnot'
+ else is_valid_braket_circuit = false
+ }
+
+ else if( operation.gate.symbol === 'S' ) {
+ if( operation.gate.parameters["phi"] === 0 ) {
+ awsGate = operation.registerIndices.length == 2 ? awsGate : "cswap"
+ return string +'.'+ awsGate +'(' +
+ operation.registerIndices.reduce( function( string, registerIndex, r ){
+
+ return string + (( r > 0 ) ? ',' : '' ) + ( registerIndex - 1 )
+
+ }, '' ) + ')'
+ }
+ awsGate = 'pswap'
+ }
+ //ltnln note: removed the if( operation.gate.symbol == '*') branch as it should be covered by
+ //the inclusion of the CURSOR gate.
+ else if( operation.gate.symbol === 'Y' || operation.gate.symbol === 'Z' || operation.gate.symbol === 'P' ) {
+ if( operation.registerIndices.length === 1) awsGate = operation.gate.symbolAmazonBraket
+ else if( operation.registerIndices.length === 2 ) awsGate = (operation.gate.symbol === 'Y') ? 'cy' : (operation.gate.symbol === 'Z') ? 'cz' : 'cphaseshift'
+ else is_valid_braket_circuit = false
+ }
+ //for all unitary gates, there must be a line of code to initialize the matrix for use
+ //in Braket's .u(matrix=my_unitary, targets[0]) function
+ else if( operation.gate.symbol === 'U') {
+ //check that this truly works as a unique id
+ is_valid_braket_circuit &= operation.registerIndices.length === 1
+ const new_matrix = `unitary_` + num_unitaries
+ num_unitaries++
+ const a = Q.ComplexNumber.toText(Math.cos(-(operation.gate.parameters[ "phi" ] + operation.gate.parameters[ "lambda" ])*Math.cos(operation.gate.parameters[ "theta" ] / 2) / 2),
+ Math.sin(-(operation.gate.parameters[ "phi" ] + operation.gate.parameters[ "lambda" ])*Math.cos(operation.gate.parameters[ "theta" ] / 2) / 2))
+ .replace('i', 'j')
+ const b = Q.ComplexNumber.toText(-Math.cos(-(operation.gate.parameters[ "phi" ] - operation.gate.parameters[ "lambda" ])*Math.sin(operation.gate.parameters[ "theta" ] / 2) / 2),
+ -Math.sin(-(operation.gate.parameters[ "phi" ] - operation.gate.parameters[ "lambda" ])*Math.sin(operation.gate.parameters[ "theta" ] / 2)) / 2)
+ .replace('i', 'j')
+ const c = Q.ComplexNumber.toText(Math.cos((operation.gate.parameters[ "phi" ] - operation.gate.parameters[ "lambda" ])*Math.sin(operation.gate.parameters[ "theta" ] / 2) / 2),
+ -Math.sin((operation.gate.parameters[ "phi" ] - operation.gate.parameters[ "lambda" ])*Math.sin(operation.gate.parameters[ "theta" ] / 2)) / 2)
+ .replace('i', 'j')
+ const d = Q.ComplexNumber.toText(Math.cos((operation.gate.parameters[ "phi" ] + operation.gate.parameters[ "lambda" ])*Math.cos(operation.gate.parameters[ "theta" ] / 2) / 2),
+ Math.sin((operation.gate.parameters[ "phi" ] + operation.gate.parameters[ "lambda" ])*Math.cos(operation.gate.parameters[ "theta" ] / 2)) / 2)
+ .replace('i', 'j')
+ variables += new_matrix + ` = np.array(` +
+ `[[` + a + ', ' + b + `],`+
+ `[` + c + ', ' + d + `]])\n`
+ return string +'.'+ awsGate +'(' + new_matrix +','+
+ operation.registerIndices.reduce( function( string, registerIndex, r ){
+
+ return string + (( r > 0 ) ? ',' : '' ) + ( registerIndex - 1 )
+
+ }, '' ) + ')'
+ }
+ // I believe this line should ensure that we don't include any controlled single-qubit gates that aren't allowed in Braket.
+ // The registerIndices.length > 1 technically shouldn't be necessary, but if changes are made later, it's just for safety.
+ else is_valid_braket_circuit &= (operation.registerIndices.length === 1) || ( operation.registerIndices.length > 1 && operation.gate.is_multi_qubit )
+ return string +'.'+ awsGate +'(' +
+ operation.registerIndices.reduce( function( string, registerIndex, r ){
+
+ return string + (( r > 0 ) ? ',' : '' ) + ( registerIndex - 1 )
+
+ }, '' ) + ((operation.gate.has_parameters) ?
+ Object.values( operation.gate.parameters ).reduce( function( string, parameter ) {
+ return string + "," + parameter
+ }, '')
+ : '') + ')'
+
+ }, 'qjs_circuit = Circuit()' )
+ variables += '\n'
+ if( this.operations.length === 0 ) circuit += '.i(0)'// Quick fix to avoid an error here!
+
+ const footer = `\n\ntask = device.run(qjs_circuit, s3_folder, shots=100)
+print(task.result().measurement_counts)`
+ return is_valid_braket_circuit ? header + variables + circuit + footer : `###This circuit is not representable as a Braket circuit!###`
+ },
+ toLatex: function(){
+
+ /*
+
+ \Qcircuit @C=1em @R=.7em {
+ & \ctrl{2} & \targ & \gate{U} & \qw \\
+ & \qw & \ctrl{-1} & \qw & \qw \\
+ & \targ & \ctrl{-1} & \ctrl{-2} & \qw \\
+ & \qw & \ctrl{-1} & \qw & \qw
+ }
+
+ No "&"" means it’s an input. So could also do this:
+ \Qcircuit @C=1.4em @R=1.2em {
+
+ a & i \\
+ 1 & x
+ }
+ */
+
+ return '\\Qcircuit @C=1.0em @R=0.7em {\n' +
+ this.toTable()
+ .reduce( function( array, moment, m ){
+
+ moment.forEach( function( operation, o, operations ){
+
+ let command = 'qw'
+ if( operation.symbol !== 'I' ){
+
+ if( operation.isMultiRegisterOperation ){
+
+ if( operation.indexAmongSiblings === 0 ){
+
+ if( operation.symbol === 'X' ) command = 'targ'
+ else command = operation.symbol.toLowerCase()
+ }
+ else if( operation.indexAmongSiblings > 0 ) command = 'ctrl{?}'
+ }
+ else command = operation.symbol.toLowerCase()
+ }
+ operations[ o ].latexCommand = command
+ })
+ const maximumCharacterWidth = moment.reduce( function( maximumCharacterWidth, operation ){
+
+ return Math.max( maximumCharacterWidth, operation.latexCommand.length )
+
+ }, 0 )
+ moment.forEach( function( operation, o ){
+
+ array[ o ] += '& \\'+ operation.latexCommand.padEnd( maximumCharacterWidth ) +' '
+ })
+ return array
+
+ }, new Array( this.bandwidth ).fill( '\n\t' ))
+ .join( '\\\\' ) +
+ '\n}'
+ },
+
+
+
+
+
+
+ //////////////
+ // //
+ // Edit //
+ // //
+ //////////////
+
+
+ get: function( momentIndex, registerIndex ){
+
+ return this.operations.find( function( op ){
+
+ return op.momentIndex === momentIndex &&
+ op.registerIndices.includes( registerIndex )
+ })
+ },
+ clear$: function( momentIndex, registerIndices ){
+
+ const circuit = this
+
+
+ // Validate our arguments.
+
+ if( arguments.length !== 2 )
+ Q.warn( `Q.Circuit.clear$ expected 2 arguments but received ${ arguments.length }.` )
+ if( Q.isUsefulInteger( momentIndex ) !== true )
+ return Q.error( `Q.Circuit attempted to clear an input on Circuit #${ circuit.index } using an invalid moment index:`, momentIndex )
+ if( Q.isUsefulInteger( registerIndices )) registerIndices = [ registerIndices ]
+ if( registerIndices instanceof Array !== true )
+ return Q.error( `Q.Circuit attempted to clear an input on Circuit #${ circuit.index } using an invalid register indices array:`, registerIndices )
+
+
+ // Let’s find any operations
+ // with a footprint at this moment index and one of these register indices
+ // and collect not only their content, but their index in the operations array.
+ // (We’ll need that index to splice the operations array later.)
+
+ const foundOperations = circuit.operations.reduce( function( filtered, operation, o ){
+
+ if( operation.momentIndex === momentIndex &&
+ operation.registerIndices.some( function( registerIndex ){
+
+ return registerIndices.includes( registerIndex )
+ })
+ ) filtered.push({
+
+ index: o,
+ momentIndex: operation.momentIndex,
+ registerIndices: operation.registerIndices,
+ gate: operation.gate
+ })
+ return filtered
+
+ }, [] )
+
+
+ // Because we held on to each found operation’s index
+ // within the circuit’s operations array
+ // we can now easily splice them out of the array.
+
+ foundOperations.reduce( function( deletionsSoFar, operation ){
+
+ circuit.operations.splice( operation.index - deletionsSoFar, 1 )
+ return deletionsSoFar + 1
+
+ }, 0 )
+
+
+ // IMPORTANT!
+ // Operations must be sorted properly
+ // for toTable to work reliably with
+ // multi-register operations!!
+
+ this.sort$()
+
+
+ // Let’s make history.
+
+ if( foundOperations.length ){
+
+ this.history.record$({
+
+ redo: {
+
+ name: 'clear$',
+ func: circuit.clear$,
+ args: Array.from( arguments )
+ },
+ undo: foundOperations.reduce( function( undos, operation ){
+
+ undos.push({
+
+ name: 'set$',
+ func: circuit.set$,
+ args: [
+
+ operation.gate,
+ operation.momentIndex,
+ operation.registerIndices
+ ]
+ })
+ return undos
+
+ }, [] )
+ })
+
+
+ // Let anyone listening,
+ // including any circuit editor interfaces,
+ // know about what we’ve just completed here.
+
+ foundOperations.forEach( function( operation ){
+
+ window.dispatchEvent( new CustomEvent(
+
+ 'Q.Circuit.clear$', { detail: {
+
+ circuit,
+ momentIndex,
+ registerIndices: operation.registerIndices
+ }}
+ ))
+ })
+ }
+
+
+ // Enable that “fluent interface” method chaining :)
+
+ return circuit
+ },
+
+
+ setProperty$: function( key, value ){
+
+ this[ key ] = value
+ return this
+ },
+ setName$: function( name ){
+
+ if( typeof name === 'function' ) name = name()
+ return this.setProperty$( 'name', name )
+ },
+
+
+ set$: function( gate, momentIndex, registerIndices, parameters = {} ){
+
+ const circuit = this
+ // Is this a valid gate?
+ // We clone the gate rather than using the constant; this way, if we change it's parameters, we don't change the constant.
+ if( typeof gate === 'string' ) gate = Q.Gate.prototype.clone( Q.Gate.findBySymbol( gate ) )
+ if( gate instanceof Q.Gate !== true ) return Q.error( `Q.Circuit attempted to add a gate (${ gate }) to circuit #${ this.index } at moment #${ momentIndex } that is not a gate:`, gate )
+
+
+ // Is this a valid moment index?
+
+ if( Q.isUsefulNumber( momentIndex ) !== true ||
+ Number.isInteger( momentIndex ) !== true ||
+ momentIndex < 1 || momentIndex > this.timewidth ){
+
+ return Q.error( `Q.Circuit attempted to add a gate to circuit #${ this.index } at a moment index that is not valid:`, momentIndex )
+ }
+
+
+ // Are these valid register indices?
+
+ if( typeof registerIndices === 'number' ) registerIndices = [ registerIndices ]
+ if( registerIndices instanceof Array !== true ) return Q.error( `Q.Circuit attempted to add a gate to circuit #${ this.index } at moment #${ momentIndex } with an invalid register indices array:`, registerIndices )
+ if( registerIndices.length === 0 ) return Q.error( `Q.Circuit attempted to add a gate to circuit #${ this.index } at moment #${ momentIndex } with an empty register indices array:`, registerIndices )
+ if( registerIndices.reduce( function( accumulator, registerIndex ){
+
+ // console.log(accumulator &&
+ // registerIndex > 0 &&
+ // registerIndex <= circuit.bandwidth)
+ return (
+
+ accumulator &&
+ registerIndex > 0 &&
+ registerIndex <= circuit.bandwidth
+ )
+
+ }, false )){
+
+ return Q.warn( `Q.Circuit attempted to add a gate to circuit #${ this.index } at moment #${ momentIndex } with some out of range qubit indices:`, registerIndices )
+ }
+
+
+ // Ok, now we can check if this set$ command
+ // is redundant.
+
+ const
+ isRedundant = !!circuit.operations.find( function( operation ){
+
+ return (
+
+ momentIndex === operation.momentIndex &&
+ gate === operation.gate &&
+ registerIndices.length === operation.registerIndices.length &&
+ registerIndices.every( val => operation.registerIndices.includes( val ))
+ )
+ })
+
+
+ // If it’s NOT redundant
+ // then we’re clear to proceed.
+
+ if( isRedundant !== true ){
+
+
+ // If there’s already an operation here,
+ // we’d better get rid of it!
+ // This will also entirely remove any multi-register operations
+ // that happen to have a component at this moment / register.
+
+ this.clear$( momentIndex, registerIndices )
+
+
+ // Finally.
+ // Finally we can actually set this operation.
+ // Aren’t you glad we handle all this for you?
+
+ const
+ //TODO: For ltnln (have to fix)
+ // a) allow users to control whatever they want! Just because it's not allowed in Braket
+ // doesn't mean they shouldn't be allowed to do it in Q! (Probably fixable by adjusting toAmazonBraket)
+ // b) Controlling a multi_qubit gate will not treat the control icon like a control gate!
+ isControlled = registerIndices.length > 1 && gate !== Q.Gate.SWAP && gate.can_be_controlled !== undefined
+ operation = {
+
+ gate,
+ momentIndex,
+ registerIndices,
+ isControlled
+ }
+ //perform parameter update here!!!
+ Object.keys(parameters).forEach( element => { parameters[element] = +parameters[element] })
+ if(gate.has_parameters) gate.updateMatrix$.apply( gate, Object.values(parameters) )
+ this.operations.push( operation )
+
+
+ // IMPORTANT!
+ // Operations must be sorted properly
+ // for toTable to work reliably with
+ // multi-register operations!!
+
+ this.sort$()
+
+
+ // Let’s make history.
+ const redo_args = Array.from( arguments )
+ Object.assign( redo_args[ redo_args.length - 1 ], parameters )
+ this.history.record$({
+
+ redo: {
+
+ name: 'set$',
+ func: circuit.set$,
+ args: redo_args
+ },
+ undo: [{
+
+ name: 'clear$',
+ func: circuit.clear$,
+ args: [ momentIndex, registerIndices ]
+ }]
+ })
+
+
+ // Emit an event that we have set an operation
+ // on this circuit.
+
+ window.dispatchEvent( new CustomEvent(
+
+ 'Q.Circuit.set$', { detail: {
+
+ circuit,
+ operation
+ }}
+ ))
+ }
+ return circuit
+ },
+
+
+
+
+ determineRanges: function( options ){
+
+ if( options === undefined ) options = {}
+ let {
+
+ qubitFirstIndex,
+ qubitRange,
+ qubitLastIndex,
+ momentFirstIndex,
+ momentRange,
+ momentLastIndex
+
+ } = options
+
+ if( typeof qubitFirstIndex !== 'number' ) qubitFirstIndex = 0
+ if( typeof qubitLastIndex !== 'number' && typeof qubitRange !== 'number' ) qubitLastIndex = this.bandwidth
+ if( typeof qubitLastIndex !== 'number' && typeof qubitRange === 'number' ) qubitLastIndex = qubitFirstIndex + qubitRange
+ else if( typeof qubitLastIndex === 'number' && typeof qubitRange !== 'number' ) qubitRange = qubitLastIndex - qubitFirstIndex
+ else return Q.error( `Q.Circuit attempted to copy a circuit but could not understand what qubits to copy.` )
+
+ if( typeof momentFirstIndex !== 'number' ) momentFirstIndex = 0
+ if( typeof momentLastIndex !== 'number' && typeof momentRange !== 'number' ) momentLastIndex = this.timewidth
+ if( typeof momentLastIndex !== 'number' && typeof momentRange === 'number' ) momentLastIndex = momentFirstIndex + momentRange
+ else if( typeof momentLastIndex === 'number' && typeof momentRange !== 'number' ) momentRange = momentLastIndex - momentFirstIndex
+ else return Q.error( `Q.Circuit attempted to copy a circuit but could not understand what moments to copy.` )
+
+ Q.log( 0.8,
+
+ '\nQ.Circuit copy operation:',
+ '\n\n qubitFirstIndex', qubitFirstIndex,
+ '\n qubitLastIndex ', qubitLastIndex,
+ '\n qubitRange ', qubitRange,
+ '\n\n momentFirstIndex', momentFirstIndex,
+ '\n momentLastIndex ', momentLastIndex,
+ '\n momentRange ', momentRange,
+ '\n\n'
+ )
+
+ return {
+
+ qubitFirstIndex,
+ qubitRange,
+ qubitLastIndex,
+ momentFirstIndex,
+ momentRange,
+ momentLastIndex
+ }
+ },
+
+
+ copy: function( options, isACutOperation ){
+
+ const original = this
+ let {
+
+ registerFirstIndex,
+ registerRange,
+ registerLastIndex,
+ momentFirstIndex,
+ momentRange,
+ momentLastIndex
+
+ } = this.determineRanges( options )
+
+ const copy = new Q.Circuit( registerRange, momentRange )
+
+ original.operations
+ .filter( function( operation ){
+
+ return ( operation.registerIndices.every( function( registerIndex ){
+
+ return (
+
+ operation.momentIndex >= momentFirstIndex &&
+ operation.momentIndex < momentLastIndex &&
+ operation.registerIndex >= registerFirstIndex &&
+ operation.registerIndex < registerLastIndex
+ )
+ }))
+ })
+ .forEach( function( operation ){
+
+ const adjustedRegisterIndices = operation.registerIndices.map( function( registerIndex ){
+
+ return registerIndex - registerFirstIndex
+ })
+ copy.set$(
+
+ operation.gate,
+ 1 + m - momentFirstIndex,
+ adjustedRegisterIndices
+ )
+ })
+
+
+ // The cut$() operation just calls copy()
+ // with the following boolean set to true.
+ // If this is a cut we need to
+ // replace all gates in this area with identity gates.
+
+ // UPDATE !!!!
+ // will come back to fix!!
+ // with new style it's now just a matter of
+ // splicing out these out of circuit.operations
+
+
+
+ if( isACutOperation === true ){
+
+ /*
+ for( let m = momentFirstIndex; m < momentLastIndex; m ++ ){
+
+ original.moments[ m ] = new Array( original.bandwidth )
+ .fill( 0 )
+ .map( function( qubit, q ){
+
+ return {
+
+ gate: Q.Gate.IDENTITY,
+ registerIndices: [ q ]
+ }
+ })
+ }*/
+ }
+ return copy
+ },
+ cut$: function( options ){
+
+ return this.copy( options, true )
+ },
+
+
+
+
+
+
+
+ /*
+
+
+
+
+ If covers all moments for 1 or more qubits then
+ 1. go through each moment and remove those qubits
+ 2. remove hanging operations. (right?? don’t want them?)
+
+
+
+
+ */
+
+ spliceCut$: function( options ){
+
+ let {
+
+ qubitFirstIndex,
+ qubitRange,
+ qubitLastIndex,
+ momentFirstIndex,
+ momentRange,
+ momentLastIndex
+
+ } = this.determineRanges( options )
+
+
+ // Only three options are valid:
+ // 1. Selection area covers ALL qubits for a series of moments.
+ // 2. Selection area covers ALL moments for a seriies of qubits.
+ // 3. Both of the above (splice the entire circuit).
+
+ if( qubitRange !== this.bandwidth &&
+ momentRange !== this.timewidth ){
+
+ return Q.error( `Q.Circuit attempted to splice circuit #${this.index} by an area that did not include all qubits _or_ all moments.` )
+ }
+
+
+ // If the selection area covers all qubits for 1 or more moments
+ // then splice the moments array.
+
+ if( qubitRange === this.bandwidth ){
+
+
+ // We cannot use Array.prototype.splice() for this
+ // because we need a DEEP copy of the array
+ // and splice() will only make a shallow copy.
+
+ this.moments = this.moments.reduce( function( accumulator, moment, m ){
+
+ if( m < momentFirstIndex - 1 || m >= momentLastIndex - 1 ) accumulator.push( moment )
+ return accumulator
+
+ }, [])
+ this.timewidth -= momentRange
+
+ //@@ And how do we implement splicePaste$() here?
+ }
+
+
+ // If the selection area covers all moments for 1 or more qubits
+ // then iterate over each moment and remove those qubits.
+
+ if( momentRange === this.timewidth ){
+
+
+ // First, let’s splice the inputs array.
+
+ this.inputs.splice( qubitFirstIndex, qubitRange )
+ //@@ this.inputs.splice( qubitFirstIndex, qubitRange, qubitsToPaste?? )
+
+
+ // Now we can make the proper adjustments
+ // to each of our moments.
+
+ this.moments = this.moments.map( function( operations ){
+
+
+ // Remove operations that pertain to the removed qubits.
+ // Renumber the remaining operations’ qubitIndices.
+
+ return operations.reduce( function( accumulator, operation ){
+
+ if( operation.qubitIndices.every( function( index ){
+
+ return index < qubitFirstIndex || index >= qubitLastIndex
+
+ })) accumulator.push( operation )
+ return accumulator
+
+ }, [])
+ .map( function( operation ){
+
+ operation.qubitIndices = operation.qubitIndices.map( function( index ){
+
+ return index >= qubitLastIndex ? index - qubitRange : index
+ })
+ return operation
+ })
+ })
+ this.bandwidth -= qubitRange
+ }
+
+
+ // Final clean up.
+
+ this.removeHangingOperations$()
+ this.fillEmptyOperations$()
+
+
+ return this// Or should we return the cut area?!
+ },
+ splicePaste$: function(){
+
+
+ },
+
+
+
+
+
+ // This is where “hanging operations” get interesting!
+ // when you paste one circuit in to another
+ // and that clipboard circuit has hanging operations
+ // those can find a home in the circuit its being pasted in to!
+
+
+ paste$: function( other, atMoment = 0, atQubit = 0, shouldClean = true ){
+
+ const scope = this
+ this.timewidth = Math.max( this.timewidth, atMoment + other.timewidth )
+ this.bandwidth = Math.max( this.bandwidth, atQubit + other.bandwidth )
+ this.ensureMomentsAreReady$()
+ this.fillEmptyOperations$()
+ other.moments.forEach( function( moment, m ){
+
+ moment.forEach( function( operation ){
+
+ //console.log( 'past over w this:', m + atMoment, operation )
+
+ scope.set$(
+
+ operation.gate,
+ m + atMoment + 1,
+ operation.qubitIndices.map( function( qubitIndex ){
+
+ return qubitIndex + atQubit
+ })
+ )
+ })
+ })
+ if( shouldClean ) this.removeHangingOperations$()
+ this.fillEmptyOperations$()
+ return this
+ },
+ pasteInsert$: function( other, atMoment, atQubit ){
+
+ // if( other.alphandwidth !== this.bandwidth &&
+ // other.timewidth !== this.timewidth ) return Q.error( 'Q.Circuit attempted to pasteInsert Circuit A', other, 'in to circuit B', this, 'but neither their bandwidth or timewidth matches.' )
+
+
+
+
+ if( shouldClean ) this.removeHangingOperations$()
+ this.fillEmptyOperations$()
+ return this
+
+ },
+ expand$: function(){
+
+ // expand either bandwidth or timewidth, fill w identity
+
+
+ this.fillEmptyOperations$()
+ return thiis
+ },
+
+
+
+
+
+
+
+ trim$: function( options ){
+
+ `
+ Edit this circuit by trimming off moments, qubits, or both.
+ We could have implemented trim$() as a wrapper around copy$(),
+ similar to how cut$ is a wrapper around copy$().
+ But this operates on the existing circuit
+ instead of returning a new one and returning that.
+ `
+
+ let {
+
+ qubitFirstIndex,
+ qubitRange,
+ qubitLastIndex,
+ momentFirstIndex,
+ momentRange,
+ momentLastIndex
+
+ } = this.determineRanges( options )
+
+
+ // First, trim the moments down to desired size.
+
+ this.moments = this.moments.slice( momentFirstIndex, momentLastIndex )
+ this.timewidth = momentRange
+
+
+ // Then, trim the bandwidth down.
+
+ this.inputs = this.inputs.slice( qubitFirstIndex, qubitLastIndex )
+ this.bandwidth = qubitRange
+
+
+ // Finally, remove all gates where
+ // gate’s qubit indices contain an index < qubitFirstIndex,
+ // gate’s qubit indices contain an index > qubitLastIndex,
+ // and fill those holes with Identity gates.
+
+ this.removeHangingOperations$()
+ this.fillEmptyOperations$()
+
+ return this
+ }
+})
+
+
+
+
+
+
+
+// Against my predilection for verbose clarity...
+// I offer you super short convenience methods
+// that do NOT use the $ suffix to delcare they are destructive.
+// Don’t shoot your foot off.
+
+Object.entries( Q.Gate.constants ).forEach( function( entry ){
+
+ const
+ gateConstantName = entry[ 0 ],
+ gate = entry[ 1 ],
+ set$ = function( momentIndex, registerIndexOrIndices ){
+
+ this.set$( gate, momentIndex, registerIndexOrIndices )
+ return this
+ }
+ Q.Circuit.prototype[ gateConstantName ] = set$
+ Q.Circuit.prototype[ gate.symbol ] = set$
+ Q.Circuit.prototype[ gate.symbol.toLowerCase() ] = set$
+})
+
+
+
+/*
+const bells = [
+
+
+ // Verbose without shortcuts.
+
+ new Q.Circuit( 2, 2 )
+ .set$( Q.Gate.HADAMARD, 1, [ 1 ])
+ .set$( Q.Gate.PAULI_X, 2, [ 1 , 2 ]),
+
+ new Q.Circuit( 2, 2 )
+ .set$( Q.Gate.HADAMARD, 1, 1 )
+ .set$( Q.Gate.PAULI_X, 2, [ 1 , 2 ]),
+
+
+ // Uses Q.Gate.findBySymbol() to lookup gates.
+
+ new Q.Circuit( 2, 2 )
+ .set$( 'H', 1, [ 1 ])
+ .set$( 'X', 2, [ 1 , 2 ]),
+
+ new Q.Circuit( 2, 2 )
+ .set$( 'H', 1, 1 )
+ .set$( 'X', 2, [ 1 , 2 ]),
+
+
+ // Convenience gate functions -- constant name.
+
+ new Q.Circuit( 2, 2 )
+ .HADAMARD( 1, [ 1 ])
+ .PAULI_X( 2, [ 1, 2 ]),
+
+ new Q.Circuit( 2, 2 )
+ .HADAMARD( 1, 1 )
+ .PAULI_X( 2, [ 1, 2 ]),
+
+
+ // Convenience gate functions -- uppercase symbol.
+
+ new Q.Circuit( 2, 2 )
+ .H( 1, [ 1 ])
+ .X( 2, [ 1, 2 ]),
+
+ new Q.Circuit( 2, 2 )
+ .H( 1, 1 )
+ .X( 2, [ 1, 2 ]),
+
+
+ // Convenience gate functions -- lowercase symbol.
+
+ new Q.Circuit( 2, 2 )
+ .h( 1, [ 1 ])
+ .x( 2, [ 1, 2 ]),
+
+ new Q.Circuit( 2, 2 )// Perhaps the closest to Braket style.
+ .h( 1, 1 )
+ .x( 2, [ 1, 2 ]),
+
+
+ // Q function -- bandwidth / timewidth arguments.
+
+ Q( 2, 2 )
+ .h( 1, [ 1 ])
+ .x( 2, [ 1, 2 ]),
+
+ Q( 2, 2 )
+ .h( 1, 1 )
+ .x( 2, [ 1, 2 ]),
+
+
+ // Q function -- text block argument
+ // with operation symbols
+ // and operation component IDs.
+
+ Q`
+ H-X.0#0
+ I-X.0#1`,
+
+
+ // Q function -- text block argument
+ // using only component IDs
+ // (ie. no operation symbols)
+ // because the operation that the
+ // components should belong to is NOT ambiguous.
+
+ Q`
+ H-X#0
+ I-X#1`,
+
+
+ // Q function -- text block argument
+ // as above, but using only whitespace
+ // to partition between moments.
+
+ Q`
+ H X#0
+ I X#1`
+],
+bellsAreEqual = !!bells.reduce( function( a, b ){
+
+ return a.toText() === b.toText() ? a : NaN
+
+})
+if( bellsAreEqual ){
+
+ console.log( `\n\nYES. All of ${ bells.length } our “Bell” circuits are equal.\n\n`, bells )
+}
+*/
+
+
+
+
+
+
+
+Q.Circuit.createConstants(
+
+ 'BELL', Q`
+
+ H X#0
+ I X#1
+ `,
+ // 'GROVER', Q`
+
+ // H X *#0 X#0 I X#0 I I I X#0 I I I X#0 I X H X I *#0
+ // H X I X#1 *#0 X#1 *#0 X#0 I I I X#0 X I H X I I I I
+ // H X I I I I I X#1 *#0 X#1 *#0 X#1 *#0 X#1 I *#0 X H X I
+ // H X *#1 I *#1 I *#1 I *#1 I *#1 I *#1 I I *#1 X H X *#1
+ // `
+
+ //https://docs.microsoft.com/en-us/quantum/concepts/circuits?view=qsharp-preview
+ // 'TELEPORT', Q.(`
+
+ // I-I--H-M---v
+ // H-C0-I-M-v-v
+ // I-C1-I-I-X-Z-
+ // `)
+)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/*
+
+
+%%HTML
+
+
+
+
+
+%%javascript
+Q.braket( element )
+
+
+
+
+*/
+
+
+
+//%%javascript
+
+
+
+// Copyright © 2019–2020, Stewart Smith. See LICENSE for details.
+
+
+
+
+Q.Circuit.Editor = function( circuit, targetEl ){
+
+
+ // First order of business,
+ // we require a valid circuit.
+
+ if( circuit instanceof Q.Circuit !== true ) circuit = new Q.Circuit()
+ this.circuit = circuit
+ this.index = Q.Circuit.Editor.index ++
+
+
+ // Q.Circuit.Editor is all about the DOM
+ // so we’re going to get some use out of this
+ // stupid (but convenient) shorthand here.
+
+ const createDiv = function(){
+
+ return document.createElement( 'div' )
+ }
+
+
+
+
+ // We want to “name” our circuit editor instance
+ // but more importantly we want to give it a unique DOM ID.
+ // Keep in mind we can have MULTIPLE editors
+ // for the SAME circuit!
+ // This is a verbose way to do it,
+ // but each step is clear and I needed clarity today! ;)
+
+ this.name = typeof circuit.name === 'string' ?
+ circuit.name :
+ 'Q Editor '+ this.index
+
+
+ // If we’ve been passed a target DOM element
+ // we should use that as our circuit element.
+
+ if( typeof targetEl === 'string' ) targetEl = document.getElementById( targetEl )
+ const circuitEl = targetEl instanceof HTMLElement ? targetEl : createDiv()
+ circuitEl.classList.add( 'Q-circuit' )
+
+
+ // If the target element already has an ID
+ // then we want to use that as our domID.
+
+ if( typeof circuitEl.getAttribute( 'id' ) === 'string' ){
+
+ this.domId = circuitEl.getAttribute( 'id' )
+ }
+
+
+ // Otherwise let’s transform our name value
+ // into a usable domId.
+
+ else {
+
+ let domIdBase = this.name
+ .replace( /^[^a-z]+|[^\w:.-]+/gi, '-' ),
+ domId = domIdBase,
+ domIdAttempt = 1
+
+ while( document.getElementById( domId ) !== null ){
+
+ domIdAttempt ++
+ domId = domIdBase +'-'+ domIdAttempt
+ }
+ this.domId = domId
+ circuitEl.setAttribute( 'id', this.domId )
+ }
+
+
+
+
+ // We want a way to easily get to the circuit
+ // from this interface’s DOM element.
+ // (But we don’t need a way to reference this DOM element
+ // from the circuit. A circuit can have many DOM elements!)
+ // And we also want an easy way to reference this DOM element
+ // from this Editor instance.
+
+ circuitEl.circuit = circuit
+ this.domElement = circuitEl
+
+
+ // Create a toolbar for containing buttons.
+
+ const toolbarEl = createDiv()
+ circuitEl.appendChild( toolbarEl )
+ toolbarEl.classList.add( 'Q-circuit-toolbar' )
+
+
+ // Create a toggle switch for locking the circuit.
+
+ const lockToggle = createDiv()
+ toolbarEl.appendChild( lockToggle )
+ lockToggle.classList.add( 'Q-circuit-button', 'Q-circuit-toggle', 'Q-circuit-toggle-lock' )
+ lockToggle.setAttribute( 'title', 'Lock / unlock' )
+ lockToggle.innerText = '🔓'
+
+
+ // Create an “Undo” button
+ // that enables and disables
+ // based on available undo history.
+
+ const undoButton = createDiv()
+ toolbarEl.appendChild( undoButton )
+ undoButton.classList.add( 'Q-circuit-button', 'Q-circuit-button-undo' )
+ undoButton.setAttribute( 'title', 'Undo' )
+ undoButton.setAttribute( 'Q-disabled', 'Q-disabled' )
+ undoButton.innerHTML = '⟲'
+ window.addEventListener( 'Q.History undo is depleted', function( event ){
+
+ if( event.detail.instance === circuit )
+ undoButton.setAttribute( 'Q-disabled', 'Q-disabled' )
+ })
+ window.addEventListener( 'Q.History undo is capable', function( event ){
+
+ if( event.detail.instance === circuit )
+ undoButton.removeAttribute( 'Q-disabled' )
+ })
+
+
+ // Create an “Redo” button
+ // that enables and disables
+ // based on available redo history.
+
+ const redoButton = createDiv()
+ toolbarEl.appendChild( redoButton )
+ redoButton.classList.add( 'Q-circuit-button', 'Q-circuit-button-redo' )
+ redoButton.setAttribute( 'title', 'Redo' )
+ redoButton.setAttribute( 'Q-disabled', 'Q-disabled' )
+ redoButton.innerHTML = '⟳'
+ window.addEventListener( 'Q.History redo is depleted', function( event ){
+
+ if( event.detail.instance === circuit )
+ redoButton.setAttribute( 'Q-disabled', 'Q-disabled' )
+ })
+ window.addEventListener( 'Q.History redo is capable', function( event ){
+
+ if( event.detail.instance === circuit )
+ redoButton.removeAttribute( 'Q-disabled' )
+ })
+
+
+ // Create a button for joining
+ // an “identity cursor”
+ // and one or more same-gate operations
+ // into a controlled operation.
+ // (Will be enabled / disabled from elsewhere.)
+
+ const controlButton = createDiv()
+ toolbarEl.appendChild( controlButton )
+ controlButton.classList.add( 'Q-circuit-button', 'Q-circuit-toggle', 'Q-circuit-toggle-control' )
+ controlButton.setAttribute( 'title', 'Create controlled operation' )
+ controlButton.setAttribute( 'Q-disabled', 'Q-disabled' )
+ controlButton.innerText = 'C'
+
+
+ // Create a button for joining
+ // two “identity cursors”
+ // into a swap operation.
+ // (Will be enabled / disabled from elsewhere.)
+
+ const swapButton = createDiv()
+ toolbarEl.appendChild( swapButton )
+ swapButton.classList.add( 'Q-circuit-button', 'Q-circuit-toggle-swap' )
+ swapButton.setAttribute( 'title', 'Create swap operation' )
+ swapButton.setAttribute( 'Q-disabled', 'Q-disabled' )
+ swapButton.innerText = 'S'
+
+
+ // Create a circuit board container
+ // so we can house a scrollable circuit board.
+
+ const boardContainerEl = createDiv()
+ circuitEl.appendChild( boardContainerEl )
+ boardContainerEl.classList.add( 'Q-circuit-board-container' )
+ //boardContainerEl.addEventListener( 'touchstart', Q.Circuit.Editor.onPointerPress )
+ boardContainerEl.addEventListener( 'mouseleave', function(){
+ Q.Circuit.Editor.unhighlightAll( circuitEl )
+ })
+
+ const boardEl = createDiv()
+ boardContainerEl.appendChild( boardEl )
+ boardEl.classList.add( 'Q-circuit-board' )
+
+ const backgroundEl = createDiv()
+ boardEl.appendChild( backgroundEl )
+ backgroundEl.classList.add( 'Q-circuit-board-background' )
+
+ const parameterEl = createDiv()
+ boardEl.appendChild( parameterEl )
+ parameterEl.classList.add( 'Q-parameters-box' )
+ // Create background highlight bars
+ // for each row.
+
+ for( let i = 0; i < circuit.bandwidth; i ++ ){
+
+ const rowEl = createDiv()
+ backgroundEl.appendChild( rowEl )
+ rowEl.style.position = 'relative'
+ rowEl.style.gridRowStart = i + 2
+ rowEl.style.gridColumnStart = 1
+ rowEl.style.gridColumnEnd = Q.Circuit.Editor.momentIndexToGridColumn( circuit.timewidth ) + 1
+ rowEl.setAttribute( 'register-index', i + 1 )
+
+ const wireEl = createDiv()
+ rowEl.appendChild( wireEl )
+ wireEl.classList.add( 'Q-circuit-register-wire' )
+ }
+
+
+ // Create background highlight bars
+ // for each column.
+
+ for( let i = 0; i < circuit.timewidth; i ++ ){
+
+ const columnEl = createDiv()
+ backgroundEl.appendChild( columnEl )
+ columnEl.style.gridRowStart = 2
+ columnEl.style.gridRowEnd = Q.Circuit.Editor.registerIndexToGridRow( circuit.bandwidth ) + 1
+ columnEl.style.gridColumnStart = i + 3
+ columnEl.setAttribute( 'moment-index', i + 1 )
+ }
+
+
+ // Create the circuit board foreground
+ // for all interactive elements.
+
+ const foregroundEl = createDiv()
+ boardEl.appendChild( foregroundEl )
+ foregroundEl.classList.add( 'Q-circuit-board-foreground' )
+
+
+ // Add “Select All” toggle button to upper-left corner.
+
+ const selectallEl = createDiv()
+ foregroundEl.appendChild( selectallEl )
+ selectallEl.classList.add( 'Q-circuit-header', 'Q-circuit-selectall' )
+ selectallEl.setAttribute( 'title', 'Select all' )
+ selectallEl.setAttribute( 'moment-index', '0' )
+ selectallEl.setAttribute( 'register-index', '0' )
+ selectallEl.innerHTML = '↘'
+
+
+ // Add register index symbols to left-hand column.
+
+ for( let i = 0; i < circuit.bandwidth; i ++ ){
+
+ const
+ registerIndex = i + 1,
+ registersymbolEl = createDiv()
+
+ foregroundEl.appendChild( registersymbolEl )
+ registersymbolEl.classList.add( 'Q-circuit-header', 'Q-circuit-register-label' )
+ registersymbolEl.setAttribute( 'title', 'Register '+ registerIndex +' of '+ circuit.bandwidth )
+ registersymbolEl.setAttribute( 'register-index', registerIndex )
+ registersymbolEl.style.gridRowStart = Q.Circuit.Editor.registerIndexToGridRow( registerIndex )
+ registersymbolEl.innerText = registerIndex
+ }
+
+
+ // Add “Add register” button.q
+
+ const addRegisterEl = createDiv()
+ foregroundEl.appendChild( addRegisterEl )
+ addRegisterEl.classList.add( 'Q-circuit-header', 'Q-circuit-register-add' )
+ addRegisterEl.setAttribute( 'title', 'Add register' )
+ addRegisterEl.style.gridRowStart = Q.Circuit.Editor.registerIndexToGridRow( circuit.bandwidth + 1 )
+ addRegisterEl.innerText = '+'
+
+
+ // Add moment index symbols to top row.
+
+ for( let i = 0; i < circuit.timewidth; i ++ ){
+
+ const
+ momentIndex = i + 1,
+ momentsymbolEl = createDiv()
+
+ foregroundEl.appendChild( momentsymbolEl )
+ momentsymbolEl.classList.add( 'Q-circuit-header', 'Q-circuit-moment-label' )
+ momentsymbolEl.setAttribute( 'title', 'Moment '+ momentIndex +' of '+ circuit.timewidth )
+ momentsymbolEl.setAttribute( 'moment-index', momentIndex )
+ momentsymbolEl.style.gridColumnStart = Q.Circuit.Editor.momentIndexToGridColumn( momentIndex )
+ momentsymbolEl.innerText = momentIndex
+ }
+
+
+ // Add “Add moment” button.
+
+ const addMomentEl = createDiv()
+ foregroundEl.appendChild( addMomentEl )
+ addMomentEl.classList.add( 'Q-circuit-header', 'Q-circuit-moment-add' )
+ addMomentEl.setAttribute( 'title', 'Add moment' )
+ addMomentEl.style.gridColumnStart = Q.Circuit.Editor.momentIndexToGridColumn( circuit.timewidth + 1 )
+ addMomentEl.innerText = '+'
+
+
+ // Add input values.
+
+ circuit.qubits.forEach( function( qubit, i ){
+
+ const
+ rowIndex = i + 1,
+ inputEl = createDiv()
+
+ inputEl.classList.add( 'Q-circuit-header', 'Q-circuit-input' )
+ inputEl.setAttribute( 'title', `Qubit #${ rowIndex } starting value` )
+ inputEl.setAttribute( 'register-index', rowIndex )
+ inputEl.style.gridRowStart = Q.Circuit.Editor.registerIndexToGridRow( rowIndex )
+ inputEl.innerText = qubit.beta.toText()
+ foregroundEl.appendChild( inputEl )
+ })
+
+
+ // Add operations.
+
+ circuit.operations.forEach( function( operation ){
+ Q.Circuit.Editor.set( circuitEl, operation )
+ })
+
+
+ // Add event listeners.
+
+ circuitEl.addEventListener( 'mousedown', Q.Circuit.Editor.onPointerPress )
+ circuitEl.addEventListener( 'touchstart', Q.Circuit.Editor.onPointerPress )
+ window.addEventListener(
+
+ 'Q.Circuit.set$',
+ Q.Circuit.Editor.prototype.onExternalSet.bind( this )
+ )
+ window.addEventListener(
+
+ 'Q.Circuit.clear$',
+ Q.Circuit.Editor.prototype.onExternalClear.bind( this )
+ )
+
+
+ // How can we interact with this circuit
+ // through code? (How cool is this?!)
+
+ const referenceEl = document.createElement( 'p' )
+ circuitEl.appendChild( referenceEl )
+ referenceEl.innerHTML = `
+ This circuit is accessible in your
+ JavaScript console
+ as document.getElementById('${ this.domId }').circuit
`
+ //document.getElementById('Q-Editor-0').circuit
+ //$('#${ this.domId }')
+
+
+ // Put a note in the JavaScript console
+ // that includes how to reference the circuit via code
+ // and an ASCII diagram for reference.
+
+ Q.log( 0.5,
+
+ `\n\nCreated a DOM interface for $('#${ this.domId }').circuit\n\n`,
+ circuit.toDiagram(),
+ '\n\n\n'
+ )
+}
+
+
+// Augment Q.Circuit to have this functionality.
+
+Q.Circuit.toDom = function( circuit, targetEl ){
+
+ return new Q.Circuit.Editor( circuit, targetEl ).domElement
+}
+Q.Circuit.prototype.toDom = function( targetEl ){
+
+ return new Q.Circuit.Editor( this, targetEl ).domElement
+}
+
+
+
+
+
+
+
+
+Object.assign( Q.Circuit.Editor, {
+
+ index: 0,
+ help: function(){ return Q.help( this )},
+ dragEl: null,
+ gridColumnToMomentIndex: function( gridColumn ){ return +gridColumn - 2 },
+ momentIndexToGridColumn: function( momentIndex ){ return momentIndex + 2 },
+ gridRowToRegisterIndex: function( gridRow ){ return +gridRow - 1 },
+ registerIndexToGridRow: function( registerIndex ){ return registerIndex + 1 },
+ gridSize: 4,// CSS: grid-auto-columns = grid-auto-rows = 4rem.
+ pointToGrid: function( p ){
+
+
+ // Take a 1-dimensional point value
+ // (so either an X or a Y but not both)
+ // and return what CSS grid cell contains it
+ // based on our 4rem × 4rem grid setup.
+
+ const rem = parseFloat( getComputedStyle( document.documentElement ).fontSize )
+ return 1 + Math.floor( p / ( rem * Q.Circuit.Editor.gridSize ))
+ },
+ gridToPoint: function( g ){
+
+
+ // Take a 1-dimensional grid cell value
+ // (so either a row or a column but not both)
+ // and return the minimum point value it contains.
+
+ const rem = parseFloat( getComputedStyle( document.documentElement ).fontSize )
+ return rem * Q.Circuit.Editor.gridSize * ( g - 1 )
+ },
+ getInteractionCoordinates: function( event, pageOrClient ){
+
+ if( typeof pageOrClient !== 'string' ) pageOrClient = 'client'//page
+ if( event.changedTouches &&
+ event.changedTouches.length ) return {
+
+ x: event.changedTouches[ 0 ][ pageOrClient +'X' ],
+ y: event.changedTouches[ 0 ][ pageOrClient +'Y' ]
+ }
+ return {
+
+ x: event[ pageOrClient +'X' ],
+ y: event[ pageOrClient +'Y' ]
+ }
+ },
+ createPalette: function( targetEl ){
+
+ if( typeof targetEl === 'string' ) targetEl = document.getElementById( targetEl )
+
+ const
+ paletteEl = targetEl instanceof HTMLElement ? targetEl : document.createElement( 'div' ),
+ randomRangeAndSign = function( min, max ){
+
+ const r = min + Math.random() * ( max - min )
+ return Math.floor( Math.random() * 2 ) ? r : -r
+ }
+
+ //ltnln: added missing Braket operations.
+ paletteEl.classList.add( 'Q-circuit-palette' )
+ 'H,X,Y,Z,P,Rx,Ry,Rz,U,V,V†,S*,S†,T,T†,00,01,10,√S,iS,XX,XY,YY,ZZ,*'
+ .split( ',' )
+ .forEach( function( symbol ){
+
+ const gate = Q.Gate.findBySymbol( symbol )
+
+ const operationEl = document.createElement( 'div' )
+ paletteEl.appendChild( operationEl )
+ operationEl.classList.add( 'Q-circuit-operation' )
+ operationEl.classList.add( 'Q-circuit-operation-'+ gate.nameCss )
+ operationEl.setAttribute( 'gate-symbol', symbol )
+ operationEl.setAttribute( 'title', gate.name )
+
+ const tileEl = document.createElement( 'div' )
+ operationEl.appendChild( tileEl )
+ tileEl.classList.add( 'Q-circuit-operation-tile' )
+ if( symbol !== Q.Gate.CURSOR.symbol ) tileEl.innerText = symbol
+
+ ;[ 'before', 'after' ].forEach( function( layer ){
+
+ tileEl.style.setProperty( '--Q-'+ layer +'-rotation', randomRangeAndSign( 0.5, 4 ) +'deg' )
+ tileEl.style.setProperty( '--Q-'+ layer +'-x', randomRangeAndSign( 1, 4 ) +'px' )
+ tileEl.style.setProperty( '--Q-'+ layer +'-y', randomRangeAndSign( 1, 3 ) +'px' )
+ })
+ })
+
+ paletteEl.addEventListener( 'mousedown', Q.Circuit.Editor.onPointerPress )
+ paletteEl.addEventListener( 'touchstart', Q.Circuit.Editor.onPointerPress )
+ return paletteEl
+ }
+})
+
+
+
+
+
+
+ /////////////////////////
+ // //
+ // Operation CLEAR //
+ // //
+/////////////////////////
+
+
+Q.Circuit.Editor.prototype.onExternalClear = function( event ){
+
+ if( event.detail.circuit === this.circuit ){
+
+ Q.Circuit.Editor.clear( this.domElement, {
+
+ momentIndex: event.detail.momentIndex,
+ registerIndices: event.detail.registerIndices
+ })
+ }
+}
+Q.Circuit.Editor.clear = function( circuitEl, operation ){
+
+ const momentIndex = operation.momentIndex
+ operation.registerIndices.forEach( function( registerIndex ){
+
+ Array
+ .from( circuitEl.querySelectorAll(
+
+ `[moment-index="${ momentIndex }"]`+
+ `[register-index="${ registerIndex }"]`
+
+ ))
+ .forEach( function( op ){
+
+ op.parentNode.removeChild( op )
+ })
+ })
+}
+
+
+
+
+
+
+ ///////////////////////
+ // //
+ // Operation SET //
+ // //
+///////////////////////
+
+
+Q.Circuit.Editor.prototype.onExternalSet = function( event ){
+
+ if( event.detail.circuit === this.circuit ){
+
+ Q.Circuit.Editor.set( this.domElement, event.detail.operation )
+ }
+}
+Q.Circuit.Editor.set = function( circuitEl, operation ){
+ const
+ backgroundEl = circuitEl.querySelector( '.Q-circuit-board-background' ),
+ foregroundEl = circuitEl.querySelector( '.Q-circuit-board-foreground' ),
+ circuit = circuitEl.circuit,
+ operationIndex = circuitEl.circuit.operations.indexOf( operation )
+
+ operation.registerIndices.forEach( function( registerIndex, i ){
+ const operationEl = document.createElement( 'div' )
+ foregroundEl.appendChild( operationEl )
+ operationEl.classList.add( 'Q-circuit-operation', 'Q-circuit-operation-'+ operation.gate.nameCss )
+ // operationEl.setAttribute( 'operation-index', operationIndex )
+ operationEl.setAttribute( 'gate-symbol', operation.gate.symbol )
+ operationEl.setAttribute( 'gate-index', operation.gate.index )// Used as an application-wide unique ID!
+ operationEl.setAttribute( 'moment-index', operation.momentIndex )
+ operationEl.setAttribute( 'register-index', registerIndex )
+ operationEl.setAttribute( 'register-array-index', i )// Where within the registerIndices array is this operations fragment located?
+ operationEl.setAttribute( 'is-controlled', operation.isControlled )
+ operationEl.setAttribute( 'title', operation.gate.name )
+ operationEl.style.gridColumnStart = Q.Circuit.Editor.momentIndexToGridColumn( operation.momentIndex )
+ operationEl.style.gridRowStart = Q.Circuit.Editor.registerIndexToGridRow( registerIndex )
+ if( operation.gate.has_parameters ) Object.keys(operation.gate.parameters).forEach( element => {
+ operationEl.setAttribute( element, operation.gate.parameters[element] ) //adds a parameter attribute to the operation!
+ })
+ const tileEl = document.createElement( 'div' )
+ operationEl.appendChild( tileEl )
+ tileEl.classList.add( 'Q-circuit-operation-tile' )
+ if( operation.gate.symbol !== Q.Gate.CURSOR.symbol ) tileEl.innerText = operation.gate.symbol
+
+
+ // Add operation link wires
+ // for multi-qubit operations.
+
+ if( operation.registerIndices.length > 1 ){
+
+ operationEl.setAttribute( 'register-indices', operation.registerIndices )
+ operationEl.setAttribute( 'register-indices-index', i )
+ operationEl.setAttribute(
+
+ 'sibling-indices',
+ operation.registerIndices
+ .filter( function( siblingRegisterIndex ){
+
+ return registerIndex !== siblingRegisterIndex
+ })
+ )
+ operation.registerIndices.forEach( function( registerIndex, i ){
+
+ if( i < operation.registerIndices.length - 1 ){
+
+ const
+ siblingRegisterIndex = operation.registerIndices[ i + 1 ],
+ registerDelta = Math.abs( siblingRegisterIndex - registerIndex ),
+ start = Math.min( registerIndex, siblingRegisterIndex ),
+ end = Math.max( registerIndex, siblingRegisterIndex ),
+ containerEl = document.createElement( 'div' ),
+ linkEl = document.createElement( 'div' )
+
+ backgroundEl.appendChild( containerEl )
+ containerEl.setAttribute( 'moment-index', operation.momentIndex )
+ containerEl.setAttribute( 'register-index', registerIndex )
+ containerEl.classList.add( 'Q-circuit-operation-link-container' )
+ containerEl.style.gridRowStart = Q.Circuit.Editor.registerIndexToGridRow( start )
+ containerEl.style.gridRowEnd = Q.Circuit.Editor.registerIndexToGridRow( end + 1 )
+ containerEl.style.gridColumn = Q.Circuit.Editor.momentIndexToGridColumn( operation.momentIndex )
+
+ containerEl.appendChild( linkEl )
+ linkEl.classList.add( 'Q-circuit-operation-link' )
+ if( registerDelta > 1 ) linkEl.classList.add( 'Q-circuit-operation-link-curved' )
+ }
+ })
+ if( operation.isControlled && i === 0 ){
+ operationEl.classList.add( 'Q-circuit-operation-control' )
+ operationEl.setAttribute( 'title', 'Control' )
+ tileEl.innerText = ''
+ }
+ else operationEl.classList.add( 'Q-circuit-operation-target' )
+ }
+ })
+}
+
+
+
+
+Q.Circuit.Editor.isValidControlCandidate = function( circuitEl ){
+
+ const
+ selectedOperations = Array
+ .from( circuitEl.querySelectorAll( '.Q-circuit-cell-selected' ))
+
+
+ // We must have at least two operations selected,
+ // hopefully a control and something else,
+ // in order to attempt a join.
+
+ if( selectedOperations.length < 2 ) return false
+
+
+ // Note the different moment indices present
+ // among the selected operations.
+
+ const moments = selectedOperations.reduce( function( moments, operationEl ){
+
+ moments[ operationEl.getAttribute( 'moment-index' )] = true
+ return moments
+
+ }, {} )
+
+
+ // All selected operations must be in the same moment.
+
+ if( Object.keys( moments ).length > 1 ) return false
+
+
+ // If there are multi-register operations present,
+ // regardless of whether those are controls or swaps,
+ // all siblings must be present
+ // in order to join a new gate to this selection.
+
+ // I’m sure we can make this whole routine much more efficient
+ // but its results are correct and boy am I tired ;)
+
+ const allSiblingsPresent = selectedOperations
+ .reduce( function( status, operationEl ){
+
+ const registerIndicesString = operationEl.getAttribute( 'register-indices' )
+
+
+ // If it’s a single-register operation
+ // there’s no need to search further.
+
+ if( !registerIndicesString ) return status
+
+
+ // How many registers are in use
+ // by this operation?
+
+ const
+ registerIndicesLength = registerIndicesString
+ .split( ',' )
+ .map( function( registerIndex ){
+
+ return +registerIndex
+ })
+ .length,
+
+
+ // How many of this operation’s siblings
+ // (including itself) can we find?
+
+ allSiblingsLength = selectedOperations
+ .reduce( function( siblings, operationEl ){
+
+ if( operationEl.getAttribute( 'register-indices' ) === registerIndicesString ){
+
+ siblings.push( operationEl )
+ }
+ return siblings
+
+ }, [])
+ .length
+
+
+ // Did we find all of the siblings for this operation?
+ // Square that with previous searches.
+
+ return status && allSiblingsLength === registerIndicesLength
+
+ }, true )
+
+
+ // If we’re missing some siblings
+ // then we cannot modify whatever we have selected here.
+
+ if( allSiblingsPresent !== true ) return false
+
+ // Note the different gate types present
+ // among the selected operations.
+
+ const gates = selectedOperations.reduce( function( gates, operationEl ){
+ const gateSymbol = operationEl.getAttribute( 'gate-symbol' )
+ if( !Q.isUsefulInteger( gates[ gateSymbol ])) gates[ gateSymbol ] = 1
+ else gates[ gateSymbol ] ++
+ return gates
+
+ }, {} )
+
+
+ // Note if each operation is already controlled or not.
+
+ const {
+
+ totalControlled,
+ totalNotControlled
+
+ } = selectedOperations
+ .reduce( function( stats, operationEl ){
+
+ if( operationEl.getAttribute( 'is-controlled' ) === 'true' )
+ stats.totalControlled ++
+ else stats.totalNotControlled ++
+ return stats
+
+ }, {
+
+ totalControlled: 0,
+ totalNotControlled: 0
+ })
+
+ // This could be ONE “identity cursor”
+ // and one or more of a regular single gate
+ // that is NOT already controlled.
+
+ if( gates[ Q.Gate.CURSOR.symbol ] === 1 &&
+ Object.keys( gates ).length === 2 &&
+ totalNotControlled === selectedOperations.length ){
+
+ return true
+ }
+
+
+ // There’s NO “identity cursor”
+ // but there is one or more of specific gate type
+ // and at least one of those is already controlled.
+
+ if( gates[ Q.Gate.CURSOR.symbol ] === undefined &&
+ Object.keys( gates ).length === 1 &&
+ totalControlled > 0 &&
+ totalNotControlled > 0 ){
+
+ return true
+ }
+
+
+ // Any other combination allowed? Nope!
+
+ return false
+}
+Q.Circuit.Editor.createControl = function( circuitEl ){
+
+ if( Q.Circuit.Editor.isValidControlCandidate( circuitEl ) !== true ) return this
+
+
+ const
+ circuit = circuitEl.circuit,
+ selectedOperations = Array
+ .from( circuitEl.querySelectorAll( '.Q-circuit-cell-selected' )),
+
+
+ // Are any of these controlled operations??
+ // If so, we need to find its control component
+ // and re-use it.
+
+ existingControlEl = selectedOperations.find( function( operationEl ){
+
+ return (
+
+ operationEl.getAttribute( 'is-controlled' ) === 'true' &&
+ operationEl.getAttribute( 'register-array-index' ) === '0'
+ )
+ }),
+
+
+ // One control. One or more targets.
+
+ control = existingControlEl || selectedOperations
+ .find( function( el ){
+
+ return el.getAttribute( 'gate-symbol' ) === Q.Gate.CURSOR.symbol
+ }),
+ targets = selectedOperations
+ .reduce( function( targets, el ){
+
+ //if( el.getAttribute( 'gate-symbol' ) !== '!' ) targets.push( el )
+ if( el !== control ) targets.push( el )
+ return targets
+
+ }, [] )
+
+
+ // Ready to roll.
+
+ circuit.history.createEntry$()
+ selectedOperations.forEach( function( operationEl ){
+
+ circuit.clear$(
+
+ +operationEl.getAttribute( 'moment-index' ),
+ +operationEl.getAttribute( 'register-index' )
+ )
+ })
+ circuit.set$(
+ targets[ 0 ].getAttribute( 'gate-symbol' ),
+ +control.getAttribute( 'moment-index' ),
+ [ +control.getAttribute( 'register-index' )].concat(
+
+ targets.reduce( function( registers, operationEl ){
+
+ registers.push( +operationEl.getAttribute( 'register-index' ))
+ return registers
+
+ }, [] )
+ )
+ )
+
+
+ // Update our toolbar button states.
+
+ Q.Circuit.Editor.onSelectionChanged( circuitEl )
+ Q.Circuit.Editor.onCircuitChanged( circuitEl )
+
+ return this
+}
+
+
+
+
+Q.Circuit.Editor.isValidSwapCandidate = function( circuitEl ){
+
+ const
+ selectedOperations = Array
+ .from( circuitEl.querySelectorAll( '.Q-circuit-cell-selected' ))
+
+
+ // We can only swap between two registers.
+ // No crazy rotation-swap bullshit. (Yet.)
+ if( selectedOperations.length !== 2 ) return false
+
+
+ // Both operations must be “identity cursors.”
+ // If so, we are good to go.
+
+ areBothCursors = selectedOperations.every( function( operationEl ){
+
+ return operationEl.getAttribute( 'gate-symbol' ) === Q.Gate.CURSOR.symbol
+ })
+ if( areBothCursors ) return true
+
+
+ // Otherwise this is not a valid swap candidate.
+
+ return false
+}
+Q.Circuit.Editor.createSwap = function( circuitEl ){
+
+ if( Q.Circuit.Editor.isValidSwapCandidate( circuitEl ) !== true ) return this
+
+ const
+ selectedOperations = Array
+ .from( circuitEl.querySelectorAll( '.Q-circuit-cell-selected' )),
+ momentIndex = +selectedOperations[ 0 ].getAttribute( 'moment-index' )
+ registerIndices = selectedOperations
+ .reduce( function( registerIndices, operationEl ){
+
+ registerIndices.push( +operationEl.getAttribute( 'register-index' ))
+ return registerIndices
+
+ }, [] ),
+ circuit = circuitEl.circuit
+
+
+ // Create the swap operation.
+
+ circuit.history.createEntry$()
+ selectedOperations.forEach( function( operation ){
+
+ circuit.clear$(
+
+ +operation.getAttribute( 'moment-index' ),
+ +operation.getAttribute( 'register-index' )
+ )
+ })
+ circuit.set$(
+
+ Q.Gate.SWAP,
+ momentIndex,
+ registerIndices
+ )
+
+
+ // Update our toolbar button states.
+
+ Q.Circuit.Editor.onSelectionChanged( circuitEl )
+ Q.Circuit.Editor.onCircuitChanged( circuitEl )
+
+ return this
+}
+
+
+
+
+Q.Circuit.Editor.onSelectionChanged = function( circuitEl ){
+
+ const controlButtonEl = circuitEl.querySelector( '.Q-circuit-toggle-control' )
+ if( Q.Circuit.Editor.isValidControlCandidate( circuitEl )){
+
+ controlButtonEl.removeAttribute( 'Q-disabled' )
+ }
+ else controlButtonEl.setAttribute( 'Q-disabled', true )
+
+ const swapButtonEl = circuitEl.querySelector( '.Q-circuit-toggle-swap' )
+ if( Q.Circuit.Editor.isValidSwapCandidate( circuitEl )){
+
+ swapButtonEl.removeAttribute( 'Q-disabled' )
+ }
+ else swapButtonEl.setAttribute( 'Q-disabled', true )
+}
+Q.Circuit.Editor.onCircuitChanged = function( circuitEl ){
+
+ const circuit = circuitEl.circuit
+ window.dispatchEvent( new CustomEvent(
+
+ 'Q gui altered circuit',
+ { detail: { circuit: circuit }}
+ ))
+
+ // Should we trigger a circuit.evaluate$() here?
+ // Particularly when we move all that to a new thread??
+ // console.log( originCircuit.report$() ) ??
+}
+
+
+
+
+
+Q.Circuit.Editor.unhighlightAll = function( circuitEl ){
+
+ Array.from( circuitEl.querySelectorAll(
+
+ '.Q-circuit-board-background > div,'+
+ '.Q-circuit-board-foreground > div'
+ ))
+ .forEach( function( el ){
+
+ el.classList.remove( 'Q-circuit-cell-highlighted' )
+ })
+}
+
+
+
+
+
+
+ //////////////////////
+ // //
+ // Pointer MOVE //
+ // //
+//////////////////////
+
+
+Q.Circuit.Editor.onPointerMove = function( event ){
+
+
+ // We need our cursor coordinates straight away.
+ // We’ll use that both for dragging (immediately below)
+ // and for hover highlighting (further below).
+ // Let’s also hold on to a list of all DOM elements
+ // that contain this X, Y point
+ // and also see if one of those is a circuit board container.
+
+ const
+ { x, y } = Q.Circuit.Editor.getInteractionCoordinates( event ),
+ foundEls = document.elementsFromPoint( x, y ),
+ boardContainerEl = foundEls.find( function( el ){
+
+ return el.classList.contains( 'Q-circuit-board-container' )
+ })
+
+
+ // Are we in the middle of a circuit clipboard drag?
+ // If so we need to move that thing!
+
+ if( Q.Circuit.Editor.dragEl !== null ){
+
+
+ // ex. Don’t scroll on touch devices!
+
+ event.preventDefault()
+
+
+ // This was a very useful resource
+ // for a reality check on DOM coordinates:
+ // https://javascript.info/coordinates
+
+ Q.Circuit.Editor.dragEl.style.left = ( x + window.pageXOffset + Q.Circuit.Editor.dragEl.offsetX ) +'px'
+ Q.Circuit.Editor.dragEl.style.top = ( y + window.pageYOffset + Q.Circuit.Editor.dragEl.offsetY ) +'px'
+
+ if( !boardContainerEl && Q.Circuit.Editor.dragEl.circuitEl ) Q.Circuit.Editor.dragEl.classList.add( 'Q-circuit-clipboard-danger' )
+ else Q.Circuit.Editor.dragEl.classList.remove( 'Q-circuit-clipboard-danger' )
+ }
+
+
+ // If we’re not over a circuit board container
+ // then there’s no highlighting work to do
+ // so let’s bail now.
+
+ if( !boardContainerEl ) return
+
+
+ // Now we know we have a circuit board
+ // so we must have a circuit
+ // and if that’s locked then highlighting changes allowed!
+
+ const circuitEl = boardContainerEl.closest( '.Q-circuit' )
+ if( circuitEl.classList.contains( 'Q-circuit-locked' )) return
+
+
+ // Ok, we’ve found a circuit board.
+ // First, un-highlight everything.
+
+ Array.from( boardContainerEl.querySelectorAll(`
+
+ .Q-circuit-board-background > div,
+ .Q-circuit-board-foreground > div
+
+ `)).forEach( function( el ){
+
+ el.classList.remove( 'Q-circuit-cell-highlighted' )
+ })
+
+
+ // Let’s prioritize any element that is “sticky”
+ // which means it can appear OVER another grid cell.
+ const
+ cellEl = foundEls.find( function( el ){
+
+ const style = window.getComputedStyle( el )
+ return (
+
+ style.position === 'sticky' && (
+
+ el.getAttribute( 'moment-index' ) !== null ||
+ el.getAttribute( 'register-index' ) !== null
+ )
+ )
+ }),
+ highlightByQuery = function( query ){
+
+ Array.from( boardContainerEl.querySelectorAll( query ))
+ .forEach( function( el ){
+
+ el.classList.add( 'Q-circuit-cell-highlighted' )
+ })
+ }
+
+
+ // If we’ve found one of these “sticky” cells
+ // let’s use its moment and/or register data
+ // to highlight moments or registers (or all).
+
+ if( cellEl ){
+
+ const
+ momentIndex = cellEl.getAttribute( 'moment-index' ),
+ registerIndex = cellEl.getAttribute( 'register-index' )
+
+ if( momentIndex === null ){
+
+ highlightByQuery( `div[register-index="${ registerIndex }"]` )
+ return
+ }
+ if( registerIndex === null ){
+
+ highlightByQuery( `div[moment-index="${ momentIndex }"]` )
+ return
+ }
+ highlightByQuery(`
+
+ .Q-circuit-board-background > div[moment-index],
+ .Q-circuit-board-foreground > .Q-circuit-operation
+
+ `)
+ return
+ }
+
+
+ // Ok, we know we’re hovering over the circuit board
+ // but we’re not on a “sticky” cell.
+ // We might be over an operation, but we might not.
+ // No matter -- we’ll infer the moment and register indices
+ // from the cursor position.
+
+ const
+ boardElBounds = boardContainerEl.getBoundingClientRect(),
+ xLocal = x - boardElBounds.left + boardContainerEl.scrollLeft + 1,
+ yLocal = y - boardElBounds.top + boardContainerEl.scrollTop + 1,
+ columnIndex = Q.Circuit.Editor.pointToGrid( xLocal ),
+ rowIndex = Q.Circuit.Editor.pointToGrid( yLocal ),
+ momentIndex = Q.Circuit.Editor.gridColumnToMomentIndex( columnIndex ),
+ registerIndex = Q.Circuit.Editor.gridRowToRegisterIndex( rowIndex )
+
+
+ // If this hover is “out of bounds”
+ // ie. on the same row or column as an “Add register” or “Add moment” button
+ // then let’s not highlight anything.
+
+ if( momentIndex > circuitEl.circuit.timewidth ||
+ registerIndex > circuitEl.circuit.bandwidth ) return
+
+
+ // If we’re at 0, 0 or below that either means
+ // we’re over the “Select all” button (already taken care of above)
+ // or over the lock toggle button.
+ // Either way, it’s time to bail.
+
+ if( momentIndex < 1 || registerIndex < 1 ) return
+
+
+ // If we’ve made it this far that means
+ // we have valid moment and register indices.
+ // Highlight them!
+
+ highlightByQuery(`
+
+ div[moment-index="${ momentIndex }"],
+ div[register-index="${ registerIndex }"]
+ `)
+ return
+}
+
+
+
+ ///////////////////////
+ // //
+ // Pointer PRESS //
+ // //
+///////////////////////
+
+
+Q.Circuit.Editor.onPointerPress = function( event ){
+ // This is just a safety net
+ // in case something terrible has ocurred.
+ // (ex. Did the user click and then their mouse ran
+ // outside the window but browser didn’t catch it?)
+ console.log("event target: ", event.target);
+ if( Q.Circuit.Editor.dragEl !== null ){
+
+ Q.Circuit.Editor.onPointerRelease( event )
+ return
+ }
+ const
+ targetEl = event.target,
+ circuitEl = targetEl.closest( '.Q-circuit' ),
+ paletteEl = targetEl.closest( '.Q-circuit-palette' )
+ parameterEl = targetEl.closest( '.Q-parameters-box' )
+
+ // If we can’t find a circuit that’s a really bad sign
+ // considering this event should be fired when a circuit
+ // is clicked on. So... bail!
+
+ if( !circuitEl && !paletteEl ) return
+
+ // This is a bit of a gamble.
+ // There’s a possibility we’re not going to drag anything,
+ // but we’ll prep these variables here anyway
+ // because both branches of if( circuitEl ) and if( paletteEl )
+ // below will have access to this scope.
+
+ dragEl = document.createElement( 'div' )
+ dragEl.classList.add( 'Q-circuit-clipboard' )
+ const { x, y } = Q.Circuit.Editor.getInteractionCoordinates( event )
+
+
+ // Are we dealing with a circuit interface?
+ // ie. NOT a palette interface.
+
+ if( circuitEl && !parameterEl ){
+
+ // Shall we toggle the circuit lock?
+
+ const
+ circuit = circuitEl.circuit,
+ circuitIsLocked = circuitEl.classList.contains( 'Q-circuit-locked' ),
+ lockEl = targetEl.closest( '.Q-circuit-toggle-lock' )
+
+ if( lockEl ){
+
+ // const toolbarEl = Array.from( circuitEl.querySelectorAll( '.Q-circuit-button' ))
+ if( circuitIsLocked ){
+
+ circuitEl.classList.remove( 'Q-circuit-locked' )
+ lockEl.innerText = '🔓'
+ }
+ else {
+
+ circuitEl.classList.add( 'Q-circuit-locked' )
+ lockEl.innerText = '🔒'
+ Q.Circuit.Editor.unhighlightAll( circuitEl )
+ }
+
+
+ // We’ve toggled the circuit lock button
+ // so we should prevent further propagation
+ // before proceeding further.
+ // That includes running all this code again
+ // if it was originally fired by a mouse event
+ // and about to be fired by a touch event!
+
+ event.preventDefault()
+ event.stopPropagation()
+ return
+ }
+
+
+ // If our circuit is already “locked”
+ // then there’s nothing more to do here.
+
+ if( circuitIsLocked ) {
+
+ Q.warn( `User attempted to interact with a circuit editor but it was locked.` )
+ return
+ }
+
+
+ const
+ cellEl = targetEl.closest(`
+
+ .Q-circuit-board-foreground > div,
+ .Q-circuit-palette > div
+ `),
+ undoEl = targetEl.closest( '.Q-circuit-button-undo' ),
+ redoEl = targetEl.closest( '.Q-circuit-button-redo' ),
+ controlEl = targetEl.closest( '.Q-circuit-toggle-control' ),
+ swapEl = targetEl.closest( '.Q-circuit-toggle-swap' ),
+ addMomentEl = targetEl.closest( '.Q-circuit-moment-add' ),
+ addRegisterEl = targetEl.closest( '.Q-circuit-register-add' )
+
+ if( !cellEl &&
+ !undoEl &&
+ !redoEl &&
+ !controlEl &&
+ !swapEl &&
+ !addMomentEl &&
+ !addRegisterEl ) return
+
+
+ // By this point we know that the circuit is unlocked
+ // and that we’ll activate a button / drag event / etc.
+ // So we need to hault futher event propagation
+ // including running this exact code again if this was
+ // fired by a touch event and about to again by mouse.
+ // This may SEEM redundant because we did this above
+ // within the lock-toggle button code
+ // but we needed to NOT stop propagation if the circuit
+ // was already locked -- for scrolling and such.
+
+ event.preventDefault()
+ event.stopPropagation()
+
+
+ if( undoEl && circuit.history.undo$() ){
+
+ Q.Circuit.Editor.onSelectionChanged( circuitEl )
+ Q.Circuit.Editor.onCircuitChanged( circuitEl )
+ }
+ if( redoEl && circuit.history.redo$() ){
+
+ Q.Circuit.Editor.onSelectionChanged( circuitEl )
+ Q.Circuit.Editor.onCircuitChanged( circuitEl )
+ }
+ if( controlEl ) Q.Circuit.Editor.createControl( circuitEl )
+ if( swapEl ) Q.Circuit.Editor.createSwap( circuitEl )
+ if( addMomentEl ) console.log( '→ Add moment' )
+ if( addRegisterEl ) console.log( '→ Add register' )
+
+
+ // We’re done dealing with external buttons.
+ // So if we can’t find a circuit CELL
+ // then there’s nothing more to do here.
+
+ if( !cellEl ) return
+
+ // Once we know what cell we’ve pressed on
+ // we can get the momentIndex and registerIndex
+ // from its pre-defined attributes.
+ // NOTE that we are getting CSS grid column and row
+ // from our own conversion function and NOT from
+ // asking its styles. Why? Because browsers convert
+ // grid commands to a shorthand less easily parsable
+ // and therefore makes our code and reasoning
+ // more prone to quirks / errors. Trust me!
+
+ const
+ momentIndex = +cellEl.getAttribute( 'moment-index' ),
+ registerIndex = +cellEl.getAttribute( 'register-index' ),
+ columnIndex = Q.Circuit.Editor.momentIndexToGridColumn( momentIndex ),
+ rowIndex = Q.Circuit.Editor.registerIndexToGridRow( registerIndex )
+
+
+ // Looks like our circuit is NOT locked
+ // and we have a valid circuit CELL
+ // so let’s find everything else we could need.
+
+ const
+ selectallEl = targetEl.closest( '.Q-circuit-selectall' ),
+ registersymbolEl = targetEl.closest( '.Q-circuit-register-label' ),
+ momentsymbolEl = targetEl.closest( '.Q-circuit-moment-label' ),
+ inputEl = targetEl.closest( '.Q-circuit-input' ),
+ operationEl = targetEl.closest( '.Q-circuit-operation' )
+
+ // +++++++++++++++
+ // We’ll have to add some input editing capability later...
+ // Of course you can already do this in code!
+ // For now though most quantum code assumes all qubits
+ // begin with a value of zero so this is mostly ok ;)
+
+ if( inputEl ){
+
+ console.log( '→ Edit input Qubit value at', registerIndex )
+ return
+ }
+
+
+ // Let’s inspect a group of items via a CSS query.
+ // If any of them are NOT “selected” (highlighted)
+ // then select them all.
+ // But if ALL of them are already selected
+ // then UNSELECT them all.
+
+ function toggleSelection( query ){
+
+ const
+ operations = Array.from( circuitEl.querySelectorAll( query )),
+ operationsSelectedLength = operations.reduce( function( sum, element ){
+
+ sum += +element.classList.contains( 'Q-circuit-cell-selected' )
+ return sum
+
+ }, 0 )
+
+ if( operationsSelectedLength === operations.length ){
+
+ operations.forEach( function( el ){
+ el.classList.remove( 'Q-circuit-cell-selected' )
+ })
+ }
+ else {
+
+ operations.forEach( function( el ){
+
+ el.classList.add( 'Q-circuit-cell-selected' )
+ })
+ }
+ Q.Circuit.Editor.onSelectionChanged( circuitEl )
+ }
+
+
+ // Clicking on the “selectAll” button
+ // or any of the Moment symbols / Register symbols
+ // causes a selection toggle.
+ // In the future we may want to add
+ // dragging of entire Moment columns / Register rows
+ // to splice them out / insert them elsewhere
+ // when a user clicks and drags them.
+
+ if( selectallEl ){
+
+ toggleSelection( '.Q-circuit-operation' )
+ return
+ }
+ if( momentsymbolEl ){
+
+ toggleSelection( `.Q-circuit-operation[moment-index="${ momentIndex }"]` )
+ return
+ }
+ if( registersymbolEl ){
+
+ toggleSelection( `.Q-circuit-operation[register-index="${ registerIndex }"]` )
+ return
+ }
+
+
+ // Right here we can made a big decision:
+ // If you’re not pressing on an operation
+ // then GO HOME.
+
+ if( !operationEl ) return
+ // If we've doubleclicked on an operation and the operation has parameters, we should be able
+ // to edit those parameters regardless of whether or not the circuit is locked.
+ if( event.detail == 2) {
+ const operation = Q.Gate.findBySymbol(operationEl.getAttribute( 'gate-symbol' ))
+ if( operation.has_parameters ) {
+ Q.Circuit.Editor.onDoubleclick( event, operationEl )
+ return
+ }
+ }
+
+ // Ok now we know we are dealing with an operation.
+ // This preserved selection state information
+ // will be useful for when onPointerRelease is fired.
+
+ if( operationEl.classList.contains( 'Q-circuit-cell-selected' )){
+ operationEl.wasSelected = true
+ }
+ else operationEl.wasSelected = false
+
+
+ // And now we can proceed knowing that
+ // we need to select this operation
+ // and possibly drag it
+ // as well as any other selected operations.
+
+ operationEl.classList.add( 'Q-circuit-cell-selected' )
+ const selectedOperations = Array.from( circuitEl.querySelectorAll( '.Q-circuit-cell-selected' ))
+ dragEl.circuitEl = circuitEl
+ dragEl.originEl = circuitEl.querySelector( '.Q-circuit-board-foreground' )
+
+
+ // These are the default values;
+ // will be used if we’re only dragging one operation around.
+ // But if dragging more than one operation
+ // and we’re dragging the clipboard by an operation
+ // that is NOT in the upper-left corner of the clipboard
+ // then we need to know what the offset is.
+ // (Will be calculated below.)
+
+ dragEl.columnIndexOffset = 1
+ dragEl.rowIndexOffset = 1
+
+
+ // Now collect all of the selected operations,
+ // rip them from the circuit board’s foreground layer
+ // and place them on the clipboard.
+
+ let
+ columnIndexMin = Infinity,
+ rowIndexMin = Infinity
+
+ selectedOperations.forEach( function( el ){
+
+
+ // WORTH REPEATING:
+ // Once we know what cell we’ve pressed on
+ // we can get the momentIndex and registerIndex
+ // from its pre-defined attributes.
+ // NOTE that we are getting CSS grid column and row
+ // from our own conversion function and NOT from
+ // asking its styles. Why? Because browsers convert
+ // grid commands to a shorthand less easily parsable
+ // and therefore makes our code and reasoning
+ // more prone to quirks / errors. Trust me!
+
+ const
+ momentIndex = +el.getAttribute( 'moment-index' ),
+ registerIndex = +el.getAttribute( 'register-index' ),
+ columnIndex = Q.Circuit.Editor.momentIndexToGridColumn( momentIndex ),
+ rowIndex = Q.Circuit.Editor.registerIndexToGridRow( registerIndex )
+
+ columnIndexMin = Math.min( columnIndexMin, columnIndex )
+ rowIndexMin = Math.min( rowIndexMin, rowIndex )
+ el.classList.remove( 'Q-circuit-cell-selected' )
+ el.origin = { momentIndex, registerIndex, columnIndex, rowIndex }
+ dragEl.appendChild( el )
+ })
+ selectedOperations.forEach( function( el ){
+
+ const
+ columnIndexForClipboard = 1 + el.origin.columnIndex - columnIndexMin,
+ rowIndexForClipboard = 1 + el.origin.rowIndex - rowIndexMin
+
+ el.style.gridColumn = columnIndexForClipboard
+ el.style.gridRow = rowIndexForClipboard
+
+
+ // If this operation element is the one we grabbed
+ // (mostly relevant if we’re moving multiple operations at once)
+ // we need to know what the “offset” so everything can be
+ // placed correctly relative to this drag-and-dropped item.
+
+ if( el.origin.columnIndex === columnIndex &&
+ el.origin.rowIndex === rowIndex ){
+
+ dragEl.columnIndexOffset = columnIndexForClipboard
+ dragEl.rowIndexOffset = rowIndexForClipboard
+ }
+ })
+
+
+ // We need an XY offset that describes the difference
+ // between the mouse / finger press position
+ // and the clipboard’s intended upper-left position.
+ // To do that we need to know the press position (obviously!),
+ // the upper-left bounds of the circuit board’s foreground,
+ // and the intended upper-left bound of clipboard.
+
+ const
+ boardEl = circuitEl.querySelector( '.Q-circuit-board-foreground' ),
+ bounds = boardEl.getBoundingClientRect(),
+ minX = Q.Circuit.Editor.gridToPoint( columnIndexMin ),
+ minY = Q.Circuit.Editor.gridToPoint( rowIndexMin )
+
+ dragEl.offsetX = bounds.left + minX - x
+ dragEl.offsetY = bounds.top + minY - y
+ dragEl.momentIndex = momentIndex
+ dragEl.registerIndex = registerIndex
+ }
+ else if( paletteEl ){
+ const operationEl = targetEl.closest( '.Q-circuit-operation' )
+
+ if( !operationEl ) return
+
+ const
+ bounds = operationEl.getBoundingClientRect(),
+ { x, y } = Q.Circuit.Editor.getInteractionCoordinates( event )
+
+ dragEl.appendChild( operationEl.cloneNode( true ))
+ dragEl.originEl = paletteEl
+ dragEl.offsetX = bounds.left - x
+ dragEl.offsetY = bounds.top - y
+ }
+ else if( parameterEl ){
+ const exitEl = targetEl.closest( '.Q-parameter-box-exit' )
+ if( !exitEl ) return
+ parameterEl.style.display = 'none'
+ const foregroundEl = circuitEl.querySelector( '.Q-circuit-board-foreground' )
+ operationEl = foregroundEl.querySelector( `[moment-index="${ parameterEl.getAttribute( 'operation-moment-index' )}"]` +
+ `[register-index="${ parameterEl.getAttribute( 'operation-register-index' )}"]` )
+ parameters = {}
+ operationSkeleton = Q.Gate.findBySymbol( operationEl.getAttribute( 'gate-symbol' ))
+ Object.keys( operationSkeleton.parameters ).forEach( key => {
+ parameters[ key ] = operationEl.getAttribute( key ) ? operationEl.getAttribute( key ) : operationSkeleton.parameters[ key ]
+ })
+ //on exiting the parameter-input-box, we should update the circuit!!
+ circuitEl.circuit.set$(
+ operationEl.getAttribute( 'gate-symbol' ),
+ +operationEl.getAttribute( 'moment-index' ),
+ operationEl.getAttribute( 'register-indices' ) ? operationEl.getAttribute( 'register-indices' ).split(',').map( i => +i ) :
+ [ +operationEl.getAttribute( 'register-index' )],
+ parameters
+ )
+ parameterEl.innerHTML = ""
+ return
+ }
+ dragEl.timestamp = Date.now()
+
+
+ // Append the clipboard to the document,
+ // establish a global reference to it,
+ // and trigger a draw of it in the correct spot.
+
+ document.body.appendChild( dragEl )
+ Q.Circuit.Editor.dragEl = dragEl
+ Q.Circuit.Editor.onPointerMove( event )
+}
+
+
+
+
+
+
+ /////////////////////////
+ // //
+ // Pointer RELEASE //
+ // //
+/////////////////////////
+
+
+Q.Circuit.Editor.onPointerRelease = function( event ){
+
+
+ // If there’s no dragEl then bail immediately.
+ if( Q.Circuit.Editor.dragEl === null ) return
+ // Looks like we’re moving forward with this plan,
+ // so we’ll take control of the input now.
+
+ event.preventDefault()
+ event.stopPropagation()
+
+
+ // We can’t get the drop target from the event.
+ // Think about it: What was under the mouse / finger
+ // when this drop event was fired? THE CLIPBOARD !
+ // So instead we need to peek at what elements are
+ // under the mouse / finger, skipping element [0]
+ // because that will be the clipboard.
+
+ // doing this because elementsFromPoint() doesnt work well with JSDOM for testing purposes
+ const { x, y } = Q.Circuit.Editor.getInteractionCoordinates( event )
+ const boardContainerAll = document.querySelectorAll(".Q-circuit-board-container")
+ if( boardContainerAll.length === 0 ) return
+ let boardContainerEl = Array.from(boardContainerAll).find((element) => {
+ let rect = element.getBoundingClientRect()
+ let clientX = rect.left
+ let clientY = rect.top
+ let height = element.offsetHeight
+ let width = element.offsetWidth
+ return ( x >= clientX && x <= clientX + width ) && ( y >= clientY && y <= clientY + height )
+ })
+ returnToOrigin = function(){
+
+
+ // We can only do a “true” return to origin
+ // if we were dragging from a circuit.
+ // If we were dragging from a palette
+ // we can just stop dragging.
+
+ if( Q.Circuit.Editor.dragEl.circuitEl ){
+
+ Array.from( Q.Circuit.Editor.dragEl.children ).forEach( function( el ){
+
+ Q.Circuit.Editor.dragEl.originEl.appendChild( el )
+ el.style.gridColumn = el.origin.columnIndex
+ el.style.gridRow = el.origin.rowIndex
+ if( el.wasSelected === true ) el.classList.remove( 'Q-circuit-cell-selected' )
+ else el.classList.add( 'Q-circuit-cell-selected' )
+ })
+ Q.Circuit.Editor.onSelectionChanged( Q.Circuit.Editor.dragEl.circuitEl )
+ }
+ document.body.removeChild( Q.Circuit.Editor.dragEl )
+ Q.Circuit.Editor.dragEl = null
+ }
+
+
+ // If we have not dragged on to a circuit board
+ // then we’re throwing away this operation.
+
+ if( !boardContainerEl ){
+
+ if( Q.Circuit.Editor.dragEl.circuitEl ){
+
+ const
+ originCircuitEl = Q.Circuit.Editor.dragEl.circuitEl
+ originCircuit = originCircuitEl.circuit
+
+ originCircuit.history.createEntry$()
+ Array
+ .from( Q.Circuit.Editor.dragEl.children )
+ .forEach( function( child ){
+
+ originCircuit.clear$(
+
+ child.origin.momentIndex,
+ child.origin.registerIndex
+ )
+ })
+ Q.Circuit.Editor.onSelectionChanged( originCircuitEl )
+ Q.Circuit.Editor.onCircuitChanged( originCircuitEl )
+ }
+
+
+ // TIME TO DIE.
+ // Let’s keep a private reference to
+ // the current clipboard.
+
+ let clipboardToDestroy = Q.Circuit.Editor.dragEl
+
+
+ // Now we can remove our dragging reference.
+
+ Q.Circuit.Editor.dragEl = null
+
+
+ // Add our CSS animation routine
+ // which will run for 1 second.
+ // If we were SUPER AWESOME
+ // we would have also calculated drag momentum
+ // and we’d let this glide away!
+
+ clipboardToDestroy.classList.add( 'Q-circuit-clipboard-destroy' )
+
+
+ // And around the time that animation is completing
+ // we can go ahead and remove our clipboard from the DOM
+ // and kill the reference.
+
+ setTimeout( function(){
+
+ document.body.removeChild( clipboardToDestroy )
+ clipboardToDestroy = null
+
+ }, 500 )
+
+
+ // No more to do here. Goodbye.
+
+ return
+ }
+
+
+ // If we couldn’t determine a circuitEl
+ // from the drop target,
+ // or if there is a target circuit but it’s locked,
+ // then we need to return these dragged items
+ // to their original circuit.
+
+ const circuitEl = boardContainerEl.closest( '.Q-circuit' )
+ if( circuitEl.classList.contains( 'Q-circuit-locked' )){
+
+ returnToOrigin()
+ return
+ }
+
+
+ // Time to get serious.
+ // Where exactly are we dropping on to this circuit??
+
+ const
+ circuit = circuitEl.circuit,
+ bounds = boardContainerEl.getBoundingClientRect(),
+ droppedAtX = x - bounds.left + boardContainerEl.scrollLeft,
+ droppedAtY = y - bounds.top + boardContainerEl.scrollTop,
+ droppedAtMomentIndex = Q.Circuit.Editor.gridColumnToMomentIndex(
+
+ Q.Circuit.Editor.pointToGrid( droppedAtX )
+ ),
+ droppedAtRegisterIndex = Q.Circuit.Editor.gridRowToRegisterIndex(
+
+ Q.Circuit.Editor.pointToGrid( droppedAtY )
+ ),
+ foregroundEl = circuitEl.querySelector( '.Q-circuit-board-foreground' )
+
+
+ // If this is a self-drop
+ // we can also just return to origin and bail.
+
+ if( Q.Circuit.Editor.dragEl.circuitEl === circuitEl &&
+ Q.Circuit.Editor.dragEl.momentIndex === droppedAtMomentIndex &&
+ Q.Circuit.Editor.dragEl.registerIndex === droppedAtRegisterIndex ){
+
+ returnToOrigin()
+ return
+ }
+
+
+ // Is this a valid drop target within this circuit?
+
+ if(
+ droppedAtMomentIndex < 1 ||
+ droppedAtMomentIndex > circuit.timewidth ||
+ droppedAtRegisterIndex < 1 ||
+ droppedAtRegisterIndex > circuit.bandwidth
+ ){
+ returnToOrigin()
+ return
+ }
+
+
+ // Finally! Work is about to be done!
+ // All we need to do is tell the circuit itself
+ // where we need to place these dragged items.
+ // It will do all the validation for us
+ // and then fire events that will place new elements
+ // where they need to go!
+
+ const
+ draggedOperations = Array.from( Q.Circuit.Editor.dragEl.children ),
+ draggedMomentDelta = droppedAtMomentIndex - Q.Circuit.Editor.dragEl.momentIndex,
+ draggedRegisterDelta = droppedAtRegisterIndex - Q.Circuit.Editor.dragEl.registerIndex,
+ setCommands = []
+
+
+ // Whatever the next action is that we perform on the circuit,
+ // this was user-initiated via the graphic user interface (GUI).
+
+ circuit.history.createEntry$()
+
+
+ // Now let’s work our way through each of the dragged operations.
+ // If some of these are components of a multi-register operation
+ // the sibling components will get spliced out of the array
+ // to avoid processing any specific operation more than once.
+
+ draggedOperations.forEach( function( childEl, i ){
+
+ let
+ momentIndexTarget = droppedAtMomentIndex,
+ registerIndexTarget = droppedAtRegisterIndex
+
+ if( Q.Circuit.Editor.dragEl.circuitEl ){
+
+ momentIndexTarget += childEl.origin.momentIndex - Q.Circuit.Editor.dragEl.momentIndex
+ registerIndexTarget += childEl.origin.registerIndex - Q.Circuit.Editor.dragEl.registerIndex
+ }
+
+
+ // Is this a multi-register operation?
+ // If so, this is also a from-circuit drop
+ // rather than a from-palette drop.
+
+ const registerIndicesString = childEl.getAttribute( 'register-indices' )
+ if( registerIndicesString ){
+
+ // What are ALL of the registerIndices
+ // associated with this multi-register operation?
+ // (We may use them later as a checklist.)
+
+ const
+ registerIndices = registerIndicesString
+ .split( ',' )
+ .map( function( str ){ return +str }),
+
+
+ // Lets look for ALL of the sibling components of this operation.
+ // Later we’ll check and see if the length of this array
+ // is equal to the total number of components for this operation.
+ // If they’re equal then we know we’re dragging the WHOLE thing.
+ // Otherwise we need to determine if it needs to break apart
+ // and if so, what that nature of that break might be.
+
+ foundComponents = Array.from(
+
+ Q.Circuit.Editor.dragEl.querySelectorAll(
+
+ `[moment-index="${ childEl.origin.momentIndex }"]`+
+ `[register-indices="${ registerIndicesString }"]`
+ )
+ )
+ .sort( function( a, b ){
+
+ const
+ aRegisterIndicesIndex = +a.getAttribute( 'register-indices-index' ),
+ bRegisterIndicesIndex = +b.getAttribute( 'register-indices-index' )
+
+ return aRegisterIndicesIndex - bRegisterIndicesIndex
+ }),
+ allComponents = Array.from( Q.Circuit.Editor.dragEl.circuitEl.querySelectorAll(
+
+ `[moment-index="${ childEl.origin.momentIndex }"]`+
+ `[register-indices="${ registerIndicesString }"]`
+ )),
+ remainingComponents = allComponents.filter( function( componentEl, i ){
+
+ return !foundComponents.includes( componentEl )
+ }),
+
+
+ // We can’t pick the gate symbol
+ // off the 0th gate in the register indices array
+ // because that will be an identity / control / null gate.
+ // We need to look at slot 1.
+
+ component1 = Q.Circuit.Editor.dragEl.querySelector(
+
+ `[moment-index="${ childEl.origin.momentIndex }"]`+
+ `[register-index="${ registerIndices[ 1 ] }"]`
+ ),
+ gatesymbol = component1 ?
+ component1.getAttribute( 'gate-symbol' ) :
+ childEl.getAttribute( 'gate-symbol' )
+
+
+ // We needed to grab the above gatesymbol information
+ // before we sent any clear$ commands
+ // which would in turn delete those componentEls.
+ // We’ve just completed that,
+ // so now’s the time to send a clear$ command
+ // before we do any set$ commands.
+
+ draggedOperations.forEach( function( childEl ){
+
+ Q.Circuit.Editor.dragEl.circuitEl.circuit.clear$(
+
+ childEl.origin.momentIndex,
+ childEl.origin.registerIndex
+ )
+ })
+
+
+ // FULL MULTI-REGISTER DRAG (TO ANY POSITION ON ANY CIRCUIT).
+ // If we are dragging all of the components
+ // of a multi-register operation
+ // then we are good to go.
+
+ if( registerIndices.length === foundComponents.length ){
+
+ const operationSkeleton = Q.Gate.findBySymbol( gatesymbol )
+ parameters = {}
+ if( operationSkeleton.has_parameters ) {
+ Object.keys( operationSkeleton.parameters ).forEach( key => {
+ parameters[ key ] = childEl.getAttribute( key ) ? childEl.getAttribute( key ) : operationSkeleton.parameters[ key ]
+ })
+ }
+ //circuit.set$(
+ setCommands.push([
+
+ gatesymbol,
+ momentIndexTarget,
+
+
+ // We need to remap EACH register index here
+ // according to the drop position.
+ // Let’s let set$ do all the validation on this.
+
+ registerIndices.map( function( registerIndex ){
+
+ const siblingDelta = registerIndex - childEl.origin.registerIndex
+ registerIndexTarget = droppedAtRegisterIndex
+ if( Q.Circuit.Editor.dragEl.circuitEl ){
+
+ registerIndexTarget += childEl.origin.registerIndex - Q.Circuit.Editor.dragEl.registerIndex + siblingDelta
+ }
+ return registerIndexTarget
+ }),
+ parameters
+ // )
+ ])
+ }
+
+
+ // IN-MOMENT (IN-CIRCUIT) PARTIAL MULTI-REGISTER DRAG.
+ // It appears we are NOT dragging all components
+ // of a multi-register operation.
+ // But if we’re dragging within the same circuit
+ // and we’re staying within the same moment index
+ // that might be ok!
+
+ else if( Q.Circuit.Editor.dragEl.circuitEl === circuitEl &&
+ momentIndexTarget === childEl.origin.momentIndex ){
+
+
+ // We must ensure that only one component
+ // can sit at each register index.
+ // This copies registerIndices,
+ // but inverts the key : property relationship.
+ const registerMap = registerIndices
+ .reduce( function( registerMap, registerIndex, r ){
+
+ registerMap[ registerIndex ] = r
+ return registerMap
+
+ }, {} )
+
+
+ // First, we must remove each dragged component
+ // from the register it was sitting at.
+
+ foundComponents.forEach( function( component ){
+
+ const componentRegisterIndex = +component.getAttribute( 'register-index' )
+
+
+ // Remove this component from
+ // where this component used to be.
+
+ delete registerMap[ componentRegisterIndex ]
+ })
+
+
+ // Now we can seat it at its new position.
+ // Note: This may OVERWRITE one of its siblings!
+ // And that’s ok.
+ foundComponents.forEach( function( component ){
+
+ const
+ componentRegisterIndex = +component.getAttribute( 'register-index' ),
+ registerGrabDelta = componentRegisterIndex - Q.Circuit.Editor.dragEl.registerIndex
+
+
+ // Now put it where it wants to go,
+ // possibly overwriting a sibling component!
+ //ltnln: if a multiqubit operation component that requires a sibling, overwrites its sibling, both/all components should be destroyed
+ registerMap[
+
+ componentRegisterIndex + draggedRegisterDelta
+
+ ] = +component.getAttribute( 'register-indices-index' )
+ })
+
+
+ // Now let’s flip that registerMap
+ // back into an array of register indices.
+
+ const fixedRegistersIndices = Object.entries( registerMap )
+ .reduce( function( registers, entry, i ){
+
+ registers[ +entry[ 1 ]] = +entry[ 0 ]
+ return registers
+
+ }, [] )
+
+
+ // This will remove any blank entries in the array
+ // ie. if a dragged sibling overwrote a seated one.
+
+ .filter( function( entry ){
+ return Q.isUsefulInteger( entry )
+ })
+
+ const operationSkeleton = Q.Gate.findBySymbol( childEl.getAttribute( 'gate-symbol' ) )
+ parameters = {}
+ if( operationSkeleton.has_parameters ) {
+ Object.keys( operationSkeleton.parameters ).forEach( key => {
+ parameters[ key ] = childEl.getAttribute( key ) ? childEl.getAttribute( key ) : operationSkeleton.parameters[ key ]
+ })
+ }
+ // Finally, we’re ready to set.
+ // circuit.set$(
+ setCommands.push([
+ //ltnln: if a component of an operation that requires a sibling pair overwrites its sibling, we want it removed entirely.
+ fixedRegistersIndices.length < 2 && Q.Gate.findBySymbol( childEl.getAttribute( 'gate-symbol' ) ).is_multi_qubit ?
+ Q.Gate.NOOP :
+ childEl.getAttribute( 'gate-symbol' ),
+ momentIndexTarget,
+ fixedRegistersIndices,
+ parameters
+ // )
+ ])
+ }
+ else {
+ remainingComponents.forEach( function( componentEl, i ){
+ //circuit.set$(
+ const operationSkeleton = Q.Gate.findBySymbol( componentEl.getAttribute( 'gate-symbol' ) )
+ parameters = {}
+ if( operationSkeleton.has_parameters ) {
+ Object.keys( operationSkeleton.parameters ).forEach( key => {
+ parameters[ key ] = +componentEl.getAttribute( key ) ? +componentEl.getAttribute( key ) : operationSkeleton.parameters[ key ]
+ })
+ }
+ setCommands.push([
+
+ +componentEl.getAttribute( 'register-indices-index' ) && !Q.Gate.findBySymbol( childEl.getAttribute( 'gate-symbol' ) ).is_multi_qubit ?
+ gatesymbol :
+ Q.Gate.NOOP,
+ +componentEl.getAttribute( 'moment-index' ),
+ +componentEl.getAttribute( 'register-index' ),
+ parameters
+ // )
+ ])
+ })
+
+
+ // Finally, let’s separate and update
+ // all the components that were part of the drag.
+
+ foundComponents.forEach( function( componentEl ){
+ const operationSkeleton = Q.Gate.findBySymbol( componentEl.getAttribute( 'gate-symbol' ) )
+ parameters = {}
+ if( operationSkeleton.has_parameters ) {
+ Object.keys( operationSkeleton.parameters ).forEach( key => {
+ parameters[ key ] = +componentEl.getAttribute( key ) ? +componentEl.getAttribute( key ) : operationSkeleton.parameters[ key ]
+ })
+ }
+ setCommands.push([
+ //ltnln: temporary fix: certain multiqubit operations should only be represented in pairs of registers. If one is removed (i.e. a single component of the pair)
+ //then the entire operation should be removed.
+ +componentEl.getAttribute( 'register-indices-index' ) && !Q.Gate.findBySymbol( componentEl.getAttribute( 'gate-symbol' ) ).is_multi_qubit ?
+ componentEl.getAttribute( 'gate-symbol' ) :
+ Q.Gate.NOOP,
+ +componentEl.getAttribute( 'moment-index' ) + draggedMomentDelta,
+ +componentEl.getAttribute( 'register-index' ) + draggedRegisterDelta,
+ parameters
+ // )
+ ])
+ })
+ }
+
+
+ // We’ve just completed the movement
+ // of a multi-register operation.
+ // But all of the sibling components
+ // will also trigger this process
+ // unless we remove them
+ // from the draggd operations array.
+
+ let j = i + 1
+ while( j < draggedOperations.length ){
+
+ const possibleSibling = draggedOperations[ j ]
+ if( possibleSibling.getAttribute( 'gate-symbol' ) === gatesymbol &&
+ possibleSibling.getAttribute( 'register-indices' ) === registerIndicesString ){
+
+ draggedOperations.splice( j, 1 )
+ }
+ else j ++
+ }
+ }
+
+
+ // This is just a single-register operation.
+ // How simple this looks
+ // compared to all the gibberish above.
+
+ else {
+
+
+ // First, if this operation comes from a circuit
+ // (and not a circuit palette)
+ // make sure the old positions are cleared away.
+
+ if( Q.Circuit.Editor.dragEl.circuitEl ){
+
+ draggedOperations.forEach( function( childEl ){
+
+ Q.Circuit.Editor.dragEl.circuitEl.circuit.clear$(
+
+ childEl.origin.momentIndex,
+ childEl.origin.registerIndex
+ )
+ })
+ }
+
+
+ // And now set$ the operation
+ // in its new home.
+
+ // circuit.set$(
+ let registerIndices = [ registerIndexTarget ]
+ //ltnln: By default, multiqubit gates appear in pairs on the circuit rather than
+ // requiring the user to have to pair them like with Swap/CNot.
+ const operationSkeleton = Q.Gate.findBySymbol( childEl.getAttribute( 'gate-symbol' ))
+ if(operationSkeleton.is_multi_qubit ) {
+ registerIndices.push( registerIndexTarget == circuit.bandwidth ? registerIndexTarget - 1 : registerIndexTarget + 1)
+ }
+ let parameters = {}
+ if( operationSkeleton.has_parameters ) {
+ Object.keys( operationSkeleton.parameters ).forEach( key => {
+ parameters[ key ] = childEl.getAttribute( key ) ? childEl.getAttribute( key ) : operationSkeleton.parameters[ key ]
+ })
+ }
+ setCommands.push([
+ childEl.getAttribute( 'gate-symbol' ),
+ momentIndexTarget,
+ registerIndices,
+ parameters
+ // )
+ ])
+ }
+ })
+
+
+ // DO IT DO IT DO IT
+
+ setCommands.forEach( function( setCommand ){
+
+ circuit.set$.apply( circuit, setCommand )
+ })
+
+
+ // Are we capable of making controls? Swaps?
+
+ Q.Circuit.Editor.onSelectionChanged( circuitEl )
+ Q.Circuit.Editor.onCircuitChanged( circuitEl )
+
+
+ // If the original circuit and destination circuit
+ // are not the same thing
+ // then we need to also eval the original circuit.
+
+ if( Q.Circuit.Editor.dragEl.circuitEl &&
+ Q.Circuit.Editor.dragEl.circuitEl !== circuitEl ){
+
+ const originCircuitEl = Q.Circuit.Editor.dragEl.circuitEl
+ Q.Circuit.Editor.onSelectionChanged( originCircuitEl )
+ Q.Circuit.Editor.onCircuitChanged( originCircuitEl )
+ }
+
+
+ // We’re finally done here.
+ // Clean up and go home.
+ // It’s been a long journey.
+ // I love you all.
+
+ document.body.removeChild( Q.Circuit.Editor.dragEl )
+ Q.Circuit.Editor.dragEl = null
+}
+
+
+ /////////////////////////
+ // //
+ // Pointer DOUBLECLICK //
+ // //
+/////////////////////////
+//ltnln: my trying out an idea for parameterized gates...
+Q.Circuit.Editor.onDoubleclick = function( event, operationEl ) {
+ // assumption for the following 3 lines is that we've already decided that we are on-top of a valid gate operation in
+ // the circuit
+ const operation = Q.Gate.findBySymbol( operationEl.getAttribute( 'gate-symbol' ))
+ const { x, y } = Q.Circuit.Editor.getInteractionCoordinates( event )
+
+ const boardContainerAll = document.querySelectorAll(".Q-circuit-board-container")
+ if( boardContainerAll.length === 0 ) return
+ let boardContainerEl = Array.from(boardContainerAll).find((element) => {
+ let rect = element.getBoundingClientRect()
+ let clientX = rect.left
+ let clientY = rect.top
+ let height = element.offsetHeight
+ let width = element.offsetWidth
+ return ( x >= clientX && x <= clientX + width ) && ( y >= clientY && y <= clientY + height )
+ })
+ if( !boardContainerEl ) return;
+ const parameterEl = boardContainerEl.querySelector('.Q-parameters-box')
+ const exit = document.createElement( 'button' )
+ parameterEl.appendChild( exit )
+ exit.classList.add( 'Q-parameter-box-exit' )
+ exit.appendChild(document.createTextNode( '⬅' ))
+ parameterEl.setAttribute( "operation-moment-index", operationEl.getAttribute( 'moment-index' ))
+ parameterEl.setAttribute( "operation-register-index", operationEl.getAttribute( 'register-index' ))
+
+
+ //here we generate queries for each parameter that the gate operation takes!
+ const parameters = Object.keys(operation.parameters)
+ parameters.forEach( element => {
+ if( operation.parameters && operation.parameters[element] !== null ) {
+ const input_fields = document.createElement( 'div' )
+ parameterEl.appendChild( input_fields )
+ input_fields.classList.add( 'Q-parameter-box-input-container' )
+ const label = document.createElement( "span" )
+ input_fields.appendChild( label )
+ label.classList.add( 'Q-parameter-input-label' )
+ label.appendChild(document.createTextNode( element ))
+
+ const textbox = document.createElement( "input" )
+ input_fields.appendChild( textbox )
+ textbox.classList.add( 'Q-parameter-box-input' )
+ textbox.setAttribute( 'type', 'text' )
+ textbox.setAttribute( 'placeholder', element )
+ textbox.setAttribute( 'value', operationEl.getAttribute(element) ? operationEl.getAttribute(element) : operation.parameters[element] )
+ //set textbox to update the operation instance (cellEl)'s parameters on value change
+ textbox.addEventListener( "change", () => {
+ let parameterValue
+ let oldValue = operationEl.getAttribute( element )
+ if( !oldValue ) oldValue = operation.parameters[ element ]
+ try {
+ //TODO: figure out how to properly import the mathjs library...
+ parameterValue = +(textbox.value.toLowerCase());
+ }
+ catch( err ) {
+ parameterValue = oldValue
+ }
+
+ if( !parameterValue || parameterValue === Infinity ) textbox.value = oldValue.toString()
+ else {
+ operationEl.setAttribute( element, parameterValue )
+ textbox.value = parameterValue.toString()
+ }
+ })
+
+
+ }
+ })
+
+ parameterEl.classList.toggle('overlay')
+ parameterEl.style.display = 'block'
+}
+
+
+ ///////////////////
+ // //
+ // Listeners //
+ // //
+///////////////////
+
+
+// These listeners must be applied
+// to the entire WINDOW (and not just document.body!)
+
+window.addEventListener( 'mousemove', Q.Circuit.Editor.onPointerMove )
+window.addEventListener( 'touchmove', Q.Circuit.Editor.onPointerMove )
+window.addEventListener( 'mouseup', Q.Circuit.Editor.onPointerRelease )
+window.addEventListener( 'touchend', Q.Circuit.Editor.onPointerRelease )
+
+
+
+
+
+
+
+/*
+
+
+%%HTML
+
+
+
+
+
+%%javascript
+Q.braket( element )
+
+
+
+
+*/
+
+
+
+//%%javascript
+
+
+
+Q.braket = function(){
+
+
+ // Create the HTML bits we need,
+ // contain them all together,
+ // and output them to Jupyter.
+ if( arguments.length === 0 || arguments.length > 3 ) return;
+ const element = arguments[0];
+ const args = (Array.from(arguments)).slice(1);
+ let circuit
+ if(args.length === 0) {
+ circuit = new Q( 4, 8 )
+ }
+ else if(args.length === 1) {
+ circuit = new Q( args[0] )
+ }
+ else {
+ circuit = new Q( args[0], args[1] )
+ }
+ container = document.createElement( 'div' )
+ container.appendChild( Q.Circuit.Editor.createPalette() )
+ container.appendChild( circuit.toDom() )
+ element.html( container )
+
+
+ // We’re going to take this SLOOOOOOOOWLY
+ // because there are many potential things to debug.
+
+ const thisCell = Jupyter.notebook.get_selected_cell()
+ // console.log( 'thisCell', thisCell )
+
+ const thisCellIndex = Jupyter.notebook.get_cells().indexOf( thisCell )
+ // console.log( 'thisCellIndex', thisCellIndex )
+
+ const nextCell = Jupyter.notebook.insert_cell_below( 'code', thisCellIndex - 1 )
+ const nextNextCell = Jupyter.notebook.insert_cell_below( 'markdown', Jupyter.notebook.get_cells().indexOf( thisCell ) - 1 )
+ // console.log( 'nextCell', nextCell )
+
+ nextCell.set_text( circuit.toAmazonBraket() )
+ nextNextCell.set_text( circuit.report$() )
+
+
+
+
+
+
+ window.addEventListener( 'Q gui altered circuit', function( event ){
+
+ // updatePlaygroundFromDom( event.detail.circuit )
+ if( event.detail.circuit === circuit ){
+
+ console.log( 'Updating circuit from GUI', circuit )
+ circuit.evaluate$()
+ nextCell.set_text( circuit.toAmazonBraket() )
+
+ }
+ })
+
+ window.addEventListener( 'Q.Circuit.evaluate completed', function( event ) {
+ if( event.detail.circuit === circuit ) {
+ nextNextCell.set_text( circuit.report$() )
+ }
+ })
+
+
+ // nextCell.render()
+
+ // console.log( 'thisCell', thisCell )
+ // console.log( 'nextCell', nextCell )
+ // console.log( 'thisCellIndex', thisCellIndex )
+
+ // code = Jupyter.notebook.insert_cell_{0}('code');
+ // code.set_text(atob("{1}"))
+
+ // var t_cell = Jupyter.notebook.get_selected_cell()
+ // t_cell.set_text(' \\n{}')
+ // var t_index = Jupyter.notebook.get_cells().indexOf(t_cell)
+ // Jupyter.notebook.to_markdown(t_index)
+ // Jupyter.notebook.get_cell(t_index).render()
+}
+
+module.exports = Q
\ No newline at end of file
diff --git a/index.js b/index.js
new file mode 100644
index 0000000..fc40a83
--- /dev/null
+++ b/index.js
@@ -0,0 +1,13 @@
+const {Q} = require('./Q/Q');
+const {Circuit} = require('./Q/Q-Circuit');
+const {Qubit} = require('./Q/Q-Qubit');
+const {Gate} = require('./Q/Q-Gate');
+const {Matrix} = require('./Q/Q-Matrix');
+const {ComplexNumber} = require('./Q/Q-ComplexNumber');
+const mathf = require('./Q/Math-Functions');
+const misc = require('./Q/Misc');
+const logger = require('./Q/Logging');
+
+console.log("Howdy! Welcome to Q.js!");
+
+module.exports = {Q, Circuit, Qubit, Gate, Matrix, ComplexNumber, mathf, misc, logger};
\ No newline at end of file
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..0573d1b
--- /dev/null
+++ b/package.json
@@ -0,0 +1,30 @@
+{
+ "name": "quantum-js",
+ "version": "1.0.0",
+ "description": "\")",
+ "main": "Q/Q.js",
+ "scripts": {
+ "test": "npm run test -ws && exit 0",
+ "lint": "eslint",
+ "prettier": "prettier --write"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://gitlab.aws.dev/ltnln/q-js"
+ },
+ "keywords": [],
+ "author": "",
+ "license": "ISC",
+ "devDependencies": {
+ "eslint": "^7.31.0",
+ "jest": "^27.0.6",
+ "jsdom": "^16.6.0",
+ "lerna": "^4.0.0",
+ "n": "^7.3.1",
+ "prettier": "2.3.2"
+ },
+ "dependencies": {},
+ "workspaces": [
+ "./packages/*"
+ ]
+}
diff --git a/packages/quantum-js-util/Logging.js b/packages/quantum-js-util/Logging.js
new file mode 100644
index 0000000..f05ebbd
--- /dev/null
+++ b/packages/quantum-js-util/Logging.js
@@ -0,0 +1,89 @@
+//Logging functions
+
+function log(verbosity = 0.5, verbosityThreshold, ...remainingArguments) {
+ if (verbosity >= verbosityThreshold) console.log(...remainingArguments);
+ return "(log)";
+}
+
+function error() {
+ console.error(...arguments);
+ return "(error)";
+}
+
+function warn() {
+ console.warn(...arguments);
+ return "(error)";
+}
+
+function extractDocumentation(f) {
+ `
+ I wanted a way to document code
+ that was cleaner, more legible, and more elegant
+ than the bullshit we put up with today.
+ Also wanted it to print nicely in the console.
+ `;
+
+ f = f.toString();
+
+ const begin = f.indexOf("`") + 1,
+ end = f.indexOf("`", begin),
+ lines = f.substring(begin, end).split("\n");
+
+ function countPrefixTabs(text) {
+ // Is counting tabs “manually”
+ // actually more performant than regex?
+
+ let count = (index = 0);
+ while (text.charAt(index++) === "\t") count++;
+ return count;
+ }
+
+ //------------------- TO DO!
+ // we should check that there is ONLY whitespace between the function opening and the tick mark!
+ // otherwise it’s not documentation.
+
+ let tabs = Number.MAX_SAFE_INTEGER;
+
+ lines.forEach(function (line) {
+ if (line) {
+ const lineTabs = countPrefixTabs(line);
+ if (tabs > lineTabs) tabs = lineTabs;
+ }
+ });
+ lines.forEach(function (line, i) {
+ if (line.trim() === "") line = "\n\n";
+ lines[i] = line.substring(tabs).replace(/ {2}$/, "\n");
+ });
+ return lines.join("");
+}
+
+function help(f) {
+ if (f === undefined) f = Q;
+ return extractDocumentation(f);
+}
+
+function toTitleCase(text) {
+ text = text.replace(/_/g, " ");
+ return text
+ .toLowerCase()
+ .split(" ")
+ .map(function (word) {
+ return word.replace(word[0], word[0].toUpperCase());
+ })
+ .join(" ");
+}
+
+function centerText(text, length, filler) {
+ if (length > text.length) {
+ if (typeof filler !== "string") filler = " ";
+
+ const padLengthLeft = Math.floor((length - text.length) / 2),
+ padLengthRight = length - text.length - padLengthLeft;
+
+ return text
+ .padStart(padLengthLeft + text.length, filler)
+ .padEnd(length, filler);
+ } else return text;
+}
+
+module.exports = { log, error, help, warn, toTitleCase, centerText };
diff --git a/packages/quantum-js-util/Math-Functions.js b/packages/quantum-js-util/Math-Functions.js
new file mode 100644
index 0000000..498c381
--- /dev/null
+++ b/packages/quantum-js-util/Math-Functions.js
@@ -0,0 +1,56 @@
+//math functions
+function hypotenuse(x, y) {
+ let a = Math.abs(x),
+ b = Math.abs(y);
+
+ if (a < 2048 && b < 2048) {
+ return Math.sqrt(a * a + b * b);
+ }
+ if (a < b) {
+ a = b;
+ b = x / y;
+ } else b = y / x;
+ return a * Math.sqrt(1 + b * b);
+}
+
+function logHypotenuse(x, y) {
+ const a = Math.abs(x),
+ b = Math.abs(y);
+
+ if (x === 0) return Math.log(b);
+ if (y === 0) return Math.log(a);
+ if (a < 2048 && b < 2048) {
+ return Math.log(x * x + y * y) / 2;
+ }
+ return Math.log(x / Math.cos(Math.atan2(y, x)));
+}
+
+function hyperbolicSine(n) {
+ return (Math.exp(n) - Math.exp(-n)) / 2;
+}
+
+function hyperbolicCosine(n) {
+ return (Math.exp(n) + Math.exp(-n)) / 2;
+}
+
+function round(n, d) {
+ if (typeof d !== "number") d = 0;
+ const f = Math.pow(10, d);
+ return Math.round(n * f) / f;
+}
+
+function isUsefulNumber(n) {
+ return (
+ isNaN(n) === false &&
+ (typeof n === "number" || n instanceof Number) &&
+ n !== Infinity &&
+ n !== -Infinity
+ );
+}
+
+function isUsefulInteger(n) {
+ return isUsefulNumber(n) && Number.isInteger(n);
+}
+
+
+module.exports = { isUsefulNumber, isUsefulInteger, hypotenuse, logHypotenuse, hyperbolicCosine, hyperbolicSine, round };
diff --git a/packages/quantum-js-util/Misc.js b/packages/quantum-js-util/Misc.js
new file mode 100644
index 0000000..4eb824d
--- /dev/null
+++ b/packages/quantum-js-util/Misc.js
@@ -0,0 +1,400 @@
+const logger = require('./Logging');
+
+const COLORS = [];
+const ANIMALS = [];
+const constants = {};
+
+function dispatchEventToGlobal(event) {
+ if(typeof window != undefined) {
+ window.dispatchEvent(event);
+ }
+ else {
+ //if window does exist, global == window is true. So maybe we can just do global.dispatchEvent instead of this wrapper?
+ global.dispatchEvent(event);
+ console.log(event);
+ }
+}
+
+function createConstant(key, value) {
+ //Object.freeze( value )
+ this[key] = value;
+ // Object.defineProperty( this, key, {
+
+ // value,
+ // writable: false
+ // })
+ // Object.defineProperty( this.constants, key, {
+
+ // value,
+ // writable: false
+ // })
+ constants[key] = this[key];
+ Object.freeze(this[key]);
+}
+
+function createConstants() {
+ if (arguments.length % 2 !== 0) {
+ return logger.error(
+ "Q attempted to create constants with invalid (KEY, VALUE) pairs."
+ );
+ }
+ for (let i = 0; i < arguments.length; i += 2) {
+ createConstant(arguments[i], arguments[i + 1]);
+ }
+}
+// function loop() {}
+
+let namesIndex = 0;
+let shuffledNames = [];
+function shuffleNames$() {
+ let m = [];
+ for (let c = 0; c < COLORS.length; c++) {
+ for (let a = 0; a < ANIMALS.length; a++) {
+ m.push([c, a, Math.random()]);
+ }
+ }
+ shuffledNames = m.sort(function (a, b) {
+ return a[2] - b[2];
+ });
+}
+
+function getRandomName$() {
+ if (shuffledNames.length === 0) shuffleNames$();
+
+ const pair = shuffledNames[namesIndex],
+ name = COLORS[pair[0]] + " " + ANIMALS[pair[1]];
+
+ namesIndex = (namesIndex + 1) % shuffledNames.length;
+ return name;
+}
+
+function hueToColorName(hue) {
+ hue = hue % 360;
+ hue = Math.floor(hue / 10);
+ return COLORS[hue];
+}
+
+function colorIndexToHue(i) {
+ return i * 10;
+}
+
+createConstants(
+ "REVISION",
+ 19,
+
+ // Yeah... F’ing floating point numbers, Man!
+ // Here’s the issue:
+ // var a = new Q.ComplexNumber( 1, 2 )
+ // a.multiply(a).isEqualTo( a.power( new Q.ComplexNumber( 2, 0 )))
+ // That’s only true if Q.EPSILON >= Number.EPSILON * 6
+
+ "EPSILON",
+ Number.EPSILON * 6,
+
+ "RADIANS_TO_DEGREES",
+ 180 / Math.PI,
+
+
+ "ANIMALS",
+ [
+ "Aardvark",
+ "Albatross",
+ "Alligator",
+ "Alpaca",
+ "Ant",
+ "Anteater",
+ "Antelope",
+ "Ape",
+ "Armadillo",
+ "Baboon",
+ "Badger",
+ "Barracuda",
+ "Bat",
+ "Bear",
+ "Beaver",
+ "Bee",
+ "Bison",
+ "Boar",
+ "Buffalo",
+ "Butterfly",
+ "Camel",
+ "Caribou",
+ "Cat",
+ "Caterpillar",
+ "Cattle",
+ "Chamois",
+ "Cheetah",
+ "Chicken",
+ "Chimpanzee",
+ "Chinchilla",
+ "Chough",
+ "Clam",
+ "Cobra",
+ "Cod",
+ "Cormorant",
+ "Coyote",
+ "Crab",
+ "Crane",
+ "Crocodile",
+ "Crow",
+ "Curlew",
+ "Deer",
+ "Dinosaur",
+ "Dog",
+ "Dogfish",
+ "Dolphin",
+ "Donkey",
+ "Dotterel",
+ "Dove",
+ "Dragonfly",
+ "Duck",
+ "Dugong",
+ "Dunlin",
+ "Eagle",
+ "Echidna",
+ "Eel",
+ "Eland",
+ "Elephant",
+ "Elephant seal",
+ "Elk",
+ "Emu",
+ "Falcon",
+ "Ferret",
+ "Finch",
+ "Fish",
+ "Flamingo",
+ "Fly",
+ "Fox",
+ "Frog",
+ "Galago",
+ "Gaur",
+ "Gazelle",
+ "Gerbil",
+ "Giant Panda",
+ "Giraffe",
+ "Gnat",
+ "Gnu",
+ "Goat",
+ "Goose",
+ "Goldfinch",
+ "Goldfish",
+ "Gorilla",
+ "Goshawk",
+ "Grasshopper",
+ "Grouse",
+ "Guanaco",
+ "Guinea fowl",
+ "Guinea pig",
+ "Gull",
+ "Guppy",
+ "Hamster",
+ "Hare",
+ "Hawk",
+ "Hedgehog",
+ "Hen",
+ "Heron",
+ "Herring",
+ "Hippopotamus",
+ "Hornet",
+ "Horse",
+ "Human",
+ "Hummingbird",
+ "Hyena",
+ "Ide",
+ "Jackal",
+ "Jaguar",
+ "Jay",
+ "Jellyfish",
+ "Kangaroo",
+ "Koala",
+ "Koi",
+ "Komodo dragon",
+ "Kouprey",
+ "Kudu",
+ "Lapwing",
+ "Lark",
+ "Lemur",
+ "Leopard",
+ "Lion",
+ "Llama",
+ "Lobster",
+ "Locust",
+ "Loris",
+ "Louse",
+ "Lyrebird",
+ "Magpie",
+ "Mallard",
+ "Manatee",
+ "Marten",
+ "Meerkat",
+ "Mink",
+ "Mole",
+ "Monkey",
+ "Moose",
+ "Mouse",
+ "Mosquito",
+ "Mule",
+ "Narwhal",
+ "Newt",
+ "Nightingale",
+ "Octopus",
+ "Okapi",
+ "Opossum",
+ "Oryx",
+ "Ostrich",
+ "Otter",
+ "Owl",
+ "Ox",
+ "Oyster",
+ "Panther",
+ "Parrot",
+ "Partridge",
+ "Peafowl",
+ "Pelican",
+ "Penguin",
+ "Pheasant",
+ "Pig",
+ "Pigeon",
+ "Pony",
+ "Porcupine",
+ "Porpoise",
+ "Prairie Dog",
+ "Quail",
+ "Quelea",
+ "Rabbit",
+ "Raccoon",
+ "Rail",
+ "Ram",
+ "Raven",
+ "Reindeer",
+ "Rhinoceros",
+ "Rook",
+ "Ruff",
+ "Salamander",
+ "Salmon",
+ "Sand Dollar",
+ "Sandpiper",
+ "Sardine",
+ "Scorpion",
+ "Sea lion",
+ "Sea Urchin",
+ "Seahorse",
+ "Seal",
+ "Shark",
+ "Sheep",
+ "Shrew",
+ "Shrimp",
+ "Skunk",
+ "Snail",
+ "Snake",
+ "Sow",
+ "Spider",
+ "Squid",
+ "Squirrel",
+ "Starling",
+ "Stingray",
+ "Stinkbug",
+ "Stork",
+ "Swallow",
+ "Swan",
+ "Tapir",
+ "Tarsier",
+ "Termite",
+ "Tiger",
+ "Toad",
+ "Trout",
+ "Tui",
+ "Turkey",
+ "Turtle",
+ // U
+ "Vicuña",
+ "Viper",
+ "Vulture",
+ "Wallaby",
+ "Walrus",
+ "Wasp",
+ "Water buffalo",
+ "Weasel",
+ "Whale",
+ "Wolf",
+ "Wolverine",
+ "Wombat",
+ "Woodcock",
+ "Woodpecker",
+ "Worm",
+ "Wren",
+ // X
+ "Yak",
+ "Zebra",
+ ],
+ "ANIMALS3",
+ [
+ "ape",
+ "bee",
+ "cat",
+ "dog",
+ "elk",
+ "fox",
+ "gup",
+ "hen",
+ "ide",
+ "jay",
+ "koi",
+ "leo",
+ "moo",
+ "nit",
+ "owl",
+ "pig",
+ // Q ?
+ "ram",
+ "sow",
+ "tui",
+ // U ?
+ // V ?
+ // W ?
+ // X ?
+ "yak",
+ "zeb",
+ ],
+ "COLORS",
+ [
+ "Red", // 0 RED
+ "Scarlet", // 10
+ "Tawny", // 20
+ "Carrot", // 30
+ "Pumpkin", // 40
+ "Mustard", // 50
+ "Lemon", // 60 Yellow
+ "Lime", // 70
+ "Spring bud", // 80
+ "Spring grass", // 90
+ "Pear", // 100
+ "Kelly", // 110
+ "Green", // 120 GREEN
+ "Malachite", // 130
+ "Sea green", // 140
+ "Sea foam", // 150
+ "Aquamarine", // 160
+ "Turquoise", // 170
+ "Cyan", // 180 Cyan
+ "Pacific blue", // 190
+ "Baby blue", // 200
+ "Ocean blue", // 210
+ "Sapphire", // 220
+ "Azure", // 230
+ "Blue", // 240 BLUE
+ "Cobalt", // 250
+ "Indigo", // 260
+ "Violet", // 270
+ "Lavender", // 280
+ "Purple", // 290
+ "Magenta", // 300 Magenta
+ "Hot pink", // 310
+ "Fuschia", // 320
+ "Ruby", // 330
+ "Crimson", // 340
+ "Carmine", // 350
+ ]
+);
+
+module.exports = { createConstant, createConstants, getRandomName$, hueToColorName, colorIndexToHue, dispatchEventToGlobal, constants };
diff --git a/Q/Q-Circuit.js b/packages/quantum-js-util/Q-Circuit.js
similarity index 77%
rename from Q/Q-Circuit.js
rename to packages/quantum-js-util/Q-Circuit.js
index 55820bb..9cba24c 100644
--- a/Q/Q-Circuit.js
+++ b/packages/quantum-js-util/Q-Circuit.js
@@ -2,34 +2,44 @@
// Copyright © 2019–2020, Stewart Smith. See LICENSE for details.
+//
+const logger = require('./Logging');
+const misc = require('./Misc');
+const mathf = require('./Math-Functions');
+const {ComplexNumber} = require('./Q-ComplexNumber');
+const {Gate} = require('./Q-Gate');
+const {Qubit} = require('./Q-Qubit');
+const {Matrix} = require('./Q-Matrix');
+const {History} = require('./Q-History');
-Q.Circuit = function( bandwidth, timewidth ){
+Circuit = function( bandwidth, timewidth ){
+
// What number Circuit is this
// that we’re attempting to make here?
- this.index = Q.Circuit.index ++
+ this.index = Circuit.index ++
// How many qubits (registers) shall we use?
- if( !Q.isUsefulInteger( bandwidth )) bandwidth = 3
+ if( !mathf.isUsefulInteger( bandwidth )) bandwidth = 3
this.bandwidth = bandwidth
// How many operations can we perform on each qubit?
// Each operation counts as one moment; one clock tick.
- if( !Q.isUsefulInteger( timewidth )) timewidth = 5
+ if( !mathf.isUsefulInteger( timewidth )) timewidth = 5
this.timewidth = timewidth
// We’ll start with Horizontal qubits (zeros) as inputs
// but we can of course modify this after initialization.
- this.qubits = new Array( bandwidth ).fill( Q.Qubit.HORIZONTAL )
+ this.qubits = new Array( bandwidth ).fill( Qubit.HORIZONTAL )
// What operations will we perform on our qubits?
@@ -52,37 +62,35 @@ Q.Circuit = function( bandwidth, timewidth ){
// Undo / Redo history.
+ this.history = new History( this )
- this.history = new Q.History( this )
}
-Object.assign( Q.Circuit, {
-
+Object.assign( Circuit, {
index: 0,
- help: function(){ return Q.help( this )},
+ help: function(){ return logger.help( this )},
constants: {},
- createConstant: Q.createConstant,
- createConstants: Q.createConstants,
+ createConstant: misc.createConstant,
+ createConstants: misc.createConstants,
fromText: function( text ){
// This is a quick way to enable `fromText()`
- // to return a default new Q.Circuit().
-
- if( text === undefined ) return new Q.Circuit()
+ // to return a default new Circuit().
+ if( text === undefined ) return new Circuit()
// Is this a String Template -- as opposed to a regular String?
// If so, let’s convert it to a regular String.
// Yes, this maintains the line breaks.
if( text.raw !== undefined ) text = ''+text.raw
- return Q.Circuit.fromTableTransposed(
+ return Circuit.fromTableTransposed(
text
.trim()
@@ -138,9 +146,9 @@ Object.assign( Q.Circuit, {
// This is a quick way to enable `fromText()`
- // to return a default new Q.Circuit().
+ // to return a default new Circuit().
- if( text === undefined ) return new Q.Circuit()
+ if( text === undefined ) return new Circuit()
// Is this a String Template -- as opposed to a regular String?
@@ -196,7 +204,6 @@ Object.assign( Q.Circuit, {
fromTableTransposed: function( table ){
-
const
bandwidth = table.length,
timewidth = table.reduce( function( max, moments ){
@@ -204,7 +211,7 @@ Object.assign( Q.Circuit, {
return Math.max( max, moments.length )
}, 0 ),
- circuit = new Q.Circuit( bandwidth, timewidth )
+ circuit = new Circuit( bandwidth, timewidth )
circuit.bandwidth = bandwidth
circuit.timewidth = timewidth
@@ -216,20 +223,18 @@ Object.assign( Q.Circuit, {
const
momentIndex = m + 1,
operation = table[ r ][ m ]
-
let siblingHasBeenFound = false
for( let s = 0; s < r; s ++ ){
const sibling = table[ s ][ m ]
if( operation.gateSymbol === sibling.gateSymbol &&
operation.operationMomentId === sibling.operationMomentId &&
- Q.isUsefulInteger( operation.mappingIndex ) &&
- Q.isUsefulInteger( sibling.mappingIndex ) &&
+ mathf.isUsefulInteger( operation.mappingIndex ) &&
+ mathf.isUsefulInteger( sibling.mappingIndex ) &&
operation.mappingIndex !== sibling.mappingIndex ){
// We’ve found a sibling !
-
const operationsIndex = circuit.operations.findIndex( function( operation ){
return (
@@ -247,10 +252,10 @@ Object.assign( Q.Circuit, {
if( siblingHasBeenFound === false && operation.gateSymbol !== 'I' ){
const
- gate = Q.Gate.findBySymbol( operation.gateSymbol ),
+ gate = Gate.findBySymbol( operation.gateSymbol ),
registerIndices = []
- if( Q.isUsefulInteger( operation.mappingIndex )){
+ if( mathf.isUsefulInteger( operation.mappingIndex )){
registerIndices[ operation.mappingIndex ] = registerIndex
}
@@ -282,7 +287,7 @@ Object.assign( Q.Circuit, {
const
size = U.getWidth(),
- result = Q.Matrix.createIdentity( size * 2 )
+ result = Matrix.createIdentity( size * 2 )
// console.log( 'U', U.toTsv() )
// console.log( 'size', size )
@@ -291,7 +296,6 @@ Object.assign( Q.Circuit, {
for( let x = 0; x < size; x ++ ){
for( let y = 0; y < size; y ++ ){
-
const v = U.read( x, y )
// console.log( `value at ${x}, ${y}`, v )
result.write$( x + size, y + size, v )
@@ -299,7 +303,14 @@ Object.assign( Q.Circuit, {
}
return result
},
-
+
+ //given an operation, return whether or not it is a valid control operation on the circuit-editor.
+ isControlledOperation: function( operation ) {
+ return (!operation.gate.is_multi_qubit || operation.gate.name === 'Swap') //assumption: we won't allow controlled multi-qubit operations
+ //..except swap or CNOT
+ && (operation.registerIndices.length >= 2)
+ && (operation.gate.can_be_controlled)
+ },
// Return transformation over entire nqubit register that applies U to
@@ -309,7 +320,6 @@ Object.assign( Q.Circuit, {
// http://148.206.53.84/tesiuami/S_pdfs/AUTOMATIC%20QUANTUM%20COMPUTER%20PROGRAMMING.pdf
expandMatrix: function( circuitBandwidth, U, qubitIndices ){
-
// console.log( 'EXPANDING THE MATRIX...' )
// console.log( 'this one: U', U.toTsv())
@@ -328,7 +338,7 @@ Object.assign( Q.Circuit, {
for( let i = 0; i < qubitIndices.length; i ++ ){
//qubitIndices[ i ] = ( circuitBandwidth - 1 ) - qubitIndices[ i ]
- qubitIndices[ i ] = ( circuitBandwidth - 0 ) - qubitIndices[ i ]
+ qubitIndices[ i ] -= 1
}
// console.log( 'qubits AFTER manipulation', qubitIndices )
@@ -349,7 +359,7 @@ Object.assign( Q.Circuit, {
- const result = new Q.Matrix.createZero( n )
+ const result = new Matrix.createZero( n )
// const X = numeric.rep([n, n], 0);
@@ -377,7 +387,6 @@ Object.assign( Q.Circuit, {
if( bitsEqual ){
// console.log( 'bits ARE equal' )
-
let
istar = 0,
jstar = 0,
@@ -389,8 +398,6 @@ Object.assign( Q.Circuit, {
istar |= (( i & ( 1 << q )) >> q ) << k
jstar |= (( j & ( 1 << q )) >> q ) << k
}
-
-
//console.log( 'U.read( istar, jstar )', U.read( istar, jstar ).toText() )
// console.log( 'before write$', result.toTsv())
@@ -413,17 +420,14 @@ Object.assign( Q.Circuit, {
},
-
-
evaluate: function( circuit ){
// console.log( circuit.toDiagram() )
+ misc.dispatchEventToGlobal(new CustomEvent(
- window.dispatchEvent( new CustomEvent(
-
- 'Q.Circuit.evaluate began', {
+ 'Circuit.evaluate began', {
detail: { circuit }
}
@@ -449,7 +453,7 @@ Object.assign( Q.Circuit, {
// │ . │
// └ ┘
- const state = new Q.Matrix( 1, Math.pow( 2, circuit.bandwidth ))
+ const state = new Matrix( 1, Math.pow( 2, circuit.bandwidth ))
state.write$( 0, 0, 1 )
@@ -475,7 +479,6 @@ Object.assign( Q.Circuit, {
let matrix = circuit.operations.reduce( function( state, operation, i ){
-
let U
if( operation.registerIndices.length < Infinity ){
@@ -505,11 +508,16 @@ Object.assign( Q.Circuit, {
// Yikes. May need to separate registerIndices in to controls[] and targets[] ??
// Works for now tho.....
-
- for( let j = 0; j < operation.registerIndices.length - 1; j ++ ){
-
- U = Q.Circuit.controlled( U )
- // console.log( 'qubitIndex #', j, 'U = Q.Circuit.controlled( U )', U.toTsv() )
+ // Houston we have a problem. Turns out, not every gate with registerIndices.length > 1 is
+ // controlled.
+ // This is a nasty fix, leads to a lot of edge cases. But just experimenting.
+ if( Circuit.isControlledOperation(operation) ) {
+ const scale = operation.registerIndices.length - ( operation.gate.is_multi_qubit ? 2 : 1)
+ for( let j = 0; j < scale; j ++ ){
+
+ U = Circuit.controlled( U )
+ // console.log( 'qubitIndex #', j, 'U = Circuit.controlled( U )', U.toTsv() )
+ }
}
@@ -519,24 +527,21 @@ Object.assign( Q.Circuit, {
// and wow -- tracking down that bug was painful!
const registerIndices = operation.registerIndices.slice()
-
-
- state = Q.Circuit.expandMatrix(
+ state = Circuit.expandMatrix(
circuit.bandwidth,
U,
registerIndices
).multiply( state )
-
-
+
operationsCompleted ++
const progress = operationsCompleted / operationsTotal
- window.dispatchEvent( new CustomEvent( 'Q.Circuit.evaluate progressed', { detail: {
+ misc.dispatchEventToGlobal(new CustomEvent( 'Circuit.evaluate progressed', { detail: {
circuit,
progress,
@@ -563,7 +568,6 @@ Object.assign( Q.Circuit, {
}, state )
- // console.log( 'result matrix', matrix.toTsv() )
@@ -587,7 +591,7 @@ Object.assign( Q.Circuit, {
- window.dispatchEvent( new CustomEvent( 'Q.Circuit.evaluate completed', { detail: {
+ misc.dispatchEventToGlobal(new CustomEvent( 'Circuit.evaluate completed', { detail: {
// circuit.dispatchEvent( new CustomEvent( 'evaluation complete', { detail: {
circuit,
@@ -597,7 +601,6 @@ Object.assign( Q.Circuit, {
-
return matrix
}
@@ -609,7 +612,7 @@ Object.assign( Q.Circuit, {
-Object.assign( Q.Circuit.prototype, {
+Object.assign( Circuit.prototype, {
clone: function(){
@@ -625,13 +628,13 @@ Object.assign( Q.Circuit.prototype, {
},
evaluate$: function(){
- Q.Circuit.evaluate( this )
+ Circuit.evaluate( this )
return this
},
report$: function( length ){
if( this.needsEvaluation ) this.evaluate$()
- if( !Q.isUsefulInteger( length )) length = 20
+ if( !mathf.isUsefulInteger( length )) length = 20
const
circuit = this,
@@ -646,7 +649,7 @@ Object.assign( Q.Circuit.prototype, {
+ outcome.state +' '
+ ''.padStart( probabilityPositive, '█' )
+ ''.padStart( probabilityNegative, '░' )
- + Q.round( Math.round( 100 * outcome.probability ), 8 ).toString().padStart( 4, ' ' ) +'% chance'
+ + mathf.round( Math.round( 100 * outcome.probability ), 8 ).toString().padStart( 4, ' ' ) +'% chance'
}, '' ) + '\n'
return text
@@ -1015,7 +1018,7 @@ because there’s another stand-alone X there tripping the logic!!!
? table.maximumCharacterWidth
: table[ x ].maximumCharacterWidth
- output[ 0 ] += Q.centerText( 'm'+ ( x + 1 ), padToLength + 4 )
+ output[ 0 ] += logger.centerText( 'm'+ ( x + 1 ), padToLength + 4 )
for( let y = 0; y < table.bandwidth; y ++ ){
let
@@ -1031,7 +1034,7 @@ because there’s another stand-alone X there tripping the logic!!!
third += ' '
first += ' '.padEnd( padToLength )
- second += Q.centerText( '○', padToLength, '─' )
+ second += logger.centerText( '○', padToLength, '─' )
third += ' '.padEnd( padToLength )
first += ' '
@@ -1054,7 +1057,7 @@ because there’s another stand-alone X there tripping the logic!!!
second += '┤ '
first += '─'.padEnd( padToLength, '─' )
- second += Q.centerText( operation.symbolDisplay, padToLength )
+ second += logger.centerText( operation.symbolDisplay, padToLength )
third += '─'.padEnd( padToLength, '─' )
@@ -1114,46 +1117,102 @@ https://cirq.readthedocs.io/en/stable/tutorial.html
return headers
},
toAmazonBraket: function(){
-
+ let isValidBraketCircuit = true
const header = `import boto3
-from braket.aws import AwsQuantumSimulator, AwsQuantumSimulatorArns
+from braket.aws import AwsDevice
from braket.circuits import Circuit
-aws_account_id = boto3.client("sts").get_caller_identity()["Account"]
-device = AwsQuantumSimulator(AwsQuantumSimulatorArns.QS1)
-s3_folder = (f"braket-output-{aws_account_id}", "folder-name")
-
-`
+my_bucket = f"amazon-braket-Your-Bucket-Name" # the name of the bucket
+my_prefix = "Your-Folder-Name" # the name of the folder in the bucket
+s3_folder = (my_bucket, my_prefix)\n
+device = LocalSimulator()\n\n`
+//TODO (ltnln): Syntax is different for simulators and actual quantum computers. Should there be a default? Should there be a way to change?
+//vs an actual quantum computer? May not be necessary.
+ let variables = ''
+ let num_unitaries = 0
//`qjs_circuit = Circuit().h(0).cnot(0,1)`
+ //ltnln change: from gate.AmazonBraketName -> gate.symbolAmazonBraket
let circuit = this.operations.reduce( function( string, operation ){
+ let awsGate = operation.gate.symbolAmazonBraket
+ isValidBraketCircuit &= awsGate !== undefined
+ if( operation.gate.symbolAmazonBraket === undefined ) isValidBraketCircuit = false
+ if( operation.gate.symbol === 'X' ) {
+ if( operation.registerIndices.length === 1 ) awsGate = operation.gate.symbolAmazonBraket
+ else if( operation.registerIndices.length === 2 ) awsGate = 'cnot'
+ else if( operation.registerIndices.length === 3) awsGate = 'ccnot'
+ else isValidBraketCircuit = false
+ }
- let awsGate = operation.gate.AmazonBraketName !== undefined ?
- operation.gate.AmazonBraketName :
- operation.gate.symbol.substr( 0, 1 ).toLowerCase()
+ else if( operation.gate.symbol === 'S' ) {
+ if( operation.gate.parameters["phi"] === 0 ) {
+ awsGate = operation.registerIndices.length == 2 ? awsGate : "cswap"
+ return string +'.'+ awsGate +'(' +
+ operation.registerIndices.reduce( function( string, registerIndex, r ){
- if( operation.gate.symbol === 'X' &&
- operation.registerIndices.length > 1 ){
+ return string + (( r > 0 ) ? ',' : '' ) + ( registerIndex - 1 )
- awsGate = 'cnot'
+ }, '' ) + ')'
+ }
+ awsGate = 'pswap'
+ }
+ //ltnln note: removed the if( operation.gate.symbol == '*') branch as it should be covered by
+ //the inclusion of the CURSOR gate.
+ else if( ['Y','Z','P'].includes( operation.gate.symbol) ) {
+ if( operation.registerIndices.length === 1) awsGate = operation.gate.symbolAmazonBraket
+ else if( operation.registerIndices.length === 2 ) awsGate = (operation.gate.symbol === 'Y') ? 'cy' : (operation.gate.symbol === 'Z') ? 'cz' : 'cphaseshift'
+ else isValidBraketCircuit = false
}
- if( operation.gate.symbol === '*' ){
+ //for all unitary gates, there must be a line of code to initialize the matrix for use
+ //in Braket's .u(matrix=my_unitary, targets[0]) function
+ else if( operation.gate.symbol === 'U') {
+ //check that this truly works as a unique id
+ isValidBraketCircuit &= operation.registerIndices.length === 1
+ const new_matrix = `unitary_` + num_unitaries
+ num_unitaries++
+ //https://en.wikipedia.org/wiki/Unitary_matrix; source for the unitary matrix values implemented below.
+ const a = ComplexNumber.toText(Math.cos(-(operation.gate.parameters[ "phi" ] + operation.gate.parameters[ "lambda" ])*Math.cos(operation.gate.parameters[ "theta" ] / 2) / 2),
+ Math.sin(-(operation.gate.parameters[ "phi" ] + operation.gate.parameters[ "lambda" ])*Math.cos(operation.gate.parameters[ "theta" ] / 2) / 2))
+ .replace('i', 'j')
+ const b = ComplexNumber.toText(-Math.cos(-(operation.gate.parameters[ "phi" ] - operation.gate.parameters[ "lambda" ])*Math.sin(operation.gate.parameters[ "theta" ] / 2) / 2),
+ -Math.sin(-(operation.gate.parameters[ "phi" ] - operation.gate.parameters[ "lambda" ])*Math.sin(operation.gate.parameters[ "theta" ] / 2)) / 2)
+ .replace('i', 'j')
+ const c = ComplexNumber.toText(Math.cos((operation.gate.parameters[ "phi" ] - operation.gate.parameters[ "lambda" ])*Math.sin(operation.gate.parameters[ "theta" ] / 2) / 2),
+ -Math.sin((operation.gate.parameters[ "phi" ] - operation.gate.parameters[ "lambda" ])*Math.sin(operation.gate.parameters[ "theta" ] / 2)) / 2)
+ .replace('i', 'j')
+ const d = ComplexNumber.toText(Math.cos((operation.gate.parameters[ "phi" ] + operation.gate.parameters[ "lambda" ])*Math.cos(operation.gate.parameters[ "theta" ] / 2) / 2),
+ Math.sin((operation.gate.parameters[ "phi" ] + operation.gate.parameters[ "lambda" ])*Math.cos(operation.gate.parameters[ "theta" ] / 2)) / 2)
+ .replace('i', 'j')
+ variables += new_matrix + ` = np.array(` +
+ `[[` + a + ', ' + b + `],`+
+ `[` + c + ', ' + d + `]])\n`
+ return string +'.'+ awsGate +'(' + new_matrix +','+
+ operation.registerIndices.reduce( function( string, registerIndex, r ){
- awsGate = 'i'
+ return string + (( r > 0 ) ? ',' : '' ) + ( registerIndex - 1 )
+
+ }, '' ) + ')'
}
-
+ // I believe this line should ensure that we don't include any controlled single-qubit gates that aren't allowed in Braket.
+ // The registerIndices.length > 1 technically shouldn't be necessary, but if changes are made later, it's just for safety.
+ else isValidBraketCircuit &= (operation.registerIndices.length === 1) || ( operation.registerIndices.length > 1 && operation.gate.is_multi_qubit )
return string +'.'+ awsGate +'(' +
operation.registerIndices.reduce( function( string, registerIndex, r ){
return string + (( r > 0 ) ? ',' : '' ) + ( registerIndex - 1 )
- }, '' ) + ')'
+ }, '' ) + ((operation.gate.has_parameters) ?
+ Object.values( operation.gate.parameters ).reduce( function( string, parameter ) {
+ return string + "," + parameter
+ }, '')
+ : '') + ')'
}, 'qjs_circuit = Circuit()' )
+ variables += '\n'
if( this.operations.length === 0 ) circuit += '.i(0)'// Quick fix to avoid an error here!
const footer = `\n\ntask = device.run(qjs_circuit, s3_folder, shots=100)
print(task.result().measurement_counts)`
- return header + circuit + footer
+ return isValidBraketCircuit ? header + variables + circuit + footer : `###This circuit is not representable as a Braket circuit!###`
},
toLatex: function(){
@@ -1240,12 +1299,12 @@ print(task.result().measurement_counts)`
// Validate our arguments.
if( arguments.length !== 2 )
- Q.warn( `Q.Circuit.clear$ expected 2 arguments but received ${ arguments.length }.` )
- if( Q.isUsefulInteger( momentIndex ) !== true )
- return Q.error( `Q.Circuit attempted to clear an input on Circuit #${ circuit.index } using an invalid moment index:`, momentIndex )
- if( Q.isUsefulInteger( registerIndices )) registerIndices = [ registerIndices ]
+ logger.warn( `Circuit.clear$ expected 2 arguments but received ${ arguments.length }.` )
+ if( mathf.isUsefulInteger( momentIndex ) !== true )
+ return logger.error( `Circuit attempted to clear an input on Circuit #${ circuit.index } using an invalid moment index:`, momentIndex )
+ if( mathf.isUsefulInteger( registerIndices )) registerIndices = [ registerIndices ]
if( registerIndices instanceof Array !== true )
- return Q.error( `Q.Circuit attempted to clear an input on Circuit #${ circuit.index } using an invalid register indices array:`, registerIndices )
+ return logger.error( `Circuit attempted to clear an input on Circuit #${ circuit.index } using an invalid register indices array:`, registerIndices )
// Let’s find any operations
@@ -1329,9 +1388,9 @@ print(task.result().measurement_counts)`
foundOperations.forEach( function( operation ){
- window.dispatchEvent( new CustomEvent(
+ misc.dispatchEventToGlobal(new CustomEvent(
- 'Q.Circuit.clear$', { detail: {
+ 'Circuit.clear$', { detail: {
circuit,
momentIndex,
@@ -1360,32 +1419,31 @@ print(task.result().measurement_counts)`
},
- set$: function( gate, momentIndex, registerIndices ){
+ set$: function( gate, momentIndex, registerIndices, parameters = {} ){
const circuit = this
-
// Is this a valid gate?
-
- if( typeof gate === 'string' ) gate = Q.Gate.findBySymbol( gate )
- if( gate instanceof Q.Gate !== true ) return Q.error( `Q.Circuit attempted to add a gate to circuit #${ this.index } at moment #${ momentIndex } that is not a gate:`, gate )
+ // We clone the gate rather than using the constant; this way, if we change it's parameters, we don't change the constant.
+ if( typeof gate === 'string' ) gate = Gate.prototype.clone( Gate.findBySymbol( gate ) )
+ if( gate instanceof Gate !== true ) return logger.error( `Circuit attempted to add a gate (${ gate }) to circuit #${ this.index } at moment #${ momentIndex } that is not a gate:`, gate )
// Is this a valid moment index?
- if( Q.isUsefulNumber( momentIndex ) !== true ||
+ if( mathf.isUsefulNumber( momentIndex ) !== true ||
Number.isInteger( momentIndex ) !== true ||
momentIndex < 1 || momentIndex > this.timewidth ){
- return Q.error( `Q.Circuit attempted to add a gate to circuit #${ this.index } at a moment index that is not valid:`, momentIndex )
+ return logger.error( `Circuit attempted to add a gate to circuit #${ this.index } at a moment index that is not valid:`, momentIndex )
}
// Are these valid register indices?
if( typeof registerIndices === 'number' ) registerIndices = [ registerIndices ]
- if( registerIndices instanceof Array !== true ) return Q.error( `Q.Circuit attempted to add a gate to circuit #${ this.index } at moment #${ momentIndex } with an invalid register indices array:`, registerIndices )
- if( registerIndices.length === 0 ) return Q.error( `Q.Circuit attempted to add a gate to circuit #${ this.index } at moment #${ momentIndex } with an empty register indices array:`, registerIndices )
+ if( registerIndices instanceof Array !== true ) return logger.error( `Circuit attempted to add a gate to circuit #${ this.index } at moment #${ momentIndex } with an invalid register indices array:`, registerIndices )
+ if( registerIndices.length === 0 ) return logger.error( `Circuit attempted to add a gate to circuit #${ this.index } at moment #${ momentIndex } with an empty register indices array:`, registerIndices )
if( registerIndices.reduce( function( accumulator, registerIndex ){
// console.log(accumulator &&
@@ -1400,7 +1458,7 @@ print(task.result().measurement_counts)`
}, false )){
- return Q.warn( `Q.Circuit attempted to add a gate to circuit #${ this.index } at moment #${ momentIndex } with some out of range qubit indices:`, registerIndices )
+ return logger.warn( `Circuit attempted to add a gate to circuit #${ this.index } at moment #${ momentIndex } with some out of range qubit indices:`, registerIndices )
}
@@ -1439,7 +1497,11 @@ print(task.result().measurement_counts)`
// Aren’t you glad we handle all this for you?
const
- isControlled = registerIndices.length > 1 && gate !== Q.Gate.SWAP,
+ //TODO: For ltnln (have to fix)
+ // a) allow users to control whatever they want! Just because it's not allowed in Braket
+ // doesn't mean they shouldn't be allowed to do it in Q! (Probably fixable by adjusting toAmazonBraket)
+ // b) Controlling a multi_qubit gate will not treat the control icon like a control gate!
+ isControlled = registerIndices.length > 1 && gate !== Gate.SWAP && gate.can_be_controlled !== undefined
operation = {
gate,
@@ -1447,6 +1509,8 @@ print(task.result().measurement_counts)`
registerIndices,
isControlled
}
+ //perform parameter update here!!!
+ if(gate.has_parameters) gate.updateMatrix$.apply( gate, Object.values(parameters) )
this.operations.push( operation )
@@ -1459,14 +1523,15 @@ print(task.result().measurement_counts)`
// Let’s make history.
-
+ const redo_args = Array.from( arguments )
+ Object.assign( redo_args[ redo_args.length - 1 ], parameters )
this.history.record$({
redo: {
name: 'set$',
func: circuit.set$,
- args: Array.from( arguments )
+ args: redo_args
},
undo: [{
@@ -1480,9 +1545,9 @@ print(task.result().measurement_counts)`
// Emit an event that we have set an operation
// on this circuit.
- window.dispatchEvent( new CustomEvent(
+ misc.dispatchEventToGlobal(new CustomEvent(
- 'Q.Circuit.set$', { detail: {
+ 'Circuit.set$', { detail: {
circuit,
operation
@@ -1513,17 +1578,17 @@ print(task.result().measurement_counts)`
if( typeof qubitLastIndex !== 'number' && typeof qubitRange !== 'number' ) qubitLastIndex = this.bandwidth
if( typeof qubitLastIndex !== 'number' && typeof qubitRange === 'number' ) qubitLastIndex = qubitFirstIndex + qubitRange
else if( typeof qubitLastIndex === 'number' && typeof qubitRange !== 'number' ) qubitRange = qubitLastIndex - qubitFirstIndex
- else return Q.error( `Q.Circuit attempted to copy a circuit but could not understand what qubits to copy.` )
+ else return logger.error( `Circuit attempted to copy a circuit but could not understand what qubits to copy.` )
if( typeof momentFirstIndex !== 'number' ) momentFirstIndex = 0
if( typeof momentLastIndex !== 'number' && typeof momentRange !== 'number' ) momentLastIndex = this.timewidth
if( typeof momentLastIndex !== 'number' && typeof momentRange === 'number' ) momentLastIndex = momentFirstIndex + momentRange
else if( typeof momentLastIndex === 'number' && typeof momentRange !== 'number' ) momentRange = momentLastIndex - momentFirstIndex
- else return Q.error( `Q.Circuit attempted to copy a circuit but could not understand what moments to copy.` )
+ else return logger.error( `Circuit attempted to copy a circuit but could not understand what moments to copy.` )
- Q.log( 0.8,
+ logger.log( 0.8,
- '\nQ.Circuit copy operation:',
+ '\nCircuit copy operation:',
'\n\n qubitFirstIndex', qubitFirstIndex,
'\n qubitLastIndex ', qubitLastIndex,
'\n qubitRange ', qubitRange,
@@ -1559,7 +1624,7 @@ print(task.result().measurement_counts)`
} = this.determineRanges( options )
- const copy = new Q.Circuit( registerRange, momentRange )
+ const copy = new Circuit( registerRange, momentRange )
original.operations
.filter( function( operation ){
@@ -1668,7 +1733,7 @@ print(task.result().measurement_counts)`
if( qubitRange !== this.bandwidth &&
momentRange !== this.timewidth ){
- return Q.error( `Q.Circuit attempted to splice circuit #${this.index} by an area that did not include all qubits _or_ all moments.` )
+ return logger.error( `Circuit attempted to splice circuit #${this.index} by an area that did not include all qubits _or_ all moments.` )
}
@@ -1792,7 +1857,7 @@ print(task.result().measurement_counts)`
pasteInsert$: function( other, atMoment, atQubit ){
// if( other.alphandwidth !== this.bandwidth &&
- // other.timewidth !== this.timewidth ) return Q.error( 'Q.Circuit attempted to pasteInsert Circuit A', other, 'in to circuit B', this, 'but neither their bandwidth or timewidth matches.' )
+ // other.timewidth !== this.timewidth ) return error( 'Circuit attempted to pasteInsert Circuit A', other, 'in to circuit B', this, 'but neither their bandwidth or timewidth matches.' )
@@ -1873,8 +1938,7 @@ print(task.result().measurement_counts)`
// I offer you super short convenience methods
// that do NOT use the $ suffix to delcare they are destructive.
// Don’t shoot your foot off.
-
-Object.entries( Q.Gate.constants ).forEach( function( entry ){
+Object.entries( Gate.constants ).forEach( function( entry ){
const
gateConstantName = entry[ 0 ],
@@ -1884,9 +1948,9 @@ Object.entries( Q.Gate.constants ).forEach( function( entry ){
this.set$( gate, momentIndex, registerIndexOrIndices )
return this
}
- Q.Circuit.prototype[ gateConstantName ] = set$
- Q.Circuit.prototype[ gate.symbol ] = set$
- Q.Circuit.prototype[ gate.symbol.toLowerCase() ] = set$
+ Circuit.prototype[ gateConstantName ] = set$
+ Circuit.prototype[ gate.symbol ] = set$
+ Circuit.prototype[ gate.symbol.toLowerCase() ] = set$
})
@@ -1897,55 +1961,55 @@ const bells = [
// Verbose without shortcuts.
- new Q.Circuit( 2, 2 )
+ new Circuit( 2, 2 )
.set$( Q.Gate.HADAMARD, 1, [ 1 ])
.set$( Q.Gate.PAULI_X, 2, [ 1 , 2 ]),
- new Q.Circuit( 2, 2 )
+ new Circuit( 2, 2 )
.set$( Q.Gate.HADAMARD, 1, 1 )
.set$( Q.Gate.PAULI_X, 2, [ 1 , 2 ]),
// Uses Q.Gate.findBySymbol() to lookup gates.
- new Q.Circuit( 2, 2 )
+ new Circuit( 2, 2 )
.set$( 'H', 1, [ 1 ])
.set$( 'X', 2, [ 1 , 2 ]),
- new Q.Circuit( 2, 2 )
+ new Circuit( 2, 2 )
.set$( 'H', 1, 1 )
.set$( 'X', 2, [ 1 , 2 ]),
// Convenience gate functions -- constant name.
- new Q.Circuit( 2, 2 )
+ new Circuit( 2, 2 )
.HADAMARD( 1, [ 1 ])
.PAULI_X( 2, [ 1, 2 ]),
- new Q.Circuit( 2, 2 )
+ new Circuit( 2, 2 )
.HADAMARD( 1, 1 )
.PAULI_X( 2, [ 1, 2 ]),
// Convenience gate functions -- uppercase symbol.
- new Q.Circuit( 2, 2 )
+ new Circuit( 2, 2 )
.H( 1, [ 1 ])
.X( 2, [ 1, 2 ]),
- new Q.Circuit( 2, 2 )
+ new Circuit( 2, 2 )
.H( 1, 1 )
.X( 2, [ 1, 2 ]),
// Convenience gate functions -- lowercase symbol.
- new Q.Circuit( 2, 2 )
+ new Circuit( 2, 2 )
.h( 1, [ 1 ])
.x( 2, [ 1, 2 ]),
- new Q.Circuit( 2, 2 )// Perhaps the closest to Braket style.
+ new Circuit( 2, 2 )// Perhaps the closest to Braket style.
.h( 1, 1 )
.x( 2, [ 1, 2 ]),
@@ -2006,13 +2070,13 @@ if( bellsAreEqual ){
-Q.Circuit.createConstants(
+Circuit.createConstants(
- 'BELL', Q`
+ 'BELL', new Circuit.fromText(`
H X#0
I X#1
- `,
+ `),
// 'GROVER', Q`
// H X *#0 X#0 I X#0 I I I X#0 I I I X#0 I X H X I *#0
@@ -2031,6 +2095,5 @@ Q.Circuit.createConstants(
)
-
-
+module.exports = {Circuit};
diff --git a/packages/quantum-js-util/Q-ComplexNumber.js b/packages/quantum-js-util/Q-ComplexNumber.js
new file mode 100644
index 0000000..79181da
--- /dev/null
+++ b/packages/quantum-js-util/Q-ComplexNumber.js
@@ -0,0 +1,586 @@
+// Copyright © 2019–2020, Stewart Smith. See LICENSE for details.
+
+const { warn, error, help } = require('./Logging');
+const mathf = require('./Math-Functions');
+const misc = require('./Misc');
+const EPSILON = misc.constants.EPSILON;
+ComplexNumber = function (real, imaginary) {
+ `
+ The set of “real numbers” (ℝ) contains any number that can be expressed
+ along an infinite timeline. https://en.wikipedia.org/wiki/Real_number
+
+ … -3 -2 -1 0 +1 +2 +3 …
+ ┄───┴───┴───┴───┴───┴─┬─┴──┬┴┬──┄
+ √2 𝒆 π
+
+
+ Meanwhile, “imaginary numbers” (𝕀) consist of a real (ℝ) multiplier and
+ the symbol 𝒊, which is the impossible solution to the equation 𝒙² = −1.
+ Note that no number when multiplied by itself can ever result in a
+ negative product, but the concept of 𝒊 gives us a way to reason around
+ this imaginary scenario nonetheless.
+ https://en.wikipedia.org/wiki/Imaginary_number
+
+ … -3𝒊 -2𝒊 -1𝒊 0𝒊 +1𝒊 +2𝒊 +3𝒊 …
+ ┄───┴───┴───┴───┴───┴───┴───┴───┄
+
+
+ A “complex number“ (ℂ) is a number that can be expressed in the form
+ 𝒂 + 𝒃𝒊, where 𝒂 is the real component (ℝ) and 𝒃𝒊 is the imaginary
+ component (𝕀). https://en.wikipedia.org/wiki/Complex_number
+
+
+ Operation functions on ComplexNumber instances generally accept as
+ arguments both sibling instances and pure Number instances, though the
+ value returned is always an instance of ComplexNumber.
+
+ `;
+
+ if (real instanceof ComplexNumber) {
+ imaginary = real.imaginary;
+ real = real.real;
+ warn(
+ "ComplexNumber tried to create a new instance with an argument that is already a ComplexNumber — and that’s weird!"
+ );
+ } else if (real === undefined) real = 0;
+ if (imaginary === undefined) imaginary = 0;
+ if (
+ (ComplexNumber.isNumberLike(real) !== true && isNaN(real) !== true) ||
+ (ComplexNumber.isNumberLike(imaginary) !== true &&
+ isNaN(imaginary) !== true)
+ )
+ return error(
+ "ComplexNumber attempted to create a new instance but the arguments provided were not actual numbers."
+ );
+
+ this.real = real;
+ this.imaginary = imaginary;
+ this.index = ComplexNumber.index++;
+};
+
+Object.assign(ComplexNumber, {
+ index: 0,
+ help: function () {
+ return help(this);
+ },
+ constants: {},
+ createConstant: function (key, value) {
+ //Object.freeze( value )
+ this[key] = value;
+ // Object.defineProperty( this, key, {
+
+ // value,
+ // writable: false
+ // })
+ // Object.defineProperty( this.constants, key, {
+
+ // value,
+ // writable: false
+ // })
+ this.constants[key] = this[key];
+ Object.freeze(this[key]);
+ },
+ createConstants: function () {
+ if (arguments.length % 2 !== 0) {
+ return error(
+ "Q attempted to create constants with invalid (KEY, VALUE) pairs."
+ );
+ }
+ for (let i = 0; i < arguments.length; i += 2) {
+ this.createConstant(arguments[i], arguments[i + 1]);
+ }
+ },
+
+ toText: function (rNumber, iNumber, roundToDecimal, padPositive) {
+ // Should we round these numbers?
+ // Our default is yes: to 3 digits.
+ // Otherwise round to specified decimal.
+
+ if (typeof roundToDecimal !== "number") roundToDecimal = 3;
+ const factor = Math.pow(10, roundToDecimal);
+ rNumber = Math.round(rNumber * factor) / factor;
+ iNumber = Math.round(iNumber * factor) / factor;
+
+ // Convert padPositive
+ // from a potential Boolean
+ // to a String.
+ // If we don’t receive a FALSE
+ // then we’ll pad the positive numbers.
+
+ padPositive = padPositive === false ? "" : " ";
+
+ // We need the absolute values of each.
+
+ let rAbsolute = Math.abs(rNumber),
+ iAbsolute = Math.abs(iNumber);
+
+ // And an absolute value string.
+
+ let rText = rAbsolute.toString(),
+ iText = iAbsolute.toString();
+
+ // Is this an IMAGINARY-ONLY number?
+ // Don’t worry: -0 === 0.
+
+ if (rNumber === 0) {
+ if (iNumber === Infinity) return padPositive + "∞i";
+ if (iNumber === -Infinity) return "-∞i";
+ if (iNumber === 0) return padPositive + "0";
+ if (iNumber === -1) return "-i";
+ if (iNumber === 1) return padPositive + "i";
+ if (iNumber >= 0) return padPositive + iText + "i";
+ if (iNumber < 0) return "-" + iText + "i";
+ return iText + "i"; // NaN
+ }
+
+ // This number contains a real component
+ // and may also contain an imaginary one as well.
+
+ if (rNumber === Infinity) rText = padPositive + "∞";
+ else if (rNumber === -Infinity) rText = "-∞";
+ else if (rNumber >= 0) rText = padPositive + rText;
+ else if (rNumber < 0) rText = "-" + rText;
+
+ if (iNumber === Infinity) return rText + " + ∞i";
+ if (iNumber === -Infinity) return rText + " - ∞i";
+ if (iNumber === 0) return rText;
+ if (iNumber === -1) return rText + " - i";
+ if (iNumber === 1) return rText + " + i";
+ if (iNumber > 0) return rText + " + " + iText + "i";
+ if (iNumber < 0) return rText + " - " + iText + "i";
+ return rText + " + " + iText + "i"; // NaN
+ },
+
+ isNumberLike: function (n) {
+ return isNaN(n) === false && (typeof n === "number" || n instanceof Number);
+ },
+ isNaN: function (n) {
+ return isNaN(n.real) || isNaN(n.imaginary);
+ },
+ isZero: function (n) {
+ return (
+ (n.real === 0 || n.real === -0) &&
+ (n.imaginary === 0 || n.imaginary === -0)
+ );
+ },
+ isFinite: function (n) {
+ return isFinite(n.real) && isFinite(n.imaginary);
+ },
+ isInfinite: function (n) {
+ return !(this.isNaN(n) || this.isFinite(n));
+ },
+ areEqual: function (a, b) {
+ return ComplexNumber.operate(
+ "areEqual",
+ a,
+ b,
+ function (a, b) {
+ return Math.abs(a - b) < EPSILON;
+ },
+ function (a, b) {
+ return (
+ Math.abs(a - b.real) < EPSILON && Math.abs(b.imaginary) < EPSILON
+ );
+ },
+ function (a, b) {
+ return (
+ Math.abs(a.real - b) < EPSILON && Math.abs(a.imaginary) < EPSILON
+ );
+ },
+ function (a, b) {
+ return (
+ Math.abs(a.real - b.real) < EPSILON &&
+ Math.abs(a.imaginary - b.imaginary) < EPSILON
+ );
+ }
+ );
+ },
+
+ absolute: function (n) {
+ return mathf.hypotenuse(n.real, n.imaginary);
+ },
+ conjugate: function (n) {
+ return new ComplexNumber(n.real, n.imaginary * -1);
+ },
+ operate: function (
+ name,
+ a,
+ b,
+ numberAndNumber,
+ numberAndComplex,
+ complexAndNumber,
+ complexAndComplex
+ ) {
+ if (ComplexNumber.isNumberLike(a)) {
+ if (ComplexNumber.isNumberLike(b)) return numberAndNumber(a, b);
+ else if (b instanceof ComplexNumber) return numberAndComplex(a, b);
+ else
+ return error(
+ "ComplexNumber attempted to",
+ name,
+ "with the number",
+ a,
+ "and something that is neither a Number or ComplexNumber:",
+ b
+ );
+ } else if (a instanceof ComplexNumber) {
+ if (ComplexNumber.isNumberLike(b)) return complexAndNumber(a, b);
+ else if (b instanceof ComplexNumber) return complexAndComplex(a, b);
+ else
+ return error(
+ "ComplexNumber attempted to",
+ name,
+ "with the complex number",
+ a,
+ "and something that is neither a Number or ComplexNumber:",
+ b
+ );
+ } else
+ return error(
+ "ComplexNumber attempted to",
+ name,
+ "with something that is neither a Number or ComplexNumber:",
+ a
+ );
+ },
+
+ sine: function (n) {
+ const a = n.real,
+ b = n.imaginary;
+
+ return new ComplexNumber(
+ Math.sin(a) * mathf.hyperbolicCosine(b),
+ Math.cos(a) * mathf.hyperbolicSine(b)
+ );
+ },
+ cosine: function (n) {
+ const a = n.real,
+ b = n.imaginary;
+
+ return new ComplexNumber(
+ Math.cos(a) * mathf.hyperbolicCosine(b),
+ -Math.sin(a) * mathf.hyperbolicSine(b)
+ );
+ },
+ arcCosine: function (n) {
+ const a = n.real,
+ b = n.imaginary,
+ t1 = ComplexNumber.squareRoot(
+ new ComplexNumber(b * b - a * a + 1, a * b * -2)
+ ),
+ t2 = ComplexNumber.log(new ComplexNumber(t1.real - b, t1.imaginary + a));
+ return new ComplexNumber(Math.PI / 2 - t2.imaginary, t2.real);
+ },
+ arcTangent: function (n) {
+ const a = n.real,
+ b = n.imaginary;
+
+ if (a === 0) {
+ if (b === 1) return new ComplexNumber(0, Infinity);
+ if (b === -1) return new ComplexNumber(0, -Infinity);
+ }
+
+ const d = a * a + (1 - b) * (1 - b),
+ t = ComplexNumber.log(
+ new ComplexNumber((1 - b * b - a * a) / d, (a / d) * -2)
+ );
+ return new ComplexNumber(t.imaginary / 2, t.real / 2);
+ },
+
+ power: function (a, b) {
+ if (ComplexNumber.isNumberLike(a)) a = new ComplexNumber(a);
+ if (ComplexNumber.isNumberLike(b)) b = new ComplexNumber(b);
+
+ // Anything raised to the Zero power is 1.
+
+ if (b.isZero()) return ComplexNumber.ONE;
+
+ // Zero raised to any power is 0.
+ // Note: What happens if b.real is zero or negative?
+ // What happens if b.imaginary is negative?
+ // Do we really need those conditionals??
+
+ if (a.isZero() && b.real > 0 && b.imaginary >= 0) {
+ return ComplexNumber.ZERO;
+ }
+
+ // If our exponent is Real (has no Imaginary component)
+ // then we’re really just raising to a power.
+
+ if (b.imaginary === 0) {
+ if (a.real >= 0 && a.imaginary === 0) {
+ return new ComplexNumber(Math.pow(a.real, b.real), 0);
+ } else if (a.real === 0) {
+ // If our base is Imaginary (has no Real component).
+
+ switch (((b.real % 4) + 4) % 4) {
+ case 0:
+ return new ComplexNumber(Math.pow(a.imaginary, b.real), 0);
+ case 1:
+ return new ComplexNumber(0, Math.pow(a.imaginary, b.real));
+ case 2:
+ return new ComplexNumber(-Math.pow(a.imaginary, b.real), 0);
+ case 3:
+ return new ComplexNumber(0, -Math.pow(a.imaginary, b.real));
+ }
+ }
+ }
+
+ const arctangent2 = Math.atan2(a.imaginary, a.real),
+ logHypotenuse = mathf.logHypotenuse(a.real, a.imaginary),
+ x = Math.exp(b.real * logHypotenuse - b.imaginary * arctangent2),
+ y = b.imaginary * logHypotenuse + b.real * arctangent2;
+
+ return new ComplexNumber(x * Math.cos(y), x * Math.sin(y));
+ },
+ squareRoot: function (a) {
+ const result = new ComplexNumber(0, 0),
+ absolute = ComplexNumber.absolute(a);
+
+ if (a.real >= 0) {
+ if (a.imaginary === 0) {
+ result.real = Math.sqrt(a.real); // and imaginary already equals 0.
+ } else {
+ result.real = Math.sqrt(2 * (absolute + a.real)) / 2;
+ }
+ } else {
+ result.real = Math.abs(a.imaginary) / Math.sqrt(2 * (absolute - a.real));
+ }
+ if (a.real <= 0) {
+ result.imaginary = Math.sqrt(2 * (absolute - a.real)) / 2;
+ } else {
+ result.imaginary =
+ Math.abs(a.imaginary) / Math.sqrt(2 * (absolute + a.real));
+ }
+ if (a.imaginary < 0) result.imaginary *= -1;
+ return result;
+ },
+ log: function (a) {
+ return new ComplexNumber(
+ mathf.logHypotenuse(a.real, a.imaginary),
+ Math.atan2(a.imaginary, a.real)
+ );
+ },
+ multiply: function (a, b) {
+ return ComplexNumber.operate(
+ "multiply",
+ a,
+ b,
+ function (a, b) {
+ return new ComplexNumber(a * b);
+ },
+ function (a, b) {
+ return new ComplexNumber(a * b.real, a * b.imaginary);
+ },
+ function (a, b) {
+ return new ComplexNumber(a.real * b, a.imaginary * b);
+ },
+ function (a, b) {
+ // FOIL Method that shit.
+ // https://en.wikipedia.org/wiki/FOIL_method
+
+ const firsts = a.real * b.real,
+ outers = a.real * b.imaginary,
+ inners = a.imaginary * b.real,
+ lasts = a.imaginary * b.imaginary * -1; // Because i² = -1.
+
+ return new ComplexNumber(firsts + lasts, outers + inners);
+ }
+ );
+ },
+ divide: function (a, b) {
+ return ComplexNumber.operate(
+ "divide",
+ a,
+ b,
+ function (a, b) {
+ return new ComplexNumber(a / b);
+ },
+ function (a, b) {
+ return new ComplexNumber(a).divide(b);
+ },
+ function (a, b) {
+ return new ComplexNumber(a.real / b, a.imaginary / b);
+ },
+ function (a, b) {
+ // Ermergerd I had to look this up because it’s been so long.
+ // https://www.khanacademy.org/math/precalculus/imaginary-and-complex-numbers/complex-conjugates-and-dividing-complex-numbers/a/dividing-complex-numbers-review
+
+ const conjugate = b.conjugate(),
+ numerator = a.multiply(conjugate),
+ // The .imaginary will be ZERO for sure,
+ // so this forces a ComplexNumber.divide( Number ) ;)
+
+ denominator = b.multiply(conjugate).real;
+
+ return numerator.divide(denominator);
+ }
+ );
+ },
+ add: function (a, b) {
+ return ComplexNumber.operate(
+ "add",
+ a,
+ b,
+ function (a, b) {
+ return new ComplexNumber(a + b);
+ },
+ function (a, b) {
+ return new ComplexNumber(b.real + a, b.imaginary);
+ },
+ function (a, b) {
+ return new ComplexNumber(a.real + b, a.imaginary);
+ },
+ function (a, b) {
+ return new ComplexNumber(a.real + b.real, a.imaginary + b.imaginary);
+ }
+ );
+ },
+ subtract: function (a, b) {
+ return ComplexNumber.operate(
+ "subtract",
+ a,
+ b,
+ function (a, b) {
+ return new ComplexNumber(a - b);
+ },
+ function (a, b) {
+ return new ComplexNumber(b.real - a, b.imaginary);
+ },
+ function (a, b) {
+ return new ComplexNumber(a.real - b, a.imaginary);
+ },
+ function (a, b) {
+ return new ComplexNumber(a.real - b.real, a.imaginary - b.imaginary);
+ }
+ );
+ },
+});
+
+ComplexNumber.createConstants(
+ "ZERO",
+ new ComplexNumber(0, 0),
+ "ONE",
+ new ComplexNumber(1, 0),
+ "E",
+ new ComplexNumber(Math.E, 0),
+ "PI",
+ new ComplexNumber(Math.PI, 0),
+ "I",
+ new ComplexNumber(0, 1),
+ "EPSILON",
+ new ComplexNumber(EPSILON, EPSILON),
+ "INFINITY",
+ new ComplexNumber(Infinity, Infinity),
+ "NAN",
+ new ComplexNumber(NaN, NaN)
+);
+
+Object.assign(ComplexNumber.prototype, {
+ // NON-destructive operations.
+
+ clone: function () {
+ return new ComplexNumber(this.real, this.imaginary);
+ },
+ reduce: function () {
+ // Note: this *might* kill function chaining.
+
+ if (this.imaginary === 0) return this.real;
+ return this;
+ },
+ toText: function (roundToDecimal, padPositive) {
+ // Note: this will kill function chaining.
+
+ return ComplexNumber.toText(
+ this.real,
+ this.imaginary,
+ roundToDecimal,
+ padPositive
+ );
+ },
+
+ isNaN: function (n) {
+ return ComplexNumber.isNaN(this); // Returned boolean will kill function chaining.
+ },
+ isZero: function (n) {
+ return ComplexNumber.isZero(this); // Returned boolean will kill function chaining.
+ },
+ isFinite: function (n) {
+ return ComplexNumber.isFinite(this); // Returned boolean will kill function chaining.
+ },
+ isInfinite: function (n) {
+ return ComplexNumber.isInfinite(this); // Returned boolean will kill function chaining.
+ },
+ isEqualTo: function (b) {
+ return ComplexNumber.areEqual(this, b); // Returned boolean will kill function chaining.
+ },
+
+ absolute: function () {
+ return ComplexNumber.absolute(this); // Returned number will kill function chaining.
+ },
+ conjugate: function () {
+ return ComplexNumber.conjugate(this);
+ },
+
+ power: function (b) {
+ return ComplexNumber.power(this, b);
+ },
+ squareRoot: function () {
+ return ComplexNumber.squareRoot(this);
+ },
+ log: function () {
+ return ComplexNumber.log(this);
+ },
+ multiply: function (b) {
+ return ComplexNumber.multiply(this, b);
+ },
+ divide: function (b) {
+ return ComplexNumber.divide(this, b);
+ },
+ add: function (b) {
+ return ComplexNumber.add(this, b);
+ },
+ subtract: function (b) {
+ return ComplexNumber.subtract(this, b);
+ },
+
+ // DESTRUCTIVE operations.
+
+ copy$: function (b) {
+ if (b instanceof ComplexNumber !== true)
+ return error(
+ `ComplexNumber attempted to copy something that was not a complex number in to this complex number #${this.index}.`,
+ this
+ );
+
+ this.real = b.real;
+ this.imaginary = b.imaginary;
+ return this;
+ },
+ conjugate$: function () {
+ return this.copy$(this.conjugate());
+ },
+ power$: function (b) {
+ return this.copy$(this.power(b));
+ },
+ squareRoot$: function () {
+ return this.copy$(this.squareRoot());
+ },
+ log$: function () {
+ return this.copy$(this.log());
+ },
+ multiply$: function (b) {
+ return this.copy$(this.multiply(b));
+ },
+ divide$: function (b) {
+ return this.copy$(this.divide(b));
+ },
+ add$: function (b) {
+ return this.copy$(this.add(b));
+ },
+ subtract$: function (b) {
+ return this.copy$(this.subtract(b));
+ },
+});
+
+module.exports = { ComplexNumber };
diff --git a/packages/quantum-js-util/Q-Gate.js b/packages/quantum-js-util/Q-Gate.js
new file mode 100644
index 0000000..472dde1
--- /dev/null
+++ b/packages/quantum-js-util/Q-Gate.js
@@ -0,0 +1,630 @@
+
+// Copyright © 2019–2020, Stewart Smith. See LICENSE for details.
+
+const mathf = require('./Math-Functions');
+const logger = require('./Logging');
+const { ComplexNumber } = require('./Q-ComplexNumber');
+const {Matrix} = require('./Q-Matrix');
+Gate = function( params ){
+
+ Object.assign( this, params )
+ this.index = Gate.index ++
+
+ if( typeof this.symbol !== 'string' ) this.symbol = '?'
+ if( typeof this.symbolAmazonBraket !== 'string' ) this.symbolAmazonBraket = this.symbol.toLowerCase()
+ const parameters = Object.assign( {}, params.parameters )
+ this.parameters = parameters
+
+ // We use symbols as unique identifiers
+ // among gate CONSTANTS
+ // so if you use the same symbol for a non-constant
+ // that’s not a deal breaker
+ // but it is good to know.
+
+ const
+ scope = this,
+ foundConstant = Object
+ .values( Gate.constants )
+ .find( function( gate ){
+
+ return gate.symbol === scope.symbol
+ })
+
+ if( foundConstant ){
+
+ logger.warn( `Gate is creating a new instance, #${ this.index }, that uses the same symbol as a pre-existing Gate constant:`, foundConstant )
+ }
+
+ if( typeof this.name !== 'string' ) this.name = 'Unknown'
+ if( typeof this.nameCss !== 'string' ) this.nameCss = 'unknown'
+
+
+ // If our gate’s matrix is to be
+ // dynamically created or updated
+ // then we ouoght to do that now.
+
+ if( typeof this.updateMatrix$ === 'function' ) this.updateMatrix$()
+
+
+ // Every gate must have an applyToQubit method.
+ // If it doesn’t exist we’ll create one
+ // based on whether a matrix property exists or not.
+
+ //Hi there. LTNLN here. We're just gonna toss the applyToQubit function entirely...Gate from here on is independent of Qubit! :)..
+}
+
+
+
+Object.assign( Gate, {
+
+ index: 0,
+ constants: {},
+ createConstant: function( key, value ){
+ this[ key ] = value
+ this.constants[ key ] = this[ key ]
+ Object.freeze( this[ key ])
+ },
+ createConstants: function(){
+
+ if( arguments.length % 2 !== 0 ){
+
+ return logger.error( 'Q attempted to create constants with invalid (KEY, VALUE) pairs.' )
+ }
+ for( let i = 0; i < arguments.length; i += 2 ){
+
+ this.createConstant( arguments[ i ], arguments[ i + 1 ])
+ }
+ },
+ findBy: function( key, value ){
+
+ return (
+
+ Object
+ .values( Gate.constants )
+ .find( function( item ){
+
+ if( typeof value === 'string' &&
+ typeof item[ key ] === 'string' ){
+
+ return value.toLowerCase() === item[ key ].toLowerCase()
+ }
+ return value === item[ key ]
+ })
+ )
+ },
+ findBySymbol: function( symbol ){
+
+ return Gate.findBy( 'symbol', symbol )
+ },
+ findByName: function( name ){
+
+ return Gate.findBy( 'name', name )
+ }
+})
+
+
+
+
+Object.assign( Gate.prototype, {
+
+ clone: function( params ){
+
+ return new Gate( Object.assign( {}, this, params ))
+ },
+ set$: function( key, value ){
+
+ this[ key ] = value
+ return this
+ },
+ setSymbol$: function( value ){
+
+ return this.set$( 'symbol', value )
+ }
+})
+
+
+
+
+Gate.createConstants (
+
+
+ // Operate on a single qubit.
+
+ 'IDENTITY', new Gate({
+
+ symbol: 'I',
+ symbolAmazonBraket: 'i',
+ symbolSvg: '',
+ name: 'Identity',
+ nameCss: 'identity',
+ matrix: Matrix.IDENTITY_2X2
+ }),
+ 'CURSOR', new Gate({
+
+ symbol: '*',
+ symbolAmazonBraket: 'i',
+ symbolSvg: '',
+ name: 'Identity',
+ nameCss: 'identity',
+ matrix: Matrix.IDENTITY_2X2
+ }),
+ 'MEASURE', new Gate({
+
+ symbol: 'M',
+ symbolAmazonBraket: 'm',
+ symbolSvg: '',
+ name: 'Measure',
+ nameCss: 'measure',
+ matrix: Matrix.IDENTITY_2X2,
+ }),
+ 'HADAMARD', new Gate({
+
+ symbol: 'H',
+ symbolAmazonBraket: 'h',
+ symbolSvg: '',
+ name: 'Hadamard',
+ nameCss: 'hadamard',
+ matrix: new Matrix(
+ [ Math.SQRT1_2, Math.SQRT1_2 ],
+ [ Math.SQRT1_2, -Math.SQRT1_2 ])
+ }),
+ 'PAULI_X', new Gate({
+
+ symbol: 'X',
+ symbolAmazonBraket: 'x',
+ symbolSvg: '',
+ name: 'Pauli X',
+ nameCss: 'pauli-x',
+ matrix: new Matrix(
+ [ 0, 1 ],
+ [ 1, 0 ]),
+ //ltnln: NOTE! can_be_controlled refers to whether or not the Braket SDK supports a controlled
+ //application of this gate; if we want Q to be able to support controlled gated regardless of whether
+ //or not Braket can, this must be changed..
+ can_be_controlled: true
+ },
+ ),
+ 'PAULI_Y', new Gate({
+
+ symbol: 'Y',
+ symbolAmazonBraket: 'y',
+ symbolSvg: '',
+ name: 'Pauli Y',
+ nameCss: 'pauli-y',
+ matrix: new Matrix(
+ [ 0, new ComplexNumber( 0, -1 )],
+ [ new ComplexNumber( 0, 1 ), 0 ]),
+ can_be_controlled: true
+ },
+ ),
+ 'PAULI_Z', new Gate({
+
+ symbol: 'Z',
+ symbolAmazonBraket: 'z',
+ symbolSvg: '',
+ name: 'Pauli Z',
+ nameCss: 'pauli-z',
+ matrix: new Matrix(
+ [ 1, 0 ],
+ [ 0, -1 ]),
+ can_be_controlled: true
+ },
+ ),
+ 'PHASE', new Gate({
+
+ symbol: 'P',
+ symbolAmazonBraket: 'phaseshift',// ltnln edit: change from 'p' to 'phaseshift'
+ symbolSvg: '',
+ name: 'Phase',
+ nameCss: 'phase',
+ parameters: { "phi" : 1 },
+ updateMatrix$: function( phi ){
+ if( mathf.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi;
+ this.matrix = new Matrix(
+ [ 1, 0 ],
+ [ 0, ComplexNumber.E.power( new ComplexNumber( 0, this.parameters[ "phi" ] ))])
+ return this
+ },
+ can_be_controlled: true,
+ has_parameters: true
+ }),
+ 'PI_8', new Gate({
+
+ symbol: 'T',
+ symbolAmazonBraket: 't',// !!! Double check this !!!
+ symbolSvg: '',
+ name: 'π ÷ 8',
+ nameCss: 'pi8',
+ matrix: new Matrix(
+ [ 1, 0 ],
+ [ 0, ComplexNumber.E.power( new ComplexNumber( 0, Math.PI / 4 )) ])
+ }),
+ 'BLOCH', new Gate({
+
+ symbol: 'B',
+ //symbolAmazonBraket: Does not exist.
+ symbolSvg: '',
+ name: 'Bloch sphere',
+ nameCss: 'bloch',
+ // applyToQubit: function( qubit ){
+
+ // // Create Bloch sphere visualizer instance.
+ // }
+ }),
+ 'RX', new Gate({
+
+ symbol: 'Rx',
+ symbolAmazonBraket: 'rx',
+ symbolSvg: '',
+ name: 'X Rotation',
+ nameCss: 'x-rotation',
+ parameters: { "phi" : Math.PI / 2 },
+ updateMatrix$: function( phi ){
+
+ if( mathf.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi
+ this.matrix = new Matrix(
+ [ Math.cos( this.parameters[ "phi" ] / 2 ), new ComplexNumber( 0, -Math.sin( this.parameters[ "phi" ] / 2 )) ],
+ [ new ComplexNumber( 0, -Math.sin( this.parameters[ "phi" ] / 2 )), Math.cos( this.parameters[ "phi" ] / 2 )])
+ return this
+ },
+ has_parameters: true
+ }),
+ 'RY', new Gate({
+
+ symbol: 'Ry',
+ symbolAmazonBraket: 'ry',
+ symbolSvg: '',
+ name: 'Y Rotation',
+ nameCss: 'y-rotation',
+ parameters: { "phi" : Math.PI / 2 },
+ updateMatrix$: function( phi ){
+
+ if( mathf.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi
+ this.matrix = new Matrix(
+ [ Math.cos( this.parameters[ "phi" ] / 2 ), -Math.sin( phi / 2 ) ],
+ [ Math.sin( this.parameters[ "phi" ] / 2 ), Math.cos( this.parameters[ "phi" ] / 2 )])
+ return this
+ },
+ has_parameters: true
+ }),
+ 'RZ', new Gate({
+
+ symbol: 'Rz',
+ symbolAmazonBraket: 'rz',
+ symbolSvg: '',
+ name: 'Z Rotation',
+ nameCss: 'z-rotation',
+ parameters: { "phi" : Math.PI / 2 },
+ updateMatrix$: function( phi ){
+
+ if( mathf.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi
+ this.matrix = new Matrix(
+ [ ComplexNumber.E.power( new ComplexNumber( 0, -this.parameters[ "phi" ] / 2 )), 0 ],
+ [ 0, ComplexNumber.E.power( new ComplexNumber( 0, this.parameters[ "phi" ] / 2 ))])
+ return this
+ },
+ has_parameters: true
+ }),
+ 'UNITARY', new Gate({
+
+ symbol: 'U',
+ symbolAmazonBraket: 'unitary',
+ symbolSvg: '',
+ name: 'Unitary',
+ nameCss: 'unitary',
+ //toAmazonBraket will have to use the following matrix as an argument for unitary()
+ parameters: { "phi" : Math.PI / 2,
+ "theta" : Math.PI / 2,
+ "lambda" : Math.PI / 2 },
+ updateMatrix$: function( phi, theta, lambda ){
+
+ if( (mathf.isUsefulNumber( +phi ) === true) && (mathf.isUsefulNumber( +theta ) === true) && (mathf.isUsefulNumber( +lambda ) === true) ) {
+ this.parameters[ "phi" ] = +phi;
+ this.parameters[ "theta" ] = +theta;
+ this.parameters[ "lambda" ] = +lambda;
+ }
+ const a = ComplexNumber.multiply(
+ ComplexNumber.E.power( new ComplexNumber( 0, -( this.parameters[ "phi" ] + this.parameters[ "lambda" ] ) / 2 )), Math.cos( this.parameters[ "theta" ] / 2 ))
+ const b = ComplexNumber.multiply(
+ ComplexNumber.E.power( new ComplexNumber( 0, -( this.parameters[ "phi" ] - this.parameters[ "lambda" ] ) / 2 )), -Math.sin( this.parameters[ "theta" ] / 2 ))
+ const c = ComplexNumber.multiply(
+ ComplexNumber.E.power( new ComplexNumber( 0, ( this.parameters[ "phi" ] - this.parameters[ "lambda" ] ) / 2 )), Math.sin( this.parameters[ "theta" ] / 2 ))
+ const d = ComplexNumber.multiply(
+ ComplexNumber.E.power( new ComplexNumber( 0, ( this.parameters[ "phi" ] + this.parameters[ "lambda" ] ) / 2 )), Math.cos( this.parameters[ "theta" ] / 2 ))
+ this.matrix = new Matrix(
+ [ a, b ],
+ [ c, d ])
+ return this
+ },
+ has_parameters: true
+ }),
+ 'NOT1_2', new Gate({
+
+ symbol: 'V',
+ symbolAmazonBraket: 'v',
+ symbolSvg: '',
+ name: '√Not',
+ nameCss: 'not1-2',
+ matrix: new Matrix(
+ [ new ComplexNumber( 1, 1 ) / 2, new ComplexNumber( 1, -1 ) / 2 ],
+ [ new ComplexNumber( 1, -1 ) / 2, new ComplexNumber( 1, 1 ) / 2 ])
+ }),
+ 'PI_8_Dagger', new Gate({
+
+ symbol: 'T†',
+ symbolAmazonBraket: 'ti',
+ symbolSvg: '',
+ name: 'PI_8_Dagger',
+ nameCss: 'pi8-dagger',
+ matrix: new Matrix(
+ [ 1, 0 ],
+ [ 0, ComplexNumber.E.power( new ComplexNumber( 0, -Math.PI / 4 )) ])
+ }),
+ 'NOT1_2_Dagger', new Gate({
+
+ symbol: 'V†',
+ symbolAmazonBraket: 'vi',
+ symbolSvg: '',
+ name: '√Not_Dagger',
+ nameCss: 'not1-2-dagger',
+ matrix: new Matrix(
+ [ new ComplexNumber( 1, -1 ) / 2, new ComplexNumber( 1, 1 ) / 2 ],
+ [ new ComplexNumber( 1, 1 ) / 2, new ComplexNumber( 1, -1 ) / 2 ])
+ }),
+ //Note that S, S_Dagger, PI_8, and PI_8_Dagger can all be implemented by applying the PHASE gate
+ //using certain values of phi.
+ //These gates are included for completeness.
+ 'S', new Gate({
+ symbol: 'S*', //Gotta think of a better symbol name...
+ symbolAmazonBraket: 's',
+ symbolSvg: '',
+ name: 'π ÷ 4',
+ nameCss: 'pi4',
+ matrix: new Matrix(
+ [ 1, 0 ],
+ [ 0, new ComplexNumber( 0, 1 ) ])
+ }),
+ 'S_Dagger', new Gate({
+
+ symbol: 'S†',
+ symbolAmazonBraket: 'si',
+ symbolSvg: '',
+ name: 'π ÷ 4 Dagger',
+ nameCss: 'pi4-dagger',
+ matrix: new Matrix(
+ [ 1, 0 ],
+ [ 0, ComplexNumber.E.power( new ComplexNumber( 0, -1 )) ])
+ }),
+ // Operate on 2 qubits.
+ 'SWAP', new Gate({
+
+ symbol: 'S',
+ symbolAmazonBraket: 'swap',
+ symbolSvg: '',
+ name: 'Swap',
+ nameCss: 'swap',
+ parameters: { "phi" : 0.0 },
+ updateMatrix$: function( phi ) {
+
+ if( mathf.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi
+ this.matrix = new Matrix(
+ [ 1, 0, 0, 0 ],
+ [ 0, 0, ComplexNumber.E.power( new ComplexNumber( 0, this.parameters[ "phi" ] )), 0 ],
+ [ 0, ComplexNumber.E.power(new ComplexNumber( 0, this.parameters[ "phi" ] )), 0, 0 ],
+ [ 0, 0, 0, 1 ])
+ return this
+ },
+ can_be_controlled: true,
+ has_parameters: true,
+ is_multi_qubit: true
+ }),
+ 'SWAP1_2', new Gate({
+
+ symbol: '√S',
+ //symbolAmazonBraket: !!! UNKNOWN !!!
+ symbolSvg: '',
+ name: '√Swap',
+ nameCss: 'swap1-2',
+ matrix: new Matrix(
+ [ 1, 0, 0, 0 ],
+ [ 0, new ComplexNumber( 0.5, 0.5 ), new ComplexNumber( 0.5, -0.5 ), 0 ],
+ [ 0, new ComplexNumber( 0.5, -0.5 ), new ComplexNumber( 0.5, 0.5 ), 0 ],
+ [ 0, 0, 0, 1 ]),
+ is_multi_qubit: true
+ }),
+ 'ISWAP', new Gate({
+
+ symbol: 'iS',
+ symbolAmazonBraket: 'iswap',
+ symbolSvg: '',
+ name: 'Imaginary Swap',
+ nameCss: 'iswap',
+ matrix: new Matrix(
+ [ 1, 0, 0, 0 ],
+ [ 0, 0, new ComplexNumber( 0, 1 ), 0 ],
+ [ 0, new ComplexNumber( 0, 1 ), 0, 0 ],
+ [ 0, 0, 0, 1 ]),
+ is_multi_qubit: true
+ }),
+ 'ISING-XX', new Gate({
+
+ symbol: 'XX',
+ symbolAmazonBraket: 'xx',
+ symbolSvg: '',
+ name: 'Ising XX Coupling',
+ nameCss: 'ising-xx-coupling',
+ parameters: { "phi" : 1 },
+ updateMatrix$: function( phi ) {
+
+ if( mathf.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi
+ this.matrix = new Matrix(
+ [ Math.cos( this.parameters[ "phi" ] / 2 ), 0, 0, new ComplexNumber( 0, -Math.sin( this.parameters[ "phi" ] / 2 )) ],
+ [ 0, Math.cos( this.parameters[ "phi" ] / 2 ), new ComplexNumber( 0, -Math.sin( this.parameters[ "phi" ] / 2 )), 0 ],
+ [ 0, new ComplexNumber( 0, -Math.sin( this.parameters[ "phi" ] / 2 )), Math.cos( this.parameters[ "phi" ] / 2 ), 0 ],
+ [ new ComplexNumber( 0, -Math.sin( this.parameters[ "phi" ] / 2 )), 0, 0, Math.cos( this.parameters[ "phi" ] / 2 ) ])
+ return this
+ },
+ is_multi_qubit: true,
+ has_parameters: true
+ }),
+ 'ISING-XY', new Gate({
+
+ symbol: 'XY',
+ symbolAmazonBraket: 'xy',
+ symbolSvg: '',
+ name: 'Ising XY Coupling',
+ nameCss: 'ising-xy-coupling',
+ parameters: { "phi" : 1 },
+ updateMatrix$: function( phi ) {
+
+ if( mathf.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi
+ this.matrix = new Matrix(
+ [ 1, 0, 0, 0 ],
+ [ 0, Math.cos( this.parameters[ "phi" ] / 2 ), new ComplexNumber( 0, Math.sin( this.parameters[ "phi" ] / 2 )), 0 ],
+ [ 0, new ComplexNumber( 0, Math.sin( this.parameters[ "phi" ] / 2 )), Math.cos( this.parameters[ "phi" ] / 2 ), 0 ],
+ [ 0, 0, 0, 1 ])
+ return this
+ },
+ is_multi_qubit: true,
+ has_parameters: true
+ }),
+ 'ISING-YY', new Gate({
+
+ symbol: 'YY',
+ symbolAmazonBraket: 'yy',
+ symbolSvg: '',
+ name: 'Ising YY Coupling',
+ nameCss: 'ising-yy-coupling',
+ parameters: { "phi" : 1 },
+ updateMatrix$: function( phi ) {
+
+ if( mathf.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi
+ this.matrix = new Matrix(
+ [ Math.cos( this.parameters[ "phi" ] / 2 ), 0, 0, new ComplexNumber( 0, Math.sin( this.parameters[ "phi" ] / 2 )) ],
+ [ 0, Math.cos( this.parameters[ "phi" ] / 2 ), new ComplexNumber( 0, -Math.sin( this.parameters[ "phi" ] / 2 )), 0 ],
+ [ 0, new ComplexNumber( 0, -Math.sin( this.parameters[ "phi" ] / 2 )), Math.cos( this.parameters[ "phi" ] / 2 ), 0 ],
+ [ new ComplexNumber( 0, -Math.sin( this.parameters[ "phi" ] / 2 )), 0, 0, Math.cos( this.parameters[ "phi" ] / 2 ) ])
+ return this
+ },
+ is_multi_qubit: true,
+ has_parameters: true
+ }),
+ 'ISING-ZZ', new Gate({
+
+ symbol: 'ZZ',
+ symbolAmazonBraket: 'zz',
+ symbolSvg: '',
+ name: 'Ising ZZ Coupling',
+ nameCss: 'ising-zz-coupling',
+ parameters: { "phi" : 1 },
+ updateMatrix$: function( phi ) {
+
+ if( mathf.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi
+ this.matrix = new Matrix(
+ [ ComplexNumber.E.power( new ComplexNumber( 0, this.parameters[ "phi" ] / 2 )), 0, 0, 0 ],
+ [ 0, ComplexNumber.E.power( new ComplexNumber( 0, this.parameters[ "phi" ] / 2 )), 0, 0 ],
+ [ 0, 0, ComplexNumber.E.power( new ComplexNumber( 0, this.parameters[ "phi" ] / 2 )), 0],
+ [ 0, 0, 0, ComplexNumber.E.power( new ComplexNumber( 0, -this.parameters[ "phi" ] / 2 )) ])
+ return this
+ },
+ is_multi_qubit: true,
+ has_parameters: true
+ }),
+ 'CPhase00', new Gate({
+
+ symbol: '00', //placeholder
+ symbolAmazonBraket: 'cphaseshift00',
+ symbolSvg: '',
+ name: 'Controlled Phase Shift 00',
+ nameCss: 'cphase00',
+ parameters: { "phi" : 1 },
+ updateMatrix$: function( phi ) {
+
+ if( mathf.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi
+ this.matrix = new Matrix(
+ [ ComplexNumber.E.power( new ComplexNumber( 0, this.parameters[ "phi" ] )), 0, 0, 0 ],
+ [ 0, 1, 0, 0 ],
+ [ 0, 0, 1, 0 ],
+ [ 0, 0, 0, 1 ])
+ return this
+ },
+ is_multi_qubit: true,
+ has_parameters: true
+ }),
+ 'CPhase01', new Gate({
+
+ symbol: '01', //placeholder
+ symbolAmazonBraket: 'cphaseshift01',
+ symbolSvg: '',
+ name: 'Controlled Phase Shift 01',
+ nameCss: 'cphase01',
+ parameters: { "phi" : 1 },
+ updateMatrix$: function( phi ) {
+
+ if( mathf.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi
+ this.matrix = new Matrix(
+ [ 1, 0, 0, 0 ],
+ [ 0, ComplexNumber.E.power( new ComplexNumber( 0, this.parameters[ "phi" ] )), 0, 0 ],
+ [ 0, 0, 1, 0 ],
+ [ 0, 0, 0, 1 ])
+ return this
+ },
+ is_multi_qubit: true,
+ has_parameters: true
+ }),
+ 'CPhase10', new Gate({
+
+ symbol: '10', //placeholder
+ symbolAmazonBraket: 'cphaseshift10',
+ symbolSvg: '',
+ name: 'Controlled Phase Shift 10',
+ nameCss: 'cphase01',
+ parameters: { "phi" : 1 },
+ updateMatrix$: function( phi ) {
+
+ if( mathf.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi
+ this.matrix = new Matrix(
+ [ 1, 0, 0, 0 ],
+ [ 0, 1, 0, 0 ],
+ [ 0, 0, ComplexNumber.E.power( new ComplexNumber( 0, this.parameters[ "phi" ] )), 0 ],
+ [ 0, 0, 0, 1 ])
+ return this
+ },
+ is_multi_qubit: true,
+ has_parameters: true
+ }),
+ 'CSWAP', new Gate({
+
+ symbol: 'CSWAP',
+ symbolAmazonBraket: 'cswap',
+ symbolSvg: '',
+ name: 'Controlled Swap',
+ nameCss: 'controlled-swap',
+ matrix: new Matrix(
+ [1, 0, 0, 0, 0, 0, 0, 0],
+ [0, 1, 0, 0, 0, 0, 0, 0],
+ [0, 0, 1, 0, 0, 0, 0, 0],
+ [0, 0, 0, 1, 0, 0, 0, 0],
+ [0, 0, 0, 0, 1, 0, 0, 0],
+ [0, 0, 0, 0, 0, 0, 1, 0],
+ [0, 0, 0, 0, 0, 1, 0, 0],
+ [0, 0, 0, 0, 0, 0, 0, 1]
+ )
+ })
+ /*
+
+
+ All further gates,
+ such as Toffoli (CCNOT)
+ or Fredkin (CSWAP)
+ can be easily constructed
+ from the above gates
+ using Q conveniences.
+
+
+ */
+)
+
+
+
+module.exports = { Gate };
\ No newline at end of file
diff --git a/Q/Q-History.js b/packages/quantum-js-util/Q-History.js
similarity index 87%
rename from Q/Q-History.js
rename to packages/quantum-js-util/Q-History.js
index db829cb..1d38945 100644
--- a/Q/Q-History.js
+++ b/packages/quantum-js-util/Q-History.js
@@ -1,10 +1,10 @@
// Copyright © 2019–2020, Stewart Smith. See LICENSE for details.
+const {dispatchEventToGlobal} = require('./Misc');
-
-Q.History = function( instance ){
+History = function( instance ){
this.instance = instance
this.entries = [[{
@@ -19,37 +19,37 @@ Q.History = function( instance ){
-Object.assign( Q.History.prototype, {
+Object.assign( History.prototype, {
assess: function(){
const instance = this.instance
if( this.index > 0 ){
- window.dispatchEvent( new CustomEvent(
+ dispatchEventToGlobal(new CustomEvent(
- 'Q.History undo is capable', { detail: { instance }}
- ))
+ 'History undo is capable', { detail: { instance }}
+ ));
}
else {
- window.dispatchEvent( new CustomEvent(
+ dispatchEventToGlobal(new CustomEvent(
- 'Q.History undo is depleted', { detail: { instance }}
+ 'History undo is depleted', { detail: { instance }}
))
}
if( this.index + 1 < this.entries.length ){
- window.dispatchEvent( new CustomEvent(
+ dispatchEventToGlobal(new CustomEvent(
- 'Q.History redo is capable', { detail: { instance }}
+ 'History redo is capable', { detail: { instance }}
))
}
else {
- window.dispatchEvent( new CustomEvent(
+ dispatchEventToGlobal(new CustomEvent(
- 'Q.History redo is depleted', { detail: { instance }}
+ 'History redo is depleted', { detail: { instance }}
))
}
return this
@@ -193,3 +193,4 @@ Object.assign( Q.History.prototype, {
+module.exports = { History };
\ No newline at end of file
diff --git a/packages/quantum-js-util/Q-Matrix.js b/packages/quantum-js-util/Q-Matrix.js
new file mode 100644
index 0000000..6f837c5
--- /dev/null
+++ b/packages/quantum-js-util/Q-Matrix.js
@@ -0,0 +1,598 @@
+// Copyright © 2019–2020, Stewart Smith. See LICENSE for details.
+
+const logger = require('./Logging');
+const {ComplexNumber} = require('./Q-ComplexNumber');
+
+Matrix = function () {
+ // We’re keeping track of how many matrices are
+ // actually being generated. Just curiosity.
+
+ this.index = Matrix.index++;
+
+ let matrixWidth = null;
+
+ // Has Matrix been called with two numerical arguments?
+ // If so, we need to create an empty Matrix
+ // with dimensions of those values.
+
+ if (arguments.length == 1 && ComplexNumber.isNumberLike(arguments[0])) {
+ matrixWidth = arguments[0];
+ this.rows = new Array(matrixWidth).fill(0).map(function () {
+ return new Array(matrixWidth).fill(0);
+ });
+ } else if (
+ arguments.length == 2 &&
+ ComplexNumber.isNumberLike(arguments[0]) &&
+ ComplexNumber.isNumberLike(arguments[1])
+ ) {
+ matrixWidth = arguments[0];
+ this.rows = new Array(arguments[1]).fill(0).map(function () {
+ return new Array(matrixWidth).fill(0);
+ });
+ } else {
+ // Matrices’ primary organization is by rows,
+ // which is more congruent with our written langauge;
+ // primarily organizated by horizontally juxtaposed glyphs.
+ // That means it’s easier to write an instance invocation in code
+ // and easier to read when inspecting properties in the console.
+
+ let matrixWidthIsBroken = false;
+ this.rows = Array.from(arguments);
+ this.rows.forEach(function (row) {
+ if (row instanceof Array !== true) row = [row];
+ if (matrixWidth === null) matrixWidth = row.length;
+ else if (matrixWidth !== row.length) matrixWidthIsBroken = true;
+ });
+ if (matrixWidthIsBroken)
+ return logger.error(
+ `Matrix found upon initialization that matrix#${this.index} row lengths were not equal. You are going to have a bad time.`,
+ this
+ );
+ }
+
+ // But for convenience we can also organize by columns.
+ // Note this represents the transposed version of itself!
+
+ const matrix = this;
+ this.columns = [];
+ for (let x = 0; x < matrixWidth; x++) {
+ const column = [];
+ for (let y = 0; y < this.rows.length; y++) {
+ // Since we’re combing through here
+ // this is a good time to convert Number to ComplexNumber!
+
+ const value = matrix.rows[y][x];
+ if (typeof value === "number") {
+ // console.log('Created a complex number!')
+ matrix.rows[y][x] = new ComplexNumber(value);
+ } else if (value instanceof ComplexNumber === false) {
+ return logger.error(
+ `Matrix found upon initialization that matrix#${this.index} contained non-quantitative values. A+ for creativity, but F for functionality.`,
+ this
+ );
+ }
+
+ // console.log( x, y, matrix.rows[ y ][ x ])
+
+ Object.defineProperty(column, y, {
+ get: function () {
+ return matrix.rows[y][x];
+ },
+ set: function (n) {
+ matrix.rows[y][x] = n;
+ },
+ });
+ }
+ this.columns.push(column);
+ }
+};
+
+///////////////////////////
+// //
+// Static properties //
+// //
+///////////////////////////
+
+Object.assign(Matrix, {
+ index: 0,
+ help: function () {
+ return help(this);
+ },
+ constants: {}, // Only holds references; an easy way to look up what constants exist.
+ createConstant: function (key, value) {
+ this[key] = value;
+ this.constants[key] = this[key];
+ Object.freeze(this[key]);
+ },
+ createConstants: function () {
+ if (arguments.length % 2 !== 0) {
+ return logger.error(
+ "Q attempted to create constants with invalid (KEY, VALUE) pairs."
+ );
+ }
+ for (let i = 0; i < arguments.length; i += 2) {
+ this.createConstant(arguments[i], arguments[i + 1]);
+ }
+ },
+
+ isMatrixLike: function (obj) {
+ //return obj instanceof Matrix || Matrix.prototype.isPrototypeOf( obj )
+ return obj instanceof this || this.prototype.isPrototypeOf(obj);
+ },
+ isWithinRange: function (n, minimum, maximum) {
+ return (
+ typeof n === "number" && n >= minimum && n <= maximum && n == parseInt(n)
+ );
+ },
+ getWidth: function (matrix) {
+ return matrix.columns.length;
+ },
+ getHeight: function (matrix) {
+ return matrix.rows.length;
+ },
+ haveEqualDimensions: function (matrix0, matrix1) {
+ return (
+ matrix0.rows.length === matrix1.rows.length &&
+ matrix0.columns.length === matrix1.columns.length
+ );
+ },
+ areEqual: function (matrix0, matrix1) {
+ if (matrix0 instanceof Matrix !== true) return false;
+ if (matrix1 instanceof Matrix !== true) return false;
+ if (Matrix.haveEqualDimensions(matrix0, matrix1) !== true) return false;
+ return matrix0.rows.reduce(function (state, row, r) {
+ return (
+ state &&
+ row.reduce(function (state, cellValue, c) {
+ return state && cellValue.isEqualTo(matrix1.rows[r][c]);
+ }, true)
+ );
+ }, true);
+ },
+
+ createSquare: function (size, f) {
+ if (typeof size !== "number") size = 2;
+ if (typeof f !== "function")
+ f = function () {
+ return 0;
+ };
+ const data = [];
+ for (let y = 0; y < size; y++) {
+ const row = [];
+ for (let x = 0; x < size; x++) {
+ row.push(f(x, y));
+ }
+ data.push(row);
+ }
+ return new Matrix(...data);
+ },
+ createZero: function (size) {
+ return new Matrix.createSquare(size);
+ },
+ createOne: function (size) {
+ return new Matrix.createSquare(size, function () {
+ return 1;
+ });
+ },
+ createIdentity: function (size) {
+ return new Matrix.createSquare(size, function (x, y) {
+ return x === y ? 1 : 0;
+ });
+ },
+
+ // Import FROM a format.
+
+ from: function (format) {
+ if (typeof format !== "string") format = "Array";
+ const f = Matrix["from" + format];
+ format = format.toLowerCase();
+ if (typeof f !== "function")
+ return logger.error(
+ `Matrix could not find an importer for “${format}” data.`
+ );
+ return f;
+ },
+ fromArray: function (array) {
+ return new Matrix(...array);
+ },
+ fromXsv: function (input, rowSeparator, valueSeparator) {
+ `
+ Ingest string data organized by row, then by column
+ where rows are separated by one token (default: \n)
+ and column values are separated by another token
+ (default: \t).
+
+ `;
+
+ if (typeof rowSeparator !== "string") rowSeparator = "\n";
+ if (typeof valueSeparator !== "string") valueSeparator = "\t";
+
+ const inputRows = input.split(rowSeparator),
+ outputRows = [];
+
+ inputRows.forEach(function (inputRow) {
+ inputRow = inputRow.trim();
+ if (inputRow === "") return;
+
+ const outputRow = [];
+ inputRow.split(valueSeparator).forEach(function (cellValue) {
+ outputRow.push(parseFloat(cellValue));
+ });
+ outputRows.push(outputRow);
+ });
+ return new Matrix(...outputRows);
+ },
+ fromCsv: function (csv) {
+ return Matrix.fromXsv(csv.replace(/\r/g, "\n"), "\n", ",");
+ },
+ fromTsv: function (tsv) {
+ return Matrix.fromXsv(tsv, "\n", "\t");
+ },
+ fromHtml: function (html) {
+ return Matrix.fromXsv(
+ html
+ .replace(/\r?\n|\r||/g, "")
+ .replace(/<\/td>(\s*)<\/tr>/g, " |
")
+ .match(/(.*)<\/table>/i)[1],
+ "",
+ ""
+ );
+ },
+
+ // Export TO a format.
+
+ toXsv: function (matrix, rowSeparator, valueSeparator) {
+ return matrix.rows.reduce(function (xsv, row) {
+ return (
+ xsv +
+ rowSeparator +
+ row.reduce(function (xsv, cell, c) {
+ return xsv + (c > 0 ? valueSeparator : "") + cell.toText();
+ }, "")
+ );
+ }, "");
+ },
+ toCsv: function (matrix) {
+ return Matrix.toXsv(matrix, "\n", ",");
+ },
+ toTsv: function (matrix) {
+ return Matrix.toXsv(matrix, "\n", "\t");
+ },
+
+ // Operate NON-destructive.
+
+ add: function (matrix0, matrix1) {
+ if (
+ Matrix.isMatrixLike(matrix0) !== true ||
+ Matrix.isMatrixLike(matrix1) !== true
+ ) {
+ return logger.error(
+ `Matrix attempted to add something that was not a matrix.`
+ );
+ }
+ if (Matrix.haveEqualDimensions(matrix0, matrix1) !== true)
+ return logger.error(
+ `Matrix cannot add matrix#${matrix0.index} of dimensions ${matrix0.columns.length}x${matrix0.rows.length} to matrix#${matrix1.index} of dimensions ${matrix1.columns.length}x${matrix1.rows.length}.`
+ );
+
+ return new Matrix(
+ ...matrix0.rows.reduce(function (resultMatrixRow, row, r) {
+ resultMatrixRow.push(
+ row.reduce(function (resultMatrixColumn, cellValue, c) {
+ // resultMatrixColumn.push( cellValue + matrix1.rows[ r ][ c ])
+ resultMatrixColumn.push(cellValue.add(matrix1.rows[r][c]));
+ return resultMatrixColumn;
+ }, [])
+ );
+ return resultMatrixRow;
+ }, [])
+ );
+ },
+ multiplyScalar: function (matrix, scalar) {
+ if (Matrix.isMatrixLike(matrix) !== true) {
+ return logger.error(
+ `Matrix attempted to scale something that was not a matrix.`
+ );
+ }
+ if (typeof scalar !== "number") {
+ return logger.error(
+ `Matrix attempted to scale this matrix#${matrix.index} by an invalid scalar: ${scalar}.`
+ );
+ }
+ return new Matrix(
+ ...matrix.rows.reduce(function (resultMatrixRow, row) {
+ resultMatrixRow.push(
+ row.reduce(function (resultMatrixColumn, cellValue) {
+ // resultMatrixColumn.push( cellValue * scalar )
+ resultMatrixColumn.push(cellValue.multiply(scalar));
+ return resultMatrixColumn;
+ }, [])
+ );
+ return resultMatrixRow;
+ }, [])
+ );
+ },
+ multiply: function (matrix0, matrix1) {
+ `
+ Two matrices can be multiplied only when
+ the number of columns in the first matrix
+ equals the number of rows in the second matrix.
+ Reminder: Matrix multiplication is not commutative
+ so the order in which you multiply matters.
+
+
+ SEE ALSO
+
+ https://en.wikipedia.org/wiki/Matrix_multiplication
+ `;
+
+ if (
+ Matrix.isMatrixLike(matrix0) !== true ||
+ Matrix.isMatrixLike(matrix1) !== true
+ ) {
+ return logger.error(
+ `Matrix attempted to multiply something that was not a matrix.`
+ );
+ }
+ if (matrix0.columns.length !== matrix1.rows.length) {
+ return logger.error(
+ `Matrix attempted to multiply Matrix#${matrix0.index}(cols==${matrix0.columns.length}) by Matrix#${matrix1.index}(rows==${matrix1.rows.length}) but their dimensions were not compatible for this.`
+ );
+ }
+ const resultMatrix = [];
+ matrix0.rows.forEach(function (matrix0Row) {
+ // Each row of THIS matrix
+
+ const resultMatrixRow = [];
+ matrix1.columns.forEach(function (matrix1Column) {
+ // Each column of OTHER matrix
+
+ const sum = new ComplexNumber();
+ matrix1Column.forEach(function (matrix1CellValue, index) {
+ // Work down the column of OTHER matrix
+
+ sum.add$(matrix0Row[index].multiply(matrix1CellValue));
+ });
+ resultMatrixRow.push(sum);
+ });
+ resultMatrix.push(resultMatrixRow);
+ });
+ //return new Matrix( ...resultMatrix )
+ return new this(...resultMatrix);
+ },
+ multiplyTensor: function (matrix0, matrix1) {
+ `
+ https://en.wikipedia.org/wiki/Kronecker_product
+ https://en.wikipedia.org/wiki/Tensor_product
+ `;
+
+ if (
+ Matrix.isMatrixLike(matrix0) !== true ||
+ Matrix.isMatrixLike(matrix1) !== true
+ ) {
+ return logger.error(
+ `Matrix attempted to tensor something that was not a matrix.`
+ );
+ }
+
+ const resultMatrix = [],
+ resultMatrixWidth = matrix0.columns.length * matrix1.columns.length,
+ resultMatrixHeight = matrix0.rows.length * matrix1.rows.length;
+
+ for (let y = 0; y < resultMatrixHeight; y++) {
+ const resultMatrixRow = [];
+ for (let x = 0; x < resultMatrixWidth; x++) {
+ const matrix0X = Math.floor(x / matrix0.columns.length),
+ matrix0Y = Math.floor(y / matrix0.rows.length),
+ matrix1X = x % matrix1.columns.length,
+ matrix1Y = y % matrix1.rows.length;
+
+ resultMatrixRow.push(
+ //matrix0.rows[ matrix0Y ][ matrix0X ] * matrix1.rows[ matrix1Y ][ matrix1X ]
+ matrix0.rows[matrix0Y][matrix0X].multiply(
+ matrix1.rows[matrix1Y][matrix1X]
+ )
+ );
+ }
+ resultMatrix.push(resultMatrixRow);
+ }
+ return new Matrix(...resultMatrix);
+ },
+});
+
+//////////////////////////////
+// //
+// Prototype properties //
+// //
+//////////////////////////////
+
+Object.assign(Matrix.prototype, {
+ isValidRow: function (r) {
+ return Matrix.isWithinRange(r, 0, this.rows.length - 1);
+ },
+ isValidColumn: function (c) {
+ return Matrix.isWithinRange(c, 0, this.columns.length - 1);
+ },
+ isValidAddress: function (x, y) {
+ return this.isValidRow(y) && this.isValidColumn(x);
+ },
+ getWidth: function () {
+ return Matrix.getWidth(this);
+ },
+ getHeight: function () {
+ return Matrix.getHeight(this);
+ },
+
+ // Read NON-destructive by nature. (Except quantum reads of course! ROFL!!)
+
+ read: function (x, y) {
+ `
+ Equivalent to
+ this.columns[ x ][ y ]
+ or
+ this.rows[ y ][ x ]
+ but with safety checks.
+ `;
+
+ if (this.isValidAddress(x, y)) return this.rows[y][x];
+ return logger.error(
+ `Matrix could not read from cell address (x=${x}, y=${y}) in matrix#${this.index}.`,
+ this
+ );
+ },
+ clone: function () {
+ return new Matrix(...this.rows);
+ },
+ isEqualTo: function (otherMatrix) {
+ return Matrix.areEqual(this, otherMatrix);
+ },
+
+ toArray: function () {
+ return this.rows;
+ },
+ toXsv: function (rowSeparator, valueSeparator) {
+ return Matrix.toXsv(this, rowSeparator, valueSeparator);
+ },
+ toCsv: function () {
+ return Matrix.toXsv(this, "\n", ",");
+ },
+ toTsv: function () {
+ return Matrix.toXsv(this, "\n", "\t");
+ },
+ toHtml: function () {
+ return (
+ this.rows.reduce(function (html, row) {
+ return (
+ html +
+ row.reduce(function (html, cell) {
+ return html + "\n\t\t" + cell.toText() + " | ";
+ }, "\n\t") +
+ "\n\t
"
+ );
+ }, "\n"
+ );
+ },
+
+ // Write is DESTRUCTIVE by nature. Not cuz I hate ya.
+
+ write$: function (x, y, n) {
+ `
+ Equivalent to
+ this.columns[ x ][ y ] = n
+ or
+ this.rows[ y ][ x ] = n
+ but with safety checks.
+ `;
+
+ if (this.isValidAddress(x, y)) {
+ if (ComplexNumber.isNumberLike(n)) n = new ComplexNumber(n);
+ if (n instanceof ComplexNumber !== true)
+ return logger.error(
+ `Attempted to write an invalid value (${n}) to matrix#${this.index} at x=${x}, y=${y}`,
+ this
+ );
+ this.rows[y][x] = n;
+ return this;
+ }
+ return logger.error(
+ `Invalid cell address for Matrix#${this.index}: x=${x}, y=${y}`,
+ this
+ );
+ },
+ copy$: function (matrix) {
+ if (Matrix.isMatrixLike(matrix) !== true)
+ return logger.error(
+ `Matrix attempted to copy something that was not a matrix in to this matrix#${matrix.index}.`,
+ this
+ );
+
+ if (Matrix.haveEqualDimensions(matrix, this) !== true)
+ return logger.error(
+ `Matrix cannot copy matrix#${matrix.index} of dimensions ${matrix.columns.length}x${matrix.rows.length} in to this matrix#${this.index} of dimensions ${this.columns.length}x${this.rows.length} because their dimensions do not match.`,
+ this
+ );
+
+ const that = this;
+ matrix.rows.forEach(function (row, r) {
+ row.forEach(function (n, c) {
+ that.rows[r][c] = n;
+ });
+ });
+ return this;
+ },
+ fromArray$: function (array) {
+ return this.copy$(Matrix.fromArray(array));
+ },
+ fromCsv$: function (csv) {
+ return this.copy$(Matrix.fromCsv(csv));
+ },
+ fromTsv$: function (tsv) {
+ return this.copy$(Matrix.fromTsv(tsv));
+ },
+ fromHtml$: function (html) {
+ return this.copy$(Matrix.fromHtml(html));
+ },
+
+ // Operate NON-destructive.
+
+ add: function (otherMatrix) {
+ return Matrix.add(this, otherMatrix);
+ },
+ multiplyScalar: function (scalar) {
+ return Matrix.multiplyScalar(this, scalar);
+ },
+ multiply: function (otherMatrix) {
+ return Matrix.multiply(this, otherMatrix);
+ },
+ multiplyTensor: function (otherMatrix) {
+ return Matrix.multiplyTensor(this, otherMatrix);
+ },
+
+ // Operate DESTRUCTIVE.
+
+ add$: function (otherMatrix) {
+ return this.copy$(this.add(otherMatrix));
+ },
+ multiplyScalar$: function (scalar) {
+ return this.copy$(this.multiplyScalar(scalar));
+ },
+});
+
+//////////////////////////
+// //
+// Static constants //
+// //
+//////////////////////////
+
+Matrix.createConstants(
+ "IDENTITY_2X2",
+ Matrix.createIdentity(2),
+ "IDENTITY_3X3",
+ Matrix.createIdentity(3),
+ "IDENTITY_4X4",
+ Matrix.createIdentity(4),
+
+ "CONSTANT0_2X2",
+ new Matrix([1, 1], [0, 0]),
+
+ "CONSTANT1_2X2",
+ new Matrix([0, 0], [1, 1]),
+
+ "NEGATION_2X2",
+ new Matrix([0, 1], [1, 0]),
+
+ "TEST_MAP_9X9",
+ new Matrix(
+ [11, 21, 31, 41, 51, 61, 71, 81, 91],
+ [12, 22, 32, 42, 52, 62, 72, 82, 92],
+ [13, 23, 33, 43, 53, 63, 73, 83, 93],
+ [14, 24, 34, 44, 54, 64, 74, 84, 94],
+ [15, 25, 35, 45, 55, 65, 75, 85, 95],
+ [16, 26, 36, 46, 56, 66, 76, 86, 96],
+ [17, 27, 37, 47, 57, 67, 77, 87, 97],
+ [18, 28, 38, 48, 58, 68, 78, 88, 98],
+ [19, 29, 39, 49, 59, 69, 79, 89, 99]
+ )
+);
+
+module.exports = { Matrix };
\ No newline at end of file
diff --git a/packages/quantum-js-util/Q-Qubit.js b/packages/quantum-js-util/Q-Qubit.js
new file mode 100644
index 0000000..48c3f1e
--- /dev/null
+++ b/packages/quantum-js-util/Q-Qubit.js
@@ -0,0 +1,349 @@
+// Copyright © 2019–2020, Stewart Smith. See LICENSE for details.
+const { Matrix } = require("./Q-Matrix");
+const { Gate } = require("./Q-Gate");
+const { ComplexNumber } = require("./Q-ComplexNumber");
+const misc = require("./Misc");
+const logger = require("./Logging");
+
+Qubit = function (a, b, symbol, name) {
+ // If we’ve received an instance of Matrix as our first argument
+ // then we’ll assume there are no further arguments
+ // and just use that matrix as our new Qubit instance.
+
+ if (Matrix.isMatrixLike(a) && b === undefined) {
+ b = a.rows[1][0];
+ a = a.rows[0][0];
+ } else {
+ // All of our internal math now uses complex numbers
+ // rather than Number literals
+ // so we’d better convert!
+
+ if (typeof a === "number") a = new ComplexNumber(a, 0);
+ if (typeof b === "number") b = new ComplexNumber(b, 0);
+
+ // If we receive undefined (or garbage inputs)
+ // let’s try to make it useable.
+ // This way we can always call Qubit with no arguments
+ // to make a new qubit available for computing with.
+
+ if (a instanceof ComplexNumber !== true) a = new ComplexNumber(1, 0);
+ if (b instanceof ComplexNumber !== true) {
+ // 1 - |𝒂|² = |𝒃|²
+ // So this does NOT account for if 𝒃 ought to be imaginary or not.
+ // Perhaps for completeness we could randomly decide
+ // to flip the real and imaginary components of 𝒃 after this line?
+
+ b = ComplexNumber.ONE.subtract(Math.pow(a.absolute(), 2)).squareRoot();
+ }
+ }
+
+ // Sanity check!
+ // Does this constraint hold true? |𝒂|² + |𝒃|² = 1
+
+ if (
+ Math.pow(a.absolute(), 2) + Math.pow(b.absolute(), 2) - 1 >
+ misc.constants.EPSILON
+ )
+ return logger.error(
+ `Qubit could not accept the initialization values of a=${a} and b=${b} because their squares do not add up to 1.`
+ );
+
+ Matrix.call(this, [a], [b]);
+ this.index = Qubit.index++;
+
+ // Convenience getters and setters for this qubit’s
+ // controll bit and target bit.
+
+ Object.defineProperty(this, "alpha", {
+ get: function () {
+ return this.rows[0][0];
+ },
+ set: function (n) {
+ this.rows[0][0] = n;
+ },
+ });
+ Object.defineProperty(this, "beta", {
+ get: function () {
+ return this.rows[1][0];
+ },
+ set: function (n) {
+ this.rows[1][0] = n;
+ },
+ });
+
+ // Used for Dirac notation: |?⟩
+
+ if (typeof symbol === "string") this.symbol = symbol;
+ if (typeof name === "string") this.name = name;
+ if (this.symbol === undefined || this.name === undefined) {
+ const found = Object.values(Qubit.constants).find(function (qubit) {
+ return a.isEqualTo(qubit.alpha) && b.isEqualTo(qubit.beta);
+ });
+ if (found === undefined) {
+ this.symbol = "?";
+ this.name = "Unnamed";
+ } else {
+ if (this.symbol === undefined) this.symbol = found.symbol;
+ if (this.name === undefined) this.name = found.name;
+ }
+ }
+};
+//Qubit inherits from Matrix.
+Qubit.prototype = Object.create(Matrix.prototype);
+Qubit.prototype.constructor = Qubit;
+
+Object.assign(Qubit, {
+ index: 0,
+ help: function () {
+ return help(this);
+ },
+ constants: {},
+ createConstant: function (key, value) {
+ this[key] = value;
+ this.constants[key] = this[key];
+ Object.freeze(this[key]);
+ },
+ createConstants: function () {
+ if (arguments.length % 2 !== 0) {
+ return logger.error(
+ "Q attempted to create constants with invalid (KEY, VALUE) pairs."
+ );
+ }
+ for (let i = 0; i < arguments.length; i += 2) {
+ this.createConstant(arguments[i], arguments[i + 1]);
+ }
+ },
+
+ findBy: function (key, value) {
+ return Object.values(Qubit.constants).find(function (item) {
+ if (typeof value === "string" && typeof item[key] === "string") {
+ return value.toLowerCase() === item[key].toLowerCase();
+ }
+ return value === item[key];
+ });
+ },
+ findBySymbol: function (symbol) {
+ return Qubit.findBy("symbol", symbol);
+ },
+ findByName: function (name) {
+ return Qubit.findBy("name", name);
+ },
+ findByBeta: function (beta) {
+ if (beta instanceof ComplexNumber === false) {
+ beta = new ComplexNumber(beta);
+ }
+ return Object.values(Qubit.constants).find(function (qubit) {
+ return qubit.beta.isEqualTo(beta);
+ });
+ },
+ areEqual: function (qubit0, qubit1) {
+ return (
+ qubit0.alpha.isEqualTo(qubit1.alpha) && qubit0.beta.isEqualTo(qubit1.beta)
+ );
+ },
+ collapse: function (qubit) {
+ const alpha2 = Math.pow(qubit.alpha.absolute(), 2),
+ beta2 = Math.pow(qubit.beta.absolute(), 2),
+ randomNumberRange = Math.pow(2, 32) - 1,
+ randomNumber = new Uint32Array(1);
+
+ // console.log( 'alpha^2', alpha2 )
+ // console.log( 'beta^2', beta2 )
+ window.crypto.getRandomValues(randomNumber);
+ const randomNumberNormalized = randomNumber / randomNumberRange;
+ if (randomNumberNormalized <= alpha2) {
+ return new Qubit(1, 0);
+ } else return new Qubit(0, 1);
+ },
+ applyGate: function (qubit, gate, ...args) {
+ //TODO test...currently you're updating the gate's matrix property rather than returning a separate instance of the matrix.
+ //this is okay if "gate" is not one of the constants. otherwise, it's bad.
+ `
+ This is means of inverting what comes first:
+ the Gate or the Qubit?
+ If the Gate only operates on a single qubit,
+ then it doesn’t matter and we can do this:
+ `;
+
+ if (gate instanceof Gate === false)
+ return logger.error(
+ `Qubit attempted to apply something that was not a gate to this qubit #${qubit.index}.`
+ );
+ if (gate == Gate.findBy(gate.symbol))
+ return logger.error(`Qubit attempted to apply a reference to the gate constant ${gate.symbol} rather than
+ a copy. This is disallowed.`);
+ else {
+ gate.updateMatrix$(...args);
+ return new Qubit(gate.matrix.multiply(qubit));
+ }
+ },
+ toText: function (qubit) {
+ //return `|${qubit.beta.toText()}⟩`
+ return qubit.alpha.toText() + "\n" + qubit.beta.toText();
+ },
+ toStateVectorText: function (qubit) {
+ return `|${qubit.beta.toText()}⟩`;
+ },
+ toStateVectorHtml: function (qubit) {
+ return `${qubit.beta.toText()}`;
+ },
+
+ // This code was a pain in the ass to figure out.
+ // I’m not fluent in trigonometry
+ // and none of the quantum primers actually lay out
+ // how to convert arbitrary qubit states
+ // to Bloch Sphere representation.
+ // Oh, they provide equivalencies for specific states, sure.
+ // I hope this is useful to you
+ // unless you are porting this to a terrible language
+ // like C# or Java or something ;)
+
+ toBlochSphere: function (qubit) {
+ `
+ Based on this qubit’s state return the
+ Polar angle θ (theta),
+ azimuth angle ϕ (phi),
+ Bloch vector,
+ corrected surface coordinate.
+
+ https://en.wikipedia.org/wiki/Bloch_sphere
+ `;
+
+ // Polar angle θ (theta).
+
+ const theta = ComplexNumber.arcCosine(qubit.alpha).multiply(2);
+ if (isNaN(theta.real)) theta.real = 0;
+ if (isNaN(theta.imaginary)) theta.imaginary = 0;
+
+ // Azimuth angle ϕ (phi).
+
+ const phi = ComplexNumber.log(
+ qubit.beta.divide(ComplexNumber.sine(theta.divide(2)))
+ ).divide(ComplexNumber.I);
+ if (isNaN(phi.real)) phi.real = 0;
+ if (isNaN(phi.imaginary)) phi.imaginary = 0;
+
+ // Bloch vector.
+
+ const vector = {
+ x: ComplexNumber.sine(theta).multiply(ComplexNumber.cosine(phi)).real,
+ y: ComplexNumber.sine(theta).multiply(ComplexNumber.sine(phi)).real,
+ z: ComplexNumber.cosine(theta).real,
+ };
+
+ // Bloch vector’s axes are wonked.
+ // Let’s “correct” them for use with Three.js, etc.
+
+ const position = {
+ x: vector.y,
+ y: vector.z,
+ z: vector.x,
+ };
+
+ return {
+ // Wow does this make tweening easier down the road.
+
+ alphaReal: qubit.alpha.real,
+ alphaImaginary: qubit.alpha.imaginary,
+ betaReal: qubit.beta.real,
+ betaImaginary: qubit.beta.imaginary,
+
+ // Ummm... I’m only returnig the REAL portions. Please forgive me!
+
+ theta: theta.real,
+ phi: phi.real,
+ vector, // Wonked YZX vector for maths because maths.
+ position, // Un-wonked XYZ for use by actual 3D engines.
+ };
+ },
+ fromBlochVector: function (x, y, z) {
+ //basically from a Pauli Rotation
+ },
+});
+
+Qubit.createConstants(
+ // Opposing pairs:
+ // |H⟩ and |V⟩
+ // |D⟩ and |A⟩
+ // |R⟩ and |L⟩
+
+ "HORIZONTAL",
+ new Qubit(1, 0, "H", "Horizontal"), // ZERO.
+ "VERTICAL",
+ new Qubit(0, 1, "V", "Vertical"), // ONE.
+ "DIAGONAL",
+ new Qubit(Math.SQRT1_2, Math.SQRT1_2, "D", "Diagonal"),
+ "ANTI_DIAGONAL",
+ new Qubit(Math.SQRT1_2, -Math.SQRT1_2, "A", "Anti-diagonal"),
+ "RIGHT_HAND_CIRCULAR_POLARIZED",
+ new Qubit(
+ Math.SQRT1_2,
+ new ComplexNumber(0, -Math.SQRT1_2),
+ "R",
+ "Right-hand Circular Polarized"
+ ), // RHCP
+ "LEFT_HAND_CIRCULAR_POLARIZED",
+ new Qubit(
+ Math.SQRT1_2,
+ new ComplexNumber(0, Math.SQRT1_2),
+ "L",
+ "Left-hand Circular Polarized"
+ ) // LHCP
+);
+
+Object.assign(Qubit.prototype, {
+ copy$: function (matrix) {
+ if (Matrix.isMatrixLike(matrix) !== true)
+ return logger.error(
+ `Qubit attempted to copy something that was not a matrix in this qubit #${qubit.index}.`,
+ this
+ );
+
+ if (Matrix.haveEqualDimensions(matrix, this) !== true)
+ return logger.error(
+ `Qubit cannot copy matrix#${matrix.index} of dimensions ${matrix.columns.length}x${matrix.rows.length} in to this qubit #${this.index} of dimensions ${this.columns.length}x${this.rows.length} because their dimensions do not match.`,
+ this
+ );
+
+ const that = this;
+ matrix.rows.forEach(function (row, r) {
+ row.forEach(function (n, c) {
+ that.rows[r][c] = n;
+ });
+ });
+ this.dirac = matrix.dirac;
+ return this;
+ },
+ clone: function () {
+ return new Qubit(this.alpha, this.beta);
+ },
+ isEqualTo: function (otherQubit) {
+ return Qubit.areEqual(this, otherQubit); // Returns a Boolean, breaks function chaining!
+ },
+ collapse: function () {
+ return Qubit.collapse(this);
+ },
+ applyGate: function (gate, ...args) {
+ return Qubit.applyGate(this, gate, ...args);
+ },
+ toText: function () {
+ return Qubit.toText(this); // Returns a String, breaks function chaining!
+ },
+ toStateVectorText: function () {
+ return Qubit.toStateVectorText(this); // Returns a String, breaks function chaining!
+ },
+ toStateVectorHtml: function () {
+ return Qubit.toStateVectorHtml(this); // Returns a String, breaks function chaining!
+ },
+ toBlochSphere: function () {
+ return Qubit.toBlochSphere(this); // Returns an Object, breaks function chaining!
+ },
+ collapse$: function () {
+ return this.copy$(Qubit.collapse(this));
+ },
+ applyGate$: function (gate) {
+ return this.copy$(Qubit.applyGate(this, gate));
+ },
+});
+
+module.exports = { Qubit };
diff --git a/packages/quantum-js-util/Q.js b/packages/quantum-js-util/Q.js
new file mode 100644
index 0000000..a4ca1d5
--- /dev/null
+++ b/packages/quantum-js-util/Q.js
@@ -0,0 +1,55 @@
+// Copyright © 2019–2020, Stewart Smith. See LICENSE for details.
+
+const logger = require('./Logging');
+const misc = require('./Misc');
+const mathf = require('./Math-Functions');
+const {ComplexNumber} = require('./Q-ComplexNumber');
+const {Gate} = require('./Q-Gate');
+const {Qubit} = require('./Q-Qubit');
+const {Matrix} = require('./Q-Matrix');
+const {History} = require('./Q-History');
+const {Circuit} = require('./Q-Circuit');
+
+
+
+const Q = function () {
+ // Did we send arguments of the form
+ // ( bandwidth, timewidth )?
+
+ if (
+ arguments.length === 2 &&
+ Array.from(arguments).every(function (argument) {
+ return isUsefulInteger(argument);
+ })
+ ) {
+ return new Circuit(arguments[0], arguments[1]);
+ }
+
+ // Otherwise assume we are creating a circuit
+ // from a text block.
+
+ return Circuit.fromText(arguments[0]);
+};
+
+
+console.log(`
+
+
+ QQQQQQ
+QQ QQ
+QQ QQ
+QQ QQ
+QQ QQ QQ
+QQ QQ
+ QQQQ ${misc.constants.REVISION}
+
+
+
+https://quantumjavascript.app
+
+
+
+`);
+
+module.exports = {logger, misc, mathf, ComplexNumber, Matrix, Gate, Qubit, History, Circuit, Q};
+
diff --git a/packages/quantum-js-util/index.js b/packages/quantum-js-util/index.js
new file mode 100644
index 0000000..c1cc69f
--- /dev/null
+++ b/packages/quantum-js-util/index.js
@@ -0,0 +1 @@
+const {Q} = require('./Q');
diff --git a/packages/quantum-js-util/package-lock.json b/packages/quantum-js-util/package-lock.json
new file mode 100644
index 0000000..9a5f4af
--- /dev/null
+++ b/packages/quantum-js-util/package-lock.json
@@ -0,0 +1,3062 @@
+{
+ "name": "quantum-js-util",
+ "version": "1.0.0",
+ "lockfileVersion": 1,
+ "requires": true,
+ "dependencies": {
+ "@babel/code-frame": {
+ "version": "7.14.5",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.14.5.tgz",
+ "integrity": "sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw==",
+ "dev": true,
+ "requires": {
+ "@babel/highlight": "^7.14.5"
+ }
+ },
+ "@babel/compat-data": {
+ "version": "7.14.7",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.14.7.tgz",
+ "integrity": "sha512-nS6dZaISCXJ3+518CWiBfEr//gHyMO02uDxBkXTKZDN5POruCnOZ1N4YBRZDCabwF8nZMWBpRxIicmXtBs+fvw==",
+ "dev": true
+ },
+ "@babel/core": {
+ "version": "7.14.8",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.14.8.tgz",
+ "integrity": "sha512-/AtaeEhT6ErpDhInbXmjHcUQXH0L0TEgscfcxk1qbOvLuKCa5aZT0SOOtDKFY96/CLROwbLSKyFor6idgNaU4Q==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.14.5",
+ "@babel/generator": "^7.14.8",
+ "@babel/helper-compilation-targets": "^7.14.5",
+ "@babel/helper-module-transforms": "^7.14.8",
+ "@babel/helpers": "^7.14.8",
+ "@babel/parser": "^7.14.8",
+ "@babel/template": "^7.14.5",
+ "@babel/traverse": "^7.14.8",
+ "@babel/types": "^7.14.8",
+ "convert-source-map": "^1.7.0",
+ "debug": "^4.1.0",
+ "gensync": "^1.0.0-beta.2",
+ "json5": "^2.1.2",
+ "semver": "^6.3.0",
+ "source-map": "^0.5.0"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+ "dev": true
+ }
+ }
+ },
+ "@babel/generator": {
+ "version": "7.14.8",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.14.8.tgz",
+ "integrity": "sha512-cYDUpvIzhBVnMzRoY1fkSEhK/HmwEVwlyULYgn/tMQYd6Obag3ylCjONle3gdErfXBW61SVTlR9QR7uWlgeIkg==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.14.8",
+ "jsesc": "^2.5.1",
+ "source-map": "^0.5.0"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+ "dev": true
+ }
+ }
+ },
+ "@babel/helper-compilation-targets": {
+ "version": "7.14.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.14.5.tgz",
+ "integrity": "sha512-v+QtZqXEiOnpO6EYvlImB6zCD2Lel06RzOPzmkz/D/XgQiUu3C/Jb1LOqSt/AIA34TYi/Q+KlT8vTQrgdxkbLw==",
+ "dev": true,
+ "requires": {
+ "@babel/compat-data": "^7.14.5",
+ "@babel/helper-validator-option": "^7.14.5",
+ "browserslist": "^4.16.6",
+ "semver": "^6.3.0"
+ }
+ },
+ "@babel/helper-function-name": {
+ "version": "7.14.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.14.5.tgz",
+ "integrity": "sha512-Gjna0AsXWfFvrAuX+VKcN/aNNWonizBj39yGwUzVDVTlMYJMK2Wp6xdpy72mfArFq5uK+NOuexfzZlzI1z9+AQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-get-function-arity": "^7.14.5",
+ "@babel/template": "^7.14.5",
+ "@babel/types": "^7.14.5"
+ }
+ },
+ "@babel/helper-get-function-arity": {
+ "version": "7.14.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.14.5.tgz",
+ "integrity": "sha512-I1Db4Shst5lewOM4V+ZKJzQ0JGGaZ6VY1jYvMghRjqs6DWgxLCIyFt30GlnKkfUeFLpJt2vzbMVEXVSXlIFYUg==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.14.5"
+ }
+ },
+ "@babel/helper-hoist-variables": {
+ "version": "7.14.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.14.5.tgz",
+ "integrity": "sha512-R1PXiz31Uc0Vxy4OEOm07x0oSjKAdPPCh3tPivn/Eo8cvz6gveAeuyUUPB21Hoiif0uoPQSSdhIPS3352nvdyQ==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.14.5"
+ }
+ },
+ "@babel/helper-member-expression-to-functions": {
+ "version": "7.14.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.14.7.tgz",
+ "integrity": "sha512-TMUt4xKxJn6ccjcOW7c4hlwyJArizskAhoSTOCkA0uZ+KghIaci0Qg9R043kUMWI9mtQfgny+NQ5QATnZ+paaA==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.14.5"
+ }
+ },
+ "@babel/helper-module-imports": {
+ "version": "7.14.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.14.5.tgz",
+ "integrity": "sha512-SwrNHu5QWS84XlHwGYPDtCxcA0hrSlL2yhWYLgeOc0w7ccOl2qv4s/nARI0aYZW+bSwAL5CukeXA47B/1NKcnQ==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.14.5"
+ }
+ },
+ "@babel/helper-module-transforms": {
+ "version": "7.14.8",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.14.8.tgz",
+ "integrity": "sha512-RyE+NFOjXn5A9YU1dkpeBaduagTlZ0+fccnIcAGbv1KGUlReBj7utF7oEth8IdIBQPcux0DDgW5MFBH2xu9KcA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-module-imports": "^7.14.5",
+ "@babel/helper-replace-supers": "^7.14.5",
+ "@babel/helper-simple-access": "^7.14.8",
+ "@babel/helper-split-export-declaration": "^7.14.5",
+ "@babel/helper-validator-identifier": "^7.14.8",
+ "@babel/template": "^7.14.5",
+ "@babel/traverse": "^7.14.8",
+ "@babel/types": "^7.14.8"
+ }
+ },
+ "@babel/helper-optimise-call-expression": {
+ "version": "7.14.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.14.5.tgz",
+ "integrity": "sha512-IqiLIrODUOdnPU9/F8ib1Fx2ohlgDhxnIDU7OEVi+kAbEZcyiF7BLU8W6PfvPi9LzztjS7kcbzbmL7oG8kD6VA==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.14.5"
+ }
+ },
+ "@babel/helper-plugin-utils": {
+ "version": "7.14.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz",
+ "integrity": "sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ==",
+ "dev": true
+ },
+ "@babel/helper-replace-supers": {
+ "version": "7.14.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.14.5.tgz",
+ "integrity": "sha512-3i1Qe9/8x/hCHINujn+iuHy+mMRLoc77b2nI9TB0zjH1hvn9qGlXjWlggdwUcju36PkPCy/lpM7LLUdcTyH4Ow==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-member-expression-to-functions": "^7.14.5",
+ "@babel/helper-optimise-call-expression": "^7.14.5",
+ "@babel/traverse": "^7.14.5",
+ "@babel/types": "^7.14.5"
+ }
+ },
+ "@babel/helper-simple-access": {
+ "version": "7.14.8",
+ "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.14.8.tgz",
+ "integrity": "sha512-TrFN4RHh9gnWEU+s7JloIho2T76GPwRHhdzOWLqTrMnlas8T9O7ec+oEDNsRXndOmru9ymH9DFrEOxpzPoSbdg==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.14.8"
+ }
+ },
+ "@babel/helper-split-export-declaration": {
+ "version": "7.14.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.14.5.tgz",
+ "integrity": "sha512-hprxVPu6e5Kdp2puZUmvOGjaLv9TCe58E/Fl6hRq4YiVQxIcNvuq6uTM2r1mT/oPskuS9CgR+I94sqAYv0NGKA==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.14.5"
+ }
+ },
+ "@babel/helper-validator-identifier": {
+ "version": "7.14.8",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.8.tgz",
+ "integrity": "sha512-ZGy6/XQjllhYQrNw/3zfWRwZCTVSiBLZ9DHVZxn9n2gip/7ab8mv2TWlKPIBk26RwedCBoWdjLmn+t9na2Gcow==",
+ "dev": true
+ },
+ "@babel/helper-validator-option": {
+ "version": "7.14.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz",
+ "integrity": "sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow==",
+ "dev": true
+ },
+ "@babel/helpers": {
+ "version": "7.14.8",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.14.8.tgz",
+ "integrity": "sha512-ZRDmI56pnV+p1dH6d+UN6GINGz7Krps3+270qqI9UJ4wxYThfAIcI5i7j5vXC4FJ3Wap+S9qcebxeYiqn87DZw==",
+ "dev": true,
+ "requires": {
+ "@babel/template": "^7.14.5",
+ "@babel/traverse": "^7.14.8",
+ "@babel/types": "^7.14.8"
+ }
+ },
+ "@babel/highlight": {
+ "version": "7.14.5",
+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz",
+ "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.14.5",
+ "chalk": "^2.0.0",
+ "js-tokens": "^4.0.0"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^1.9.0"
+ }
+ },
+ "chalk": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ }
+ },
+ "color-convert": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+ "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+ "dev": true,
+ "requires": {
+ "color-name": "1.1.3"
+ }
+ },
+ "color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+ "dev": true
+ },
+ "has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^3.0.0"
+ }
+ }
+ }
+ },
+ "@babel/parser": {
+ "version": "7.14.8",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.8.tgz",
+ "integrity": "sha512-syoCQFOoo/fzkWDeM0dLEZi5xqurb5vuyzwIMNZRNun+N/9A4cUZeQaE7dTrB8jGaKuJRBtEOajtnmw0I5hvvA==",
+ "dev": true
+ },
+ "@babel/plugin-syntax-async-generators": {
+ "version": "7.8.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz",
+ "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ }
+ },
+ "@babel/plugin-syntax-bigint": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz",
+ "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ }
+ },
+ "@babel/plugin-syntax-class-properties": {
+ "version": "7.12.13",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz",
+ "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.12.13"
+ }
+ },
+ "@babel/plugin-syntax-import-meta": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz",
+ "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.10.4"
+ }
+ },
+ "@babel/plugin-syntax-json-strings": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz",
+ "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ }
+ },
+ "@babel/plugin-syntax-logical-assignment-operators": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz",
+ "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.10.4"
+ }
+ },
+ "@babel/plugin-syntax-nullish-coalescing-operator": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz",
+ "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ }
+ },
+ "@babel/plugin-syntax-numeric-separator": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz",
+ "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.10.4"
+ }
+ },
+ "@babel/plugin-syntax-object-rest-spread": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz",
+ "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ }
+ },
+ "@babel/plugin-syntax-optional-catch-binding": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz",
+ "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ }
+ },
+ "@babel/plugin-syntax-optional-chaining": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz",
+ "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ }
+ },
+ "@babel/plugin-syntax-top-level-await": {
+ "version": "7.14.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz",
+ "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.14.5"
+ }
+ },
+ "@babel/plugin-syntax-typescript": {
+ "version": "7.14.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.14.5.tgz",
+ "integrity": "sha512-u6OXzDaIXjEstBRRoBCQ/uKQKlbuaeE5in0RvWdA4pN6AhqxTIwUsnHPU1CFZA/amYObMsuWhYfRl3Ch90HD0Q==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.14.5"
+ }
+ },
+ "@babel/template": {
+ "version": "7.14.5",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.14.5.tgz",
+ "integrity": "sha512-6Z3Po85sfxRGachLULUhOmvAaOo7xCvqGQtxINai2mEGPFm6pQ4z5QInFnUrRpfoSV60BnjyF5F3c+15fxFV1g==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.14.5",
+ "@babel/parser": "^7.14.5",
+ "@babel/types": "^7.14.5"
+ }
+ },
+ "@babel/traverse": {
+ "version": "7.14.8",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.14.8.tgz",
+ "integrity": "sha512-kexHhzCljJcFNn1KYAQ6A5wxMRzq9ebYpEDV4+WdNyr3i7O44tanbDOR/xjiG2F3sllan+LgwK+7OMk0EmydHg==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.14.5",
+ "@babel/generator": "^7.14.8",
+ "@babel/helper-function-name": "^7.14.5",
+ "@babel/helper-hoist-variables": "^7.14.5",
+ "@babel/helper-split-export-declaration": "^7.14.5",
+ "@babel/parser": "^7.14.8",
+ "@babel/types": "^7.14.8",
+ "debug": "^4.1.0",
+ "globals": "^11.1.0"
+ }
+ },
+ "@babel/types": {
+ "version": "7.14.8",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.14.8.tgz",
+ "integrity": "sha512-iob4soQa7dZw8nodR/KlOQkPh9S4I8RwCxwRIFuiMRYjOzH/KJzdUfDgz6cGi5dDaclXF4P2PAhCdrBJNIg68Q==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.14.8",
+ "to-fast-properties": "^2.0.0"
+ }
+ },
+ "@bcoe/v8-coverage": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz",
+ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==",
+ "dev": true
+ },
+ "@istanbuljs/load-nyc-config": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz",
+ "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==",
+ "dev": true,
+ "requires": {
+ "camelcase": "^5.3.1",
+ "find-up": "^4.1.0",
+ "get-package-type": "^0.1.0",
+ "js-yaml": "^3.13.1",
+ "resolve-from": "^5.0.0"
+ }
+ },
+ "@istanbuljs/schema": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz",
+ "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==",
+ "dev": true
+ },
+ "@jest/console": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/@jest/console/-/console-27.0.6.tgz",
+ "integrity": "sha512-fMlIBocSHPZ3JxgWiDNW/KPj6s+YRd0hicb33IrmelCcjXo/pXPwvuiKFmZz+XuqI/1u7nbUK10zSsWL/1aegg==",
+ "dev": true,
+ "requires": {
+ "@jest/types": "^27.0.6",
+ "@types/node": "*",
+ "chalk": "^4.0.0",
+ "jest-message-util": "^27.0.6",
+ "jest-util": "^27.0.6",
+ "slash": "^3.0.0"
+ }
+ },
+ "@jest/core": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/@jest/core/-/core-27.0.6.tgz",
+ "integrity": "sha512-SsYBm3yhqOn5ZLJCtccaBcvD/ccTLCeuDv8U41WJH/V1MW5eKUkeMHT9U+Pw/v1m1AIWlnIW/eM2XzQr0rEmow==",
+ "dev": true,
+ "requires": {
+ "@jest/console": "^27.0.6",
+ "@jest/reporters": "^27.0.6",
+ "@jest/test-result": "^27.0.6",
+ "@jest/transform": "^27.0.6",
+ "@jest/types": "^27.0.6",
+ "@types/node": "*",
+ "ansi-escapes": "^4.2.1",
+ "chalk": "^4.0.0",
+ "emittery": "^0.8.1",
+ "exit": "^0.1.2",
+ "graceful-fs": "^4.2.4",
+ "jest-changed-files": "^27.0.6",
+ "jest-config": "^27.0.6",
+ "jest-haste-map": "^27.0.6",
+ "jest-message-util": "^27.0.6",
+ "jest-regex-util": "^27.0.6",
+ "jest-resolve": "^27.0.6",
+ "jest-resolve-dependencies": "^27.0.6",
+ "jest-runner": "^27.0.6",
+ "jest-runtime": "^27.0.6",
+ "jest-snapshot": "^27.0.6",
+ "jest-util": "^27.0.6",
+ "jest-validate": "^27.0.6",
+ "jest-watcher": "^27.0.6",
+ "micromatch": "^4.0.4",
+ "p-each-series": "^2.1.0",
+ "rimraf": "^3.0.0",
+ "slash": "^3.0.0",
+ "strip-ansi": "^6.0.0"
+ }
+ },
+ "@jest/environment": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-27.0.6.tgz",
+ "integrity": "sha512-4XywtdhwZwCpPJ/qfAkqExRsERW+UaoSRStSHCCiQTUpoYdLukj+YJbQSFrZjhlUDRZeNiU9SFH0u7iNimdiIg==",
+ "dev": true,
+ "requires": {
+ "@jest/fake-timers": "^27.0.6",
+ "@jest/types": "^27.0.6",
+ "@types/node": "*",
+ "jest-mock": "^27.0.6"
+ }
+ },
+ "@jest/fake-timers": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.0.6.tgz",
+ "integrity": "sha512-sqd+xTWtZ94l3yWDKnRTdvTeZ+A/V7SSKrxsrOKSqdyddb9CeNRF8fbhAU0D7ZJBpTTW2nbp6MftmKJDZfW2LQ==",
+ "dev": true,
+ "requires": {
+ "@jest/types": "^27.0.6",
+ "@sinonjs/fake-timers": "^7.0.2",
+ "@types/node": "*",
+ "jest-message-util": "^27.0.6",
+ "jest-mock": "^27.0.6",
+ "jest-util": "^27.0.6"
+ }
+ },
+ "@jest/globals": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-27.0.6.tgz",
+ "integrity": "sha512-DdTGCP606rh9bjkdQ7VvChV18iS7q0IMJVP1piwTWyWskol4iqcVwthZmoJEf7obE1nc34OpIyoVGPeqLC+ryw==",
+ "dev": true,
+ "requires": {
+ "@jest/environment": "^27.0.6",
+ "@jest/types": "^27.0.6",
+ "expect": "^27.0.6"
+ }
+ },
+ "@jest/reporters": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-27.0.6.tgz",
+ "integrity": "sha512-TIkBt09Cb2gptji3yJXb3EE+eVltW6BjO7frO7NEfjI9vSIYoISi5R3aI3KpEDXlB1xwB+97NXIqz84qYeYsfA==",
+ "dev": true,
+ "requires": {
+ "@bcoe/v8-coverage": "^0.2.3",
+ "@jest/console": "^27.0.6",
+ "@jest/test-result": "^27.0.6",
+ "@jest/transform": "^27.0.6",
+ "@jest/types": "^27.0.6",
+ "chalk": "^4.0.0",
+ "collect-v8-coverage": "^1.0.0",
+ "exit": "^0.1.2",
+ "glob": "^7.1.2",
+ "graceful-fs": "^4.2.4",
+ "istanbul-lib-coverage": "^3.0.0",
+ "istanbul-lib-instrument": "^4.0.3",
+ "istanbul-lib-report": "^3.0.0",
+ "istanbul-lib-source-maps": "^4.0.0",
+ "istanbul-reports": "^3.0.2",
+ "jest-haste-map": "^27.0.6",
+ "jest-resolve": "^27.0.6",
+ "jest-util": "^27.0.6",
+ "jest-worker": "^27.0.6",
+ "slash": "^3.0.0",
+ "source-map": "^0.6.0",
+ "string-length": "^4.0.1",
+ "terminal-link": "^2.0.0",
+ "v8-to-istanbul": "^8.0.0"
+ }
+ },
+ "@jest/source-map": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-27.0.6.tgz",
+ "integrity": "sha512-Fek4mi5KQrqmlY07T23JRi0e7Z9bXTOOD86V/uS0EIW4PClvPDqZOyFlLpNJheS6QI0FNX1CgmPjtJ4EA/2M+g==",
+ "dev": true,
+ "requires": {
+ "callsites": "^3.0.0",
+ "graceful-fs": "^4.2.4",
+ "source-map": "^0.6.0"
+ }
+ },
+ "@jest/test-result": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-27.0.6.tgz",
+ "integrity": "sha512-ja/pBOMTufjX4JLEauLxE3LQBPaI2YjGFtXexRAjt1I/MbfNlMx0sytSX3tn5hSLzQsR3Qy2rd0hc1BWojtj9w==",
+ "dev": true,
+ "requires": {
+ "@jest/console": "^27.0.6",
+ "@jest/types": "^27.0.6",
+ "@types/istanbul-lib-coverage": "^2.0.0",
+ "collect-v8-coverage": "^1.0.0"
+ }
+ },
+ "@jest/test-sequencer": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-27.0.6.tgz",
+ "integrity": "sha512-bISzNIApazYOlTHDum9PwW22NOyDa6VI31n6JucpjTVM0jD6JDgqEZ9+yn575nDdPF0+4csYDxNNW13NvFQGZA==",
+ "dev": true,
+ "requires": {
+ "@jest/test-result": "^27.0.6",
+ "graceful-fs": "^4.2.4",
+ "jest-haste-map": "^27.0.6",
+ "jest-runtime": "^27.0.6"
+ }
+ },
+ "@jest/transform": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-27.0.6.tgz",
+ "integrity": "sha512-rj5Dw+mtIcntAUnMlW/Vju5mr73u8yg+irnHwzgtgoeI6cCPOvUwQ0D1uQtc/APmWgvRweEb1g05pkUpxH3iCA==",
+ "dev": true,
+ "requires": {
+ "@babel/core": "^7.1.0",
+ "@jest/types": "^27.0.6",
+ "babel-plugin-istanbul": "^6.0.0",
+ "chalk": "^4.0.0",
+ "convert-source-map": "^1.4.0",
+ "fast-json-stable-stringify": "^2.0.0",
+ "graceful-fs": "^4.2.4",
+ "jest-haste-map": "^27.0.6",
+ "jest-regex-util": "^27.0.6",
+ "jest-util": "^27.0.6",
+ "micromatch": "^4.0.4",
+ "pirates": "^4.0.1",
+ "slash": "^3.0.0",
+ "source-map": "^0.6.1",
+ "write-file-atomic": "^3.0.0"
+ }
+ },
+ "@jest/types": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.0.6.tgz",
+ "integrity": "sha512-aSquT1qa9Pik26JK5/3rvnYb4bGtm1VFNesHKmNTwmPIgOrixvhL2ghIvFRNEpzy3gU+rUgjIF/KodbkFAl++g==",
+ "dev": true,
+ "requires": {
+ "@types/istanbul-lib-coverage": "^2.0.0",
+ "@types/istanbul-reports": "^3.0.0",
+ "@types/node": "*",
+ "@types/yargs": "^16.0.0",
+ "chalk": "^4.0.0"
+ }
+ },
+ "@sinonjs/commons": {
+ "version": "1.8.3",
+ "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz",
+ "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==",
+ "dev": true,
+ "requires": {
+ "type-detect": "4.0.8"
+ }
+ },
+ "@sinonjs/fake-timers": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-7.1.2.tgz",
+ "integrity": "sha512-iQADsW4LBMISqZ6Ci1dupJL9pprqwcVFTcOsEmQOEhW+KLCVn/Y4Jrvg2k19fIHCp+iFprriYPTdRcQR8NbUPg==",
+ "dev": true,
+ "requires": {
+ "@sinonjs/commons": "^1.7.0"
+ }
+ },
+ "@tootallnate/once": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
+ "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==",
+ "dev": true
+ },
+ "@types/babel__core": {
+ "version": "7.1.15",
+ "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.15.tgz",
+ "integrity": "sha512-bxlMKPDbY8x5h6HBwVzEOk2C8fb6SLfYQ5Jw3uBYuYF1lfWk/kbLd81la82vrIkBb0l+JdmrZaDikPrNxpS/Ew==",
+ "dev": true,
+ "requires": {
+ "@babel/parser": "^7.1.0",
+ "@babel/types": "^7.0.0",
+ "@types/babel__generator": "*",
+ "@types/babel__template": "*",
+ "@types/babel__traverse": "*"
+ }
+ },
+ "@types/babel__generator": {
+ "version": "7.6.3",
+ "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.3.tgz",
+ "integrity": "sha512-/GWCmzJWqV7diQW54smJZzWbSFf4QYtF71WCKhcx6Ru/tFyQIY2eiiITcCAeuPbNSvT9YCGkVMqqvSk2Z0mXiA==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "@types/babel__template": {
+ "version": "7.4.1",
+ "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz",
+ "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==",
+ "dev": true,
+ "requires": {
+ "@babel/parser": "^7.1.0",
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "@types/babel__traverse": {
+ "version": "7.14.2",
+ "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.14.2.tgz",
+ "integrity": "sha512-K2waXdXBi2302XUdcHcR1jCeU0LL4TD9HRs/gk0N2Xvrht+G/BfJa4QObBQZfhMdxiCpV3COl5Nfq4uKTeTnJA==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.3.0"
+ }
+ },
+ "@types/graceful-fs": {
+ "version": "4.1.5",
+ "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz",
+ "integrity": "sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==",
+ "dev": true,
+ "requires": {
+ "@types/node": "*"
+ }
+ },
+ "@types/istanbul-lib-coverage": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz",
+ "integrity": "sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw==",
+ "dev": true
+ },
+ "@types/istanbul-lib-report": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz",
+ "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==",
+ "dev": true,
+ "requires": {
+ "@types/istanbul-lib-coverage": "*"
+ }
+ },
+ "@types/istanbul-reports": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz",
+ "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==",
+ "dev": true,
+ "requires": {
+ "@types/istanbul-lib-report": "*"
+ }
+ },
+ "@types/node": {
+ "version": "16.4.5",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-16.4.5.tgz",
+ "integrity": "sha512-+0GPv/hIFNoy8r5MFf7vRpBjnqNYNrlHdetoy23E7TYc7JB2ctwyi3GMKpviozaHQ/Sy2kBNUTvG9ywN66zV1g==",
+ "dev": true
+ },
+ "@types/prettier": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.3.2.tgz",
+ "integrity": "sha512-eI5Yrz3Qv4KPUa/nSIAi0h+qX0XyewOliug5F2QAtuRg6Kjg6jfmxe1GIwoIRhZspD1A0RP8ANrPwvEXXtRFog==",
+ "dev": true
+ },
+ "@types/stack-utils": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz",
+ "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==",
+ "dev": true
+ },
+ "@types/yargs": {
+ "version": "16.0.4",
+ "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz",
+ "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==",
+ "dev": true,
+ "requires": {
+ "@types/yargs-parser": "*"
+ }
+ },
+ "@types/yargs-parser": {
+ "version": "20.2.1",
+ "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.1.tgz",
+ "integrity": "sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw==",
+ "dev": true
+ },
+ "abab": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz",
+ "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==",
+ "dev": true
+ },
+ "acorn": {
+ "version": "8.4.1",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.4.1.tgz",
+ "integrity": "sha512-asabaBSkEKosYKMITunzX177CXxQ4Q8BSSzMTKD+FefUhipQC70gfW5SiUDhYQ3vk8G+81HqQk7Fv9OXwwn9KA==",
+ "dev": true
+ },
+ "acorn-globals": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz",
+ "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==",
+ "dev": true,
+ "requires": {
+ "acorn": "^7.1.1",
+ "acorn-walk": "^7.1.1"
+ },
+ "dependencies": {
+ "acorn": {
+ "version": "7.4.1",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
+ "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==",
+ "dev": true
+ }
+ }
+ },
+ "acorn-walk": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz",
+ "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==",
+ "dev": true
+ },
+ "agent-base": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
+ "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
+ "dev": true,
+ "requires": {
+ "debug": "4"
+ }
+ },
+ "ansi-escapes": {
+ "version": "4.3.2",
+ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
+ "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==",
+ "dev": true,
+ "requires": {
+ "type-fest": "^0.21.3"
+ }
+ },
+ "ansi-regex": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
+ "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
+ "dev": true
+ },
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "anymatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
+ "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
+ "dev": true,
+ "requires": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ }
+ },
+ "argparse": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+ "dev": true,
+ "requires": {
+ "sprintf-js": "~1.0.2"
+ }
+ },
+ "asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
+ "dev": true
+ },
+ "babel-jest": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-27.0.6.tgz",
+ "integrity": "sha512-iTJyYLNc4wRofASmofpOc5NK9QunwMk+TLFgGXsTFS8uEqmd8wdI7sga0FPe2oVH3b5Agt/EAK1QjPEuKL8VfA==",
+ "dev": true,
+ "requires": {
+ "@jest/transform": "^27.0.6",
+ "@jest/types": "^27.0.6",
+ "@types/babel__core": "^7.1.14",
+ "babel-plugin-istanbul": "^6.0.0",
+ "babel-preset-jest": "^27.0.6",
+ "chalk": "^4.0.0",
+ "graceful-fs": "^4.2.4",
+ "slash": "^3.0.0"
+ }
+ },
+ "babel-plugin-istanbul": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz",
+ "integrity": "sha512-AF55rZXpe7trmEylbaE1Gv54wn6rwU03aptvRoVIGP8YykoSxqdVLV1TfwflBCE/QtHmqtP8SWlTENqbK8GCSQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@istanbuljs/load-nyc-config": "^1.0.0",
+ "@istanbuljs/schema": "^0.1.2",
+ "istanbul-lib-instrument": "^4.0.0",
+ "test-exclude": "^6.0.0"
+ }
+ },
+ "babel-plugin-jest-hoist": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.0.6.tgz",
+ "integrity": "sha512-CewFeM9Vv2gM7Yr9n5eyyLVPRSiBnk6lKZRjgwYnGKSl9M14TMn2vkN02wTF04OGuSDLEzlWiMzvjXuW9mB6Gw==",
+ "dev": true,
+ "requires": {
+ "@babel/template": "^7.3.3",
+ "@babel/types": "^7.3.3",
+ "@types/babel__core": "^7.0.0",
+ "@types/babel__traverse": "^7.0.6"
+ }
+ },
+ "babel-preset-current-node-syntax": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz",
+ "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==",
+ "dev": true,
+ "requires": {
+ "@babel/plugin-syntax-async-generators": "^7.8.4",
+ "@babel/plugin-syntax-bigint": "^7.8.3",
+ "@babel/plugin-syntax-class-properties": "^7.8.3",
+ "@babel/plugin-syntax-import-meta": "^7.8.3",
+ "@babel/plugin-syntax-json-strings": "^7.8.3",
+ "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3",
+ "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3",
+ "@babel/plugin-syntax-numeric-separator": "^7.8.3",
+ "@babel/plugin-syntax-object-rest-spread": "^7.8.3",
+ "@babel/plugin-syntax-optional-catch-binding": "^7.8.3",
+ "@babel/plugin-syntax-optional-chaining": "^7.8.3",
+ "@babel/plugin-syntax-top-level-await": "^7.8.3"
+ }
+ },
+ "babel-preset-jest": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-27.0.6.tgz",
+ "integrity": "sha512-WObA0/Biw2LrVVwZkF/2GqbOdzhKD6Fkdwhoy9ASIrOWr/zodcSpQh72JOkEn6NWyjmnPDjNSqaGN4KnpKzhXw==",
+ "dev": true,
+ "requires": {
+ "babel-plugin-jest-hoist": "^27.0.6",
+ "babel-preset-current-node-syntax": "^1.0.0"
+ }
+ },
+ "balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true
+ },
+ "brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "braces": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+ "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+ "dev": true,
+ "requires": {
+ "fill-range": "^7.0.1"
+ }
+ },
+ "browser-process-hrtime": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz",
+ "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==",
+ "dev": true
+ },
+ "browserslist": {
+ "version": "4.16.6",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz",
+ "integrity": "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==",
+ "dev": true,
+ "requires": {
+ "caniuse-lite": "^1.0.30001219",
+ "colorette": "^1.2.2",
+ "electron-to-chromium": "^1.3.723",
+ "escalade": "^3.1.1",
+ "node-releases": "^1.1.71"
+ }
+ },
+ "bser": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz",
+ "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==",
+ "dev": true,
+ "requires": {
+ "node-int64": "^0.4.0"
+ }
+ },
+ "buffer-from": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
+ "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
+ "dev": true
+ },
+ "callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true
+ },
+ "camelcase": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+ "dev": true
+ },
+ "caniuse-lite": {
+ "version": "1.0.30001248",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001248.tgz",
+ "integrity": "sha512-NwlQbJkxUFJ8nMErnGtT0QTM2TJ33xgz4KXJSMIrjXIbDVdaYueGyjOrLKRtJC+rTiWfi6j5cnZN1NBiSBJGNw==",
+ "dev": true
+ },
+ "chalk": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz",
+ "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ }
+ },
+ "char-regex": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz",
+ "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==",
+ "dev": true
+ },
+ "ci-info": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.2.0.tgz",
+ "integrity": "sha512-dVqRX7fLUm8J6FgHJ418XuIgDLZDkYcDFTeL6TA2gt5WlIZUQrrH6EZrNClwT/H0FateUsZkGIOPRrLbP+PR9A==",
+ "dev": true
+ },
+ "cjs-module-lexer": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz",
+ "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==",
+ "dev": true
+ },
+ "cliui": {
+ "version": "7.0.4",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
+ "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
+ "dev": true,
+ "requires": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.0",
+ "wrap-ansi": "^7.0.0"
+ }
+ },
+ "co": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
+ "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=",
+ "dev": true
+ },
+ "collect-v8-coverage": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz",
+ "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==",
+ "dev": true
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "colorette": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz",
+ "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==",
+ "dev": true
+ },
+ "combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "dev": true,
+ "requires": {
+ "delayed-stream": "~1.0.0"
+ }
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
+ "dev": true
+ },
+ "convert-source-map": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz",
+ "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "~5.1.1"
+ }
+ },
+ "cross-env": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz",
+ "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==",
+ "dev": true,
+ "requires": {
+ "cross-spawn": "^7.0.1"
+ }
+ },
+ "cross-spawn": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
+ "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+ "dev": true,
+ "requires": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ }
+ },
+ "cssom": {
+ "version": "0.4.4",
+ "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz",
+ "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==",
+ "dev": true
+ },
+ "cssstyle": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz",
+ "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==",
+ "dev": true,
+ "requires": {
+ "cssom": "~0.3.6"
+ },
+ "dependencies": {
+ "cssom": {
+ "version": "0.3.8",
+ "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz",
+ "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==",
+ "dev": true
+ }
+ }
+ },
+ "data-urls": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz",
+ "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==",
+ "dev": true,
+ "requires": {
+ "abab": "^2.0.3",
+ "whatwg-mimetype": "^2.3.0",
+ "whatwg-url": "^8.0.0"
+ }
+ },
+ "debug": {
+ "version": "4.3.2",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz",
+ "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==",
+ "dev": true,
+ "requires": {
+ "ms": "2.1.2"
+ }
+ },
+ "decimal.js": {
+ "version": "10.3.1",
+ "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.3.1.tgz",
+ "integrity": "sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==",
+ "dev": true
+ },
+ "dedent": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz",
+ "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=",
+ "dev": true
+ },
+ "deep-is": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
+ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
+ "dev": true
+ },
+ "deepmerge": {
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz",
+ "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==",
+ "dev": true
+ },
+ "delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
+ "dev": true
+ },
+ "detect-newline": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz",
+ "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==",
+ "dev": true
+ },
+ "diff-sequences": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.0.6.tgz",
+ "integrity": "sha512-ag6wfpBFyNXZ0p8pcuIDS//D8H062ZQJ3fzYxjpmeKjnz8W4pekL3AI8VohmyZmsWW2PWaHgjsmqR6L13101VQ==",
+ "dev": true
+ },
+ "domexception": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz",
+ "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==",
+ "dev": true,
+ "requires": {
+ "webidl-conversions": "^5.0.0"
+ },
+ "dependencies": {
+ "webidl-conversions": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz",
+ "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==",
+ "dev": true
+ }
+ }
+ },
+ "electron-to-chromium": {
+ "version": "1.3.789",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.789.tgz",
+ "integrity": "sha512-lK4xn6C6ZF1kgLaC/EhOtC1MSKENExj3rMwGVnBTfHW81Z/Hb1Rge5YaWawN/YOXy3xCaESuE4KWSD50kOZ9rQ==",
+ "dev": true
+ },
+ "emittery": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz",
+ "integrity": "sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg==",
+ "dev": true
+ },
+ "emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true
+ },
+ "escalade": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
+ "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
+ "dev": true
+ },
+ "escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
+ "dev": true
+ },
+ "escodegen": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz",
+ "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==",
+ "dev": true,
+ "requires": {
+ "esprima": "^4.0.1",
+ "estraverse": "^5.2.0",
+ "esutils": "^2.0.2",
+ "optionator": "^0.8.1",
+ "source-map": "~0.6.1"
+ }
+ },
+ "esprima": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+ "dev": true
+ },
+ "estraverse": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz",
+ "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==",
+ "dev": true
+ },
+ "esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true
+ },
+ "execa": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
+ "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
+ "dev": true,
+ "requires": {
+ "cross-spawn": "^7.0.3",
+ "get-stream": "^6.0.0",
+ "human-signals": "^2.1.0",
+ "is-stream": "^2.0.0",
+ "merge-stream": "^2.0.0",
+ "npm-run-path": "^4.0.1",
+ "onetime": "^5.1.2",
+ "signal-exit": "^3.0.3",
+ "strip-final-newline": "^2.0.0"
+ }
+ },
+ "exit": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
+ "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=",
+ "dev": true
+ },
+ "expect": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/expect/-/expect-27.0.6.tgz",
+ "integrity": "sha512-psNLt8j2kwg42jGBDSfAlU49CEZxejN1f1PlANWDZqIhBOVU/c2Pm888FcjWJzFewhIsNWfZJeLjUjtKGiPuSw==",
+ "dev": true,
+ "requires": {
+ "@jest/types": "^27.0.6",
+ "ansi-styles": "^5.0.0",
+ "jest-get-type": "^27.0.6",
+ "jest-matcher-utils": "^27.0.6",
+ "jest-message-util": "^27.0.6",
+ "jest-regex-util": "^27.0.6"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
+ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
+ "dev": true
+ }
+ }
+ },
+ "fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true
+ },
+ "fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
+ "dev": true
+ },
+ "fb-watchman": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz",
+ "integrity": "sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==",
+ "dev": true,
+ "requires": {
+ "bser": "2.1.1"
+ }
+ },
+ "fill-range": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+ "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+ "dev": true,
+ "requires": {
+ "to-regex-range": "^5.0.1"
+ }
+ },
+ "find-up": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
+ "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+ "dev": true,
+ "requires": {
+ "locate-path": "^5.0.0",
+ "path-exists": "^4.0.0"
+ }
+ },
+ "form-data": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz",
+ "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==",
+ "dev": true,
+ "requires": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "mime-types": "^2.1.12"
+ }
+ },
+ "fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+ "dev": true
+ },
+ "fsevents": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+ "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+ "dev": true,
+ "optional": true
+ },
+ "function-bind": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
+ "dev": true
+ },
+ "gensync": {
+ "version": "1.0.0-beta.2",
+ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
+ "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
+ "dev": true
+ },
+ "get-caller-file": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+ "dev": true
+ },
+ "get-package-type": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz",
+ "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==",
+ "dev": true
+ },
+ "get-stream": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
+ "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
+ "dev": true
+ },
+ "glob": {
+ "version": "7.1.7",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz",
+ "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "globals": {
+ "version": "11.12.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
+ "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
+ "dev": true
+ },
+ "graceful-fs": {
+ "version": "4.2.6",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz",
+ "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==",
+ "dev": true
+ },
+ "has": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+ "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+ "dev": true,
+ "requires": {
+ "function-bind": "^1.1.1"
+ }
+ },
+ "has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true
+ },
+ "html-encoding-sniffer": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz",
+ "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==",
+ "dev": true,
+ "requires": {
+ "whatwg-encoding": "^1.0.5"
+ }
+ },
+ "html-escaper": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
+ "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
+ "dev": true
+ },
+ "http-proxy-agent": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz",
+ "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==",
+ "dev": true,
+ "requires": {
+ "@tootallnate/once": "1",
+ "agent-base": "6",
+ "debug": "4"
+ }
+ },
+ "https-proxy-agent": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz",
+ "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==",
+ "dev": true,
+ "requires": {
+ "agent-base": "6",
+ "debug": "4"
+ }
+ },
+ "human-signals": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
+ "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",
+ "dev": true
+ },
+ "iconv-lite": {
+ "version": "0.4.24",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+ "dev": true,
+ "requires": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ }
+ },
+ "import-local": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.2.tgz",
+ "integrity": "sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA==",
+ "dev": true,
+ "requires": {
+ "pkg-dir": "^4.2.0",
+ "resolve-cwd": "^3.0.0"
+ }
+ },
+ "imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
+ "dev": true
+ },
+ "inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+ "dev": true,
+ "requires": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "dev": true
+ },
+ "is-ci": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.0.tgz",
+ "integrity": "sha512-kDXyttuLeslKAHYL/K28F2YkM3x5jvFPEw3yXbRptXydjD9rpLEz+C5K5iutY9ZiUu6AP41JdvRQwF4Iqs4ZCQ==",
+ "dev": true,
+ "requires": {
+ "ci-info": "^3.1.1"
+ }
+ },
+ "is-core-module": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.5.0.tgz",
+ "integrity": "sha512-TXCMSDsEHMEEZ6eCA8rwRDbLu55MRGmrctljsBX/2v1d9/GzqHOxW5c5oPSgrUt2vBFXebu9rGqckXGPWOlYpg==",
+ "dev": true,
+ "requires": {
+ "has": "^1.0.3"
+ }
+ },
+ "is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true
+ },
+ "is-generator-fn": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz",
+ "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==",
+ "dev": true
+ },
+ "is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true
+ },
+ "is-potential-custom-element-name": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz",
+ "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==",
+ "dev": true
+ },
+ "is-stream": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
+ "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
+ "dev": true
+ },
+ "is-typedarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
+ "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
+ "dev": true
+ },
+ "isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
+ "dev": true
+ },
+ "istanbul-lib-coverage": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz",
+ "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==",
+ "dev": true
+ },
+ "istanbul-lib-instrument": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz",
+ "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==",
+ "dev": true,
+ "requires": {
+ "@babel/core": "^7.7.5",
+ "@istanbuljs/schema": "^0.1.2",
+ "istanbul-lib-coverage": "^3.0.0",
+ "semver": "^6.3.0"
+ }
+ },
+ "istanbul-lib-report": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz",
+ "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==",
+ "dev": true,
+ "requires": {
+ "istanbul-lib-coverage": "^3.0.0",
+ "make-dir": "^3.0.0",
+ "supports-color": "^7.1.0"
+ }
+ },
+ "istanbul-lib-source-maps": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz",
+ "integrity": "sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg==",
+ "dev": true,
+ "requires": {
+ "debug": "^4.1.1",
+ "istanbul-lib-coverage": "^3.0.0",
+ "source-map": "^0.6.1"
+ }
+ },
+ "istanbul-reports": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz",
+ "integrity": "sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw==",
+ "dev": true,
+ "requires": {
+ "html-escaper": "^2.0.0",
+ "istanbul-lib-report": "^3.0.0"
+ }
+ },
+ "jest": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/jest/-/jest-27.0.6.tgz",
+ "integrity": "sha512-EjV8aETrsD0wHl7CKMibKwQNQc3gIRBXlTikBmmHUeVMKaPFxdcUIBfoDqTSXDoGJIivAYGqCWVlzCSaVjPQsA==",
+ "dev": true,
+ "requires": {
+ "@jest/core": "^27.0.6",
+ "import-local": "^3.0.2",
+ "jest-cli": "^27.0.6"
+ },
+ "dependencies": {
+ "jest-cli": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-27.0.6.tgz",
+ "integrity": "sha512-qUUVlGb9fdKir3RDE+B10ULI+LQrz+MCflEH2UJyoUjoHHCbxDrMxSzjQAPUMsic4SncI62ofYCcAvW6+6rhhg==",
+ "dev": true,
+ "requires": {
+ "@jest/core": "^27.0.6",
+ "@jest/test-result": "^27.0.6",
+ "@jest/types": "^27.0.6",
+ "chalk": "^4.0.0",
+ "exit": "^0.1.2",
+ "graceful-fs": "^4.2.4",
+ "import-local": "^3.0.2",
+ "jest-config": "^27.0.6",
+ "jest-util": "^27.0.6",
+ "jest-validate": "^27.0.6",
+ "prompts": "^2.0.1",
+ "yargs": "^16.0.3"
+ }
+ }
+ }
+ },
+ "jest-changed-files": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-27.0.6.tgz",
+ "integrity": "sha512-BuL/ZDauaq5dumYh5y20sn4IISnf1P9A0TDswTxUi84ORGtVa86ApuBHqICL0vepqAnZiY6a7xeSPWv2/yy4eA==",
+ "dev": true,
+ "requires": {
+ "@jest/types": "^27.0.6",
+ "execa": "^5.0.0",
+ "throat": "^6.0.1"
+ }
+ },
+ "jest-circus": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-27.0.6.tgz",
+ "integrity": "sha512-OJlsz6BBeX9qR+7O9lXefWoc2m9ZqcZ5Ohlzz0pTEAG4xMiZUJoacY8f4YDHxgk0oKYxj277AfOk9w6hZYvi1Q==",
+ "dev": true,
+ "requires": {
+ "@jest/environment": "^27.0.6",
+ "@jest/test-result": "^27.0.6",
+ "@jest/types": "^27.0.6",
+ "@types/node": "*",
+ "chalk": "^4.0.0",
+ "co": "^4.6.0",
+ "dedent": "^0.7.0",
+ "expect": "^27.0.6",
+ "is-generator-fn": "^2.0.0",
+ "jest-each": "^27.0.6",
+ "jest-matcher-utils": "^27.0.6",
+ "jest-message-util": "^27.0.6",
+ "jest-runtime": "^27.0.6",
+ "jest-snapshot": "^27.0.6",
+ "jest-util": "^27.0.6",
+ "pretty-format": "^27.0.6",
+ "slash": "^3.0.0",
+ "stack-utils": "^2.0.3",
+ "throat": "^6.0.1"
+ }
+ },
+ "jest-config": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-27.0.6.tgz",
+ "integrity": "sha512-JZRR3I1Plr2YxPBhgqRspDE2S5zprbga3swYNrvY3HfQGu7p/GjyLOqwrYad97tX3U3mzT53TPHVmozacfP/3w==",
+ "dev": true,
+ "requires": {
+ "@babel/core": "^7.1.0",
+ "@jest/test-sequencer": "^27.0.6",
+ "@jest/types": "^27.0.6",
+ "babel-jest": "^27.0.6",
+ "chalk": "^4.0.0",
+ "deepmerge": "^4.2.2",
+ "glob": "^7.1.1",
+ "graceful-fs": "^4.2.4",
+ "is-ci": "^3.0.0",
+ "jest-circus": "^27.0.6",
+ "jest-environment-jsdom": "^27.0.6",
+ "jest-environment-node": "^27.0.6",
+ "jest-get-type": "^27.0.6",
+ "jest-jasmine2": "^27.0.6",
+ "jest-regex-util": "^27.0.6",
+ "jest-resolve": "^27.0.6",
+ "jest-runner": "^27.0.6",
+ "jest-util": "^27.0.6",
+ "jest-validate": "^27.0.6",
+ "micromatch": "^4.0.4",
+ "pretty-format": "^27.0.6"
+ }
+ },
+ "jest-diff": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.0.6.tgz",
+ "integrity": "sha512-Z1mqgkTCSYaFgwTlP/NUiRzdqgxmmhzHY1Tq17zL94morOHfHu3K4bgSgl+CR4GLhpV8VxkuOYuIWnQ9LnFqmg==",
+ "dev": true,
+ "requires": {
+ "chalk": "^4.0.0",
+ "diff-sequences": "^27.0.6",
+ "jest-get-type": "^27.0.6",
+ "pretty-format": "^27.0.6"
+ }
+ },
+ "jest-docblock": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-27.0.6.tgz",
+ "integrity": "sha512-Fid6dPcjwepTFraz0YxIMCi7dejjJ/KL9FBjPYhBp4Sv1Y9PdhImlKZqYU555BlN4TQKaTc+F2Av1z+anVyGkA==",
+ "dev": true,
+ "requires": {
+ "detect-newline": "^3.0.0"
+ }
+ },
+ "jest-each": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-27.0.6.tgz",
+ "integrity": "sha512-m6yKcV3bkSWrUIjxkE9OC0mhBZZdhovIW5ergBYirqnkLXkyEn3oUUF/QZgyecA1cF1QFyTE8bRRl8Tfg1pfLA==",
+ "dev": true,
+ "requires": {
+ "@jest/types": "^27.0.6",
+ "chalk": "^4.0.0",
+ "jest-get-type": "^27.0.6",
+ "jest-util": "^27.0.6",
+ "pretty-format": "^27.0.6"
+ }
+ },
+ "jest-environment-jsdom": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-27.0.6.tgz",
+ "integrity": "sha512-FvetXg7lnXL9+78H+xUAsra3IeZRTiegA3An01cWeXBspKXUhAwMM9ycIJ4yBaR0L7HkoMPaZsozCLHh4T8fuw==",
+ "dev": true,
+ "requires": {
+ "@jest/environment": "^27.0.6",
+ "@jest/fake-timers": "^27.0.6",
+ "@jest/types": "^27.0.6",
+ "@types/node": "*",
+ "jest-mock": "^27.0.6",
+ "jest-util": "^27.0.6",
+ "jsdom": "^16.6.0"
+ }
+ },
+ "jest-environment-node": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-27.0.6.tgz",
+ "integrity": "sha512-+Vi6yLrPg/qC81jfXx3IBlVnDTI6kmRr08iVa2hFCWmJt4zha0XW7ucQltCAPhSR0FEKEoJ3i+W4E6T0s9is0w==",
+ "dev": true,
+ "requires": {
+ "@jest/environment": "^27.0.6",
+ "@jest/fake-timers": "^27.0.6",
+ "@jest/types": "^27.0.6",
+ "@types/node": "*",
+ "jest-mock": "^27.0.6",
+ "jest-util": "^27.0.6"
+ }
+ },
+ "jest-get-type": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.0.6.tgz",
+ "integrity": "sha512-XTkK5exIeUbbveehcSR8w0bhH+c0yloW/Wpl+9vZrjzztCPWrxhHwkIFpZzCt71oRBsgxmuUfxEqOYoZI2macg==",
+ "dev": true
+ },
+ "jest-haste-map": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-27.0.6.tgz",
+ "integrity": "sha512-4ldjPXX9h8doB2JlRzg9oAZ2p6/GpQUNAeiYXqcpmrKbP0Qev0wdZlxSMOmz8mPOEnt4h6qIzXFLDi8RScX/1w==",
+ "dev": true,
+ "requires": {
+ "@jest/types": "^27.0.6",
+ "@types/graceful-fs": "^4.1.2",
+ "@types/node": "*",
+ "anymatch": "^3.0.3",
+ "fb-watchman": "^2.0.0",
+ "fsevents": "^2.3.2",
+ "graceful-fs": "^4.2.4",
+ "jest-regex-util": "^27.0.6",
+ "jest-serializer": "^27.0.6",
+ "jest-util": "^27.0.6",
+ "jest-worker": "^27.0.6",
+ "micromatch": "^4.0.4",
+ "walker": "^1.0.7"
+ }
+ },
+ "jest-jasmine2": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-27.0.6.tgz",
+ "integrity": "sha512-cjpH2sBy+t6dvCeKBsHpW41mjHzXgsavaFMp+VWRf0eR4EW8xASk1acqmljFtK2DgyIECMv2yCdY41r2l1+4iA==",
+ "dev": true,
+ "requires": {
+ "@babel/traverse": "^7.1.0",
+ "@jest/environment": "^27.0.6",
+ "@jest/source-map": "^27.0.6",
+ "@jest/test-result": "^27.0.6",
+ "@jest/types": "^27.0.6",
+ "@types/node": "*",
+ "chalk": "^4.0.0",
+ "co": "^4.6.0",
+ "expect": "^27.0.6",
+ "is-generator-fn": "^2.0.0",
+ "jest-each": "^27.0.6",
+ "jest-matcher-utils": "^27.0.6",
+ "jest-message-util": "^27.0.6",
+ "jest-runtime": "^27.0.6",
+ "jest-snapshot": "^27.0.6",
+ "jest-util": "^27.0.6",
+ "pretty-format": "^27.0.6",
+ "throat": "^6.0.1"
+ }
+ },
+ "jest-leak-detector": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-27.0.6.tgz",
+ "integrity": "sha512-2/d6n2wlH5zEcdctX4zdbgX8oM61tb67PQt4Xh8JFAIy6LRKUnX528HulkaG6nD5qDl5vRV1NXejCe1XRCH5gQ==",
+ "dev": true,
+ "requires": {
+ "jest-get-type": "^27.0.6",
+ "pretty-format": "^27.0.6"
+ }
+ },
+ "jest-matcher-utils": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.0.6.tgz",
+ "integrity": "sha512-OFgF2VCQx9vdPSYTHWJ9MzFCehs20TsyFi6bIHbk5V1u52zJOnvF0Y/65z3GLZHKRuTgVPY4Z6LVePNahaQ+tA==",
+ "dev": true,
+ "requires": {
+ "chalk": "^4.0.0",
+ "jest-diff": "^27.0.6",
+ "jest-get-type": "^27.0.6",
+ "pretty-format": "^27.0.6"
+ }
+ },
+ "jest-message-util": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.0.6.tgz",
+ "integrity": "sha512-rBxIs2XK7rGy+zGxgi+UJKP6WqQ+KrBbD1YMj517HYN3v2BG66t3Xan3FWqYHKZwjdB700KiAJ+iES9a0M+ixw==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.12.13",
+ "@jest/types": "^27.0.6",
+ "@types/stack-utils": "^2.0.0",
+ "chalk": "^4.0.0",
+ "graceful-fs": "^4.2.4",
+ "micromatch": "^4.0.4",
+ "pretty-format": "^27.0.6",
+ "slash": "^3.0.0",
+ "stack-utils": "^2.0.3"
+ }
+ },
+ "jest-mock": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-27.0.6.tgz",
+ "integrity": "sha512-lzBETUoK8cSxts2NYXSBWT+EJNzmUVtVVwS1sU9GwE1DLCfGsngg+ZVSIe0yd0ZSm+y791esiuo+WSwpXJQ5Bw==",
+ "dev": true,
+ "requires": {
+ "@jest/types": "^27.0.6",
+ "@types/node": "*"
+ }
+ },
+ "jest-pnp-resolver": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz",
+ "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==",
+ "dev": true
+ },
+ "jest-regex-util": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-27.0.6.tgz",
+ "integrity": "sha512-SUhPzBsGa1IKm8hx2F4NfTGGp+r7BXJ4CulsZ1k2kI+mGLG+lxGrs76veN2LF/aUdGosJBzKgXmNCw+BzFqBDQ==",
+ "dev": true
+ },
+ "jest-resolve": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.0.6.tgz",
+ "integrity": "sha512-yKmIgw2LgTh7uAJtzv8UFHGF7Dm7XfvOe/LQ3Txv101fLM8cx2h1QVwtSJ51Q/SCxpIiKfVn6G2jYYMDNHZteA==",
+ "dev": true,
+ "requires": {
+ "@jest/types": "^27.0.6",
+ "chalk": "^4.0.0",
+ "escalade": "^3.1.1",
+ "graceful-fs": "^4.2.4",
+ "jest-pnp-resolver": "^1.2.2",
+ "jest-util": "^27.0.6",
+ "jest-validate": "^27.0.6",
+ "resolve": "^1.20.0",
+ "slash": "^3.0.0"
+ }
+ },
+ "jest-resolve-dependencies": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-27.0.6.tgz",
+ "integrity": "sha512-mg9x9DS3BPAREWKCAoyg3QucCr0n6S8HEEsqRCKSPjPcu9HzRILzhdzY3imsLoZWeosEbJZz6TKasveczzpJZA==",
+ "dev": true,
+ "requires": {
+ "@jest/types": "^27.0.6",
+ "jest-regex-util": "^27.0.6",
+ "jest-snapshot": "^27.0.6"
+ }
+ },
+ "jest-runner": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-27.0.6.tgz",
+ "integrity": "sha512-W3Bz5qAgaSChuivLn+nKOgjqNxM7O/9JOJoKDCqThPIg2sH/d4A/lzyiaFgnb9V1/w29Le11NpzTJSzga1vyYQ==",
+ "dev": true,
+ "requires": {
+ "@jest/console": "^27.0.6",
+ "@jest/environment": "^27.0.6",
+ "@jest/test-result": "^27.0.6",
+ "@jest/transform": "^27.0.6",
+ "@jest/types": "^27.0.6",
+ "@types/node": "*",
+ "chalk": "^4.0.0",
+ "emittery": "^0.8.1",
+ "exit": "^0.1.2",
+ "graceful-fs": "^4.2.4",
+ "jest-docblock": "^27.0.6",
+ "jest-environment-jsdom": "^27.0.6",
+ "jest-environment-node": "^27.0.6",
+ "jest-haste-map": "^27.0.6",
+ "jest-leak-detector": "^27.0.6",
+ "jest-message-util": "^27.0.6",
+ "jest-resolve": "^27.0.6",
+ "jest-runtime": "^27.0.6",
+ "jest-util": "^27.0.6",
+ "jest-worker": "^27.0.6",
+ "source-map-support": "^0.5.6",
+ "throat": "^6.0.1"
+ }
+ },
+ "jest-runtime": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.0.6.tgz",
+ "integrity": "sha512-BhvHLRVfKibYyqqEFkybsznKwhrsu7AWx2F3y9G9L95VSIN3/ZZ9vBpm/XCS2bS+BWz3sSeNGLzI3TVQ0uL85Q==",
+ "dev": true,
+ "requires": {
+ "@jest/console": "^27.0.6",
+ "@jest/environment": "^27.0.6",
+ "@jest/fake-timers": "^27.0.6",
+ "@jest/globals": "^27.0.6",
+ "@jest/source-map": "^27.0.6",
+ "@jest/test-result": "^27.0.6",
+ "@jest/transform": "^27.0.6",
+ "@jest/types": "^27.0.6",
+ "@types/yargs": "^16.0.0",
+ "chalk": "^4.0.0",
+ "cjs-module-lexer": "^1.0.0",
+ "collect-v8-coverage": "^1.0.0",
+ "exit": "^0.1.2",
+ "glob": "^7.1.3",
+ "graceful-fs": "^4.2.4",
+ "jest-haste-map": "^27.0.6",
+ "jest-message-util": "^27.0.6",
+ "jest-mock": "^27.0.6",
+ "jest-regex-util": "^27.0.6",
+ "jest-resolve": "^27.0.6",
+ "jest-snapshot": "^27.0.6",
+ "jest-util": "^27.0.6",
+ "jest-validate": "^27.0.6",
+ "slash": "^3.0.0",
+ "strip-bom": "^4.0.0",
+ "yargs": "^16.0.3"
+ }
+ },
+ "jest-serializer": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-27.0.6.tgz",
+ "integrity": "sha512-PtGdVK9EGC7dsaziskfqaAPib6wTViY3G8E5wz9tLVPhHyiDNTZn/xjZ4khAw+09QkoOVpn7vF5nPSN6dtBexA==",
+ "dev": true,
+ "requires": {
+ "@types/node": "*",
+ "graceful-fs": "^4.2.4"
+ }
+ },
+ "jest-snapshot": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-27.0.6.tgz",
+ "integrity": "sha512-NTHaz8He+ATUagUgE7C/UtFcRoHqR2Gc+KDfhQIyx+VFgwbeEMjeP+ILpUTLosZn/ZtbNdCF5LkVnN/l+V751A==",
+ "dev": true,
+ "requires": {
+ "@babel/core": "^7.7.2",
+ "@babel/generator": "^7.7.2",
+ "@babel/parser": "^7.7.2",
+ "@babel/plugin-syntax-typescript": "^7.7.2",
+ "@babel/traverse": "^7.7.2",
+ "@babel/types": "^7.0.0",
+ "@jest/transform": "^27.0.6",
+ "@jest/types": "^27.0.6",
+ "@types/babel__traverse": "^7.0.4",
+ "@types/prettier": "^2.1.5",
+ "babel-preset-current-node-syntax": "^1.0.0",
+ "chalk": "^4.0.0",
+ "expect": "^27.0.6",
+ "graceful-fs": "^4.2.4",
+ "jest-diff": "^27.0.6",
+ "jest-get-type": "^27.0.6",
+ "jest-haste-map": "^27.0.6",
+ "jest-matcher-utils": "^27.0.6",
+ "jest-message-util": "^27.0.6",
+ "jest-resolve": "^27.0.6",
+ "jest-util": "^27.0.6",
+ "natural-compare": "^1.4.0",
+ "pretty-format": "^27.0.6",
+ "semver": "^7.3.2"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "7.3.5",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
+ "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==",
+ "dev": true,
+ "requires": {
+ "lru-cache": "^6.0.0"
+ }
+ }
+ }
+ },
+ "jest-util": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.0.6.tgz",
+ "integrity": "sha512-1JjlaIh+C65H/F7D11GNkGDDZtDfMEM8EBXsvd+l/cxtgQ6QhxuloOaiayt89DxUvDarbVhqI98HhgrM1yliFQ==",
+ "dev": true,
+ "requires": {
+ "@jest/types": "^27.0.6",
+ "@types/node": "*",
+ "chalk": "^4.0.0",
+ "graceful-fs": "^4.2.4",
+ "is-ci": "^3.0.0",
+ "picomatch": "^2.2.3"
+ }
+ },
+ "jest-validate": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-27.0.6.tgz",
+ "integrity": "sha512-yhZZOaMH3Zg6DC83n60pLmdU1DQE46DW+KLozPiPbSbPhlXXaiUTDlhHQhHFpaqIFRrInko1FHXjTRpjWRuWfA==",
+ "dev": true,
+ "requires": {
+ "@jest/types": "^27.0.6",
+ "camelcase": "^6.2.0",
+ "chalk": "^4.0.0",
+ "jest-get-type": "^27.0.6",
+ "leven": "^3.1.0",
+ "pretty-format": "^27.0.6"
+ },
+ "dependencies": {
+ "camelcase": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz",
+ "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==",
+ "dev": true
+ }
+ }
+ },
+ "jest-watcher": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-27.0.6.tgz",
+ "integrity": "sha512-/jIoKBhAP00/iMGnTwUBLgvxkn7vsOweDrOTSPzc7X9uOyUtJIDthQBTI1EXz90bdkrxorUZVhJwiB69gcHtYQ==",
+ "dev": true,
+ "requires": {
+ "@jest/test-result": "^27.0.6",
+ "@jest/types": "^27.0.6",
+ "@types/node": "*",
+ "ansi-escapes": "^4.2.1",
+ "chalk": "^4.0.0",
+ "jest-util": "^27.0.6",
+ "string-length": "^4.0.1"
+ }
+ },
+ "jest-worker": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.0.6.tgz",
+ "integrity": "sha512-qupxcj/dRuA3xHPMUd40gr2EaAurFbkwzOh7wfPaeE9id7hyjURRQoqNfHifHK3XjJU6YJJUQKILGUnwGPEOCA==",
+ "dev": true,
+ "requires": {
+ "@types/node": "*",
+ "merge-stream": "^2.0.0",
+ "supports-color": "^8.0.0"
+ },
+ "dependencies": {
+ "supports-color": {
+ "version": "8.1.1",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+ "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^4.0.0"
+ }
+ }
+ }
+ },
+ "js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "dev": true
+ },
+ "js-yaml": {
+ "version": "3.14.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
+ "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
+ "dev": true,
+ "requires": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ }
+ },
+ "jsdom": {
+ "version": "16.6.0",
+ "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.6.0.tgz",
+ "integrity": "sha512-Ty1vmF4NHJkolaEmdjtxTfSfkdb8Ywarwf63f+F8/mDD1uLSSWDxDuMiZxiPhwunLrn9LOSVItWj4bLYsLN3Dg==",
+ "dev": true,
+ "requires": {
+ "abab": "^2.0.5",
+ "acorn": "^8.2.4",
+ "acorn-globals": "^6.0.0",
+ "cssom": "^0.4.4",
+ "cssstyle": "^2.3.0",
+ "data-urls": "^2.0.0",
+ "decimal.js": "^10.2.1",
+ "domexception": "^2.0.1",
+ "escodegen": "^2.0.0",
+ "form-data": "^3.0.0",
+ "html-encoding-sniffer": "^2.0.1",
+ "http-proxy-agent": "^4.0.1",
+ "https-proxy-agent": "^5.0.0",
+ "is-potential-custom-element-name": "^1.0.1",
+ "nwsapi": "^2.2.0",
+ "parse5": "6.0.1",
+ "saxes": "^5.0.1",
+ "symbol-tree": "^3.2.4",
+ "tough-cookie": "^4.0.0",
+ "w3c-hr-time": "^1.0.2",
+ "w3c-xmlserializer": "^2.0.0",
+ "webidl-conversions": "^6.1.0",
+ "whatwg-encoding": "^1.0.5",
+ "whatwg-mimetype": "^2.3.0",
+ "whatwg-url": "^8.5.0",
+ "ws": "^7.4.5",
+ "xml-name-validator": "^3.0.0"
+ }
+ },
+ "jsesc": {
+ "version": "2.5.2",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
+ "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
+ "dev": true
+ },
+ "json5": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz",
+ "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==",
+ "dev": true,
+ "requires": {
+ "minimist": "^1.2.5"
+ }
+ },
+ "kleur": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz",
+ "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==",
+ "dev": true
+ },
+ "leven": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",
+ "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==",
+ "dev": true
+ },
+ "levn": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
+ "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=",
+ "dev": true,
+ "requires": {
+ "prelude-ls": "~1.1.2",
+ "type-check": "~0.3.2"
+ }
+ },
+ "locate-path": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
+ "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+ "dev": true,
+ "requires": {
+ "p-locate": "^4.1.0"
+ }
+ },
+ "lodash": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
+ "dev": true
+ },
+ "lru-cache": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+ "dev": true,
+ "requires": {
+ "yallist": "^4.0.0"
+ }
+ },
+ "make-dir": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
+ "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
+ "dev": true,
+ "requires": {
+ "semver": "^6.0.0"
+ }
+ },
+ "makeerror": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz",
+ "integrity": "sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw=",
+ "dev": true,
+ "requires": {
+ "tmpl": "1.0.x"
+ }
+ },
+ "merge-stream": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
+ "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
+ "dev": true
+ },
+ "micromatch": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz",
+ "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==",
+ "dev": true,
+ "requires": {
+ "braces": "^3.0.1",
+ "picomatch": "^2.2.3"
+ }
+ },
+ "mime-db": {
+ "version": "1.49.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.49.0.tgz",
+ "integrity": "sha512-CIc8j9URtOVApSFCQIF+VBkX1RwXp/oMMOrqdyXSBXq5RWNEsRfyj1kiRnQgmNXmHxPoFIxOroKA3zcU9P+nAA==",
+ "dev": true
+ },
+ "mime-types": {
+ "version": "2.1.32",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.32.tgz",
+ "integrity": "sha512-hJGaVS4G4c9TSMYh2n6SQAGrC4RnfU+daP8G7cSCmaqNjiOoUY0VHCMS42pxnQmVF1GWwFhbHWn3RIxCqTmZ9A==",
+ "dev": true,
+ "requires": {
+ "mime-db": "1.49.0"
+ }
+ },
+ "mimic-fn": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
+ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
+ "dev": true
+ },
+ "minimatch": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
+ "minimist": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
+ "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
+ "dev": true
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ },
+ "natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
+ "dev": true
+ },
+ "node-int64": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz",
+ "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=",
+ "dev": true
+ },
+ "node-modules-regexp": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz",
+ "integrity": "sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=",
+ "dev": true
+ },
+ "node-releases": {
+ "version": "1.1.73",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.73.tgz",
+ "integrity": "sha512-uW7fodD6pyW2FZNZnp/Z3hvWKeEW1Y8R1+1CnErE8cXFXzl5blBOoVB41CvMer6P6Q0S5FXDwcHgFd1Wj0U9zg==",
+ "dev": true
+ },
+ "normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "dev": true
+ },
+ "npm-run-path": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
+ "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
+ "dev": true,
+ "requires": {
+ "path-key": "^3.0.0"
+ }
+ },
+ "nwsapi": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz",
+ "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==",
+ "dev": true
+ },
+ "once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "dev": true,
+ "requires": {
+ "wrappy": "1"
+ }
+ },
+ "onetime": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
+ "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
+ "dev": true,
+ "requires": {
+ "mimic-fn": "^2.1.0"
+ }
+ },
+ "optionator": {
+ "version": "0.8.3",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz",
+ "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==",
+ "dev": true,
+ "requires": {
+ "deep-is": "~0.1.3",
+ "fast-levenshtein": "~2.0.6",
+ "levn": "~0.3.0",
+ "prelude-ls": "~1.1.2",
+ "type-check": "~0.3.2",
+ "word-wrap": "~1.2.3"
+ }
+ },
+ "p-each-series": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.2.0.tgz",
+ "integrity": "sha512-ycIL2+1V32th+8scbpTvyHNaHe02z0sjgh91XXjAk+ZeXoPN4Z46DVUnzdso0aX4KckKw0FNNFHdjZ2UsZvxiA==",
+ "dev": true
+ },
+ "p-limit": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+ "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+ "dev": true,
+ "requires": {
+ "p-try": "^2.0.0"
+ }
+ },
+ "p-locate": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
+ "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+ "dev": true,
+ "requires": {
+ "p-limit": "^2.2.0"
+ }
+ },
+ "p-try": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+ "dev": true
+ },
+ "parse5": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
+ "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==",
+ "dev": true
+ },
+ "path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true
+ },
+ "path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+ "dev": true
+ },
+ "path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "dev": true
+ },
+ "path-parse": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+ "dev": true
+ },
+ "picomatch": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz",
+ "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==",
+ "dev": true
+ },
+ "pirates": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz",
+ "integrity": "sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==",
+ "dev": true,
+ "requires": {
+ "node-modules-regexp": "^1.0.0"
+ }
+ },
+ "pkg-dir": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
+ "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
+ "dev": true,
+ "requires": {
+ "find-up": "^4.0.0"
+ }
+ },
+ "prelude-ls": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
+ "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=",
+ "dev": true
+ },
+ "pretty-format": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.0.6.tgz",
+ "integrity": "sha512-8tGD7gBIENgzqA+UBzObyWqQ5B778VIFZA/S66cclyd5YkFLYs2Js7gxDKf0MXtTc9zcS7t1xhdfcElJ3YIvkQ==",
+ "dev": true,
+ "requires": {
+ "@jest/types": "^27.0.6",
+ "ansi-regex": "^5.0.0",
+ "ansi-styles": "^5.0.0",
+ "react-is": "^17.0.1"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
+ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
+ "dev": true
+ }
+ }
+ },
+ "prompts": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.1.tgz",
+ "integrity": "sha512-EQyfIuO2hPDsX1L/blblV+H7I0knhgAd82cVneCwcdND9B8AuCDuRcBH6yIcG4dFzlOUqbazQqwGjx5xmsNLuQ==",
+ "dev": true,
+ "requires": {
+ "kleur": "^3.0.3",
+ "sisteransi": "^1.0.5"
+ }
+ },
+ "psl": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz",
+ "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==",
+ "dev": true
+ },
+ "punycode": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
+ "dev": true
+ },
+ "react-is": {
+ "version": "17.0.2",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
+ "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
+ "dev": true
+ },
+ "require-directory": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+ "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
+ "dev": true
+ },
+ "resolve": {
+ "version": "1.20.0",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz",
+ "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==",
+ "dev": true,
+ "requires": {
+ "is-core-module": "^2.2.0",
+ "path-parse": "^1.0.6"
+ }
+ },
+ "resolve-cwd": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz",
+ "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==",
+ "dev": true,
+ "requires": {
+ "resolve-from": "^5.0.0"
+ }
+ },
+ "resolve-from": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
+ "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
+ "dev": true
+ },
+ "rimraf": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+ "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+ "dev": true,
+ "requires": {
+ "glob": "^7.1.3"
+ }
+ },
+ "safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+ "dev": true
+ },
+ "safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "dev": true
+ },
+ "saxes": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz",
+ "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==",
+ "dev": true,
+ "requires": {
+ "xmlchars": "^2.2.0"
+ }
+ },
+ "semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "dev": true
+ },
+ "shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dev": true,
+ "requires": {
+ "shebang-regex": "^3.0.0"
+ }
+ },
+ "shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true
+ },
+ "signal-exit": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz",
+ "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==",
+ "dev": true
+ },
+ "sisteransi": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
+ "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==",
+ "dev": true
+ },
+ "slash": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+ "dev": true
+ },
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ },
+ "source-map-support": {
+ "version": "0.5.19",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz",
+ "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==",
+ "dev": true,
+ "requires": {
+ "buffer-from": "^1.0.0",
+ "source-map": "^0.6.0"
+ }
+ },
+ "sprintf-js": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
+ "dev": true
+ },
+ "stack-utils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.3.tgz",
+ "integrity": "sha512-gL//fkxfWUsIlFL2Tl42Cl6+HFALEaB1FU76I/Fy+oZjRreP7OPMXFlGbxM7NQsI0ZpUfw76sHnv0WNYuTb7Iw==",
+ "dev": true,
+ "requires": {
+ "escape-string-regexp": "^2.0.0"
+ },
+ "dependencies": {
+ "escape-string-regexp": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz",
+ "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==",
+ "dev": true
+ }
+ }
+ },
+ "string-length": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz",
+ "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==",
+ "dev": true,
+ "requires": {
+ "char-regex": "^1.0.2",
+ "strip-ansi": "^6.0.0"
+ }
+ },
+ "string-width": {
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz",
+ "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==",
+ "dev": true,
+ "requires": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
+ "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^5.0.0"
+ }
+ },
+ "strip-bom": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz",
+ "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==",
+ "dev": true
+ },
+ "strip-final-newline": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
+ "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^4.0.0"
+ }
+ },
+ "supports-hyperlinks": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz",
+ "integrity": "sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^4.0.0",
+ "supports-color": "^7.0.0"
+ }
+ },
+ "symbol-tree": {
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
+ "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==",
+ "dev": true
+ },
+ "terminal-link": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz",
+ "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==",
+ "dev": true,
+ "requires": {
+ "ansi-escapes": "^4.2.1",
+ "supports-hyperlinks": "^2.0.0"
+ }
+ },
+ "test-exclude": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz",
+ "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==",
+ "dev": true,
+ "requires": {
+ "@istanbuljs/schema": "^0.1.2",
+ "glob": "^7.1.4",
+ "minimatch": "^3.0.4"
+ }
+ },
+ "throat": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz",
+ "integrity": "sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w==",
+ "dev": true
+ },
+ "tmpl": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz",
+ "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=",
+ "dev": true
+ },
+ "to-fast-properties": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
+ "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=",
+ "dev": true
+ },
+ "to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
+ "requires": {
+ "is-number": "^7.0.0"
+ }
+ },
+ "tough-cookie": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz",
+ "integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==",
+ "dev": true,
+ "requires": {
+ "psl": "^1.1.33",
+ "punycode": "^2.1.1",
+ "universalify": "^0.1.2"
+ }
+ },
+ "tr46": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz",
+ "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==",
+ "dev": true,
+ "requires": {
+ "punycode": "^2.1.1"
+ }
+ },
+ "type-check": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
+ "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=",
+ "dev": true,
+ "requires": {
+ "prelude-ls": "~1.1.2"
+ }
+ },
+ "type-detect": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
+ "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==",
+ "dev": true
+ },
+ "type-fest": {
+ "version": "0.21.3",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz",
+ "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==",
+ "dev": true
+ },
+ "typedarray-to-buffer": {
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz",
+ "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==",
+ "dev": true,
+ "requires": {
+ "is-typedarray": "^1.0.0"
+ }
+ },
+ "universalify": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
+ "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
+ "dev": true
+ },
+ "v8-to-istanbul": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-8.0.0.tgz",
+ "integrity": "sha512-LkmXi8UUNxnCC+JlH7/fsfsKr5AU110l+SYGJimWNkWhxbN5EyeOtm1MJ0hhvqMMOhGwBj1Fp70Yv9i+hX0QAg==",
+ "dev": true,
+ "requires": {
+ "@types/istanbul-lib-coverage": "^2.0.1",
+ "convert-source-map": "^1.6.0",
+ "source-map": "^0.7.3"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.7.3",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
+ "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
+ "dev": true
+ }
+ }
+ },
+ "w3c-hr-time": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz",
+ "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==",
+ "dev": true,
+ "requires": {
+ "browser-process-hrtime": "^1.0.0"
+ }
+ },
+ "w3c-xmlserializer": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz",
+ "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==",
+ "dev": true,
+ "requires": {
+ "xml-name-validator": "^3.0.0"
+ }
+ },
+ "walker": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz",
+ "integrity": "sha1-L3+bj9ENZ3JisYqITijRlhjgKPs=",
+ "dev": true,
+ "requires": {
+ "makeerror": "1.0.x"
+ }
+ },
+ "webidl-conversions": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz",
+ "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==",
+ "dev": true
+ },
+ "whatwg-encoding": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz",
+ "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==",
+ "dev": true,
+ "requires": {
+ "iconv-lite": "0.4.24"
+ }
+ },
+ "whatwg-mimetype": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz",
+ "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==",
+ "dev": true
+ },
+ "whatwg-url": {
+ "version": "8.7.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz",
+ "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==",
+ "dev": true,
+ "requires": {
+ "lodash": "^4.7.0",
+ "tr46": "^2.1.0",
+ "webidl-conversions": "^6.1.0"
+ }
+ },
+ "which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "requires": {
+ "isexe": "^2.0.0"
+ }
+ },
+ "word-wrap": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
+ "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
+ "dev": true
+ },
+ "wrap-ansi": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ }
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+ "dev": true
+ },
+ "write-file-atomic": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz",
+ "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==",
+ "dev": true,
+ "requires": {
+ "imurmurhash": "^0.1.4",
+ "is-typedarray": "^1.0.0",
+ "signal-exit": "^3.0.2",
+ "typedarray-to-buffer": "^3.1.5"
+ }
+ },
+ "ws": {
+ "version": "7.5.3",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.3.tgz",
+ "integrity": "sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg==",
+ "dev": true
+ },
+ "xml-name-validator": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz",
+ "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==",
+ "dev": true
+ },
+ "xmlchars": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
+ "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",
+ "dev": true
+ },
+ "y18n": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "dev": true
+ },
+ "yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "dev": true
+ },
+ "yargs": {
+ "version": "16.2.0",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
+ "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
+ "dev": true,
+ "requires": {
+ "cliui": "^7.0.2",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.0",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^20.2.2"
+ }
+ },
+ "yargs-parser": {
+ "version": "20.2.9",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
+ "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
+ "dev": true
+ }
+ }
+}
diff --git a/packages/quantum-js-util/package.json b/packages/quantum-js-util/package.json
new file mode 100644
index 0000000..55c7fe1
--- /dev/null
+++ b/packages/quantum-js-util/package.json
@@ -0,0 +1,21 @@
+{
+ "name": "quantum-js-util",
+ "version": "1.0.0",
+ "description": "",
+ "main": "Q.js",
+ "scripts": {
+ "test": "jest",
+ "prettier": "echo 'I am a prettier util!' && exit 0"
+ },
+ "devDependencies": {
+ "cross-env": "^7.0.3",
+ "eslint": "^7.31.0",
+ "jest": "^27.0.6",
+ "jsdom": "^16.6.0",
+ "prettier": "2.3.2"
+ },
+ "dependencies": {},
+ "keywords": [],
+ "author": "",
+ "license": "ISC"
+}