Repository: intel/depth-camera-web-demo Branch: master Commit: 4bca725c901e Files: 30 Total size: 5.6 MB Directory structure: gitextract_lz9578_u/ ├── DEVELOPMENT.md ├── LICENSE.md ├── README.md ├── depth-camera.js ├── depth-to-color-sync-render.js ├── depthdemo.html ├── gesture/ │ ├── depth_and_segments.js │ ├── index.html │ └── indexaframe.html ├── index.html ├── libs/ │ ├── aframe/ │ │ ├── LICENSE.txt │ │ └── aframe-v0.7.1.js │ ├── ammo.js/ │ │ ├── LICENSE │ │ └── ammo.js │ ├── gl-matrix.js │ └── picogl.js/ │ ├── picogl.js │ └── utils.js ├── nn/ │ └── using-deeplab/ │ ├── README.md │ ├── argmax257_2/ │ │ ├── LICENSE │ │ ├── group1-shard1of2 │ │ ├── group1-shard2of2 │ │ ├── tensorflowjs_model.pb │ │ └── weights_manifest.json │ ├── index.html │ └── tfjs/ │ ├── LICENSE │ └── tf-core.js ├── tools/ │ └── generate_concentric_circles_indices.js └── typing_in_the_air/ ├── doc/ │ └── tutorial.html ├── front_capture_typing.html └── front_capture_typing.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: DEVELOPMENT.md ================================================ ### How to specify calibration parameters for RealSense cameras After exploring possibility to implement JavaScript API that would fetch calibration parameters from camera ([link to outdated specification for the reference](https://www.w3.org/TR/2017/WD-mediacapture-depth-20170328/)), that approach got abandoned calibration parameters are instead, specified in JavaScript code here, in [depth-camera.js](https://github.com/01org/depth-camera-web-demo/blob/926fd23c535e3a5a07fcfb94bf9afea0e31a9dc4/depth-camera.js#L149) file. Given that all RealSense cameras of the same model are factory calibrated to the same values and that firmware update (so far) doesn't change calibration parameters, specifying constant values in depth-camera.js and lookup based on camera label worked fine,... so far. The section here explains where from, and how to, add numerical values to depth-camera.js file. This work is required for new camera models or if you want to support depth or color stream track resolutions that are not yet specified in depth-camera.js. We would need [rs-enumerate-devices](https://github.com/IntelRealSense/librealsense/tree/master/tools/enumerate-devices) tool. It is part of RealSense SDK. Install SDK binary [binary or build it from source code](https://github.com/IntelRealSense/librealsense/). Connect the camera and run ```rs-enumerate-devices -c``` to get the calibration data printed out to terminal. Copy values from there to [depth-camera.js](https://github.com/01org/depth-camera-web-demo/blob/926fd23c535e3a5a07fcfb94bf9afea0e31a9dc4/depth-camera.js#L274) following the example: ``` } else if (cameraName === "SR300") { result = { ``` Run `rs-enumerate-devices -o` - depthScale value is in a row containing `Depth Units` label. ``` depthScale: 0.0001249866472790017724, ``` Provide depth intrinstics for resolutions you plan to use. `rs-enumerate-devices -c` prints out this: ``` Intrinsic of "Depth" 640x480 Z16 Width: 640 Height: 480 PPX: 307.147125244141 PPY: 245.624420166016 Fx: 474.499542236328 Fy: 474.499420166016 Distortion: Inverse Brown Conrady Coeffs: 0.126395508646965 0.0701233819127083 0.00355594046413898 0.00548861175775528 0.103697031736374 ``` From the output, we just copy values to `getDepthIntrinsics` and `depthDistortionModel` below. `getColorIntrinsics`data is populated following the same pattern - note that there multiple resolutions (Intrinsic of "Color" 640x480, Intrinsic of "Color" 1280x720, ...) supported with different constants to be copied from `rs-enumerate-devices -c` output. ``` getDepthIntrinsics: function(width, height) { if (width == 640 && height == 480) { return { offset: [307.147125244141, 245.624420166016], focalLength: [474.499542236328, 474.499420166016], }; } else { throwUnsupportedSizeError(); } }, getColorIntrinsics: function(width, height) { if (width == 640 && height == 480) { return { offset: [305.502166748047, 247.462982177734], focalLength: [618.239440917969, 618.239562988281], }; } else if (width == 1280 && height == 720) { return { offset: [618.253234863281, 371.194458007812], focalLength: [927.359130859375, 927.359313964844], }; } else if (width == 1920 && height == 1080) { return { offset: [927.3798828125, 556.791687011719], focalLength: [1391.03869628906, 1391.03894042969], }; } else { throwUnsupportedSizeError(); } }, colorOffset: new Float32Array( [305.502166748047, 247.462982177734] ), colorFocalLength: new Float32Array( [618.239440917969, 618.239562988281] ), ``` depthToColor values are copied from `rs-enumerate-devices -c` `Extrinsic from "Depth" To "Color"` section. ``` Extrinsic from "Depth" To "Color" : Rotation Matrix: 0.99999 0.0034401 -0.0016265 -0.003436 0.99999 0.0024992 0.0016351 -0.0024936 1 Translation Vector: 0.0256999991834164 0.00126673700287938 0.00358582031913102 ``` Note how the command output is column major compared to depthToColor layout below - three elements of the first printed row are the first three depthToColor column elements. We have patched [this line in rs-enumerate-devices](https://github.com/IntelRealSense/librealsense/blob/d0f0e5e5238ad8c729957c1d82297452c32e8d72/tools/enumerate-devices/rs-enumerate-devices.cpp#L26) to obtain higher precision values. The fourth row of depthToColor matrix is populated from `Translation Vector`values. ``` depthToColor: [ 0.999992787837982, -0.00343602383509278, 0.00163511745631695, 0, 0.00344009511172771, 0.999990999698639, -0.00249356147833169, 0, -0.00162653462029994, 0.00249916850589216, 0.999995589256287, 0, 0.0256999991834164, 0.00126673700287938, 0.00358582031913102, 1 ], colorToDepth: [ 0.999992787837982, -0.00343602383509278, 0.00163511745631695, 0, 0.00344009511172771, 0.999990999698639, -0.00249356147833169, 0, -0.00162653462029994, 0.00249916850589216, 0.999995589256287, 0, -0.0257013235241175, -0.00134619453456253, -0.00354716833680868, 1 ], depthDistortionModel: DistortionModel.INVERSE_BROWN_CONRADY, depthDistortioncoeffs: [ 0.126395508646965, 0.0701233819127083, 0.00355594046413898, 0.00548861175775528, 0.103697031736374, ], colorDistortionModel: DistortionModel.NONE, colorDistortioncoeffs: [0, 0, 0, 0, 0], }; } ``` ================================================ FILE: LICENSE.md ================================================ # License Everything in this repo is BSD style license unless otherwise specified. Copyright (c) 2016 Intel Corporation. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: README.md ================================================ DISCONTINUATION OF PROJECT This project will no longer be maintained by Intel. Intel has ceased development and contributions including, but not limited to, maintenance, bug fixes, new releases, or updates, to this project. Intel no longer accepts patches to this project. If you have an ongoing need to use this project, are interested in independently developing it, or would like to maintain patches for the open source software community, please create your own fork of this project. Contact: webadmin@linux.intel.com # Depth camera capture in HTML5
Video is not yet loaded.
hands_interaction.gif is not yet loaded.

Moving boxes using hands (or a paper) demo shows live depth captured mesh interaction with scene objects; combining 3D world and depth captured hands (or other objects) rendering and Bullet Physics. Run live demo.

backgroundremoval.gif is not yet loaded.

Simple background removal implemented as flood-fill of background color to similarly colored pixels. Works only with simple backgrounds - e.g. room walls on the demo gif. Check the tutorial article and run live demo.

typing_in_the_air.gif is not yet loaded.

Typing in the air tutorial shows how to use depth stream and WebGL transform feedback to do simple gesture recognition. Check the tutorial article and run live demo.

https://github.com/01org/depthcamera-pointcloud-web-demo/raw/master/recording.gif is not yet loaded.

3D point cloud rendering demo shows how to render and synchronize depth and color video on GPU. Check the tutorial article and run live demo.

how_the_demo_looks.gif is not yet loaded.

HTML5 Depth Capture tutorial shows how to access depth stream, check the tutorial article and run live demo.

To capture and manipulate depth camera stream in HTML5, you'll need: * Chrome browser version 62 or later (the official release and no need for additional extensions), * Intel® RealSense™ 3D camera plugged to USB 3.0 port * SR300 (and related cameras like Razer Stargazer or Creative BlasterX Senz3D) or R200, * Windows, Linux or ChromeOS PC. These are the constraints of current implementation. The plan is to support other depth cameras and OSX and Android, too. ## Articles related to the demos: * [Depth Camera Capture in HTML5](https://01.org/node/5101), * [Typing in the air using depth camera, Chrome, JavaScript, and WebGL transform feedback](https://software.intel.com/en-us/blogs/2017/06/22/tutorial-typing-in-the-air-using-depth-camera-chrome-javascript-and-webgl-transform) * [AR marker detection on GPU using WebGL](https://01.org/node/26012) * [Background removal with Intel® RealSense™ Depth Camera, WebRTC*, and WebGL*](https://01.org/node/28902) * [Background removal using TensorFlow.js](https://01.org/node/29971) ================================================ FILE: depth-camera.js ================================================ /*jshint esversion: 6 */ // Copyright 2017 Intel Corporation. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. class DepthCamera { constructor() { } static async getDepthStream() { if (!navigator.mediaDevices || !navigator.mediaDevices.enumerateDevices || !navigator.mediaDevices.getUserMedia) { throw new Error("Your browser doesn't support the required mediaDevices APIs."); } // Use videoKind if it is supported. At the moment it is experimental; to // use it. Chrome needs to be started with command line argument: // --enable-blink-features=MediaCaptureDepthVideoKind const supported_constraints = navigator.mediaDevices.getSupportedConstraints(); if (supported_constraints.videoKind) { let stream = await navigator.mediaDevices.getUserMedia({ video: { videoKind: {exact: "depth"}, frameRate: {exact: 60} } }); const track = stream.getVideoTracks()[0]; let settings = track.getSettings ? track.getSettings() : null; // TODO: following is a browser bug if happening. if (settings.videoKind != "depth") throw new Error("No RealSense depth camera connected."); return stream; } // We cannot use videoKind yet, so try to make a constraint that would // most likely resolve to a depth camera. Later, we use camera label to // check if we really got a depth track. const constraints = { audio: false, video: { // videoKind: {exact: "depth"}, R200 related hack: prefer // depth (width = 628) to IR (width = 641) stream. width: {ideal: 628}, // SR300 depth camera enables capture at 110 frames per second. frameRate: {ideal: 110}, } } let stream = await navigator.mediaDevices.getUserMedia(constraints); let track = stream.getVideoTracks()[0]; if (track.label.indexOf("RealSense") == -1) { throw new Error(chromeVersion() < 58 ? "Your browser version is too old. Get Chrome version 58 or later." : "No RealSense camera connected."); } if (track.getSettings && track.getSettings().frameRate > 60) { // After Chrome 59, returned track is scaled to 628 and frameCount 110. // We got the deviceId, so we the deviceId to select the stream with // default resolution and frameRate. track.stop(); const constraints = { audio: false, video: { deviceId: {exact: track.getSettings().deviceId}, frameRate: {exact: 60} } } stream = await navigator.mediaDevices.getUserMedia(constraints); } return stream; } // Call the method after getting depth_stream using getDepthStream. static async getColorStreamForDepthStream(depthStream, w = 640, h = 480) { // To get color stream from the same physical device providing the depth // stream, we will use groupId, once it is implemented: // See https://crbug.com/627793 // For now, enumerate devices based on label. // Note: depth_stream is not used, for now, but deliberately added as a // parameter to mandate the need for previous call to getDepthStream. let depth_device_id = null; const depth = depthStream.getVideoTracks()[0]; // Chrome, starting with version 59, implements getSettings() API. if (depth.getSettings) { depth_device_id = depth.getSettings().deviceId; } else if (idealWidth) { console.warn(`Not able to set ideal width for color video as MediaStreamTrack getSettings() API is not available. Try with Chromium version > 59.`); } var all_devices = await navigator.mediaDevices.enumerateDevices(); let devices = all_devices.filter((device) => ( device.kind == 'videoinput' && device.label.includes('RealSense') && device.label.includes('RGB') && (device.label != depth.label || device.deviceId != depth_device_id))); if (devices.length < 1) { throw new Error("No RealSense camera connected."); } else if (devices.length > 1) { devices = devices.sort((a, b) => { // Heuristics, as everything else in this method: pick camera with // 'RGB' at the end return b.label.lastIndexOf('RGB') - a.label.lastIndexOf('RGB'); }); } // Select stream the id, so that some other camera doesn't get selected // (e.g. if the user has another rgb camera). const id = devices[0].deviceId; // Select color stream. const constraints = { video: { width: w, height: h, deviceId: {exact: id}, } }; return navigator.mediaDevices.getUserMedia(constraints); } // Figure out the camera intristics and extrinsics based on the depth stream // camera model. // // This should be rewritten once the MediaCapture-Depth API works - don't // hardcode the values based on camera model, but query it from the API. // // See the documentation at // https://w3c.github.io/mediacapture-depth/#synchronizing-depth-and-color-video-rendering static getCameraCalibration(depth_stream) { const label = depth_stream.getVideoTracks()[0].label; const cameraName = label.includes("R200") ? "R200" : (label.includes("Camera S") || label.includes("SR300")) ? "SR300" : label.includes("ZR300") ? "ZR300" : label.includes("415") ? "D415" : label.includes("430") ? "D435" : label.includes("435i") ? "D435i" : label.includes("435") ? "D435" : label.includes(") 4") ? "generic4" : label; const DistortionModel = { NONE: 0, MODIFIED_BROWN_CONRADY: 1, INVERSE_BROWN_CONRADY: 2, }; function throwUnsupportedSizeError() { const error = new Error("Depth intrinsics for size " + width + "x" + height + " are not available."); error.name = "UnsupportedSizeError"; throw error; } let result; if (cameraName === "R200") { result = { depthScale: 0.001, getDepthIntrinsics: function(width, height) { if (width == 628 && height == 469) { return { offset: [305.558075, 233.5], focalLength: [582.154968, 582.154968], }; } else if (width == 628 && height == 361) { return { offset: [233.3975067138671875, 179.2618865966796875], focalLength: [447.320953369140625, 447.320953369140625], }; } else { throwUnsupportedSizeError(); } }, colorOffset: new Float32Array( [311.841033935546875, 229.7513275146484375] ), colorFocalLength: new Float32Array( [627.9630126953125, 634.02410888671875] ), // Rotation [0..2] goes to 1st column, [3..6] to second, etc. The // row at the bottom is translation. depthToColor: [ 0.99998325109481811523, 0.002231199527159333229, 0.00533978315070271492, 0, -0.0021383403800427913666, 0.99984747171401977539, -0.017333013936877250671, 0, -0.0053776423446834087372, 0.017321307212114334106, 0.99983555078506469727, 0, -0.058898702263832092285, -0.00020283895719330757856, -0.0001998419174924492836, 1 ], depthDistortionModel: DistortionModel.NONE, depthDistortioncoeffs: [0, 0, 0, 0, 0], colorDistortionModel: DistortionModel.MODIFIED_BROWN_CONRADY, colorDistortioncoeffs: [ -0.078357703983783721924, 0.041351985186338424683, -0.00025565386749804019928, 0.0012357287341728806496, 0 ], }; } else if (cameraName === "SR300 Senz3D") { result = { depthScale: 0.0001249866472790017724, getDepthIntrinsics: function(width, height) { if (width == 640 && height == 480) { return { offset: [310.743988037109375, 245.1811676025390625], focalLength: [475.900726318359375, 475.900726318359375], }; } else { throwUnsupportedSizeError(); } }, getColorIntrinsics: function(width, height) { if (width == 640 && height == 480) { return { offset: [312.073974609375, 241.969329833984375], focalLength: [617.65087890625, 617.65093994140625], }; } else if (width == 1280 && height == 720) { return { offset: [628.110961914062, 362.953979492188], focalLength: [926.476318359375, 926.476440429688], }; } else if (width == 1920 && height == 1080) { return { offset: [942.166442871094, 544.430969238281], focalLength: [1389.71447753906, 1389.71472167969], }; } else { throwUnsupportedSizeError(); } }, colorOffset: new Float32Array( [312.073974609375, 241.969329833984375] ), colorFocalLength: new Float32Array( [617.65087890625, 617.65093994140625] ), depthToColor: [ 0.99998641014099121094, -0.0051436689682304859161, 0.00084982655243948101997, 0, 0.0051483912393450737, 0.99997079372406005859, -0.005651625804603099823, 0, -0.00082073162775486707687, 0.0056559243239462375641, 0.99998366832733154297, 0, 0.025699997320771217346, -0.00073326355777680873871, 0.0039400043897330760956, 1 ], depthDistortionModel: DistortionModel.INVERSE_BROWN_CONRADY, depthDistortioncoeffs: [ 0.14655706286430358887, 0.078352205455303192139, 0.0026113723870366811752, 0.0029218809213489294052, 0.066788062453269958496, ], colorDistortionModel: DistortionModel.NONE, colorDistortioncoeffs: [0, 0, 0, 0, 0], }; } else if (cameraName === "SR300") { result = { depthScale: 0.0001249866472790017724, getDepthIntrinsics: function(width, height) { if (width == 640 && height == 480) { return { offset: [307.147125244141, 245.624420166016], focalLength: [474.499542236328, 474.499420166016], }; } else { throwUnsupportedSizeError(); } }, getColorIntrinsics: function(width, height) { if (width == 640 && height == 480) { return { offset: [305.502166748047, 247.462982177734], focalLength: [618.239440917969, 618.239562988281], }; } else if (width == 1280 && height == 720) { return { offset: [618.253234863281, 371.194458007812], focalLength: [927.359130859375, 927.359313964844], }; } else if (width == 1920 && height == 1080) { return { offset: [927.3798828125, 556.791687011719], focalLength: [1391.03869628906, 1391.03894042969], }; } else { throwUnsupportedSizeError(); } }, colorOffset: new Float32Array( [305.502166748047, 247.462982177734] ), colorFocalLength: new Float32Array( [618.239440917969, 618.239562988281] ), depthToColor: [ 0.999992787837982, -0.00343602383509278, 0.00163511745631695, 0, 0.00344009511172771, 0.999990999698639, -0.00249356147833169, 0, -0.00162653462029994, 0.00249916850589216, 0.999995589256287, 0, 0.0256999991834164, 0.00126673700287938, 0.00358582031913102, 1 ], colorToDepth: [ 0.999992787837982, -0.00343602383509278, 0.00163511745631695, 0, 0.00344009511172771, 0.999990999698639, -0.00249356147833169, 0, -0.00162653462029994, 0.00249916850589216, 0.999995589256287, 0, -0.0257013235241175, -0.00134619453456253, -0.00354716833680868, 1 ], depthDistortionModel: DistortionModel.INVERSE_BROWN_CONRADY, depthDistortioncoeffs: [ 0.126395508646965, 0.0701233819127083, 0.00355594046413898, 0.00548861175775528, 0.103697031736374, ], colorDistortionModel: DistortionModel.NONE, colorDistortioncoeffs: [0, 0, 0, 0, 0], }; } else if (cameraName === "ZR300") { result = { depthScale: 0.00100000005, getDepthIntrinsics: function(width, height) { if (width == 628 && height == 469) { return { offset: [309.912567, 234.410904], focalLength: [575.729980, 575.729980], }; } else if (width == 628 && height == 361) { return { offset: [238.683838, 180.205521], focalLength: [445.920288, 445.920288], }; } else { throwUnsupportedSizeError(); } }, colorOffset: new Float32Array( [312.271545, 233.118652] ), colorFocalLength: new Float32Array( [616.316895, 617.343323] ), depthToColor: [ 0.999995947, 0.00140406948, 0.00246621366, 0, -0.00140700850, 0.999998271, 0.00119038881, 0, -0.00246453821, -0.00119385391, 0.999996245, 0, -0.0587307774, 7.03283295e-05, 0.000553227146, 1 ], depthDistortionModel: DistortionModel.NONE, depthDistortioncoeffs: [0, 0, 0, 0, 0], colorDistortionModel: DistortionModel.MODIFIED_BROWN_CONRADY, colorDistortioncoeffs: [ 0.0727398321, -0.138192296, 0.000800351670, 0.000444319186, 0 ], }; } else if (cameraName === "D415") { result = { depthScale: 0.00100000005, getDepthIntrinsics: function(width, height) { if (width == 640 && height == 480) { return { offset: [315.847442626953, 241.684616088867], focalLength: [643.142272949219, 643.142272949219], }; } else if (width == 1280 && height == 720) { return { offset: [633.771179199219, 362.526947021484], focalLength: [964.713439941406, 964.713439941406], }; } else { throwUnsupportedSizeError(); } }, getColorIntrinsics: function(width, height) { if (width == 640 && height == 480) { return { offset: [321.308288574219, 231.349639892578], focalLength: [617.459838867188, 617.65087890625], }; } else if (width == 1280 && height == 720) { return { offset: [641.96240234375, 347.024475097656], focalLength: [926.189697265625, 926.476257324219], }; } else if (width == 1920 && height == 1080) { return { offset: [962.943664550781, 520.536682128906], focalLength: [1389.28454589844, 1389.71447753906], }; } else { throwUnsupportedSizeError(); } }, colorOffset: new Float32Array( [321.308288574219, 231.349639892578] ), colorFocalLength: new Float32Array( [617.459838867188, 617.65087890625] ), colorToDepth: [ 0.999988317489624, -0.000426474376581609, 0.00481635145843029, 0, 0.000353455223375931, 0.999885141849518, 0.0151513637974858, 0, -0.00482225976884365, -0.0151494843885303, 0.999873638153076, 0, -0.0150478817522526, 0.0000661657468299381, 0.000241686851950362, 1 ], depthToColor: [ 0.999988317489624, 0.000353455223375931, -0.00482225976884365, 0, -0.000426474376581609, 0.999885141849518, -0.0151494843885303, 0, 0.00481635145843029, 0.0151513637974858, 0.999873638153076, 0, 0.0150465695187449, -0.0000645012842142023, -0.00031321871210821, 1, ], depthDistortionModel: DistortionModel.NONE, depthDistortioncoeffs: [0, 0, 0, 0, 0], colorDistortionModel: DistortionModel.NONE, colorDistortioncoeffs: [0, 0, 0, 0, 0], }; } else if (cameraName === "D435") { result = { depthScale: 0.00100000005, getDepthIntrinsics: function(width, height) { if (width == 640 && height == 480) { return { offset: [318.229400634766, 239.944534301758], focalLength: [381.902008056641, 381.902008056641], }; } else if (width == 1280 && height == 720) { return { offset: [637.048950195312, 359.907562255859], focalLength: [636.503356933594, 636.503356933594], }; } else { throwUnsupportedSizeError(); } }, getColorIntrinsics: function(width, height) { if (width == 640 && height == 480) { return { offset: [324.276763916016, 233.025253295898], focalLength: [616.862121582031, 617.127319335938], }; } else if (width == 1280 && height == 720) { return { offset: [646.415161132812, 349.537872314453], focalLength: [925.293212890625, 925.691040039062], }; } else if (width == 1920 && height == 1080) { return { offset: [969.622741699219, 524.306823730469], focalLength: [1387.93981933594, 1388.53649902344], }; } else { throwUnsupportedSizeError(); } }, colorOffset: new Float32Array( [324.276763916016, 233.025253295898] ), colorFocalLength: new Float32Array( [616.862121582031, 617.127319335938] ), depthToColor: [ 0.999992370605469, 0.000624090549536049, -0.00385748990811408, 0, -0.000635052449069917, 0.999995768070221, -0.00284114643000066, 0, 0.00385570037178695, 0.00284357438795269, 0.999988496303558, 0, 0.0149379102513194, 0.000216223328607157, 0.000277608894975856, 1, ], colorToDepth: [ 0.999992370605469, -0.000635052449069917, 0.00385570037178695, 0, 0.000624090549536049, 0.999995768070221, 0.00284357438795269, 0, -0.00385748990811408, -0.00284114643000066, 0.999988496303558, 0, -0.0149368597194552, -0.000205947319045663, -0.000335816672304645, 1 ], depthDistortionModel: DistortionModel.NONE, depthDistortioncoeffs: [0, 0, 0, 0, 0], colorDistortionModel: DistortionModel.NONE, colorDistortioncoeffs: [0, 0, 0, 0, 0], }; } else if (cameraName === "D435i") { result = { depthScale: 0.00100000005, getDepthIntrinsics: function(width, height) { if (width == 640 && height == 480) { return { offset: [319.640411376953, 234.501083374023], focalLength: [383.972534179688, 383.972534179688], }; } else if (width == 1280 && height == 720) { return { offset: [639.400695800781, 350.835144042969], focalLength: [639.954223632812, 639.954223632812], }; } else { throwUnsupportedSizeError(); } }, getColorIntrinsics: function(width, height) { if (width == 640 && height == 480) { return { offset: [326.527374267578, 241.035064697266], focalLength: [613.288269042969, 613.207214355469], }; } else if (width == 1280 && height == 720) { return { offset: [649.791015625, 361.552581787109], focalLength: [919.932373046875, 919.810852050781], }; } else { throwUnsupportedSizeError(); } }, colorOffset: new Float32Array( [326.527374267578, 241.035064697266] ), colorFocalLength: new Float32Array( [613.288269042969, 613.207214355469] ), depthToColor: [ 0.999998152256012, 0.000072939285018947, -0.00191376695875078, 0, -0.0000624307940597646, 0.999984920024872, 0.00549048557877541, 0, 0.00191413855645806, -0.00549035612493753, 0.999983072280884, 0, 0.0145636992529035, 0.0000774716536398046, 0.00038804262294434, 1, ], colorToDepth: [ 0.999998152256012, -0.0000624307940597646, 0.00191413855645806, 0, 0.000072939285018947, 0.999984920024872, -0.00549035612493753, 0, -0.00191376695875078, 0.00549048557877541, 0.999983072280884, 0, -0.0145629355683923, -0.0000786918026278727, -0.000415487680584192, 1 ], depthDistortionModel: DistortionModel.MODIFIED_BROWN_CONRADY, depthDistortioncoeffs: [0, 0, 0, 0, 0], colorDistortionModel: DistortionModel.MODIFIED_BROWN_CONRADY, colorDistortioncoeffs: [0, 0, 0, 0, 0], }; } else if (cameraName === "generic4") { result = { depthScale: 0.00100000005, getDepthIntrinsics: function(width, height) { if (width == 640 && height == 480) { return { offset: [321.17535400390625, 248.4362640380859375], focalLength: [402.60308837890625, 402.60308837890625], }; } else { throwUnsupportedSizeError(); } }, colorOffset: new Float32Array( [331.870422363281, 242.991546630859] ), colorFocalLength: new Float32Array( [629.172912597656, 628.130920410156] ), depthToColor: [ 0.999902248382, 0.010088876821, 0.009682051837, 0, -0.010075648315, 0.9999482631683, -0.001414125669, 0, 0.009695817716, 0.001316434470, 0.99995213747, 0, 0.036090422422, 0.000611198542174, -0.00184865354, 1 ], depthDistortionModel: DistortionModel.NONE, depthDistortioncoeffs: [0, 0, 0, 0, 0], colorDistortionModel: DistortionModel.NONE, colorDistortioncoeffs: [0, 0, 0, 0, 0], }; } else { throw { name: "CameraNotSupported", message: "Sorry, your camera '" + cameraName + "' is not supported", }; } // This also de-normalizes the depth value (it's originally a 16-bit // integer normalized into a float between 0 and 1). result.depthScale = result.depthScale * 65535; result.cameraName = cameraName; return result; } } function chromeVersion() { const raw = navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./); return raw ? parseInt(raw[2], 10) : false; } ================================================ FILE: depth-to-color-sync-render.js ================================================ /*jshint esversion: 6 */ /* * Copyright (c) 2018, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Intel Corporation nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ const REDUCE_BLACK_PASSES = 7; class DepthToColorSyncRender { constructor(canvas) { let gl; try { gl = canvas.getContext('webgl2', {antialias: false}); } catch (e) { console.error('Your browser doesn\'t support WebGL2.'); throw new Error(`Could not create WebGL2 context: ${e}`); } this.gl = gl; this.programs = this.setupPrograms(gl); gl.getExtension('EXT_color_buffer_float'); gl.getExtension('OES_texture_float_linear'); this.PAUSE_REQUESTED = 1; this.PAUSED = 2; } createVideo(w = 640, h = 480) { var video = document.createElement("video"); video.crossOrigin = "anonymous"; video.width = w; video.height = h; video.autoplay = true; video.loop = true; video.oncanplay = function(){ video.video_loaded=true; }; return video; } async setupCamera(depth_video = null) { if (depth_video) this.depthVideo = depth_video; if (!this.depthVideo) this.depthVideo = this.createVideo(); if (!this.colorVideo) this.colorVideo = this.createVideo(this.gl.canvas.width, this.gl.canvas.height); if (!this.depthVideo.srcObject) this.depthVideo.srcObject = await DepthCamera.getDepthStream(); const depthStream = this.depthVideo.srcObject; const calibration = DepthCamera.getCameraCalibration(depthStream); // Supported only for D400-Series Depth Cameras if (calibration.cameraName.indexOf('D4') == -1) { throw new Error('Background removal is supported only for Intel\u00ae RealSense\u2122 D400-Series Depth Cameras.'); } if (!this.colorVideo.srcObject) { const colorStream = await DepthCamera.getColorStreamForDepthStream(depthStream, this.colorVideo.width, this.colorVideo.height); this.colorVideo.srcObject = colorStream; } return calibration; } showBackgroundColor(on) { this.backgroundColor = on; } showBackgroundVideo(on) { this.backgroundVideo = on; if (!this.backgroundVideoElement) { const video = this.createVideo(); this.backgroundVideoElement = video; video.src = "res/landscape.mp4"; } if (on) this.backgroundVideoElement.play(); else this.backgroundVideoElement.pause(); } // Create textures into which the camera output will be stored. setupTextures(gl, programs, width, height, colorwidth, colorheight) { let lastTextureId = 0; function createTexture2D(format, w, h, filter = gl.NEAREST) { gl.activeTexture(gl[`TEXTURE${lastTextureId}`]); const texture = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, texture); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, filter); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, filter); gl.texStorage2D( gl.TEXTURE_2D, 1, // number of mip-map levels format, // internal format w, h, ); texture.unit = lastTextureId++; texture.w = w; texture.h = h; return texture; } const depth0 = createTexture2D(gl.R32F, width, height); const depth1 = createTexture2D(gl.R32F, width, height); const color = createTexture2D(gl.RGBA8, colorwidth, colorheight); const colorFilter = createTexture2D(gl.RGBA8, colorwidth, colorheight); const noHoles = createTexture2D(gl.RGBA8, colorwidth, colorheight); const reduceBlack = [createTexture2D(gl.RGBA8, colorwidth, colorheight), createTexture2D(gl.RGBA8, colorwidth, colorheight), createTexture2D(gl.RGBA8, colorwidth, colorheight), createTexture2D(gl.RGBA8, colorwidth, colorheight), createTexture2D(gl.RGBA8, colorwidth, colorheight), createTexture2D(gl.RGBA8, colorwidth, colorheight), createTexture2D(gl.RGBA8, colorwidth, colorheight)]; const background = createTexture2D(gl.RGBA8, colorwidth, colorheight); const previousBackground = createTexture2D(gl.RGBA8, colorwidth, colorheight); const cleanup = createTexture2D(gl.RGBA8, colorwidth, colorheight); const backgroundVideo = createTexture2D(gl.RGBA8, 1920, 1080, gl.LINEAR); return { depth: depth0, previousDepth: depth1, color: color, colorFilter: colorFilter, noHoles: noHoles, reduceBlack: reduceBlack, background: background, previousBackground: previousBackground, cleanup: cleanup, backgroundVideo: backgroundVideo }; } setupPrograms(gl) { this.vao = gl.createVertexArray(); gl.bindVertexArray(this.vao); const vertex_buffer = gl.createBuffer(); this.vertex_buffer = vertex_buffer; gl.bindBuffer(gl.ARRAY_BUFFER, this.vertex_buffer); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([0,0,1,0,1,1,0,1]), gl.STATIC_DRAW); this.index_buffer= gl.createBuffer(); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.index_buffer); gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array([0,1,2,0,2,3]), gl.STATIC_DRAW); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null); const noHolesVertex = `#version 300 es in vec2 v; out vec2 t; void main(){ gl_Position = vec4(v.x * 2.0 - 1.0, v.y * 2.0 - 1.0, 0, 1); t = v; }`; const noHolesPixel = `#version 300 es precision highp float; layout(location = 0) out vec4 fragColor; layout(location = 1) out vec4 backgroundColor; in vec2 t; uniform sampler2D sDepth; uniform sampler2D sPreviousDepth; uniform sampler2D sColor; uniform sampler2D sPreviousBackground; uniform vec3 dd; // vec3(1/w, 1/h, 0) uniform vec3 ddDepth; // vec3(1/w, 1/h, 0) uniform float depthScale; uniform vec2 depthOffset; uniform vec2 colorOffset; uniform vec2 depthFocalLength; uniform vec2 colorFocalLengthInv; uniform mat4 colorToDepth; const vec4 rgbmask = vec4(1.0, 1.0, 1.0, 0.0); const float range = 0.9; vec4 normalizeRG(vec4 c) { float sum = dot(c, rgbmask); return vec4(c.rgb / sum, c.a); } vec4 colorDeproject(vec2 index, float z) { vec2 position2d = (index - colorOffset) * colorFocalLengthInv; return vec4(position2d * z, z, 1.0); } float nonZeroDepth(sampler2D s, vec2 v) { // If the depth is 0, return 1.0. We return 1.0 only because it is max // value and we use it to find minimum among neighbour values. vec4 c = texture(s, v); return (c.r == 0.0) ? 1.0 : c.r; } void main(){ fragColor = texture(sColor, t); vec4 backColor = texture(sPreviousBackground, t); // Get the depth for color pixel. vec4 colorPos = colorDeproject(t, 0.5); vec4 depthPos = colorToDepth * colorPos; vec2 position2d = depthPos.xy / depthPos.z; vec2 v = position2d * depthFocalLength + depthOffset; float z = texture(sDepth, v).r; float z_around = min(min(nonZeroDepth(sDepth, v + ddDepth.rb), nonZeroDepth(sDepth, v - ddDepth.rb)), min(texture(sDepth, v + ddDepth.bg).r, //deliberatelly nonZeroDepth(sDepth, v - ddDepth.bg))); z = (z == 1.0) ? 0.0 : z; z *= depthScale; z_around *= depthScale; // As depth and color are not sampled in the same time, prevent overlap // during (moderate) movement. Overlap would render foreground to be // transparent for a frame and we mitigate it by checking pixels around. // z = min(z, z_around); z = z_around < z ? z_around : z; backColor = z > range ? vec4(fragColor.rgb, z) : backColor; backgroundColor = backColor; // TODO: use previous depth frame to get depth value if not defined // float z1 = texture(sPreviousDepth, v).r; // z1 = (z1 == 1.0) ? 0.0 : z1; // z = (z == 0.0) ? z1 * depthScale : z; // clamp up to 0.95 for expressing value using color alpha channel. // [0-0.9] would express foreground, [0.9-0.95] background from depth // camera and [0.95-1] computed background based on color fill. z = (z > 0.95) ? 0.95 : z; // TODO: use background map to fix edges. /* vec4 distN = normalizeRG(fragColor) - normalizeRG(backColor); vec4 dist = fragColor - backColor; z = (z == 0.0 && dot(distN.rgb, distN.rgb) < 0.003 && dot(dist.rgb, dist.rgb) < 0.008 && backColor.a > 0.9) ? backColor.a : z; z = (z > 0.95) ? 0.95 : z; */ fragColor.rgb = sqrt(fragColor.rgb); // gamma correction aproximation. fragColor.a = z; }`; const reduceBlackVertex = `#version 300 es in vec2 v; out vec2 t; void main(){ gl_Position = vec4(v.x * 2.0 - 1.0, v.y * 2.0 - 1.0, 0, 1); t = v; }`; const reduceBlackPixel = `#version 300 es precision mediump float; uniform sampler2D s; uniform vec4 samplingStep; vec4 bckgndThreshold = vec4(0.9); in vec2 t; out vec4 fragColor; void main(){ vec4 c = texture(s, t); fragColor = c; if (c.a > 0.9) return; const vec4 similarThreshold = vec4(0.011); const float k = similarThreshold.r / 0.035; vec4 dd3 = samplingStep; vec4 dd4 = dd3 * 1.414213562; vec4 dd6 = 3.0 * dd3; vec4 dd8 = 3.0 * dd4; vec4 dd1 = 0.3333 * dd3; vec4 dd2 = 0.3333 * dd4; vec4 c11 = texture(s, t - dd3.rg); vec4 c15 = texture(s, t + dd3.rg); vec4 c13 = texture(s, t - dd3.bg); vec4 c17 = texture(s, t + dd3.bg); vec4 c12 = texture(s, t - dd4.ag); vec4 c16 = texture(s, t + dd4.ag); vec4 c18 = texture(s, t - dd4.ra); vec4 c14 = texture(s, t + dd4.ra); vec4 c21 = texture(s, t - dd6.rg); vec4 c25 = texture(s, t + dd6.rg); vec4 c23 = texture(s, t - dd6.bg); vec4 c27 = texture(s, t + dd6.bg); vec4 c22 = texture(s, t - dd8.ag); vec4 c26 = texture(s, t + dd8.ag); vec4 c28 = texture(s, t - dd8.ra); vec4 c24 = texture(s, t + dd8.ra); vec4 c01 = texture(s, t - dd1.rg); vec4 c05 = texture(s, t + dd1.rg); vec4 c03 = texture(s, t - dd1.bg); vec4 c07 = texture(s, t + dd1.bg); vec4 c02 = texture(s, t - dd2.ag); vec4 c06 = texture(s, t + dd2.ag); vec4 c08 = texture(s, t - dd2.ra); vec4 c04 = texture(s, t + dd2.ra); // ci1 and ci5, ci2 and ci6,... are opposite. From z01 and z02, which // contain depth of the nearest 8 pixels, to z21 and z22 for the // furthest pixels. vec4 z01 = vec4(c01.a, c02.a, c03.a, c04.a); vec4 z02 = vec4(c05.a, c06.a, c07.a, c08.a); vec4 z11 = vec4(c11.a, c12.a, c13.a, c14.a); vec4 z12 = vec4(c15.a, c16.a, c17.a, c18.a); vec4 z21 = vec4(c21.a, c22.a, c23.a, c24.a); vec4 z22 = vec4(c25.a, c26.a, c27.a, c28.a); vec4 background01 = vec4(greaterThan(z01, bckgndThreshold)); vec4 background02 = vec4(greaterThan(z02, bckgndThreshold)); vec4 background11 = vec4(greaterThan(z11, bckgndThreshold)); vec4 background12 = vec4(greaterThan(z12, bckgndThreshold)); vec4 background21 = vec4(greaterThan(z21, bckgndThreshold)); vec4 background22 = vec4(greaterThan(z22, bckgndThreshold)); // Current pixel values, packed for parallel diff. vec4 rn = vec4(c.r); vec4 gn = vec4(c.g); vec4 bn = vec4(c.b); // Use naive RGB diff for first prototype to evaluate similar colors. vec4 r01 = vec4(c01.r, c02.r, c03.r, c04.r); vec4 r02 = vec4(c05.r, c06.r, c07.r, c08.r); vec4 g01 = vec4(c01.g, c02.g, c03.g, c04.g); vec4 g02 = vec4(c05.g, c06.g, c07.g, c08.g); vec4 b01 = vec4(c01.b, c02.b, c03.b, c04.b); vec4 b02 = vec4(c05.b, c06.b, c07.b, c08.b); vec4 diffr1 = abs(r01 - rn - g01 + gn); vec4 diffr2 = abs(r02 - rn - g02 + gn); vec4 diffg1 = max(abs(g01 - gn - b01 + bn), abs(r01 - rn) * k); vec4 diffg2 = max(abs(g02 - gn - b02 + bn), abs(r02 - rn) * k); vec4 diffb1 = max(abs(b01 - bn), abs(g01 - gn)) * k; vec4 diffb2 = max(abs(b02 - bn), abs(g02 - gn)) * k; vec4 sim01 = vec4(lessThan(max(max(diffr1, diffg1), diffb1), similarThreshold)); vec4 sim02 = vec4(lessThan(max(max(diffr2, diffg2), diffb2), similarThreshold)); vec4 r11 = vec4(c11.r, c12.r, c13.r, c14.r); vec4 r12 = vec4(c15.r, c16.r, c17.r, c18.r); vec4 g11 = vec4(c11.g, c12.g, c13.g, c14.g); vec4 g12 = vec4(c15.g, c16.g, c17.g, c18.g); vec4 b11 = vec4(c11.b, c12.b, c13.b, c14.b); vec4 b12 = vec4(c15.b, c16.b, c17.b, c18.b); diffr1 = abs(r11 - rn - g11 + gn); diffr2 = abs(r12 - rn - g12 + gn); diffg1 = max(abs(g11 - gn - b11 + bn), abs(r11 - rn) * k); diffg2 = max(abs(g12 - gn - b12 + bn), abs(r12 - rn) * k); diffb1 = max(abs(b11 - bn), abs(g11 - gn)) * k; diffb2 = max(abs(b12 - bn), abs(g12 - gn)) * k; vec4 sim11 = vec4(lessThan(max(max(diffr1, diffg1), diffb1), similarThreshold)); vec4 sim12 = vec4(lessThan(max(max(diffr2, diffg2), diffb2), similarThreshold)); vec4 r21 = vec4(c21.r, c22.r, c23.r, c24.r); vec4 r22 = vec4(c25.r, c26.r, c27.r, c28.r); vec4 g21 = vec4(c21.g, c22.g, c23.g, c24.g); vec4 g22 = vec4(c25.g, c26.g, c27.g, c28.g); vec4 b21 = vec4(c21.b, c22.b, c23.b, c24.b); vec4 b22 = vec4(c25.b, c26.b, c27.b, c28.b); diffr1 = abs(r21 - rn - g21 + gn); diffr2 = abs(r22 - rn - g22 + gn); diffg1 = max(abs(g21 - gn - b21 + bn), abs(r21 - rn) * k); diffg2 = max(abs(g22 - gn - b22 + bn), abs(r22 - rn) * k); diffb1 = max(abs(b21 - bn), abs(g21 - gn)) * k; diffb2 = max(abs(b22 - bn), abs(g22 - gn)) * k; vec4 sim21 = vec4(lessThan(max(max(diffr1, diffg1), diffb1), similarThreshold)); vec4 sim22 = vec4(lessThan(max(max(diffr2, diffg2), diffb2), similarThreshold)); vec4 sb11 = sim11 * background11; vec4 sb12 = sim12 * background12; vec4 sb01 = sim01 * background01; vec4 sb02 = sim02 * background02; vec4 sdocfodbz1 = sim01 * background01 + sim11 * (background11 + sim21 * background21); vec4 sdocfodbz2 = sim02 * background02 + sim12 * (background12 + sim22 * background22); if (dot(sdocfodbz1, sdocfodbz1) + dot(sdocfodbz2, sdocfodbz2) > 0.0) { float coef = 0.3; float count1 = dot(sb11, sb11) + dot(sb12, sb12) + coef; float count0 = dot(sb01, sb01) + dot(sb02, sb02) + coef; vec3 ch = c.rgb * coef; if (count0 > coef) { float rf = dot(sb01, r01) + dot(sb02, r02); float gf = dot(sb01, g01) + dot(sb02, g02); float bf = dot(sb01, b01) + dot(sb02, b02); ch = (ch + vec3(rf, gf, bf)) / count0; fragColor = vec4(ch, 0.9804); // 0.9804 is for debugging. return; } float rf = dot(sb11, r11) + dot(sb12, r12); float gf = dot(sb11, g11) + dot(sb12, g12); float bf = dot(sb11, b11) + dot(sb12, b12); ch = (ch + vec3(rf, gf, bf)) / count1; fragColor = vec4(ch, count1 > coef ? 0.9804 : 1.0); return; } }`; const cleanupVertex = `#version 300 es in vec2 v; out vec2 t; void main(){ gl_Position = vec4(v.x * 2.0 - 1.0, v.y * 2.0 - 1.0, 0, 1); t = v; }`; const cleanupPixel = `#version 300 es precision mediump float; uniform sampler2D s; uniform vec4 dd; uniform vec4 mappedRectangle; in vec2 t; out vec4 fragColor; const vec4 range = vec4(0.9); void main(){ vec4 c = texture(s, t); vec4 d1 = vec4(texture(s, t + dd.ra).a, texture(s, t + dd.rg).a, texture(s, t + dd.ag).a, texture(s, t + dd.bg).a); vec4 d2 = vec4(texture(s, t - dd.ra).a, texture(s, t - dd.rg).a, texture(s, t - dd.ag).a, texture(s, t - dd.bg).a); d1 = vec4(lessThan(d1, range)); d2 = vec4(lessThan(d2, range)); float count = dot(d1, d1) + dot(d2, d2); c.a = (c.a < range.x) ? (count <= 3.0 ? 0.01 : count <= 6.0 ? 0.03 : c.a) : (c.a > range.x && count >= 5.0) ? 0.5 : c.a; float a = t.x < mappedRectangle.x ? 1.0 : c.a; fragColor = vec4(c.rgb, a); }`; const renderVertex = ` attribute vec2 v; varying vec2 t; void main(){ gl_Position = vec4(v.x * 2.0 - 1.0, -v.y * 2.0 + 1.0, 0, 1); t = v; }`; const renderPixel = ` precision mediump float; uniform sampler2D s; uniform sampler2D sBackground; uniform float backgroundMode; uniform sampler2D backgroundVideo; uniform vec2 backgroundVideoScale; varying vec2 t; const vec4 rgbmask = vec4(1.0, 1.0, 1.0, 0.0); const float range = 0.9; void main(){ vec4 tex = texture2D(s, t); vec2 bs = t * backgroundVideoScale + (vec2(1.0) - backgroundVideoScale) * 0.5; vec4 video = texture2D(backgroundVideo, bs); gl_FragColor = (tex.a < range) ? tex : vec4(0.0); float alpha = tex.a > 0.0 ? tex.a < 0.015 ? 0.0 : (tex.a < 0.0350 ? 0.3 : 1.0) : 1.0; vec4 background = vec4(0.0); if (tex.a == 1.0) { background = vec4(1.0, 0.6, 0.1, 1.0);; } else if (tex.a > 0.99) background = vec4(1.0, 0.5, 0.5, 1.0); else if (tex.a > 0.9803) background = vec4(0.3, 0.8, 1.0, 1.0); else if (tex.a > 0.972) background = vec4(0.5, 0.5, 0.7, 1.0); else if (tex.a == 0.0) background = vec4(1.0, 1.0, 0.0, 1.0); else if (tex.a > range) background = vec4(0.0, 1.0, 1.0, 1.0); else if (tex.a > 0.035 && tex.a < 0.05) background = vec4(1.0, 0.0, 0.0, 1.0); background = vec4(0.0, 1.0, 1.0, 1.0); background = backgroundMode == 2.0 ? video : background; alpha = backgroundMode == 0.0 ? 1.0 : tex.a > range ? 0.0 : alpha; // Square the RGB values to revert noHolesPixel's gamma approximation. gl_FragColor = mix(background, vec4(tex.rgb * tex.rgb, 1.0), alpha); }`; function createProgram(gl, vs, ps) { var vertex_shader = gl.createShader(gl.VERTEX_SHADER); gl.shaderSource(vertex_shader, vs); gl.compileShader(vertex_shader); var pixel_shader = gl.createShader(gl.FRAGMENT_SHADER); gl.shaderSource(pixel_shader, ps); gl.compileShader(pixel_shader); var program = gl.createProgram(); gl.attachShader(program, vertex_shader); gl.attachShader(program, pixel_shader); gl.linkProgram(program); const vinfo = gl.getShaderInfoLog(vertex_shader); const pinfo = gl.getShaderInfoLog(pixel_shader); if (vinfo.length > 0) console.error(vinfo); if (pinfo.length > 0) console.error(pinfo); gl.useProgram(program); const vertex_location = gl.getAttribLocation(program, "v"); if (vertex_location == -1) return program; gl.enableVertexAttribArray(vertex_location); program.vertex_location = vertex_location; gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer); gl.vertexAttribPointer(vertex_location, 2, gl.FLOAT, false, 0, 0); return program; } return { noHoles: createProgram(gl, noHolesVertex, noHolesPixel), reduceBlack: createProgram(gl, reduceBlackVertex, reduceBlackPixel), cleanup: createProgram(gl, cleanupVertex, cleanupPixel), render: createProgram(gl, renderVertex, renderPixel), } } setup(gl, cameraParams, depthW, depthH, colorW, colorH) { const createFramebuffer2D = (gl, textureList) => { const framebuffer = gl.createFramebuffer(); const drawBuffers = []; gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer); for (let i = 0; i < textureList.length; i += 1) { const texture = textureList[i]; drawBuffers.push(gl[`COLOR_ATTACHMENT${i}`]); gl.framebufferTexture2D( gl.FRAMEBUFFER, gl[`COLOR_ATTACHMENT${i}`], gl.TEXTURE_2D, texture, 0, // mip-map level ); } gl.drawBuffers(drawBuffers); return framebuffer; } if (!this.textures) this.textures = this.setupTextures(gl, this.programs, depthW, depthH, colorW, colorH); this.initUniforms(gl, cameraParams, depthW, depthH); const textures = this.textures; const sk = 11; const push = 32; // init passes with framebuffers if (!this.passes) { this.passes = [{ framebuffer: createFramebuffer2D(gl, [textures.noHoles, textures.background]), program: this.programs.noHoles, }, { in: textures.noHoles, samplingStep: [sk / colorW, sk / colorH, -sk / colorW, 0.0], framebuffer: createFramebuffer2D(gl, [textures.reduceBlack[0]]), program: this.programs.reduceBlack, }, { in: textures.reduceBlack[0], samplingStep: [sk / colorW, sk / colorH, -sk / colorW, 0.0], framebuffer: createFramebuffer2D(gl, [textures.reduceBlack[1]]), program: this.programs.reduceBlack, }, { in: textures.reduceBlack[1], samplingStep: [(sk - 2) / colorW, (sk - 2) / colorH, -(sk - 2) / colorW, 0.0], framebuffer: createFramebuffer2D(gl, [textures.reduceBlack[2]]), program: this.programs.reduceBlack, }, { in: textures.reduceBlack[2], samplingStep: [(sk - 4) / colorW, (sk - 4) / colorH, -(sk - 4) / colorW, 0.0], framebuffer: createFramebuffer2D(gl, [textures.reduceBlack[3]]), program: this.programs.reduceBlack, }, { in: textures.reduceBlack[3], samplingStep: [(sk - 5) / colorW, (sk - 5) / colorH, -(sk - 5) / colorW, 0.0], framebuffer: createFramebuffer2D(gl, [textures.reduceBlack[4]]), program: this.programs.reduceBlack, }, { in: textures.reduceBlack[4], samplingStep: [(sk - 7) / colorW, (sk - 7) / colorH, -(sk - 7) / colorW, 0.0], framebuffer: createFramebuffer2D(gl, [textures.reduceBlack[5]]), program: this.programs.reduceBlack, }, { in: textures.reduceBlack[5], samplingStep: [(sk - 7) / colorW, (sk - 7) / colorH, -(sk - 7) / colorW, 0.0], framebuffer: createFramebuffer2D(gl, [textures.reduceBlack[6]]), program: this.programs.reduceBlack, }, { samplingStep: [2 / colorW, 2 / colorH, -2 / colorW, 0.0], framebuffer: createFramebuffer2D(gl, [textures.cleanup]), program: this.programs.cleanup, }, { framebuffer: null, program: this.programs.render }]; } this.initAttributes(gl); } initAttributes(gl) { gl.bindVertexArray(this.vao); gl.useProgram(this.programs.render); gl.bindBuffer(gl.ARRAY_BUFFER, this.vertex_buffer); gl.vertexAttribPointer(this.programs.render.vertex_location, 2, gl.FLOAT, false, 0, 0); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.index_buffer); } initUniforms(gl, cameraParams, width, height) { const textures = this.textures; const color = textures.color; const intrin = cameraParams.getDepthIntrinsics(width, height); const colorIntrin = cameraParams.getColorIntrinsics(color.w, color.h); const offsetx = (intrin.offset[0] / width); const offsety = (intrin.offset[1] / height); const focalxinv = width / intrin.focalLength[0]; const focalyinv = height / intrin.focalLength[1]; const focalx = intrin.focalLength[0] / width; const focaly = intrin.focalLength[1] / height; const coloroffsetx = colorIntrin.offset[0] / color.w; const coloroffsety = colorIntrin.offset[1] / color.h; const colorfocalx = colorIntrin.focalLength[0] / color.w; const colorfocaly = colorIntrin.focalLength[1] / color.h; const colorfocalxinv = color.w / colorIntrin.focalLength[0]; const colorfocalyinv = color.h / colorIntrin.focalLength[1]; // Shaders asume const range up to 0.9, in order to express the depth using // color alpha channel. const range = 1.5; // meaning 1.1 meters away from camera should be hidden. const scale = cameraParams.depthScale * 0.9 / range; const noHoles = this.programs.noHoles; gl.useProgram(noHoles); noHoles.sDepth = gl.getUniformLocation(noHoles, "sDepth"); noHoles.sPreviousDepth = gl.getUniformLocation(noHoles, "sPreviousDepth"); noHoles.sColor = gl.getUniformLocation(noHoles, "sColor"); noHoles.sPreviousBackground = gl.getUniformLocation(noHoles, "sPreviousBackground"); gl.uniform3f(gl.getUniformLocation(noHoles, 'ddDepth'), 5 / width, 5 / height, 0); gl.uniform3f(gl.getUniformLocation(noHoles, 'dd'), 1 / color.w, 1 / color.h, 0); gl.uniform1f(gl.getUniformLocation(noHoles, 'depthScale'), scale); gl.uniform2f(gl.getUniformLocation(noHoles, 'depthFocalLength'), focalx, focaly); gl.uniform2f(gl.getUniformLocation(noHoles, 'depthOffset'), offsetx, offsety); gl.uniform2f(gl.getUniformLocation(noHoles, 'colorFocalLengthInv'), colorfocalxinv, colorfocalyinv); gl.uniform2f(gl.getUniformLocation(noHoles, 'colorOffset'), coloroffsetx, coloroffsety); gl.uniformMatrix4fv(gl.getUniformLocation(noHoles, "colorToDepth"), false, cameraParams.colorToDepth); const reduceBlack = this.programs.reduceBlack; gl.useProgram(reduceBlack); reduceBlack.s = gl.getUniformLocation(reduceBlack, "s"); reduceBlack.samplingStep = gl.getUniformLocation(reduceBlack, "samplingStep"); const cleanup = this.programs.cleanup; gl.useProgram(cleanup); const last = textures.reduceBlack[REDUCE_BLACK_PASSES - 1]; gl.uniform1i(gl.getUniformLocation(cleanup, "s"), last.unit); gl.uniform4f(gl.getUniformLocation(cleanup, 'mappedRectangle'), cameraParams.cameraName == "D415" ? 0.08 : 0, 0, 1, 1); gl.uniform4f(gl.getUniformLocation(cleanup, 'dd'), 1 / color.w, 1 / color.h, -1 / color.w, 0); const render = this.programs.render; gl.useProgram(render); gl.uniform1i(gl.getUniformLocation(render, "s"), textures.cleanup.unit); gl.uniform1i(gl.getUniformLocation(render, "backgroundVideo"), textures.backgroundVideo.unit); gl.uniform2f(gl.getUniformLocation(render, "backgroundVideoScale"), (textures.backgroundVideo.h / color.h) / (textures.backgroundVideo.w / color.w) , 1.0); render.backgroundMode = gl.getUniformLocation(render, "backgroundMode"); render.sBackground = gl.getUniformLocation(render, "sBackground"); } // it is loaded externally. setDepthVideo(video) { this.depthVideo = video; if (video.videoWidth > 2) { this.depthVideo.video_loaded = true; return; } video.oncanplay = function() { video.video_loaded=true; } } async play() { const cameraParams = await this.setupCamera(this.depthVideo).catch((error) => { console.error(error); }); let frame = 0; let textures; const colorVideo = this.colorVideo; const depthVideo = this.depthVideo; const programs = this.programs; const renderer = this; const gl = renderer.gl; let width = 0; let height = 0; let currentDepthTime = 0; let currentColorTime = 0; let currentBackgroundVideoTime = 0; let this_ = this; if (this.paused == this.PAUSE_REQUESTED) { // if we get new play before paused is fulfilled, avoid second // requestAnimationFrame issue; this.paused = 0; return; } else if (this.paused == 0) { console.error("DCHECK failed on paused"); return; } // Run for each frame. Will do nothing if the camera is not ready yet. const animate = function () { if (depthVideo.video_loaded && colorVideo.video_loaded) { if (frame === 0) { width = depthVideo.videoWidth; height = depthVideo.videoHeight; renderer.setup(gl, cameraParams, width, height, colorVideo.videoWidth, colorVideo.videoHeight); textures = renderer.textures; } try { if (depthVideo.currentTime != currentDepthTime) { const temp = textures.depth; textures.depth = textures.previousDepth; textures.previousDepth = temp; currentDepthTime = depthVideo.currentTime; gl.activeTexture(gl[`TEXTURE${textures.depth.unit}`]); gl.bindTexture(gl.TEXTURE_2D, textures.depth); gl.texSubImage2D( gl.TEXTURE_2D, 0, // mip-map level 0, // x-offset 0, // y-offset width, height, gl.RED, gl.FLOAT, depthVideo, ); } if (colorVideo.currentTime != currentColorTime) { const temp = textures.background; textures.previousBackground = textures.background; textures.background = textures.previousBackground; gl.bindFramebuffer(gl.FRAMEBUFFER, renderer.passes[0].framebuffer); gl.framebufferTexture2D( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT1, gl.TEXTURE_2D, textures.background, 0, // mip-map level ); currentColorTime = colorVideo.currentTime; gl.activeTexture(gl[`TEXTURE${textures.color.unit}`]); gl.bindTexture(gl.TEXTURE_2D, textures.color); gl.texSubImage2D( gl.TEXTURE_2D, 0, // mip-map level 0, // x-offset 0, // y-offset colorVideo.videoWidth, colorVideo.videoHeight, gl.RGBA, gl.UNSIGNED_BYTE, colorVideo, ); } const back = this_.backgroundVideoElement; if (this_.backgroundVideo && currentBackgroundVideoTime != back.currentTime) { currentBackgroundVideoTime = back.currentTime; gl.activeTexture(gl[`TEXTURE${textures.backgroundVideo.unit}`]); gl.bindTexture(gl.TEXTURE_2D, textures.backgroundVideo); gl.texSubImage2D( gl.TEXTURE_2D, 0, // mip-map level 0, // x-offset 0, // y-offset back.videoWidth, back.videoHeight, gl.RGBA, gl.UNSIGNED_BYTE, back, ); } } catch (e) { console.error(`Error uploading video to WebGL: ${e.name}, ${e.message}`); } let l; let program; gl.bindVertexArray(renderer.vao); for (let i = 0; i < renderer.passes.length; ++i) { const pass = renderer.passes[i]; // comment previous two lines and uncomment following to measure // latency of rendering only // { const pass = gl.passes[6]; gl.useProgram(pass.program); if (pass.in && pass.program.s) gl.uniform1i(pass.program.s, pass.in.unit); if (pass.samplingStep && pass.program.samplingStep) gl.uniform4fv(pass.program.samplingStep, pass.samplingStep); if (pass.program.sDepth) gl.uniform1i(pass.program.sDepth, textures.depth.unit); if (pass.program.sPreviousDepth) gl.uniform1i(pass.program.sPreviousDepth, textures.previousDepth.unit); if (pass.program.sColor) gl.uniform1i(pass.program.sColor, textures.color.unit); if (pass.program.sPreviousBackground) gl.uniform1i(pass.program.sPreviousBackground, textures.previousBackground.unit); if (pass.program.sBackground) gl.uniform1i(pass.program.sBackground, textures.background.unit); if (pass.program.backgroundMode) { gl.uniform1f(pass.program.backgroundMode, this_.backgroundColor ? 1 : this_.backgroundVideo ? 2 : 0); } gl.bindFramebuffer(gl.FRAMEBUFFER, pass.framebuffer); gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0); } frame += 1; } if (renderer.paused == renderer.PAUSE_REQUESTED) { renderer.paused = renderer.PAUSED; return; } window.requestAnimationFrame(animate); }; animate(); } pause() { this.paused = 2; } } ================================================ FILE: depthdemo.html ================================================

Depth Capture Demo

background video
background color
================================================ FILE: gesture/depth_and_segments.js ================================================ /*jshint esversion: 6 */ // Copyright 2017 Intel Corporation. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. const INTERPOLATE_INV = 1 / 20.0; // interpolate depth physics at 20 pixels. class DepthAndSegments { constructor(gl, drawGL = null) { this.gl = gl; gl.depth_tex_unit = gl.depth_tex_unit | gl.TEXTURE0; initGL(gl, drawGL); reload(); // this.createDepthInfoCanvas(); this.out = {}; this.out1 = {}; this.out.segment_data = {}; this.out1.segment_data = {}; this.transform_feedback_draw_done = false; this.gbsd_async_ready = false; this.width = 640; this.height = 480; } // In case when we use one WebGL context for processing (WebGL 2.0) and // another |drawGL| for rendering depth, we upload depth texture to both. // process(drawGL) { if (!video_loaded) return false; if (!init_done) { this.videoLoaded(video, window.stream); init_done = true; } const gl = this.gl; if (this.transform_feedback_draw_done) { // This is used only when WEBGL_get_buffer_sub_data_async extension is not // available. gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, gl.tf_bo); gl.getBufferSubData(gl.TRANSFORM_FEEDBACK_BUFFER, 0, tf_output, 0, tf_output.length); gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, null); processOnCPU(); this.identifyJointsAndFixNoise(out.segment_data); this.out1 = this.out; this.out = out; putReadPixelsToTestCanvas(this.testContext); this.transform_feedback_draw_done = false; return true; } let processed = false; if (this.gbsd_async_ready) { // gbsd = getBufferSubData. Process the results from the previous frame // make asynchronous request for this frame data below. this.gbsd_async_ready = false; processOnCPU(); this.identifyJointsAndFixNoise(out.segment_data); this.out1 = this.out; this.out = out; putReadPixelsToTestCanvas(this.testContext); processed = true; } if (video_last_upload_time == video.currentTime) { return processed; } video_last_upload_time = video.currentTime; gl.activeTexture(gl.depth_tex_unit); gl.bindTexture(gl.TEXTURE_2D, gl.depth_texture); // Upload the video frame to texture. if (gl.color_buffer_float_ext) { gl.texImage2D(gl.TEXTURE_2D, 0, gl.R32F, gl.RED, gl.FLOAT, video); } else { gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA32F, gl.RGBA, gl.FLOAT, video); } if (drawGL && drawGL != gl) { drawGL.activeTexture(gl.depth_tex_unit); drawGL.bindTexture(gl.TEXTURE_2D, drawGL.depth_texture); drawGL.texImage2D(drawGL.TEXTURE_2D, 0, drawGL.RGBA, drawGL.RGBA, drawGL.FLOAT, video); } gl.enable(gl.RASTERIZER_DISCARD); gl.useProgram(gl.compute_program); gl.bindVertexArray(gl.depth_vao); gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, gl.transform_feedback) gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, gl.tf_bo) gl.beginTransformFeedback(gl.POINTS); gl.drawArrays(gl.POINTS, 0, tf_output.length); gl.endTransformFeedback(); gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, null); gl.bindVertexArray(null); gl.disable(gl.RASTERIZER_DISCARD); this.transform_feedback_draw_done = !gl.WEBGL_get_buffer_sub_data_async; if (gl.WEBGL_get_buffer_sub_data_async) { gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, gl.tf_bo); this.gbsd_async_ready = false; const this_ = this; gl.WEBGL_get_buffer_sub_data_async.getBufferSubDataAsync( gl.TRANSFORM_FEEDBACK_BUFFER, 0, tf_output, 0, tf_output.length). then(function(buffer) { this_.gbsd_async_ready = true; }); gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, null); } return processed; } getMVPMatrix() { return getMvpMatrix(window.innerWidth, window.innerHeight); } draw(mvp = null, lightmvp, light_position, shadow_map_unit) { const gl = this.gl; if (!video_loaded) return; gl.useProgram(gl.render_program); gl.uniformMatrix4fv(gl.render_u_mvp, false, mvp || this.getMVPMatrix()); const point_size = ((window.innerHeight / height) | 0) + 1; gl.uniform1f(gl.render_u_pointSize, point_size); if (lightmvp) { gl.uniformMatrix4fv(gl.u_mvp_from_light, false, lightmvp); gl.uniform1f(gl.u_draw_lighting, 1.0); gl.uniform1i(gl.render_u_shadow_map, shadow_map_unit); } else { // TODO: separate program for shadow rendering. gl.uniform1f(gl.u_draw_lighting, 0); // shadow map texture is bound to framebuffer. prevent loop as we use the // same program for lighting and read from the same texture. gl.uniform1i(gl.render_u_shadow_map, shadow_map_unit + 1); } if (light_position) gl.uniform3fv(gl.render_u_light_position, light_position); gl.bindVertexArray(gl.depth_vao); gl.activeTexture(gl.depth_tex_unit); gl.bindTexture(gl.TEXTURE_2D, gl.depth_texture); gl.drawArrays(gl.POINTS, 0, width * height); gl.bindVertexArray(null); } createDepthInfoCanvas() { var canvas = document.createElement('canvas'); canvas.id = "testCanvas2D"; canvas.width = 640; canvas.height = 480; canvas.style.zIndex = 8; canvas.style.position = "absolute"; canvas.style.border = "1px solid"; var body = document.getElementsByTagName("body")[0]; body.appendChild(canvas); this.testContext = canvas.getContext("2d"); } videoLoaded(video, stream) { const gl = this.gl; gl.bindBuffer(gl.ARRAY_BUFFER, gl.tf_bo); tf_output = new Float32Array(video.videoWidth * video.videoHeight); gl.bufferData(gl.ARRAY_BUFFER, tf_output.length * 4, gl.DYNAMIC_READ); gl.bindBuffer(gl.ARRAY_BUFFER, null); links = new Array(video.videoWidth * video.videoHeight).fill(-1); width = video.videoWidth; height = video.videoHeight; this.width = width; this.height = height; try { this.setCameraParameters(DepthCamera.getCameraCalibration(stream)); } catch(e) { return handleError(e); } if (this.depthVideoLoadedCallback) this.depthVideoLoadedCallback(); } setCameraParameters(parameters) { const gl = this.gl; const nearplane = 0.0; const farplane = 0.75 / parameters.depthScale; const program = gl.render_program; gl.useProgram(program); let shaderVar = gl.getUniformLocation(program, "u_depth_scale"); gl.uniform1f(shaderVar, parameters.depthScale); const depthIntrinsics = parameters.getDepthIntrinsics(width, height); shaderVar = gl.getUniformLocation(program, "u_depth_focal_length_inv"); const inv_focal_length = [1 / depthIntrinsics.focalLength[0], 1 / depthIntrinsics.focalLength[1]]; gl.uniform2fv(shaderVar, inv_focal_length); shaderVar = gl.getUniformLocation(program, "u_depth_offset"); gl.uniform2fv(shaderVar, depthIntrinsics.offset); gl.uniform2f(gl.getUniformLocation(program, "u_depth_texture_size"), width, height); var shaderDepthTexture = gl.getUniformLocation(program, "u_depth_texture"); gl.uniform1i(shaderDepthTexture, gl.depth_tex_unit - gl.TEXTURE0); this.depth_focal_inv = [1 / depthIntrinsics.focalLength[0], 1 / depthIntrinsics.focalLength[1]]; this.depth_offset = depthIntrinsics.offset; this.depth_scale = parameters.depthScale; gl.useProgram(gl.compute_program); gl.uniform2f(gl.getUniformLocation(gl.compute_program, "u_plane"), nearplane, farplane); gl.uniform2f(gl.getUniformLocation(gl.compute_program, "u_depth_size"), width, height); // Coefficient is used to calculate the width of finger (in pixels) on given // distance (depth) from camera. Assume that finger is wider than 0.6 cm. const finger_half_width = 0.0027 * depthIntrinsics.focalLength[0] / parameters.depthScale; gl.uniform1f(gl.getUniformLocation(gl.compute_program, "finger_half_width"), finger_half_width); // 5 cm for longest finger segment. Fingers usually have 2-3 of those. segment_coef = 0.05 * depthIntrinsics.focalLength[0] / parameters.depthScale; this.depth_coef = this.depth_scale * this.depth_focal_inv[0]; // cache the computed } setXZFlip(value) { const gl = this.gl; gl.useProgram(gl.render_program); gl.uniform1f(gl.render_u_xz_flip, value ? 0.0 : 1.0); gl.useProgram(gl.compute_program); gl.uniform1f(gl.compute_u_xz_flip, value ? 0.0 : 1.0); } identifyJointsAndFixNoise(segment_data) { // in segment data, identify end points that are joints. let keys = Object.keys(segment_data); function square(p) { return p * p; } function pointsNear(p, seg1, coef, scale) { const distance = square(coef * (seg1.x - p.x)) + square(coef * (seg1.y - p.y)) + square(scale * (seg1.depth - p.depth)); return distance < 0.0009; } for (let k = 0; k < keys.length; k++) { const seg0 = segment_data[keys[k]]; const fl = seg0.far_left; const fr = seg0.far_right; for (let l = k + 1; l < keys.length; l++) { const seg1 = segment_data[keys[l]]; // rough/fast estimation on arbitrary threshold. const coef = seg1.depth * this.depth_coef; const scale = this.depth_scale; if (pointsNear(fl, seg1, coef, scale)) { fl.joint = seg1.index; seg1.joint = fl.index; } if (pointsNear(fr, seg1, coef, scale)) { fr.joint = seg1.index; seg1.joint = fr.index; } } // check the special cases: vertical and horizontal orientation. const maxy = Math.max(fr.y, fl.y); if (fr.x - fl.x < 0.3 * (maxy - seg0.y)) { // vertical thing, use the furthest one. const in_the_middle = (fr.y < fl.y) ? fr : fl; in_the_middle.joint = in_the_middle.index; } else { const ignore = seg0.count_right < seg0.count_left ? fr : fl; ignore.joint = ignore.index; } } // For endpoints that are not joints, we average using center points where // available. function useCenterIfAvailable(p) { if (p.center.x == -1) return; p.x_original = p.x; p.y_original = p.y; p.x = p.center.x; p.y = p.center.y; p.index = p.x + p.y * width; p.depth = modf(tf_output[p.index]); } function moveAwayFromEdge(p, to) { // Limit the movement to 5mm. const pixels = segment_coef_5mm / p.depth; let xstep = 1; let ystep = 1; let steps = pixels; const xdiff = Math.abs(p.x - to.x); const ydiff = Math.abs(p.y - to.y); if (xdiff < 2 && ydiff < 2) return; const hypotenuse = Math.sqrt(xdiff * xdiff + ydiff * ydiff); if (xdiff > ydiff) { xstep = Math.sign(p.x - to.x); ystep = (p.y - to.y) / hypotenuse; steps = pixels * xdiff / hypotenuse; } else { ystep = Math.sign(p.y - to.y); xstep = (p.x - to.x) / hypotenuse; steps = pixels * ydiff / hypotenuse; } let hitedge = false; let x = p.x + 0.5; let y = p.y + 0.5; let i = 1; while (i < steps) { x += xstep; y += ystep; if (tf_output[(y | 0) * width + (x | 0)] == 0) { break; } i++; } if (i >= steps) return; x = p.x - xstep * (steps - i); y = p.y - ystep * (steps - i); p.x = (x + 0.5) | 0; p.y = (y + 0.5) | 0; } const segment_coef_5mm = segment_coef * 0.12; for (let k = 0; k < keys.length; k++) { const seg0 = segment_data[keys[k]]; if (!seg0.far_left.hasOwnProperty("joint")) useCenterIfAvailable(seg0.far_left); if (!seg0.far_right.hasOwnProperty("joint")) useCenterIfAvailable(seg0.far_right); // Move ends away from the edge. const end = seg0.far_left.hasOwnProperty("joint") ? seg0.far_right : seg0.far_left; if (!end.hasOwnProperty("joint")) moveAwayFromEdge(end, seg0); if (!seg0.hasOwnProperty("joint")) moveAwayFromEdge(seg0, end); } } // Non skeleton means that client knows the depth value is not on skeleton and // here we know it is encoded as negative. Saves one abs - premature optimization :) getDepthNonSkeleton(x, y) { return modf(-tf_output[y * width + x]); } getSegmentInterpolatedCount(segment) { const end = this.getSegmentEnd(segment); return (end.distance2D * INTERPOLATE_INV) | 0; } // vec is float[3]: x, y in pixels define the screen point and depth is the captured // value at the point. getInterpolatedPoint(vec, segment, i) { const end = this.getSegmentEnd(segment); const coef = (i + 1) / (end.distance2D * INTERPOLATE_INV); vec[0] = (segment.x + coef * (end.x - segment.x)) | 0; vec[1] = (segment.y + coef * (end.y - segment.y)) | 0; vec[2] = segment.depth + coef * (end.depth - segment.depth); } getSegmentEnd(seg) { // TODO: move this to identifyJoints if used always. let end = (seg.count_left > seg.count_right) ? seg.far_left : seg.far_right; const jl = seg.far_left.hasOwnProperty("joint"); const jr = seg.far_right.hasOwnProperty("joint"); if (jl && !jr) end = seg.far_right; else if (jr && !jl) end = seg.far_left; if (!end.distance2D) { const x = end.x - seg.x; const y = end.y - seg.y; end.distance2D = Math.sqrt(x * x + y * y); } return end; } } var video_loaded = false; function handleError(error) { if (error.name == 'TrackStartError' || error.name == 'UnsupportedSizeError') { return reload(); } if (error.name == "OverconstrainedError" && error.constraint == "videoKind") return console.error("No device with \"videoKind == depth\" capture available."); console.error(error); } // Offscreen |video| we use to upload depth content to WebGL texture. let init_done = false; let video_last_upload_time = -1; let video = createDepthVideo(); function createDepthVideo() { var video = document.createElement("video"); video.autoplay = true; video.loop = true; video.crossOrigin = "anonymous"; video.width = 640; video.height = 480; video.oncanplay = function(){ video_loaded=true; init_done = false; }; return video; } function reload() { if (window.stream) { window.stream.getTracks().forEach(function(track) { track.stop(); }); } function streamOpened(stream) { video.srcObject = stream; window.stream = stream; video_loaded = false; init_done = false; retrycount = 2; }; DepthCamera.getDepthStream().then(streamOpened).catch(handleError); } function putReadPixelsToTestCanvas(testContext) { if (testContext == undefined) return; const img = testContext.getImageData(0, 0, video.width, video.height); const data = img.data; const segment_data = out.segment_data; for (let i = 0, j = 0; i < data.length; i += 4) { let val = tf_output[i / 4]; let depth = val > 0.0 ? 110 : (modf(-val) * 1200); if (val < 0 && links[i / 4] != -1) depth = 60; // visited points. data[i] = depth; data[i + 1] = depth; data[i + 1] = depth; if (val > -1) data[i + 2] = depth; else data[i + 2] = 0; data[i+3] = 255; } testContext.putImageData(img, 0, 0); // draw the connected segments testContext.strokeStyle="#00FF00"; let keys = Object.keys(segment_data); for (let k = 0; k < keys.length; k++) { const keystring = keys[k]; const segment = segment_data[keystring]; const index = parseInt(keystring); let strokeStyle = "#00FF00"; if (segment.hasOwnProperty("discarded")) strokeStyle="#FF0000"; const column = index % width; const row = Math.floor(index / width); if (column !== segment.x && row !== segment.y) console.log("error column/row wrong"); let item = segment.far_left; if (item.index !== undefined) { if (item.hasOwnProperty("joint")) testContext.strokeStyle = "#AAAA00"; else testContext.strokeStyle = strokeStyle; testContext.beginPath(); testContext.fillStyle="#FFFF00"; testContext.fillRect(column, row, 2, 2); if (item.hasOwnProperty("x_original")) { testContext.fillStyle="#007F7F"; testContext.fillRect(item.x_original, item.y_original, 2, 2); testContext.fillStyle="#FFFFFF"; } else { testContext.fillStyle="#0000FF"; } testContext.fillRect(item.x, item.y, 2, 2); testContext.moveTo(column, row); testContext.lineTo(item.x, item.y); testContext.stroke(); } item = segment.far_right; if (item.index !== undefined) { if (item.hasOwnProperty("joint")) testContext.strokeStyle="#AAAA00"; else testContext.strokeStyle = strokeStyle; testContext.beginPath(); testContext.moveTo(column, row); testContext.lineTo(item.x, item.y); if (item.hasOwnProperty("x_original")) { testContext.fillStyle="#7F7F00"; testContext.fillRect(item.x_original, item.y_original, 2, 2); testContext.fillStyle="#FFFFFF"; } else { testContext.fillStyle="#FF0000"; } testContext.fillRect(item.x, item.y, 2, 2); testContext.stroke(); } // Draw joints // testContext.fillStyle="#FF0000"; // testContext.fillRect(column, row, 2, 2); // testContext.stroke(); } const img1 = testContext.getImageData(0, 0, video.width, video.height); testContext.putImageData(img1, 0, 0); } // Creates WebGL/WebGL2 context used to upload depth video to texture. function initGL(gl, drawGL) { // EXT_color_buffer_float to use single component R32F texture format. gl.color_buffer_float_ext = gl.getExtension('EXT_color_buffer_float'); gl.WEBGL_get_buffer_sub_data_async = gl.getExtension("WEBGL_get_buffer_sub_data_async"); if (drawGL) { drawGL.color_buffer_float_ext = drawGL.getExtension('EXT_color_buffer_float') || drawGL.getExtension('OES_texture_float'); } if (!gl || !gl.color_buffer_float_ext) { alert("The depth capture demo doesn't run because it requires WebGL2 support with EXT_color_buffer_float."); return; } gl.enable(gl.BLEND); gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); // Transform feedback vertex shader is used for detecting "finger skeleton" // candidate points. Passes the result as "out depth": if depth is > 1.0 then // the CPU side of algorithm collects nearby skeleton points to segments. var tf_vertex_shader = gl.createShader(gl.VERTEX_SHADER); gl.shaderSource(tf_vertex_shader, `#version 300 es uniform sampler2D s_depth; uniform vec2 u_depth_size; uniform vec2 u_plane; uniform float finger_half_width; uniform float xz_flip; out float depth; void main() { vec2 depth_pixel; depth_pixel.x = mod(float(gl_VertexID), u_depth_size.x) + 0.5; depth_pixel.y = clamp(floor(float(gl_VertexID) / u_depth_size.x), 0.0, u_depth_size.y) + 0.5; vec2 tex_pos = depth_pixel / u_depth_size; // If camera faces towards user, mirror the display. if (xz_flip != 0.0) tex_pos.x = 1.0 - tex_pos.x; depth = texture(s_depth, tex_pos).r; if (depth <= u_plane.x || depth >= u_plane.y) { depth = 0.0; return; } vec2 step = vec2(1.0, 1.0) / u_depth_size; float d_0; float d_90; float d_180; float d_270; // Calculate max_width of the finger at the given distance. Asuming that finger is // wider than ~0.6cm, min_width_half is width (in pixels) related to 0.3 cm. float min_width_half = finger_half_width / depth; float max_width_half = min_width_half * 6.0; // Sample around and increase the distance of samples to the point. // The idea is that on distance D all the samples are inside the area // but on the distance D + 3, 3 or 4 out of 4 are outside the area. This // would make the point "fingertip point" (e.g part of the skeleton) for // the area. float width_step = min_width_half * 0.8; float inside_count = 4.0; float k = 0.0; float s_y = 1.0; float s_x = 1.0; float i = max(min_width_half * 0.19, 1.0); // 0.19 + 0.8 = 0.99, check k. for (; i < max_width_half; i += width_step, k++) { d_0 = texture(s_depth, tex_pos + vec2( i, 0.0) * step).r; d_90 = texture(s_depth, tex_pos - vec2( 0.0, i) * step).r; d_180 = texture(s_depth, tex_pos - vec2( i, 0.0) * step).r; d_270 = texture(s_depth, tex_pos + vec2( 0.0, i) * step).r; if (d_0 * d_90 * d_180 * d_270 == 0.0) { s_x = sign(d_0) + sign(d_180); s_y = sign(d_90) + sign(d_270); inside_count = s_x + s_y; break; } } // k > 2.0 serves to eliminate "thin" areas. We pass depth > 0 through // transform feedback, so that CPU side of algorithm would understands // that this point is "part of finger bone" point and process it further. if (k > 2.0 && inside_count <= 1.0) { depth = depth + k; return; } else if (k > 2.0 && inside_count == 2.0 && (s_x * s_y == 0.0)) return; // We also need large areas info as they are modeled using circles - as a // net of pearls. depth = -depth; if (inside_count > 3.0) depth -= 1.0; }` ); gl.compileShader(tf_vertex_shader); var tf_dummy_pixel_shader = gl.createShader(gl.FRAGMENT_SHADER); gl.shaderSource(tf_dummy_pixel_shader, `#version 300 es precision mediump float; in float depth; void main() { }` ); gl.compileShader(tf_dummy_pixel_shader); var compute_program = gl.createProgram(); gl.attachShader(compute_program, tf_vertex_shader); gl.attachShader(compute_program, tf_dummy_pixel_shader); gl.transformFeedbackVaryings(compute_program, ["depth"], gl.SEPARATE_ATTRIBS); gl.linkProgram(compute_program); console.log(gl.getShaderInfoLog(tf_vertex_shader)); gl.useProgram(compute_program); gl.depth_vao = gl.createVertexArray(); gl.bindVertexArray(gl.depth_vao); // To restore state of vertex attrib arrays const vattrib_count = Math.min(32, gl.getParameter(gl.MAX_VERTEX_ATTRIBS)); for (let i = 0; i < vattrib_count; i++) gl.disableVertexAttribArray(i); gl.bindVertexArray(null); var tf_bo = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, tf_bo); var transform_feedback = gl.createTransformFeedback(); gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, transform_feedback) gl.uniform1i(gl.getUniformLocation(compute_program, "s_depth"), gl.depth_tex_unit - gl.TEXTURE0); gl.compute_u_xz_flip = gl.getUniformLocation(compute_program, "xz_flip"); gl.uniform1i(gl.compute_u_xz_flip, 0); // 3D Pointcloud rendering. var vertex_shader = gl.createShader(gl.VERTEX_SHADER); gl.shaderSource(vertex_shader, `#version 300 es precision mediump float; // Run a vertex shader instance for each depth data point to create // 3D model of the data (pointcloud). //////////////////////////////////////////////////////////////////// // Parameters of the currently used camera, see // https://github.com/IntelRealSense/librealsense/blob/master/doc/projection.md // and the documentation at // https://w3c.github.io/mediacapture-depth/#synchronizing-depth-and-color-video-rendering // Used to convert the raw depth data into meters. // Corresponds to rs_get_device_depth_scale() in librealsense. uniform float u_depth_scale; // Center of projection of the depth camera data. uniform vec2 u_depth_offset; // Focal length of the depth data. uniform vec2 u_depth_focal_length_inv; //////////////////////////////////////////////////////////////////// // Model-View-Projection matrix. uniform mat4 u_mvp; uniform mat4 u_mvp_from_light; uniform float u_draw_lighting; uniform float u_pointSize; uniform float xz_flip; uniform sampler2D u_depth_texture; // Width and height of the depth data. uniform vec2 u_depth_texture_size; out vec3 v_normal; out vec3 v_position; out vec4 v_position_from_light; // Convert the index of the depth data (ranged from [0, 0] to // [u_depth_texture_size.x, u_depth_texture_size.y]) into a position // in 3D space. The depth parameter needs to be in meters. // This should be equivalent to what rs_deproject_pixel_to_point() // in librealsense does. vec4 depth_deproject(vec2 index, float depth) { vec2 position2d = (index - u_depth_offset) * u_depth_focal_length_inv; return vec4(position2d * depth, depth, 1.0); } void main() { // Get the texture coordinates in range from [0, 0] to [1, 1] vec2 depth_pixel; depth_pixel.x = mod(float(gl_VertexID), u_depth_texture_size.x) + 0.5; depth_pixel.y = clamp(floor(float(gl_VertexID) / u_depth_texture_size.x), 0.0, u_depth_texture_size.y) + 0.5; vec2 depth_texture_coord = depth_pixel / u_depth_texture_size; if (xz_flip != 0.0) depth_texture_coord.x = 1.0 - depth_texture_coord.x; // The values of R, G and B should be equal, so we can just // select any of them. float depth = texture(u_depth_texture, depth_texture_coord).r; if (depth == 0.0) return; // For example, a value of 1.5 means the current point is 1.5 // meters away. float depth_scaled = u_depth_scale * depth; // X and Y are the position within the depth texture (adjusted // so that it matches the position of the RGB texture), Z is // the depth. vec4 position = depth_deproject(depth_pixel, depth_scaled); if (u_draw_lighting != 0.0) { // Calculate normal based on surrounding pixels. vec2 stepx = vec2(1.0, 0.0) / u_depth_texture_size; vec2 stepy = vec2(0.0, 1.0) / u_depth_texture_size; float d_0 = texture(u_depth_texture, depth_texture_coord + stepx).r; float d_90 = texture(u_depth_texture, depth_texture_coord + stepy).r; float d_180 = texture(u_depth_texture, depth_texture_coord - stepx).r; float d_270 = texture(u_depth_texture, depth_texture_coord - stepy).r; // Edges are tricky, for 0 depth pixels on edges, don't render. // if (d_0 * d_90 * d_180 * d_270 == 0.0) // return; float ddx = (d_0 - d_180) * 0.5; float ddy = (d_90 - d_270) * 0.5; v_normal = vec3(ddx, ddy, depth * u_depth_focal_length_inv.x); v_position_from_light = u_mvp_from_light * position; } v_position = vec3(position); gl_Position = u_mvp * position; gl_PointSize = u_pointSize; }` ); gl.compileShader(vertex_shader); var pixel_shader = gl.createShader(gl.FRAGMENT_SHADER); gl.shaderSource(pixel_shader, `#version 300 es precision mediump float; precision highp sampler2DShadow; uniform sampler2DShadow u_shadow_map; uniform float u_draw_lighting; uniform vec3 u_light_position; in vec3 v_normal; in vec3 v_position; in vec4 v_position_from_light; out vec4 fragColor; void main() { if (u_draw_lighting != 1.0) return; vec3 s = (v_position_from_light.xyz / v_position_from_light.w) * 0.5 + 0.49; float shadow = texture(u_shadow_map, s); vec3 normal = normalize(v_normal); vec3 to_eye = normalize(-v_position); // eye is in 0,0,0 // TODO: to_eye shouldnt be hardcoded. vec3 to_light = normalize(vec3(0.7, -3.0, 1.0) - v_position); // no specular component for hands. float diffuse = shadow * max(dot(to_light, normal), 0.0) * 0.7; float ambient = 0.3; fragColor = vec4(vec3(0.9, 0.9, 0.9) * (diffuse + ambient), 1.0); }` ); gl.compileShader(pixel_shader); var program = gl.createProgram(); gl.attachShader(program, vertex_shader); gl.attachShader(program, pixel_shader); gl.linkProgram(program); console.log(gl.getShaderInfoLog(vertex_shader)); console.log(gl.getShaderInfoLog(pixel_shader)); console.log(gl.getProgramInfoLog(program)); gl.useProgram(program); gl.uniform1i(gl.getUniformLocation(program, "u_depth_texture"), 0); gl.render_u_mvp = gl.getUniformLocation(program, "u_mvp"); gl.u_mvp_from_light = gl.getUniformLocation(program, "u_mvp_from_light"); gl.u_draw_lighting = gl.getUniformLocation(program, "u_draw_lighting"); gl.render_u_pointSize = gl.getUniformLocation(program, "u_pointSize"); gl.render_u_light_position = gl.getUniformLocation(program, "u_light_position"); gl.render_u_shadow_map = gl.getUniformLocation(program, "u_shadow_map"); gl.render_u_xz_flip = gl.getUniformLocation(program, "xz_flip"); gl.uniform1i(gl.render_u_xz_flip, 0); function createDepthTexture(gl) { var depth_texture = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, depth_texture); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); return depth_texture; }; // Upload the latest depth frame to this texture. gl.depth_texture = createDepthTexture(gl); if (drawGL) drawGL.depth_texture = createDepthTexture(drawGL); gl.render_program = program; gl.compute_program = compute_program; gl.transform_feedback = transform_feedback; gl.tf_bo = tf_bo; } function getMvpMatrix(screenwidth, screenheight) { var model = new mat4.create(); var view = new mat4.create(); mat4.lookAt(view, vec3.fromValues(0, 0, 0), // eye vec3.fromValues(0, 0, 0.4), // target vec3.fromValues(0, -1, 0)); // up var aspect = screenwidth / screenheight; var projection = new mat4.create(); mat4.perspective(projection, glMatrix.toRadian(60.0), aspect, 0.1, 20.0); var mv = mat4.create(); mat4.multiply(mv, view, model); var mvp = mat4.create(); mat4.multiply(mvp, projection, mv); mat4.scale(mvp, mvp, [1.1, 1.1, 1]); return mvp; } let u_plane; let tf_output; // Analysis intermediate data. // If != -1, marking that the point is connected to subsegment starting point, // and the value is sub-segment starting point's index. Only skeleton points // are procesed in the algorithm (speed concern) and only those have the values // != -1 - see markConnectedPoints. let links; let out; let out1; let out2; let width; let height; // The max length of a finger segment - determines the radius of circular // propagation using generate_concentric_circles_indices.js (5 cm) let segment_coef; function processOnCPU() { links.fill(-1); segment_farthest_point_map_left = {}; segment_farthest_point_map_right = {}; const segment_data = {}; const net_inv = INTERPOLATE_INV; const netw = Math.ceil(width * net_inv); const neth = Math.ceil(height * net_inv); const net = (out ? out.net : new Array(netw * neth)).fill(-1); // We limit the gesture processing to area of the screen with margins 60 for // screen size 640x480. This is to avoid the need of checking against the // screen bounds. const x_offset = Math.min(radius_offset.length, (width * 0.1) | 0, (height * 0.125) | 0); const y_offset = x_offset; const max_radius = x_offset; let x_count = width - (2 * x_offset); let y_end = height - y_offset; for (let row = y_offset; row < y_end; row++) { const row_offset = row * width; let x = row_offset + x_offset; const x_end = x + x_count; for (; x < x_end; ++x) { const value = tf_output[x]; if (value < -1) { // Maintain the net of wide areas. const column = x - row_offset; const ncol = (column * net_inv) | 0; const nrow = (row * net_inv) | 0; const ni = netw * nrow + ncol; if (net[ni] == -1) net[ni] = (column << 16) | row; continue; } else if (value <= 0.0) { continue; // Far away pixel if 0, or pixel that is not on a bone if < 0. } if (links[x] > -1) continue; // If already on a segment. const depth = modf(value); // Convert hand size from cm to pixels on given depth. const radius = Math.min(max_radius, segment_coef / depth * 0.8); markConnectedPoints(x, x - row_offset, row, radius, segment_data); } } out2 = out1; out1 = out; out = {segment_data: segment_data, net: net, netw: netw, neth: neth}; } function markConnectedPoints(index, column, row, radius, segment_data) { connected.fill(false); connected[0] = true; links[index] = index; // farthest index left and right. let farthest_index_left = {index: index, x: column, y: row, center: {x: -1, y: -1}}; let farthest_index_right = {index: index, x: column, y: row, center: {x: -1, y: -1}}; let count_left = 0; let count_right = 0; for (var j = 1; j < radius; j++) { let had_connection_on_radius = false; // Don't go further if there are no points to connect. const count_on_this_radius = count_per_radius[j]; // generate_concentric_circles_indices.js generates full circle, and we want // to only go through lower half of the circle. const r_start = radius_offset[j] + (count_on_this_radius >> 2); const r_end = r_start + 1 + (count_on_this_radius >> 1); for (var i = r_start; i < r_end; i++) { const element_towards_center = towards_center[i]; // We get the index of element towards center. If the point is not // connected to the center, don't bother analyzing it. if (!connected[element_towards_center]) continue; let elem = index + xs[i] + ys[i] * width; const x_offset = xs[i]; const y_offset = ys[i]; let l = tf_output[elem]; if (l != 0.0) { connected[i] = true; had_connection_on_radius = true; const x = column + x_offset; const y = row + y_offset; if (links[elem] > -1) { // continue as the point is already allocated to other segment. continue; } links[elem] = index; if (l < 0.0) continue; // The pixel is not on the finger bone. // Track the farthest indices to the left and to the right. if (x_offset < 0) { if (l > 1.0) { farthest_index_left.center.x = x; farthest_index_left.center.y = y; } farthest_index_left.index = elem; farthest_index_left.x = x; farthest_index_left.y = y; count_left++; } else { if (l > 1.0) { farthest_index_right.center.x = x; farthest_index_right.center.y = y; } farthest_index_right.index = elem; farthest_index_right.x = x; farthest_index_right.y = y; count_right++; } } } if (!had_connection_on_radius) break; } let data = {x:column, y:row, index: index, far_left: farthest_index_left, far_right: farthest_index_right, count_left: count_left, count_right: count_right, depth: modf(tf_output[index])}; farthest_index_left.depth = modf(tf_output[farthest_index_left.index]); farthest_index_right.depth = modf(tf_output[farthest_index_right.index]); segment_data[index] = data; } function modf(v) { return v - (v | 0); } // Generated using generate_concentric_circles_indices.js for kernel 60. // This is used to enumerate through points on the same distance from the center, // similar to spreading of vawes of concentric circles and for each point of // the circle, use towards_center to calculate if the point is connected to the // center. var xs = [0,0,1,1,1,0,-1,-1,-1,0,1,2,2,2,2,2,1,0,-1,-2,-2,-2,-2,-2,-1,0,1,2,3,3,3,3,3,2,1,0,-1,-2,-3,-3,-3,-3,-3,-2,-1,0,1,2,3,4,4,4,4,4,3,2,1,0,-1,-2,-3,-4,-4,-4,-4,-4,-3,-2,-1,0,1,2,3,3,4,4,5,5,5,5,5,5,5,4,4,3,3,2,1,0,-1,-2,-3,-3,-4,-4,-5,-5,-5,-5,-5,-5,-5,-4,-4,-3,-3,-2,-1,0,1,2,3,4,5,6,6,6,6,6,6,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-6,-6,-6,-6,-6,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,5,6,6,7,7,7,7,7,7,7,6,6,5,5,4,3,2,1,0,-1,-2,-3,-4,-5,-5,-6,-6,-7,-7,-7,-7,-7,-7,-7,-6,-6,-5,-5,-4,-3,-2,-1,0,1,2,3,4,4,5,6,7,7,8,8,8,8,8,8,8,8,8,7,7,6,5,4,4,3,2,1,0,-1,-2,-3,-4,-4,-5,-6,-7,-7,-8,-8,-8,-8,-8,-8,-8,-8,-8,-7,-7,-6,-5,-4,-4,-3,-2,-1,0,1,2,3,4,5,6,7,7,8,9,9,9,9,9,9,9,9,9,8,7,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-7,-8,-9,-9,-9,-9,-9,-9,-9,-9,-9,-8,-7,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,6,7,8,8,9,9,10,10,10,10,10,10,10,10,10,9,9,8,8,7,6,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-6,-7,-8,-8,-9,-9,-10,-10,-10,-10,-10,-10,-10,-10,-10,-9,-9,-8,-8,-7,-6,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,10,11,11,11,11,11,11,11,11,11,10,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-10,-11,-11,-11,-11,-11,-11,-11,-11,-11,-10,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,8,9,9,10,10,11,11,12,12,12,12,12,12,12,12,12,11,11,10,10,9,9,8,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-8,-9,-9,-10,-10,-11,-11,-12,-12,-12,-12,-12,-12,-12,-12,-12,-11,-11,-10,-10,-9,-9,-8,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,5,6,7,7,8,9,10,11,11,12,12,12,13,13,13,13,13,13,13,13,13,13,13,12,12,12,11,11,10,9,8,7,7,6,5,5,4,3,2,1,0,-1,-2,-3,-4,-5,-5,-6,-7,-7,-8,-9,-10,-11,-11,-12,-12,-12,-13,-13,-13,-13,-13,-13,-13,-13,-13,-13,-13,-12,-12,-12,-11,-11,-10,-9,-8,-7,-7,-6,-5,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,10,11,11,12,13,13,14,14,14,14,14,14,14,14,14,14,14,13,13,12,11,11,10,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-10,-11,-11,-12,-13,-13,-14,-14,-14,-14,-14,-14,-14,-14,-14,-14,-14,-13,-13,-12,-11,-11,-10,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,9,10,11,12,12,13,13,14,14,15,15,15,15,15,15,15,15,15,15,15,14,14,13,13,12,12,11,10,9,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-9,-10,-11,-12,-12,-13,-13,-14,-14,-15,-15,-15,-15,-15,-15,-15,-15,-15,-15,-15,-14,-14,-13,-13,-12,-12,-11,-10,-9,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,12,13,14,14,15,15,16,16,16,16,16,16,16,16,16,16,16,15,15,14,14,13,12,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-12,-13,-14,-14,-15,-15,-16,-16,-16,-16,-16,-16,-16,-16,-16,-16,-16,-15,-15,-14,-14,-13,-12,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,8,9,10,11,11,12,13,13,14,14,15,15,16,16,16,17,17,17,17,17,17,17,17,17,17,17,16,16,16,15,15,14,14,13,13,12,11,11,10,9,8,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-8,-9,-10,-11,-11,-12,-13,-13,-14,-14,-15,-15,-16,-16,-16,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-16,-16,-16,-15,-15,-14,-14,-13,-13,-12,-11,-11,-10,-9,-8,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,6,7,8,9,10,10,11,12,13,14,15,15,16,16,17,17,17,18,18,18,18,18,18,18,18,18,18,18,18,18,17,17,17,16,16,15,15,14,13,12,11,10,10,9,8,7,6,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-6,-7,-8,-9,-10,-10,-11,-12,-13,-14,-15,-15,-16,-16,-17,-17,-17,-18,-18,-18,-18,-18,-18,-18,-18,-18,-18,-18,-18,-18,-17,-17,-17,-16,-16,-15,-15,-14,-13,-12,-11,-10,-10,-9,-8,-7,-6,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,13,14,14,15,15,16,17,17,18,18,19,19,19,19,19,19,19,19,19,19,19,19,19,18,18,17,17,16,15,15,14,14,13,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-13,-14,-14,-15,-15,-16,-17,-17,-18,-18,-19,-19,-19,-19,-19,-19,-19,-19,-19,-19,-19,-19,-19,-18,-18,-17,-17,-16,-15,-15,-14,-14,-13,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,12,13,14,15,16,16,17,17,18,18,19,19,20,20,20,20,20,20,20,20,20,20,20,20,20,19,19,18,18,17,17,16,16,15,14,13,12,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-12,-13,-14,-15,-16,-16,-17,-17,-18,-18,-19,-19,-20,-20,-20,-20,-20,-20,-20,-20,-20,-20,-20,-20,-20,-19,-19,-18,-18,-17,-17,-16,-16,-15,-14,-13,-12,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,9,10,11,11,12,13,14,15,15,16,16,17,18,18,19,19,19,20,20,20,21,21,21,21,21,21,21,21,21,21,21,21,21,20,20,20,19,19,19,18,18,17,16,16,15,15,14,13,12,11,11,10,9,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-9,-10,-11,-11,-12,-13,-14,-15,-15,-16,-16,-17,-18,-18,-19,-19,-19,-20,-20,-20,-21,-21,-21,-21,-21,-21,-21,-21,-21,-21,-21,-21,-21,-20,-20,-20,-19,-19,-19,-18,-18,-17,-16,-16,-15,-15,-14,-13,-12,-11,-11,-10,-9,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,14,15,16,17,17,18,18,19,20,20,21,21,21,22,22,22,22,22,22,22,22,22,22,22,22,22,21,21,21,20,20,19,18,18,17,17,16,15,14,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-14,-15,-16,-17,-17,-18,-18,-19,-20,-20,-21,-21,-21,-22,-22,-22,-22,-22,-22,-22,-22,-22,-22,-22,-22,-22,-21,-21,-21,-20,-20,-19,-18,-18,-17,-17,-16,-15,-14,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,13,14,15,16,17,18,19,19,20,20,21,21,22,22,22,23,23,23,23,23,23,23,23,23,23,23,23,23,22,22,22,21,21,20,20,19,19,18,17,16,15,14,13,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-13,-14,-15,-16,-17,-18,-19,-19,-20,-20,-21,-21,-22,-22,-22,-23,-23,-23,-23,-23,-23,-23,-23,-23,-23,-23,-23,-23,-22,-22,-22,-21,-21,-20,-20,-19,-19,-18,-17,-16,-15,-14,-13,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,16,17,17,18,18,19,19,20,21,21,22,22,23,23,23,24,24,24,24,24,24,24,24,24,24,24,24,24,23,23,23,22,22,21,21,20,19,19,18,18,17,17,16,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-16,-17,-17,-18,-18,-19,-19,-20,-21,-21,-22,-22,-23,-23,-23,-24,-24,-24,-24,-24,-24,-24,-24,-24,-24,-24,-24,-24,-23,-23,-23,-22,-22,-21,-21,-20,-19,-19,-18,-18,-17,-17,-16,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,7,8,9,10,11,12,12,13,14,15,15,16,17,18,19,20,20,21,21,22,22,23,23,23,24,24,24,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,24,24,24,23,23,23,22,22,21,21,20,20,19,18,17,16,15,15,14,13,12,12,11,10,9,8,7,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-7,-8,-9,-10,-11,-12,-12,-13,-14,-15,-15,-16,-17,-18,-19,-20,-20,-21,-21,-22,-22,-23,-23,-23,-24,-24,-24,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-24,-24,-24,-23,-23,-23,-22,-22,-21,-21,-20,-20,-19,-18,-17,-16,-15,-15,-14,-13,-12,-12,-11,-10,-9,-8,-7,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,10,11,12,13,14,14,15,16,17,18,18,19,19,20,20,21,22,22,23,23,24,24,24,25,25,25,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,25,25,25,24,24,24,23,23,22,22,21,20,20,19,19,18,18,17,16,15,14,14,13,12,11,10,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-10,-11,-12,-13,-14,-14,-15,-16,-17,-18,-18,-19,-19,-20,-20,-21,-22,-22,-23,-23,-24,-24,-24,-25,-25,-25,-26,-26,-26,-26,-26,-26,-26,-26,-26,-26,-26,-26,-26,-26,-26,-25,-25,-25,-24,-24,-24,-23,-23,-22,-22,-21,-20,-20,-19,-19,-18,-18,-17,-16,-15,-14,-14,-13,-12,-11,-10,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,17,18,19,20,21,21,22,22,23,24,24,25,25,26,26,26,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,26,26,26,25,25,24,24,23,22,22,21,21,20,19,18,17,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-17,-18,-19,-20,-21,-21,-22,-22,-23,-24,-24,-25,-25,-26,-26,-26,-27,-27,-27,-27,-27,-27,-27,-27,-27,-27,-27,-27,-27,-27,-27,-26,-26,-26,-25,-25,-24,-24,-23,-22,-22,-21,-21,-20,-19,-18,-17,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,16,17,18,19,20,21,22,23,23,24,24,25,25,26,26,27,27,27,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,27,27,27,26,26,25,25,24,24,23,23,22,21,20,19,18,17,16,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-16,-17,-18,-19,-20,-21,-22,-23,-23,-24,-24,-25,-25,-26,-26,-27,-27,-27,-28,-28,-28,-28,-28,-28,-28,-28,-28,-28,-28,-28,-28,-28,-28,-27,-27,-27,-26,-26,-25,-25,-24,-24,-23,-23,-22,-21,-20,-19,-18,-17,-16,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,13,14,15,16,17,18,19,19,20,20,21,21,22,22,23,23,24,25,25,26,26,27,27,27,28,28,28,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,28,28,28,27,27,27,26,26,25,25,24,23,23,22,22,21,21,20,20,19,19,18,17,16,15,14,13,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-13,-14,-15,-16,-17,-18,-19,-19,-20,-20,-21,-21,-22,-22,-23,-23,-24,-25,-25,-26,-26,-27,-27,-27,-28,-28,-28,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-28,-28,-28,-27,-27,-27,-26,-26,-25,-25,-24,-23,-23,-22,-22,-21,-21,-20,-20,-19,-19,-18,-17,-16,-15,-14,-13,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,15,16,17,18,18,19,20,21,22,23,24,24,25,25,26,26,27,27,28,28,28,29,29,29,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,29,29,29,28,28,28,27,27,26,26,25,25,24,24,23,22,21,20,19,18,18,17,16,15,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-15,-16,-17,-18,-18,-19,-20,-21,-22,-23,-24,-24,-25,-25,-26,-26,-27,-27,-28,-28,-28,-29,-29,-29,-30,-30,-30,-30,-30,-30,-30,-30,-30,-30,-30,-30,-30,-30,-30,-29,-29,-29,-28,-28,-28,-27,-27,-26,-26,-25,-25,-24,-24,-23,-22,-21,-20,-19,-18,-18,-17,-16,-15,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,11,12,13,14,15,16,17,17,18,19,20,21,21,22,22,23,23,24,24,25,26,26,27,27,28,28,29,29,29,30,30,30,30,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,30,30,30,30,29,29,29,28,28,27,27,26,26,25,24,24,23,23,22,22,21,21,20,19,18,17,17,16,15,14,13,12,11,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-11,-12,-13,-14,-15,-16,-17,-17,-18,-19,-20,-21,-21,-22,-22,-23,-23,-24,-24,-25,-26,-26,-27,-27,-28,-28,-29,-29,-29,-30,-30,-30,-30,-31,-31,-31,-31,-31,-31,-31,-31,-31,-31,-31,-31,-31,-31,-31,-30,-30,-30,-30,-29,-29,-29,-28,-28,-27,-27,-26,-26,-25,-24,-24,-23,-23,-22,-22,-21,-21,-20,-19,-18,-17,-17,-16,-15,-14,-13,-12,-11,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,8,9,10,11,12,13,14,15,16,17,18,19,20,20,21,22,23,24,25,25,26,26,27,28,28,29,29,30,30,31,31,31,31,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,31,31,31,31,30,30,29,29,28,28,27,26,26,25,25,24,23,22,21,20,20,19,18,17,16,15,14,13,12,11,10,9,8,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-20,-20,-21,-22,-23,-24,-25,-25,-26,-26,-27,-28,-28,-29,-29,-30,-30,-31,-31,-31,-31,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-31,-31,-31,-31,-30,-30,-29,-29,-28,-28,-27,-26,-26,-25,-25,-24,-23,-22,-21,-20,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,19,20,21,22,23,23,24,24,25,25,26,27,27,28,28,29,29,30,30,31,31,32,32,32,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,32,32,32,31,31,30,30,29,29,28,28,27,27,26,25,25,24,24,23,23,22,21,20,19,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-19,-20,-21,-22,-23,-23,-24,-24,-25,-25,-26,-27,-27,-28,-28,-29,-29,-30,-30,-31,-31,-32,-32,-32,-33,-33,-33,-33,-33,-33,-33,-33,-33,-33,-33,-33,-33,-33,-33,-33,-33,-32,-32,-32,-31,-31,-30,-30,-29,-29,-28,-28,-27,-27,-26,-25,-25,-24,-24,-23,-23,-22,-21,-20,-19,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,14,15,16,16,17,18,18,19,20,21,22,22,23,24,25,26,26,27,27,28,29,29,30,30,30,31,31,31,32,32,32,33,33,33,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,33,33,33,32,32,32,31,31,31,30,30,30,29,29,28,27,27,26,26,25,24,23,22,22,21,20,19,18,18,17,16,16,15,14,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-14,-15,-16,-16,-17,-18,-18,-19,-20,-21,-22,-22,-23,-24,-25,-26,-26,-27,-27,-28,-29,-29,-30,-30,-30,-31,-31,-31,-32,-32,-32,-33,-33,-33,-34,-34,-34,-34,-34,-34,-34,-34,-34,-34,-34,-34,-34,-34,-34,-34,-34,-33,-33,-33,-32,-32,-32,-31,-31,-31,-30,-30,-30,-29,-29,-28,-27,-27,-26,-26,-25,-24,-23,-22,-22,-21,-20,-19,-18,-18,-17,-16,-16,-15,-14,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,21,22,23,24,25,26,27,28,28,29,29,30,31,31,32,32,33,33,33,34,34,34,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,34,34,34,33,33,33,32,32,31,31,30,29,29,28,28,27,26,25,24,23,22,21,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-20,-21,-21,-22,-23,-24,-25,-26,-27,-28,-28,-29,-29,-30,-31,-31,-32,-32,-33,-33,-33,-34,-34,-34,-35,-35,-35,-35,-35,-35,-35,-35,-35,-35,-35,-35,-35,-35,-35,-35,-35,-34,-34,-34,-33,-33,-33,-32,-32,-31,-31,-30,-29,-29,-28,-28,-27,-26,-25,-24,-23,-22,-21,-21,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,20,21,22,23,24,24,25,25,26,26,27,27,28,28,29,30,30,31,31,32,32,33,33,34,34,34,35,35,35,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,35,35,35,34,34,34,33,33,32,32,31,31,30,30,29,28,28,27,27,26,26,25,25,24,24,23,22,21,20,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-20,-20,-21,-22,-23,-24,-24,-25,-25,-26,-26,-27,-27,-28,-28,-29,-30,-30,-31,-31,-32,-32,-33,-33,-34,-34,-34,-35,-35,-35,-36,-36,-36,-36,-36,-36,-36,-36,-36,-36,-36,-36,-36,-36,-36,-36,-36,-35,-35,-35,-34,-34,-34,-33,-33,-32,-32,-31,-31,-30,-30,-29,-28,-28,-27,-27,-26,-26,-25,-25,-24,-24,-23,-22,-21,-20,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,12,13,14,15,16,17,18,19,20,21,22,23,23,24,25,26,27,28,29,29,30,30,31,32,32,33,33,34,34,35,35,35,36,36,36,36,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,36,36,36,36,35,35,35,34,34,33,33,32,32,31,30,30,29,29,28,27,26,25,24,23,23,22,21,20,19,18,17,16,15,14,13,12,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-12,-13,-14,-15,-16,-17,-18,-19,-20,-21,-22,-23,-23,-24,-25,-26,-27,-28,-29,-29,-30,-30,-31,-32,-32,-33,-33,-34,-34,-35,-35,-35,-36,-36,-36,-36,-37,-37,-37,-37,-37,-37,-37,-37,-37,-37,-37,-37,-37,-37,-37,-37,-37,-36,-36,-36,-36,-35,-35,-35,-34,-34,-33,-33,-32,-32,-31,-30,-30,-29,-29,-28,-27,-26,-25,-24,-23,-23,-22,-21,-20,-19,-18,-17,-16,-15,-14,-13,-12,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,17,18,19,19,20,21,22,22,23,24,25,26,26,27,27,28,28,29,29,30,31,31,32,32,33,33,34,34,34,35,35,35,36,36,37,37,37,37,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,37,37,37,37,36,36,35,35,35,34,34,34,33,33,32,32,31,31,30,29,29,28,28,27,27,26,26,25,24,23,22,22,21,20,19,19,18,17,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-17,-18,-19,-19,-20,-21,-22,-22,-23,-24,-25,-26,-26,-27,-27,-28,-28,-29,-29,-30,-31,-31,-32,-32,-33,-33,-34,-34,-34,-35,-35,-35,-36,-36,-37,-37,-37,-37,-38,-38,-38,-38,-38,-38,-38,-38,-38,-38,-38,-38,-38,-38,-38,-38,-38,-37,-37,-37,-37,-36,-36,-35,-35,-35,-34,-34,-34,-33,-33,-32,-32,-31,-31,-30,-29,-29,-28,-28,-27,-27,-26,-26,-25,-24,-23,-22,-22,-21,-20,-19,-19,-18,-17,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,15,16,17,18,19,20,21,21,22,23,24,25,25,26,27,28,29,30,30,31,31,32,33,33,34,34,35,35,36,36,36,37,37,37,38,38,38,38,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,38,38,38,38,37,37,37,36,36,36,35,35,34,34,33,33,32,31,31,30,30,29,28,27,26,25,25,24,23,22,21,21,20,19,18,17,16,15,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-15,-16,-17,-18,-19,-20,-21,-21,-22,-23,-24,-25,-25,-26,-27,-28,-29,-30,-30,-31,-31,-32,-33,-33,-34,-34,-35,-35,-36,-36,-36,-37,-37,-37,-38,-38,-38,-38,-39,-39,-39,-39,-39,-39,-39,-39,-39,-39,-39,-39,-39,-39,-39,-39,-39,-38,-38,-38,-38,-37,-37,-37,-36,-36,-36,-35,-35,-34,-34,-33,-33,-32,-31,-31,-30,-30,-29,-28,-27,-26,-25,-25,-24,-23,-22,-21,-21,-20,-19,-18,-17,-16,-15,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,24,25,26,27,28,29,30,31,32,32,33,33,34,35,35,36,36,37,37,38,38,38,39,39,39,39,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,39,39,39,39,38,38,38,37,37,36,36,35,35,34,33,33,32,32,31,30,29,28,27,26,25,24,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-20,-21,-22,-23,-24,-24,-25,-26,-27,-28,-29,-30,-31,-32,-32,-33,-33,-34,-35,-35,-36,-36,-37,-37,-38,-38,-38,-39,-39,-39,-39,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-39,-39,-39,-39,-38,-38,-38,-37,-37,-36,-36,-35,-35,-34,-33,-33,-32,-32,-31,-30,-29,-28,-27,-26,-25,-24,-24,-23,-22,-21,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,23,24,25,26,27,27,28,28,29,29,30,30,31,31,32,32,33,34,34,35,35,36,36,37,37,38,38,39,39,39,40,40,40,40,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,40,40,40,40,39,39,39,38,38,37,37,36,36,35,35,34,34,33,32,32,31,31,30,30,29,29,28,28,27,27,26,25,24,23,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-20,-21,-22,-23,-23,-24,-25,-26,-27,-27,-28,-28,-29,-29,-30,-30,-31,-31,-32,-32,-33,-34,-34,-35,-35,-36,-36,-37,-37,-38,-38,-39,-39,-39,-40,-40,-40,-40,-41,-41,-41,-41,-41,-41,-41,-41,-41,-41,-41,-41,-41,-41,-41,-41,-41,-41,-41,-40,-40,-40,-40,-39,-39,-39,-38,-38,-37,-37,-36,-36,-35,-35,-34,-34,-33,-32,-32,-31,-31,-30,-30,-29,-29,-28,-28,-27,-27,-26,-25,-24,-23,-23,-22,-21,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,18,19,20,20,21,22,23,24,25,26,26,27,28,29,30,31,32,33,33,34,34,35,36,36,37,37,38,38,38,39,39,39,40,40,40,41,41,41,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,41,41,41,40,40,40,39,39,39,38,38,38,37,37,36,36,35,34,34,33,33,32,31,30,29,28,27,26,26,25,24,23,22,21,20,20,19,18,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-18,-19,-20,-20,-21,-22,-23,-24,-25,-26,-26,-27,-28,-29,-30,-31,-32,-33,-33,-34,-34,-35,-36,-36,-37,-37,-38,-38,-38,-39,-39,-39,-40,-40,-40,-41,-41,-41,-42,-42,-42,-42,-42,-42,-42,-42,-42,-42,-42,-42,-42,-42,-42,-42,-42,-42,-42,-41,-41,-41,-40,-40,-40,-39,-39,-39,-38,-38,-38,-37,-37,-36,-36,-35,-34,-34,-33,-33,-32,-31,-30,-29,-28,-27,-26,-26,-25,-24,-23,-22,-21,-20,-20,-19,-18,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,13,14,15,16,17,18,19,20,21,22,22,23,24,25,25,26,27,28,29,29,30,30,31,31,32,32,33,33,34,35,35,36,36,37,37,38,38,39,39,40,40,40,41,41,41,42,42,42,42,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,42,42,42,42,41,41,41,40,40,40,39,39,38,38,37,37,36,36,35,35,34,33,33,32,32,31,31,30,30,29,29,28,27,26,25,25,24,23,22,22,21,20,19,18,17,16,15,14,13,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-13,-14,-15,-16,-17,-18,-19,-20,-21,-22,-22,-23,-24,-25,-25,-26,-27,-28,-29,-29,-30,-30,-31,-31,-32,-32,-33,-33,-34,-35,-35,-36,-36,-37,-37,-38,-38,-39,-39,-40,-40,-40,-41,-41,-41,-42,-42,-42,-42,-43,-43,-43,-43,-43,-43,-43,-43,-43,-43,-43,-43,-43,-43,-43,-43,-43,-43,-43,-42,-42,-42,-42,-41,-41,-41,-40,-40,-40,-39,-39,-38,-38,-37,-37,-36,-36,-35,-35,-34,-33,-33,-32,-32,-31,-31,-30,-30,-29,-29,-28,-27,-26,-25,-25,-24,-23,-22,-22,-21,-20,-19,-18,-17,-16,-15,-14,-13,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,16,17,18,19,20,21,22,23,24,24,25,26,27,28,28,29,30,31,32,33,34,34,35,35,36,37,37,38,38,39,39,40,40,41,41,41,42,42,42,43,43,43,43,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,43,43,43,43,42,42,42,41,41,41,40,40,39,39,38,38,37,37,36,35,35,34,34,33,32,31,30,29,28,28,27,26,25,24,24,23,22,21,20,19,18,17,16,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-16,-17,-18,-19,-20,-21,-22,-23,-24,-24,-25,-26,-27,-28,-28,-29,-30,-31,-32,-33,-34,-34,-35,-35,-36,-37,-37,-38,-38,-39,-39,-40,-40,-41,-41,-41,-42,-42,-42,-43,-43,-43,-43,-44,-44,-44,-44,-44,-44,-44,-44,-44,-44,-44,-44,-44,-44,-44,-44,-44,-44,-44,-43,-43,-43,-43,-42,-42,-42,-41,-41,-41,-40,-40,-39,-39,-38,-38,-37,-37,-36,-35,-35,-34,-34,-33,-32,-31,-30,-29,-28,-28,-27,-26,-25,-24,-24,-23,-22,-21,-20,-19,-18,-17,-16,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,27,28,29,30,31,32,32,33,33,34,35,36,36,37,37,38,39,39,40,40,41,41,42,42,43,43,43,44,44,44,44,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,44,44,44,44,43,43,43,42,42,41,41,40,40,39,39,38,37,37,36,36,35,34,33,33,32,32,31,30,29,28,27,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-20,-21,-22,-23,-24,-25,-26,-27,-27,-28,-29,-30,-31,-32,-32,-33,-33,-34,-35,-36,-36,-37,-37,-38,-39,-39,-40,-40,-41,-41,-42,-42,-43,-43,-43,-44,-44,-44,-44,-45,-45,-45,-45,-45,-45,-45,-45,-45,-45,-45,-45,-45,-45,-45,-45,-45,-45,-45,-44,-44,-44,-44,-43,-43,-43,-42,-42,-41,-41,-40,-40,-39,-39,-38,-37,-37,-36,-36,-35,-34,-33,-33,-32,-32,-31,-30,-29,-28,-27,-27,-26,-25,-24,-23,-22,-21,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,21,22,23,24,25,26,26,27,28,29,30,30,31,31,32,33,34,34,35,35,36,36,37,38,38,39,39,40,40,41,41,42,42,42,43,43,44,44,44,45,45,45,45,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,45,45,45,45,44,44,44,43,43,42,42,42,41,41,40,40,39,39,38,38,37,36,36,35,35,34,34,33,32,31,31,30,30,29,28,27,26,26,25,24,23,22,21,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-20,-21,-21,-22,-23,-24,-25,-26,-26,-27,-28,-29,-30,-30,-31,-31,-32,-33,-34,-34,-35,-35,-36,-36,-37,-38,-38,-39,-39,-40,-40,-41,-41,-42,-42,-42,-43,-43,-44,-44,-44,-45,-45,-45,-45,-46,-46,-46,-46,-46,-46,-46,-46,-46,-46,-46,-46,-46,-46,-46,-46,-46,-46,-46,-45,-45,-45,-45,-44,-44,-44,-43,-43,-42,-42,-42,-41,-41,-40,-40,-39,-39,-38,-38,-37,-36,-36,-35,-35,-34,-34,-33,-32,-31,-31,-30,-30,-29,-28,-27,-26,-26,-25,-24,-23,-22,-21,-21,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,19,20,21,22,23,23,24,25,26,27,28,29,29,30,31,32,33,34,35,36,37,37,38,38,39,40,40,41,41,42,42,43,43,43,44,44,44,45,45,45,46,46,46,46,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,46,46,46,46,45,45,45,44,44,44,43,43,43,42,42,41,41,40,40,39,38,38,37,37,36,35,34,33,32,31,30,29,29,28,27,26,25,24,23,23,22,21,20,19,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-19,-20,-21,-22,-23,-23,-24,-25,-26,-27,-28,-29,-29,-30,-31,-32,-33,-34,-35,-36,-37,-37,-38,-38,-39,-40,-40,-41,-41,-42,-42,-43,-43,-43,-44,-44,-44,-45,-45,-45,-46,-46,-46,-46,-47,-47,-47,-47,-47,-47,-47,-47,-47,-47,-47,-47,-47,-47,-47,-47,-47,-47,-47,-46,-46,-46,-46,-45,-45,-45,-44,-44,-44,-43,-43,-43,-42,-42,-41,-41,-40,-40,-39,-38,-38,-37,-37,-36,-35,-34,-33,-32,-31,-30,-29,-29,-28,-27,-26,-25,-24,-23,-23,-22,-21,-20,-19,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,25,26,27,28,28,29,30,31,32,32,33,33,34,34,35,35,36,36,37,37,38,39,39,40,40,41,41,42,42,43,43,44,44,45,45,45,46,46,46,47,47,47,47,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,47,47,47,47,46,46,46,45,45,45,44,44,43,43,42,42,41,41,40,40,39,39,38,37,37,36,36,35,35,34,34,33,33,32,32,31,30,29,28,28,27,26,25,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-20,-21,-22,-23,-24,-25,-25,-26,-27,-28,-28,-29,-30,-31,-32,-32,-33,-33,-34,-34,-35,-35,-36,-36,-37,-37,-38,-39,-39,-40,-40,-41,-41,-42,-42,-43,-43,-44,-44,-45,-45,-45,-46,-46,-46,-47,-47,-47,-47,-48,-48,-48,-48,-48,-48,-48,-48,-48,-48,-48,-48,-48,-48,-48,-48,-48,-48,-48,-47,-47,-47,-47,-46,-46,-46,-45,-45,-45,-44,-44,-43,-43,-42,-42,-41,-41,-40,-40,-39,-39,-38,-37,-37,-36,-36,-35,-35,-34,-34,-33,-33,-32,-32,-31,-30,-29,-28,-28,-27,-26,-25,-25,-24,-23,-22,-21,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,17,18,19,20,21,22,23,24,25,26,27,27,28,29,30,31,31,32,33,34,35,36,37,38,38,39,39,40,41,41,42,42,43,43,44,44,45,45,46,46,46,47,47,47,47,48,48,48,48,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,48,48,48,48,47,47,47,47,46,46,46,45,45,44,44,43,43,42,42,41,41,40,39,39,38,38,37,36,35,34,33,32,31,31,30,29,28,27,27,26,25,24,23,22,21,20,19,18,17,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-17,-18,-19,-20,-21,-22,-23,-24,-25,-26,-27,-27,-28,-29,-30,-31,-31,-32,-33,-34,-35,-36,-37,-38,-38,-39,-39,-40,-41,-41,-42,-42,-43,-43,-44,-44,-45,-45,-46,-46,-46,-47,-47,-47,-47,-48,-48,-48,-48,-49,-49,-49,-49,-49,-49,-49,-49,-49,-49,-49,-49,-49,-49,-49,-49,-49,-49,-49,-48,-48,-48,-48,-47,-47,-47,-47,-46,-46,-46,-45,-45,-44,-44,-43,-43,-42,-42,-41,-41,-40,-39,-39,-38,-38,-37,-36,-35,-34,-33,-32,-31,-31,-30,-29,-28,-27,-27,-26,-25,-24,-23,-22,-21,-20,-19,-18,-17,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,10,11,12,13,14,14,15,16,17,18,19,20,21,22,22,23,24,25,26,27,28,29,30,30,31,32,33,34,34,35,35,36,36,37,37,38,38,39,40,40,41,41,42,43,43,44,44,45,45,46,46,46,47,47,48,48,48,48,49,49,49,49,49,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,49,49,49,49,49,48,48,48,48,47,47,46,46,46,45,45,44,44,43,43,42,41,41,40,40,39,38,38,37,37,36,36,35,35,34,34,33,32,31,30,30,29,28,27,26,25,24,23,22,22,21,20,19,18,17,16,15,14,14,13,12,11,10,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-10,-11,-12,-13,-14,-14,-15,-16,-17,-18,-19,-20,-21,-22,-22,-23,-24,-25,-26,-27,-28,-29,-30,-30,-31,-32,-33,-34,-34,-35,-35,-36,-36,-37,-37,-38,-38,-39,-40,-40,-41,-41,-42,-43,-43,-44,-44,-45,-45,-46,-46,-46,-47,-47,-48,-48,-48,-48,-49,-49,-49,-49,-49,-50,-50,-50,-50,-50,-50,-50,-50,-50,-50,-50,-50,-50,-50,-50,-50,-50,-50,-50,-50,-50,-49,-49,-49,-49,-49,-48,-48,-48,-48,-47,-47,-46,-46,-46,-45,-45,-44,-44,-43,-43,-42,-41,-41,-40,-40,-39,-38,-38,-37,-37,-36,-36,-35,-35,-34,-34,-33,-32,-31,-30,-30,-29,-28,-27,-26,-25,-24,-23,-22,-22,-21,-20,-19,-18,-17,-16,-15,-14,-14,-13,-12,-11,-10,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,24,25,26,26,27,28,29,29,30,31,32,33,33,34,35,36,37,38,39,39,40,40,41,42,42,43,43,44,44,45,45,45,46,46,47,47,47,48,48,49,49,49,50,50,50,50,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,50,50,50,50,49,49,49,48,48,47,47,47,46,46,45,45,45,44,44,43,43,42,42,41,40,40,39,39,38,37,36,35,34,33,33,32,31,30,29,29,28,27,26,26,25,24,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-20,-21,-22,-23,-24,-24,-25,-26,-26,-27,-28,-29,-29,-30,-31,-32,-33,-33,-34,-35,-36,-37,-38,-39,-39,-40,-40,-41,-42,-42,-43,-43,-44,-44,-45,-45,-45,-46,-46,-47,-47,-47,-48,-48,-49,-49,-49,-50,-50,-50,-50,-51,-51,-51,-51,-51,-51,-51,-51,-51,-51,-51,-51,-51,-51,-51,-51,-51,-51,-51,-51,-51,-50,-50,-50,-50,-49,-49,-49,-48,-48,-47,-47,-47,-46,-46,-45,-45,-45,-44,-44,-43,-43,-42,-42,-41,-40,-40,-39,-39,-38,-37,-36,-35,-34,-33,-33,-32,-31,-30,-29,-29,-28,-27,-26,-26,-25,-24,-24,-23,-22,-21,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,20,21,22,23,24,25,26,27,28,29,30,31,32,32,33,34,35,36,37,38,39,40,41,41,42,42,43,44,44,45,46,46,47,47,48,48,48,49,49,49,50,50,50,51,51,51,51,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,51,51,51,51,50,50,50,49,49,49,48,48,48,47,47,46,46,45,44,44,43,42,42,41,41,40,39,38,37,36,35,34,33,32,32,31,30,29,28,27,26,25,24,23,22,21,20,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-20,-20,-21,-22,-23,-24,-25,-26,-27,-28,-29,-30,-31,-32,-32,-33,-34,-35,-36,-37,-38,-39,-40,-41,-41,-42,-42,-43,-44,-44,-45,-46,-46,-47,-47,-48,-48,-48,-49,-49,-49,-50,-50,-50,-51,-51,-51,-51,-52,-52,-52,-52,-52,-52,-52,-52,-52,-52,-52,-52,-52,-52,-52,-52,-52,-52,-52,-52,-52,-51,-51,-51,-51,-50,-50,-50,-49,-49,-49,-48,-48,-48,-47,-47,-46,-46,-45,-44,-44,-43,-42,-42,-41,-41,-40,-39,-38,-37,-36,-35,-34,-33,-32,-32,-31,-30,-29,-28,-27,-26,-25,-24,-23,-22,-21,-20,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,28,29,30,31,31,32,33,34,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,43,43,44,44,45,45,46,46,47,47,48,48,49,49,50,50,50,51,51,51,52,52,52,52,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,52,52,52,52,51,51,51,50,50,50,49,49,48,48,47,47,46,46,45,45,44,44,43,43,42,41,41,40,40,39,39,38,38,37,37,36,36,35,35,34,33,32,31,31,30,29,28,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-20,-21,-22,-23,-24,-25,-26,-27,-28,-28,-29,-30,-31,-31,-32,-33,-34,-35,-35,-36,-36,-37,-37,-38,-38,-39,-39,-40,-40,-41,-41,-42,-43,-43,-44,-44,-45,-45,-46,-46,-47,-47,-48,-48,-49,-49,-50,-50,-50,-51,-51,-51,-52,-52,-52,-52,-53,-53,-53,-53,-53,-53,-53,-53,-53,-53,-53,-53,-53,-53,-53,-53,-53,-53,-53,-53,-53,-52,-52,-52,-52,-51,-51,-51,-50,-50,-50,-49,-49,-48,-48,-47,-47,-46,-46,-45,-45,-44,-44,-43,-43,-42,-41,-41,-40,-40,-39,-39,-38,-38,-37,-37,-36,-36,-35,-35,-34,-33,-32,-31,-31,-30,-29,-28,-28,-27,-26,-25,-24,-23,-22,-21,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,30,31,32,33,34,34,35,36,37,38,39,40,41,42,42,43,43,44,45,45,46,46,47,47,48,48,49,49,50,50,51,51,51,52,52,52,53,53,53,53,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,53,53,53,53,52,52,52,51,51,51,50,50,49,49,48,48,47,47,46,46,45,45,44,43,43,42,42,41,40,39,38,37,36,35,34,34,33,32,31,30,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-20,-21,-22,-23,-24,-25,-26,-27,-28,-29,-30,-30,-31,-32,-33,-34,-34,-35,-36,-37,-38,-39,-40,-41,-42,-42,-43,-43,-44,-45,-45,-46,-46,-47,-47,-48,-48,-49,-49,-50,-50,-51,-51,-51,-52,-52,-52,-53,-53,-53,-53,-54,-54,-54,-54,-54,-54,-54,-54,-54,-54,-54,-54,-54,-54,-54,-54,-54,-54,-54,-54,-54,-53,-53,-53,-53,-52,-52,-52,-51,-51,-51,-50,-50,-49,-49,-48,-48,-47,-47,-46,-46,-45,-45,-44,-43,-43,-42,-42,-41,-40,-39,-38,-37,-36,-35,-34,-34,-33,-32,-31,-30,-30,-29,-28,-27,-26,-25,-24,-23,-22,-21,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,18,19,20,21,22,23,23,24,25,25,26,27,27,28,29,30,31,32,33,33,34,35,36,37,37,38,38,39,39,40,40,41,41,42,42,43,44,44,45,45,46,47,47,48,48,49,49,49,50,50,50,51,51,51,52,52,52,53,53,53,53,54,54,54,54,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,54,54,54,54,53,53,53,53,52,52,52,51,51,51,50,50,50,49,49,49,48,48,47,47,46,45,45,44,44,43,42,42,41,41,40,40,39,39,38,38,37,37,36,35,34,33,33,32,31,30,29,28,27,27,26,25,25,24,23,23,22,21,20,19,18,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-18,-19,-20,-21,-22,-23,-23,-24,-25,-25,-26,-27,-27,-28,-29,-30,-31,-32,-33,-33,-34,-35,-36,-37,-37,-38,-38,-39,-39,-40,-40,-41,-41,-42,-42,-43,-44,-44,-45,-45,-46,-47,-47,-48,-48,-49,-49,-49,-50,-50,-50,-51,-51,-51,-52,-52,-52,-53,-53,-53,-53,-54,-54,-54,-54,-55,-55,-55,-55,-55,-55,-55,-55,-55,-55,-55,-55,-55,-55,-55,-55,-55,-55,-55,-55,-55,-54,-54,-54,-54,-53,-53,-53,-53,-52,-52,-52,-51,-51,-51,-50,-50,-50,-49,-49,-49,-48,-48,-47,-47,-46,-45,-45,-44,-44,-43,-42,-42,-41,-41,-40,-40,-39,-39,-38,-38,-37,-37,-36,-35,-34,-33,-33,-32,-31,-30,-29,-28,-27,-27,-26,-25,-25,-24,-23,-23,-22,-21,-20,-19,-18,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,29,30,31,32,32,33,34,35,36,36,37,38,39,40,41,42,43,43,44,44,45,46,46,47,47,48,48,49,49,50,50,51,51,52,52,52,53,53,54,54,54,54,55,55,55,55,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,55,55,55,55,54,54,54,54,53,53,52,52,52,51,51,50,50,49,49,48,48,47,47,46,46,45,44,44,43,43,42,41,40,39,38,37,36,36,35,34,33,32,32,31,30,29,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-20,-21,-22,-23,-24,-25,-26,-27,-28,-29,-29,-30,-31,-32,-32,-33,-34,-35,-36,-36,-37,-38,-39,-40,-41,-42,-43,-43,-44,-44,-45,-46,-46,-47,-47,-48,-48,-49,-49,-50,-50,-51,-51,-52,-52,-52,-53,-53,-54,-54,-54,-54,-55,-55,-55,-55,-56,-56,-56,-56,-56,-56,-56,-56,-56,-56,-56,-56,-56,-56,-56,-56,-56,-56,-56,-56,-56,-55,-55,-55,-55,-54,-54,-54,-54,-53,-53,-52,-52,-52,-51,-51,-50,-50,-49,-49,-48,-48,-47,-47,-46,-46,-45,-44,-44,-43,-43,-42,-41,-40,-39,-38,-37,-36,-36,-35,-34,-33,-32,-32,-31,-30,-29,-29,-28,-27,-26,-25,-24,-23,-22,-21,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,15,16,17,18,19,20,21,21,22,23,24,25,26,27,28,29,30,31,31,32,33,34,35,35,36,37,38,39,40,41,41,42,43,44,45,45,46,46,47,48,48,49,49,50,50,51,51,52,52,53,53,53,54,54,54,55,55,55,55,56,56,56,56,56,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,56,56,56,56,56,55,55,55,55,54,54,54,53,53,53,52,52,51,51,50,50,49,49,48,48,47,46,46,45,45,44,43,42,41,41,40,39,38,37,36,35,35,34,33,32,31,31,30,29,28,27,26,25,24,23,22,21,21,20,19,18,17,16,15,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-15,-16,-17,-18,-19,-20,-21,-21,-22,-23,-24,-25,-26,-27,-28,-29,-30,-31,-31,-32,-33,-34,-35,-35,-36,-37,-38,-39,-40,-41,-41,-42,-43,-44,-45,-45,-46,-46,-47,-48,-48,-49,-49,-50,-50,-51,-51,-52,-52,-53,-53,-53,-54,-54,-54,-55,-55,-55,-55,-56,-56,-56,-56,-56,-57,-57,-57,-57,-57,-57,-57,-57,-57,-57,-57,-57,-57,-57,-57,-57,-57,-57,-57,-57,-57,-56,-56,-56,-56,-56,-55,-55,-55,-55,-54,-54,-54,-53,-53,-53,-52,-52,-51,-51,-50,-50,-49,-49,-48,-48,-47,-46,-46,-45,-45,-44,-43,-42,-41,-41,-40,-39,-38,-37,-36,-35,-35,-34,-33,-32,-31,-31,-30,-29,-28,-27,-26,-25,-24,-23,-22,-21,-21,-20,-19,-18,-17,-16,-15,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,34,35,36,37,38,38,39,39,40,40,41,42,42,43,43,44,44,45,45,46,47,47,48,48,49,50,50,51,51,52,52,53,53,54,54,55,55,55,56,56,56,57,57,57,57,57,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,57,57,57,57,57,56,56,56,55,55,55,54,54,53,53,52,52,51,51,50,50,49,48,48,47,47,46,45,45,44,44,43,43,42,42,41,40,40,39,39,38,38,37,36,35,34,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-20,-21,-22,-23,-24,-25,-26,-27,-28,-29,-30,-31,-32,-33,-34,-34,-35,-36,-37,-38,-38,-39,-39,-40,-40,-41,-42,-42,-43,-43,-44,-44,-45,-45,-46,-47,-47,-48,-48,-49,-50,-50,-51,-51,-52,-52,-53,-53,-54,-54,-55,-55,-55,-56,-56,-56,-57,-57,-57,-57,-57,-58,-58,-58,-58,-58,-58,-58,-58,-58,-58,-58,-58,-58,-58,-58,-58,-58,-58,-58,-58,-58,-57,-57,-57,-57,-57,-56,-56,-56,-55,-55,-55,-54,-54,-53,-53,-52,-52,-51,-51,-50,-50,-49,-48,-48,-47,-47,-46,-45,-45,-44,-44,-43,-43,-42,-42,-41,-40,-40,-39,-39,-38,-38,-37,-36,-35,-34,-34,-33,-32,-31,-30,-29,-28,-27,-26,-25,-24,-23,-22,-21,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,26,27,28,28,29,30,31,32,33,33,34,35,36,37,37,38,39,40,41,42,43,44,45,46,46,47,47,48,49,49,50,50,51,51,52,52,53,53,53,54,54,54,55,55,56,56,56,57,57,57,58,58,58,58,58,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,58,58,58,58,58,57,57,57,56,56,56,55,55,54,54,54,53,53,53,52,52,51,51,50,50,49,49,48,47,47,46,46,45,44,43,42,41,40,39,38,37,37,36,35,34,33,33,32,31,30,29,28,28,27,26,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-20,-21,-22,-23,-24,-25,-26,-26,-27,-28,-28,-29,-30,-31,-32,-33,-33,-34,-35,-36,-37,-37,-38,-39,-40,-41,-42,-43,-44,-45,-46,-46,-47,-47,-48,-49,-49,-50,-50,-51,-51,-52,-52,-53,-53,-53,-54,-54,-54,-55,-55,-56,-56,-56,-57,-57,-57,-58,-58,-58,-58,-58,-59,-59,-59,-59,-59,-59,-59,-59,-59,-59,-59,-59,-59,-59,-59,-59,-59,-59,-59,-59,-59,-58,-58,-58,-58,-58,-57,-57,-57,-56,-56,-56,-55,-55,-54,-54,-54,-53,-53,-53,-52,-52,-51,-51,-50,-50,-49,-49,-48,-47,-47,-46,-46,-45,-44,-43,-42,-41,-40,-39,-38,-37,-37,-36,-35,-34,-33,-33,-32,-31,-30,-29,-28,-28,-27,-26,-26,-25,-24,-23,-22,-21,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1]; var ys = [0,-1,-1,0,1,1,1,0,-1,-2,-2,-2,-1,0,1,2,2,2,2,2,1,0,-1,-2,-2,-3,-3,-3,-2,-1,0,1,2,3,3,3,3,3,2,1,0,-1,-2,-3,-3,-4,-4,-4,-3,-2,-1,0,1,2,3,4,4,4,4,4,3,2,1,0,-1,-2,-3,-4,-4,-5,-5,-5,-5,-4,-4,-3,-3,-2,-1,0,1,2,3,3,4,4,5,5,5,5,5,5,5,4,4,3,3,2,1,0,-1,-2,-3,-3,-4,-4,-5,-5,-5,-6,-6,-6,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,6,6,6,6,6,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-6,-6,-7,-7,-7,-7,-6,-6,-5,-5,-4,-3,-2,-1,0,1,2,3,4,5,5,6,6,7,7,7,7,7,7,7,6,6,5,5,4,3,2,1,0,-1,-2,-3,-4,-5,-5,-6,-6,-7,-7,-7,-8,-8,-8,-8,-8,-7,-7,-6,-5,-4,-4,-3,-2,-1,0,1,2,3,4,4,5,6,7,7,8,8,8,8,8,8,8,8,8,7,7,6,5,4,4,3,2,1,0,-1,-2,-3,-4,-4,-5,-6,-7,-7,-8,-8,-8,-8,-9,-9,-9,-9,-9,-8,-7,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,7,8,9,9,9,9,9,9,9,9,9,8,7,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-7,-8,-9,-9,-9,-9,-10,-10,-10,-10,-10,-9,-9,-8,-8,-7,-6,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,6,7,8,8,9,9,10,10,10,10,10,10,10,10,10,9,9,8,8,7,6,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-6,-7,-8,-8,-9,-9,-10,-10,-10,-10,-11,-11,-11,-11,-11,-10,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,10,11,11,11,11,11,11,11,11,11,10,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-10,-11,-11,-11,-11,-12,-12,-12,-12,-12,-11,-11,-10,-10,-9,-9,-8,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,8,9,9,10,10,11,11,12,12,12,12,12,12,12,12,12,11,11,10,10,9,9,8,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-8,-9,-9,-10,-10,-11,-11,-12,-12,-12,-12,-13,-13,-13,-13,-13,-13,-12,-12,-12,-11,-11,-10,-9,-8,-7,-7,-6,-5,-5,-4,-3,-2,-1,0,1,2,3,4,5,5,6,7,7,8,9,10,11,11,12,12,12,13,13,13,13,13,13,13,13,13,13,13,12,12,12,11,11,10,9,8,7,7,6,5,5,4,3,2,1,0,-1,-2,-3,-4,-5,-5,-6,-7,-7,-8,-9,-10,-11,-11,-12,-12,-12,-13,-13,-13,-13,-13,-14,-14,-14,-14,-14,-14,-13,-13,-12,-11,-11,-10,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,10,11,11,12,13,13,14,14,14,14,14,14,14,14,14,14,14,13,13,12,11,11,10,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-10,-11,-11,-12,-13,-13,-14,-14,-14,-14,-14,-15,-15,-15,-15,-15,-15,-14,-14,-13,-13,-12,-12,-11,-10,-9,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,9,10,11,12,12,13,13,14,14,15,15,15,15,15,15,15,15,15,15,15,14,14,13,13,12,12,11,10,9,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-9,-10,-11,-12,-12,-13,-13,-14,-14,-15,-15,-15,-15,-15,-16,-16,-16,-16,-16,-16,-15,-15,-14,-14,-13,-12,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,12,13,14,14,15,15,16,16,16,16,16,16,16,16,16,16,16,15,15,14,14,13,12,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-12,-13,-14,-14,-15,-15,-16,-16,-16,-16,-16,-17,-17,-17,-17,-17,-17,-16,-16,-16,-15,-15,-14,-14,-13,-13,-12,-11,-11,-10,-9,-8,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,8,9,10,11,11,12,13,13,14,14,15,15,16,16,16,17,17,17,17,17,17,17,17,17,17,17,16,16,16,15,15,14,14,13,13,12,11,11,10,9,8,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-8,-9,-10,-11,-11,-12,-13,-13,-14,-14,-15,-15,-16,-16,-16,-17,-17,-17,-17,-17,-18,-18,-18,-18,-18,-18,-18,-17,-17,-17,-16,-16,-15,-15,-14,-13,-12,-11,-10,-10,-9,-8,-7,-6,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,6,7,8,9,10,10,11,12,13,14,15,15,16,16,17,17,17,18,18,18,18,18,18,18,18,18,18,18,18,18,17,17,17,16,16,15,15,14,13,12,11,10,10,9,8,7,6,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-6,-7,-8,-9,-10,-10,-11,-12,-13,-14,-15,-15,-16,-16,-17,-17,-17,-18,-18,-18,-18,-18,-18,-19,-19,-19,-19,-19,-19,-19,-18,-18,-17,-17,-16,-15,-15,-14,-14,-13,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,13,14,14,15,15,16,17,17,18,18,19,19,19,19,19,19,19,19,19,19,19,19,19,18,18,17,17,16,15,15,14,14,13,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-13,-14,-14,-15,-15,-16,-17,-17,-18,-18,-19,-19,-19,-19,-19,-19,-20,-20,-20,-20,-20,-20,-20,-19,-19,-18,-18,-17,-17,-16,-16,-15,-14,-13,-12,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,12,13,14,15,16,16,17,17,18,18,19,19,20,20,20,20,20,20,20,20,20,20,20,20,20,19,19,18,18,17,17,16,16,15,14,13,12,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-12,-13,-14,-15,-16,-16,-17,-17,-18,-18,-19,-19,-20,-20,-20,-20,-20,-20,-21,-21,-21,-21,-21,-21,-21,-20,-20,-20,-19,-19,-19,-18,-18,-17,-16,-16,-15,-15,-14,-13,-12,-11,-11,-10,-9,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,9,10,11,11,12,13,14,15,15,16,16,17,18,18,19,19,19,20,20,20,21,21,21,21,21,21,21,21,21,21,21,21,21,20,20,20,19,19,19,18,18,17,16,16,15,15,14,13,12,11,11,10,9,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-9,-10,-11,-11,-12,-13,-14,-15,-15,-16,-16,-17,-18,-18,-19,-19,-19,-20,-20,-20,-21,-21,-21,-21,-21,-21,-22,-22,-22,-22,-22,-22,-22,-21,-21,-21,-20,-20,-19,-18,-18,-17,-17,-16,-15,-14,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,14,15,16,17,17,18,18,19,20,20,21,21,21,22,22,22,22,22,22,22,22,22,22,22,22,22,21,21,21,20,20,19,18,18,17,17,16,15,14,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-14,-15,-16,-17,-17,-18,-18,-19,-20,-20,-21,-21,-21,-22,-22,-22,-22,-22,-22,-23,-23,-23,-23,-23,-23,-23,-22,-22,-22,-21,-21,-20,-20,-19,-19,-18,-17,-16,-15,-14,-13,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,13,14,15,16,17,18,19,19,20,20,21,21,22,22,22,23,23,23,23,23,23,23,23,23,23,23,23,23,22,22,22,21,21,20,20,19,19,18,17,16,15,14,13,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-13,-14,-15,-16,-17,-18,-19,-19,-20,-20,-21,-21,-22,-22,-22,-23,-23,-23,-23,-23,-23,-24,-24,-24,-24,-24,-24,-24,-23,-23,-23,-22,-22,-21,-21,-20,-19,-19,-18,-18,-17,-17,-16,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,16,17,17,18,18,19,19,20,21,21,22,22,23,23,23,24,24,24,24,24,24,24,24,24,24,24,24,24,23,23,23,22,22,21,21,20,19,19,18,18,17,17,16,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-16,-17,-17,-18,-18,-19,-19,-20,-21,-21,-22,-22,-23,-23,-23,-24,-24,-24,-24,-24,-24,-25,-25,-25,-25,-25,-25,-25,-25,-24,-24,-24,-23,-23,-23,-22,-22,-21,-21,-20,-20,-19,-18,-17,-16,-15,-15,-14,-13,-12,-12,-11,-10,-9,-8,-7,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,7,8,9,10,11,12,12,13,14,15,15,16,17,18,19,20,20,21,21,22,22,23,23,23,24,24,24,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,24,24,24,23,23,23,22,22,21,21,20,20,19,18,17,16,15,15,14,13,12,12,11,10,9,8,7,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-7,-8,-9,-10,-11,-12,-12,-13,-14,-15,-15,-16,-17,-18,-19,-20,-20,-21,-21,-22,-22,-23,-23,-23,-24,-24,-24,-25,-25,-25,-25,-25,-25,-25,-26,-26,-26,-26,-26,-26,-26,-26,-25,-25,-25,-24,-24,-24,-23,-23,-22,-22,-21,-20,-20,-19,-19,-18,-18,-17,-16,-15,-14,-14,-13,-12,-11,-10,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,10,11,12,13,14,14,15,16,17,18,18,19,19,20,20,21,22,22,23,23,24,24,24,25,25,25,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,25,25,25,24,24,24,23,23,22,22,21,20,20,19,19,18,18,17,16,15,14,14,13,12,11,10,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-10,-11,-12,-13,-14,-14,-15,-16,-17,-18,-18,-19,-19,-20,-20,-21,-22,-22,-23,-23,-24,-24,-24,-25,-25,-25,-26,-26,-26,-26,-26,-26,-26,-27,-27,-27,-27,-27,-27,-27,-27,-26,-26,-26,-25,-25,-24,-24,-23,-22,-22,-21,-21,-20,-19,-18,-17,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,17,18,19,20,21,21,22,22,23,24,24,25,25,26,26,26,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,26,26,26,25,25,24,24,23,22,22,21,21,20,19,18,17,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-17,-18,-19,-20,-21,-21,-22,-22,-23,-24,-24,-25,-25,-26,-26,-26,-27,-27,-27,-27,-27,-27,-27,-28,-28,-28,-28,-28,-28,-28,-28,-27,-27,-27,-26,-26,-25,-25,-24,-24,-23,-23,-22,-21,-20,-19,-18,-17,-16,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,16,17,18,19,20,21,22,23,23,24,24,25,25,26,26,27,27,27,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,27,27,27,26,26,25,25,24,24,23,23,22,21,20,19,18,17,16,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-16,-17,-18,-19,-20,-21,-22,-23,-23,-24,-24,-25,-25,-26,-26,-27,-27,-27,-28,-28,-28,-28,-28,-28,-28,-29,-29,-29,-29,-29,-29,-29,-29,-28,-28,-28,-27,-27,-27,-26,-26,-25,-25,-24,-23,-23,-22,-22,-21,-21,-20,-20,-19,-19,-18,-17,-16,-15,-14,-13,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,13,14,15,16,17,18,19,19,20,20,21,21,22,22,23,23,24,25,25,26,26,27,27,27,28,28,28,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,28,28,28,27,27,27,26,26,25,25,24,23,23,22,22,21,21,20,20,19,19,18,17,16,15,14,13,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-13,-14,-15,-16,-17,-18,-19,-19,-20,-20,-21,-21,-22,-22,-23,-23,-24,-25,-25,-26,-26,-27,-27,-27,-28,-28,-28,-29,-29,-29,-29,-29,-29,-29,-30,-30,-30,-30,-30,-30,-30,-30,-29,-29,-29,-28,-28,-28,-27,-27,-26,-26,-25,-25,-24,-24,-23,-22,-21,-20,-19,-18,-18,-17,-16,-15,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,15,16,17,18,18,19,20,21,22,23,24,24,25,25,26,26,27,27,28,28,28,29,29,29,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,29,29,29,28,28,28,27,27,26,26,25,25,24,24,23,22,21,20,19,18,18,17,16,15,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-15,-16,-17,-18,-18,-19,-20,-21,-22,-23,-24,-24,-25,-25,-26,-26,-27,-27,-28,-28,-28,-29,-29,-29,-30,-30,-30,-30,-30,-30,-30,-31,-31,-31,-31,-31,-31,-31,-31,-30,-30,-30,-30,-29,-29,-29,-28,-28,-27,-27,-26,-26,-25,-24,-24,-23,-23,-22,-22,-21,-21,-20,-19,-18,-17,-17,-16,-15,-14,-13,-12,-11,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,11,12,13,14,15,16,17,17,18,19,20,21,21,22,22,23,23,24,24,25,26,26,27,27,28,28,29,29,29,30,30,30,30,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,30,30,30,30,29,29,29,28,28,27,27,26,26,25,24,24,23,23,22,22,21,21,20,19,18,17,17,16,15,14,13,12,11,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-11,-12,-13,-14,-15,-16,-17,-17,-18,-19,-20,-21,-21,-22,-22,-23,-23,-24,-24,-25,-26,-26,-27,-27,-28,-28,-29,-29,-29,-30,-30,-30,-30,-31,-31,-31,-31,-31,-31,-31,-32,-32,-32,-32,-32,-32,-32,-32,-32,-31,-31,-31,-31,-30,-30,-29,-29,-28,-28,-27,-26,-26,-25,-25,-24,-23,-22,-21,-20,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,8,9,10,11,12,13,14,15,16,17,18,19,20,20,21,22,23,24,25,25,26,26,27,28,28,29,29,30,30,31,31,31,31,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,31,31,31,31,30,30,29,29,28,28,27,26,26,25,25,24,23,22,21,20,20,19,18,17,16,15,14,13,12,11,10,9,8,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-20,-20,-21,-22,-23,-24,-25,-25,-26,-26,-27,-28,-28,-29,-29,-30,-30,-31,-31,-31,-31,-32,-32,-32,-32,-32,-32,-32,-32,-33,-33,-33,-33,-33,-33,-33,-33,-33,-32,-32,-32,-31,-31,-30,-30,-29,-29,-28,-28,-27,-27,-26,-25,-25,-24,-24,-23,-23,-22,-21,-20,-19,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,19,20,21,22,23,23,24,24,25,25,26,27,27,28,28,29,29,30,30,31,31,32,32,32,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,32,32,32,31,31,30,30,29,29,28,28,27,27,26,25,25,24,24,23,23,22,21,20,19,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-19,-20,-21,-22,-23,-23,-24,-24,-25,-25,-26,-27,-27,-28,-28,-29,-29,-30,-30,-31,-31,-32,-32,-32,-33,-33,-33,-33,-33,-33,-33,-33,-34,-34,-34,-34,-34,-34,-34,-34,-34,-33,-33,-33,-32,-32,-32,-31,-31,-31,-30,-30,-30,-29,-29,-28,-27,-27,-26,-26,-25,-24,-23,-22,-22,-21,-20,-19,-18,-18,-17,-16,-16,-15,-14,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,14,15,16,16,17,18,18,19,20,21,22,22,23,24,25,26,26,27,27,28,29,29,30,30,30,31,31,31,32,32,32,33,33,33,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,33,33,33,32,32,32,31,31,31,30,30,30,29,29,28,27,27,26,26,25,24,23,22,22,21,20,19,18,18,17,16,16,15,14,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-14,-15,-16,-16,-17,-18,-18,-19,-20,-21,-22,-22,-23,-24,-25,-26,-26,-27,-27,-28,-29,-29,-30,-30,-30,-31,-31,-31,-32,-32,-32,-33,-33,-33,-34,-34,-34,-34,-34,-34,-34,-34,-35,-35,-35,-35,-35,-35,-35,-35,-35,-34,-34,-34,-33,-33,-33,-32,-32,-31,-31,-30,-29,-29,-28,-28,-27,-26,-25,-24,-23,-22,-21,-21,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,21,22,23,24,25,26,27,28,28,29,29,30,31,31,32,32,33,33,33,34,34,34,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,34,34,34,33,33,33,32,32,31,31,30,29,29,28,28,27,26,25,24,23,22,21,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-20,-21,-21,-22,-23,-24,-25,-26,-27,-28,-28,-29,-29,-30,-31,-31,-32,-32,-33,-33,-33,-34,-34,-34,-35,-35,-35,-35,-35,-35,-35,-35,-36,-36,-36,-36,-36,-36,-36,-36,-36,-35,-35,-35,-34,-34,-34,-33,-33,-32,-32,-31,-31,-30,-30,-29,-28,-28,-27,-27,-26,-26,-25,-25,-24,-24,-23,-22,-21,-20,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,20,21,22,23,24,24,25,25,26,26,27,27,28,28,29,30,30,31,31,32,32,33,33,34,34,34,35,35,35,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,35,35,35,34,34,34,33,33,32,32,31,31,30,30,29,28,28,27,27,26,26,25,25,24,24,23,22,21,20,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-20,-20,-21,-22,-23,-24,-24,-25,-25,-26,-26,-27,-27,-28,-28,-29,-30,-30,-31,-31,-32,-32,-33,-33,-34,-34,-34,-35,-35,-35,-36,-36,-36,-36,-36,-36,-36,-36,-37,-37,-37,-37,-37,-37,-37,-37,-37,-36,-36,-36,-36,-35,-35,-35,-34,-34,-33,-33,-32,-32,-31,-30,-30,-29,-29,-28,-27,-26,-25,-24,-23,-23,-22,-21,-20,-19,-18,-17,-16,-15,-14,-13,-12,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,12,13,14,15,16,17,18,19,20,21,22,23,23,24,25,26,27,28,29,29,30,30,31,32,32,33,33,34,34,35,35,35,36,36,36,36,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,36,36,36,36,35,35,35,34,34,33,33,32,32,31,30,30,29,29,28,27,26,25,24,23,23,22,21,20,19,18,17,16,15,14,13,12,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-12,-13,-14,-15,-16,-17,-18,-19,-20,-21,-22,-23,-23,-24,-25,-26,-27,-28,-29,-29,-30,-30,-31,-32,-32,-33,-33,-34,-34,-35,-35,-35,-36,-36,-36,-36,-37,-37,-37,-37,-37,-37,-37,-37,-38,-38,-38,-38,-38,-38,-38,-38,-38,-37,-37,-37,-37,-36,-36,-35,-35,-35,-34,-34,-34,-33,-33,-32,-32,-31,-31,-30,-29,-29,-28,-28,-27,-27,-26,-26,-25,-24,-23,-22,-22,-21,-20,-19,-19,-18,-17,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,17,18,19,19,20,21,22,22,23,24,25,26,26,27,27,28,28,29,29,30,31,31,32,32,33,33,34,34,34,35,35,35,36,36,37,37,37,37,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,37,37,37,37,36,36,35,35,35,34,34,34,33,33,32,32,31,31,30,29,29,28,28,27,27,26,26,25,24,23,22,22,21,20,19,19,18,17,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-17,-18,-19,-19,-20,-21,-22,-22,-23,-24,-25,-26,-26,-27,-27,-28,-28,-29,-29,-30,-31,-31,-32,-32,-33,-33,-34,-34,-34,-35,-35,-35,-36,-36,-37,-37,-37,-37,-38,-38,-38,-38,-38,-38,-38,-38,-39,-39,-39,-39,-39,-39,-39,-39,-39,-38,-38,-38,-38,-37,-37,-37,-36,-36,-36,-35,-35,-34,-34,-33,-33,-32,-31,-31,-30,-30,-29,-28,-27,-26,-25,-25,-24,-23,-22,-21,-21,-20,-19,-18,-17,-16,-15,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,15,16,17,18,19,20,21,21,22,23,24,25,25,26,27,28,29,30,30,31,31,32,33,33,34,34,35,35,36,36,36,37,37,37,38,38,38,38,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,38,38,38,38,37,37,37,36,36,36,35,35,34,34,33,33,32,31,31,30,30,29,28,27,26,25,25,24,23,22,21,21,20,19,18,17,16,15,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-15,-16,-17,-18,-19,-20,-21,-21,-22,-23,-24,-25,-25,-26,-27,-28,-29,-30,-30,-31,-31,-32,-33,-33,-34,-34,-35,-35,-36,-36,-36,-37,-37,-37,-38,-38,-38,-38,-39,-39,-39,-39,-39,-39,-39,-39,-40,-40,-40,-40,-40,-40,-40,-40,-40,-39,-39,-39,-39,-38,-38,-38,-37,-37,-36,-36,-35,-35,-34,-33,-33,-32,-32,-31,-30,-29,-28,-27,-26,-25,-24,-24,-23,-22,-21,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,24,25,26,27,28,29,30,31,32,32,33,33,34,35,35,36,36,37,37,38,38,38,39,39,39,39,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,39,39,39,39,38,38,38,37,37,36,36,35,35,34,33,33,32,32,31,30,29,28,27,26,25,24,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-20,-21,-22,-23,-24,-24,-25,-26,-27,-28,-29,-30,-31,-32,-32,-33,-33,-34,-35,-35,-36,-36,-37,-37,-38,-38,-38,-39,-39,-39,-39,-40,-40,-40,-40,-40,-40,-40,-40,-41,-41,-41,-41,-41,-41,-41,-41,-41,-41,-40,-40,-40,-40,-39,-39,-39,-38,-38,-37,-37,-36,-36,-35,-35,-34,-34,-33,-32,-32,-31,-31,-30,-30,-29,-29,-28,-28,-27,-27,-26,-25,-24,-23,-23,-22,-21,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,23,24,25,26,27,27,28,28,29,29,30,30,31,31,32,32,33,34,34,35,35,36,36,37,37,38,38,39,39,39,40,40,40,40,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,40,40,40,40,39,39,39,38,38,37,37,36,36,35,35,34,34,33,32,32,31,31,30,30,29,29,28,28,27,27,26,25,24,23,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-20,-21,-22,-23,-23,-24,-25,-26,-27,-27,-28,-28,-29,-29,-30,-30,-31,-31,-32,-32,-33,-34,-34,-35,-35,-36,-36,-37,-37,-38,-38,-39,-39,-39,-40,-40,-40,-40,-41,-41,-41,-41,-41,-41,-41,-41,-41,-42,-42,-42,-42,-42,-42,-42,-42,-42,-42,-41,-41,-41,-40,-40,-40,-39,-39,-39,-38,-38,-38,-37,-37,-36,-36,-35,-34,-34,-33,-33,-32,-31,-30,-29,-28,-27,-26,-26,-25,-24,-23,-22,-21,-20,-20,-19,-18,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,18,19,20,20,21,22,23,24,25,26,26,27,28,29,30,31,32,33,33,34,34,35,36,36,37,37,38,38,38,39,39,39,40,40,40,41,41,41,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,41,41,41,40,40,40,39,39,39,38,38,38,37,37,36,36,35,34,34,33,33,32,31,30,29,28,27,26,26,25,24,23,22,21,20,20,19,18,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-18,-19,-20,-20,-21,-22,-23,-24,-25,-26,-26,-27,-28,-29,-30,-31,-32,-33,-33,-34,-34,-35,-36,-36,-37,-37,-38,-38,-38,-39,-39,-39,-40,-40,-40,-41,-41,-41,-42,-42,-42,-42,-42,-42,-42,-42,-42,-43,-43,-43,-43,-43,-43,-43,-43,-43,-43,-42,-42,-42,-42,-41,-41,-41,-40,-40,-40,-39,-39,-38,-38,-37,-37,-36,-36,-35,-35,-34,-33,-33,-32,-32,-31,-31,-30,-30,-29,-29,-28,-27,-26,-25,-25,-24,-23,-22,-22,-21,-20,-19,-18,-17,-16,-15,-14,-13,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,13,14,15,16,17,18,19,20,21,22,22,23,24,25,25,26,27,28,29,29,30,30,31,31,32,32,33,33,34,35,35,36,36,37,37,38,38,39,39,40,40,40,41,41,41,42,42,42,42,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,42,42,42,42,41,41,41,40,40,40,39,39,38,38,37,37,36,36,35,35,34,33,33,32,32,31,31,30,30,29,29,28,27,26,25,25,24,23,22,22,21,20,19,18,17,16,15,14,13,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-13,-14,-15,-16,-17,-18,-19,-20,-21,-22,-22,-23,-24,-25,-25,-26,-27,-28,-29,-29,-30,-30,-31,-31,-32,-32,-33,-33,-34,-35,-35,-36,-36,-37,-37,-38,-38,-39,-39,-40,-40,-40,-41,-41,-41,-42,-42,-42,-42,-43,-43,-43,-43,-43,-43,-43,-43,-43,-44,-44,-44,-44,-44,-44,-44,-44,-44,-44,-43,-43,-43,-43,-42,-42,-42,-41,-41,-41,-40,-40,-39,-39,-38,-38,-37,-37,-36,-35,-35,-34,-34,-33,-32,-31,-30,-29,-28,-28,-27,-26,-25,-24,-24,-23,-22,-21,-20,-19,-18,-17,-16,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,16,17,18,19,20,21,22,23,24,24,25,26,27,28,28,29,30,31,32,33,34,34,35,35,36,37,37,38,38,39,39,40,40,41,41,41,42,42,42,43,43,43,43,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,43,43,43,43,42,42,42,41,41,41,40,40,39,39,38,38,37,37,36,35,35,34,34,33,32,31,30,29,28,28,27,26,25,24,24,23,22,21,20,19,18,17,16,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-16,-17,-18,-19,-20,-21,-22,-23,-24,-24,-25,-26,-27,-28,-28,-29,-30,-31,-32,-33,-34,-34,-35,-35,-36,-37,-37,-38,-38,-39,-39,-40,-40,-41,-41,-41,-42,-42,-42,-43,-43,-43,-43,-44,-44,-44,-44,-44,-44,-44,-44,-44,-45,-45,-45,-45,-45,-45,-45,-45,-45,-45,-44,-44,-44,-44,-43,-43,-43,-42,-42,-41,-41,-40,-40,-39,-39,-38,-37,-37,-36,-36,-35,-34,-33,-33,-32,-32,-31,-30,-29,-28,-27,-27,-26,-25,-24,-23,-22,-21,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,27,28,29,30,31,32,32,33,33,34,35,36,36,37,37,38,39,39,40,40,41,41,42,42,43,43,43,44,44,44,44,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,44,44,44,44,43,43,43,42,42,41,41,40,40,39,39,38,37,37,36,36,35,34,33,33,32,32,31,30,29,28,27,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-20,-21,-22,-23,-24,-25,-26,-27,-27,-28,-29,-30,-31,-32,-32,-33,-33,-34,-35,-36,-36,-37,-37,-38,-39,-39,-40,-40,-41,-41,-42,-42,-43,-43,-43,-44,-44,-44,-44,-45,-45,-45,-45,-45,-45,-45,-45,-45,-46,-46,-46,-46,-46,-46,-46,-46,-46,-46,-45,-45,-45,-45,-44,-44,-44,-43,-43,-42,-42,-42,-41,-41,-40,-40,-39,-39,-38,-38,-37,-36,-36,-35,-35,-34,-34,-33,-32,-31,-31,-30,-30,-29,-28,-27,-26,-26,-25,-24,-23,-22,-21,-21,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,21,22,23,24,25,26,26,27,28,29,30,30,31,31,32,33,34,34,35,35,36,36,37,38,38,39,39,40,40,41,41,42,42,42,43,43,44,44,44,45,45,45,45,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,45,45,45,45,44,44,44,43,43,42,42,42,41,41,40,40,39,39,38,38,37,36,36,35,35,34,34,33,32,31,31,30,30,29,28,27,26,26,25,24,23,22,21,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-20,-21,-21,-22,-23,-24,-25,-26,-26,-27,-28,-29,-30,-30,-31,-31,-32,-33,-34,-34,-35,-35,-36,-36,-37,-38,-38,-39,-39,-40,-40,-41,-41,-42,-42,-42,-43,-43,-44,-44,-44,-45,-45,-45,-45,-46,-46,-46,-46,-46,-46,-46,-46,-46,-47,-47,-47,-47,-47,-47,-47,-47,-47,-47,-46,-46,-46,-46,-45,-45,-45,-44,-44,-44,-43,-43,-43,-42,-42,-41,-41,-40,-40,-39,-38,-38,-37,-37,-36,-35,-34,-33,-32,-31,-30,-29,-29,-28,-27,-26,-25,-24,-23,-23,-22,-21,-20,-19,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,19,20,21,22,23,23,24,25,26,27,28,29,29,30,31,32,33,34,35,36,37,37,38,38,39,40,40,41,41,42,42,43,43,43,44,44,44,45,45,45,46,46,46,46,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,46,46,46,46,45,45,45,44,44,44,43,43,43,42,42,41,41,40,40,39,38,38,37,37,36,35,34,33,32,31,30,29,29,28,27,26,25,24,23,23,22,21,20,19,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-19,-20,-21,-22,-23,-23,-24,-25,-26,-27,-28,-29,-29,-30,-31,-32,-33,-34,-35,-36,-37,-37,-38,-38,-39,-40,-40,-41,-41,-42,-42,-43,-43,-43,-44,-44,-44,-45,-45,-45,-46,-46,-46,-46,-47,-47,-47,-47,-47,-47,-47,-47,-47,-48,-48,-48,-48,-48,-48,-48,-48,-48,-48,-47,-47,-47,-47,-46,-46,-46,-45,-45,-45,-44,-44,-43,-43,-42,-42,-41,-41,-40,-40,-39,-39,-38,-37,-37,-36,-36,-35,-35,-34,-34,-33,-33,-32,-32,-31,-30,-29,-28,-28,-27,-26,-25,-25,-24,-23,-22,-21,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,25,26,27,28,28,29,30,31,32,32,33,33,34,34,35,35,36,36,37,37,38,39,39,40,40,41,41,42,42,43,43,44,44,45,45,45,46,46,46,47,47,47,47,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,47,47,47,47,46,46,46,45,45,45,44,44,43,43,42,42,41,41,40,40,39,39,38,37,37,36,36,35,35,34,34,33,33,32,32,31,30,29,28,28,27,26,25,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-20,-21,-22,-23,-24,-25,-25,-26,-27,-28,-28,-29,-30,-31,-32,-32,-33,-33,-34,-34,-35,-35,-36,-36,-37,-37,-38,-39,-39,-40,-40,-41,-41,-42,-42,-43,-43,-44,-44,-45,-45,-45,-46,-46,-46,-47,-47,-47,-47,-48,-48,-48,-48,-48,-48,-48,-48,-48,-49,-49,-49,-49,-49,-49,-49,-49,-49,-49,-48,-48,-48,-48,-47,-47,-47,-47,-46,-46,-46,-45,-45,-44,-44,-43,-43,-42,-42,-41,-41,-40,-39,-39,-38,-38,-37,-36,-35,-34,-33,-32,-31,-31,-30,-29,-28,-27,-27,-26,-25,-24,-23,-22,-21,-20,-19,-18,-17,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,17,18,19,20,21,22,23,24,25,26,27,27,28,29,30,31,31,32,33,34,35,36,37,38,38,39,39,40,41,41,42,42,43,43,44,44,45,45,46,46,46,47,47,47,47,48,48,48,48,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,48,48,48,48,47,47,47,47,46,46,46,45,45,44,44,43,43,42,42,41,41,40,39,39,38,38,37,36,35,34,33,32,31,31,30,29,28,27,27,26,25,24,23,22,21,20,19,18,17,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-17,-18,-19,-20,-21,-22,-23,-24,-25,-26,-27,-27,-28,-29,-30,-31,-31,-32,-33,-34,-35,-36,-37,-38,-38,-39,-39,-40,-41,-41,-42,-42,-43,-43,-44,-44,-45,-45,-46,-46,-46,-47,-47,-47,-47,-48,-48,-48,-48,-49,-49,-49,-49,-49,-49,-49,-49,-49,-50,-50,-50,-50,-50,-50,-50,-50,-50,-50,-50,-49,-49,-49,-49,-49,-48,-48,-48,-48,-47,-47,-46,-46,-46,-45,-45,-44,-44,-43,-43,-42,-41,-41,-40,-40,-39,-38,-38,-37,-37,-36,-36,-35,-35,-34,-34,-33,-32,-31,-30,-30,-29,-28,-27,-26,-25,-24,-23,-22,-22,-21,-20,-19,-18,-17,-16,-15,-14,-14,-13,-12,-11,-10,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,10,11,12,13,14,14,15,16,17,18,19,20,21,22,22,23,24,25,26,27,28,29,30,30,31,32,33,34,34,35,35,36,36,37,37,38,38,39,40,40,41,41,42,43,43,44,44,45,45,46,46,46,47,47,48,48,48,48,49,49,49,49,49,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,49,49,49,49,49,48,48,48,48,47,47,46,46,46,45,45,44,44,43,43,42,41,41,40,40,39,38,38,37,37,36,36,35,35,34,34,33,32,31,30,30,29,28,27,26,25,24,23,22,22,21,20,19,18,17,16,15,14,14,13,12,11,10,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-10,-11,-12,-13,-14,-14,-15,-16,-17,-18,-19,-20,-21,-22,-22,-23,-24,-25,-26,-27,-28,-29,-30,-30,-31,-32,-33,-34,-34,-35,-35,-36,-36,-37,-37,-38,-38,-39,-40,-40,-41,-41,-42,-43,-43,-44,-44,-45,-45,-46,-46,-46,-47,-47,-48,-48,-48,-48,-49,-49,-49,-49,-49,-50,-50,-50,-50,-50,-50,-50,-50,-50,-50,-51,-51,-51,-51,-51,-51,-51,-51,-51,-51,-51,-50,-50,-50,-50,-49,-49,-49,-48,-48,-47,-47,-47,-46,-46,-45,-45,-45,-44,-44,-43,-43,-42,-42,-41,-40,-40,-39,-39,-38,-37,-36,-35,-34,-33,-33,-32,-31,-30,-29,-29,-28,-27,-26,-26,-25,-24,-24,-23,-22,-21,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,24,25,26,26,27,28,29,29,30,31,32,33,33,34,35,36,37,38,39,39,40,40,41,42,42,43,43,44,44,45,45,45,46,46,47,47,47,48,48,49,49,49,50,50,50,50,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,50,50,50,50,49,49,49,48,48,47,47,47,46,46,45,45,45,44,44,43,43,42,42,41,40,40,39,39,38,37,36,35,34,33,33,32,31,30,29,29,28,27,26,26,25,24,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-20,-21,-22,-23,-24,-24,-25,-26,-26,-27,-28,-29,-29,-30,-31,-32,-33,-33,-34,-35,-36,-37,-38,-39,-39,-40,-40,-41,-42,-42,-43,-43,-44,-44,-45,-45,-45,-46,-46,-47,-47,-47,-48,-48,-49,-49,-49,-50,-50,-50,-50,-51,-51,-51,-51,-51,-51,-51,-51,-51,-51,-52,-52,-52,-52,-52,-52,-52,-52,-52,-52,-52,-51,-51,-51,-51,-50,-50,-50,-49,-49,-49,-48,-48,-48,-47,-47,-46,-46,-45,-44,-44,-43,-42,-42,-41,-41,-40,-39,-38,-37,-36,-35,-34,-33,-32,-32,-31,-30,-29,-28,-27,-26,-25,-24,-23,-22,-21,-20,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,20,21,22,23,24,25,26,27,28,29,30,31,32,32,33,34,35,36,37,38,39,40,41,41,42,42,43,44,44,45,46,46,47,47,48,48,48,49,49,49,50,50,50,51,51,51,51,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,51,51,51,51,50,50,50,49,49,49,48,48,48,47,47,46,46,45,44,44,43,42,42,41,41,40,39,38,37,36,35,34,33,32,32,31,30,29,28,27,26,25,24,23,22,21,20,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-20,-20,-21,-22,-23,-24,-25,-26,-27,-28,-29,-30,-31,-32,-32,-33,-34,-35,-36,-37,-38,-39,-40,-41,-41,-42,-42,-43,-44,-44,-45,-46,-46,-47,-47,-48,-48,-48,-49,-49,-49,-50,-50,-50,-51,-51,-51,-51,-52,-52,-52,-52,-52,-52,-52,-52,-52,-52,-53,-53,-53,-53,-53,-53,-53,-53,-53,-53,-53,-52,-52,-52,-52,-51,-51,-51,-50,-50,-50,-49,-49,-48,-48,-47,-47,-46,-46,-45,-45,-44,-44,-43,-43,-42,-41,-41,-40,-40,-39,-39,-38,-38,-37,-37,-36,-36,-35,-35,-34,-33,-32,-31,-31,-30,-29,-28,-28,-27,-26,-25,-24,-23,-22,-21,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,28,29,30,31,31,32,33,34,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,43,43,44,44,45,45,46,46,47,47,48,48,49,49,50,50,50,51,51,51,52,52,52,52,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,52,52,52,52,51,51,51,50,50,50,49,49,48,48,47,47,46,46,45,45,44,44,43,43,42,41,41,40,40,39,39,38,38,37,37,36,36,35,35,34,33,32,31,31,30,29,28,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-20,-21,-22,-23,-24,-25,-26,-27,-28,-28,-29,-30,-31,-31,-32,-33,-34,-35,-35,-36,-36,-37,-37,-38,-38,-39,-39,-40,-40,-41,-41,-42,-43,-43,-44,-44,-45,-45,-46,-46,-47,-47,-48,-48,-49,-49,-50,-50,-50,-51,-51,-51,-52,-52,-52,-52,-53,-53,-53,-53,-53,-53,-53,-53,-53,-53,-54,-54,-54,-54,-54,-54,-54,-54,-54,-54,-54,-53,-53,-53,-53,-52,-52,-52,-51,-51,-51,-50,-50,-49,-49,-48,-48,-47,-47,-46,-46,-45,-45,-44,-43,-43,-42,-42,-41,-40,-39,-38,-37,-36,-35,-34,-34,-33,-32,-31,-30,-30,-29,-28,-27,-26,-25,-24,-23,-22,-21,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,30,31,32,33,34,34,35,36,37,38,39,40,41,42,42,43,43,44,45,45,46,46,47,47,48,48,49,49,50,50,51,51,51,52,52,52,53,53,53,53,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,53,53,53,53,52,52,52,51,51,51,50,50,49,49,48,48,47,47,46,46,45,45,44,43,43,42,42,41,40,39,38,37,36,35,34,34,33,32,31,30,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-20,-21,-22,-23,-24,-25,-26,-27,-28,-29,-30,-30,-31,-32,-33,-34,-34,-35,-36,-37,-38,-39,-40,-41,-42,-42,-43,-43,-44,-45,-45,-46,-46,-47,-47,-48,-48,-49,-49,-50,-50,-51,-51,-51,-52,-52,-52,-53,-53,-53,-53,-54,-54,-54,-54,-54,-54,-54,-54,-54,-54,-55,-55,-55,-55,-55,-55,-55,-55,-55,-55,-55,-54,-54,-54,-54,-53,-53,-53,-53,-52,-52,-52,-51,-51,-51,-50,-50,-50,-49,-49,-49,-48,-48,-47,-47,-46,-45,-45,-44,-44,-43,-42,-42,-41,-41,-40,-40,-39,-39,-38,-38,-37,-37,-36,-35,-34,-33,-33,-32,-31,-30,-29,-28,-27,-27,-26,-25,-25,-24,-23,-23,-22,-21,-20,-19,-18,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,18,19,20,21,22,23,23,24,25,25,26,27,27,28,29,30,31,32,33,33,34,35,36,37,37,38,38,39,39,40,40,41,41,42,42,43,44,44,45,45,46,47,47,48,48,49,49,49,50,50,50,51,51,51,52,52,52,53,53,53,53,54,54,54,54,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,54,54,54,54,53,53,53,53,52,52,52,51,51,51,50,50,50,49,49,49,48,48,47,47,46,45,45,44,44,43,42,42,41,41,40,40,39,39,38,38,37,37,36,35,34,33,33,32,31,30,29,28,27,27,26,25,25,24,23,23,22,21,20,19,18,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-18,-19,-20,-21,-22,-23,-23,-24,-25,-25,-26,-27,-27,-28,-29,-30,-31,-32,-33,-33,-34,-35,-36,-37,-37,-38,-38,-39,-39,-40,-40,-41,-41,-42,-42,-43,-44,-44,-45,-45,-46,-47,-47,-48,-48,-49,-49,-49,-50,-50,-50,-51,-51,-51,-52,-52,-52,-53,-53,-53,-53,-54,-54,-54,-54,-55,-55,-55,-55,-55,-55,-55,-55,-55,-55,-56,-56,-56,-56,-56,-56,-56,-56,-56,-56,-56,-55,-55,-55,-55,-54,-54,-54,-54,-53,-53,-52,-52,-52,-51,-51,-50,-50,-49,-49,-48,-48,-47,-47,-46,-46,-45,-44,-44,-43,-43,-42,-41,-40,-39,-38,-37,-36,-36,-35,-34,-33,-32,-32,-31,-30,-29,-29,-28,-27,-26,-25,-24,-23,-22,-21,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,29,30,31,32,32,33,34,35,36,36,37,38,39,40,41,42,43,43,44,44,45,46,46,47,47,48,48,49,49,50,50,51,51,52,52,52,53,53,54,54,54,54,55,55,55,55,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,55,55,55,55,54,54,54,54,53,53,52,52,52,51,51,50,50,49,49,48,48,47,47,46,46,45,44,44,43,43,42,41,40,39,38,37,36,36,35,34,33,32,32,31,30,29,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-20,-21,-22,-23,-24,-25,-26,-27,-28,-29,-29,-30,-31,-32,-32,-33,-34,-35,-36,-36,-37,-38,-39,-40,-41,-42,-43,-43,-44,-44,-45,-46,-46,-47,-47,-48,-48,-49,-49,-50,-50,-51,-51,-52,-52,-52,-53,-53,-54,-54,-54,-54,-55,-55,-55,-55,-56,-56,-56,-56,-56,-56,-56,-56,-56,-56,-57,-57,-57,-57,-57,-57,-57,-57,-57,-57,-57,-56,-56,-56,-56,-56,-55,-55,-55,-55,-54,-54,-54,-53,-53,-53,-52,-52,-51,-51,-50,-50,-49,-49,-48,-48,-47,-46,-46,-45,-45,-44,-43,-42,-41,-41,-40,-39,-38,-37,-36,-35,-35,-34,-33,-32,-31,-31,-30,-29,-28,-27,-26,-25,-24,-23,-22,-21,-21,-20,-19,-18,-17,-16,-15,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,15,16,17,18,19,20,21,21,22,23,24,25,26,27,28,29,30,31,31,32,33,34,35,35,36,37,38,39,40,41,41,42,43,44,45,45,46,46,47,48,48,49,49,50,50,51,51,52,52,53,53,53,54,54,54,55,55,55,55,56,56,56,56,56,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,56,56,56,56,56,55,55,55,55,54,54,54,53,53,53,52,52,51,51,50,50,49,49,48,48,47,46,46,45,45,44,43,42,41,41,40,39,38,37,36,35,35,34,33,32,31,31,30,29,28,27,26,25,24,23,22,21,21,20,19,18,17,16,15,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-15,-16,-17,-18,-19,-20,-21,-21,-22,-23,-24,-25,-26,-27,-28,-29,-30,-31,-31,-32,-33,-34,-35,-35,-36,-37,-38,-39,-40,-41,-41,-42,-43,-44,-45,-45,-46,-46,-47,-48,-48,-49,-49,-50,-50,-51,-51,-52,-52,-53,-53,-53,-54,-54,-54,-55,-55,-55,-55,-56,-56,-56,-56,-56,-57,-57,-57,-57,-57,-57,-57,-57,-57,-57,-58,-58,-58,-58,-58,-58,-58,-58,-58,-58,-58,-57,-57,-57,-57,-57,-56,-56,-56,-55,-55,-55,-54,-54,-53,-53,-52,-52,-51,-51,-50,-50,-49,-48,-48,-47,-47,-46,-45,-45,-44,-44,-43,-43,-42,-42,-41,-40,-40,-39,-39,-38,-38,-37,-36,-35,-34,-34,-33,-32,-31,-30,-29,-28,-27,-26,-25,-24,-23,-22,-21,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,34,35,36,37,38,38,39,39,40,40,41,42,42,43,43,44,44,45,45,46,47,47,48,48,49,50,50,51,51,52,52,53,53,54,54,55,55,55,56,56,56,57,57,57,57,57,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,57,57,57,57,57,56,56,56,55,55,55,54,54,53,53,52,52,51,51,50,50,49,48,48,47,47,46,45,45,44,44,43,43,42,42,41,40,40,39,39,38,38,37,36,35,34,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-20,-21,-22,-23,-24,-25,-26,-27,-28,-29,-30,-31,-32,-33,-34,-34,-35,-36,-37,-38,-38,-39,-39,-40,-40,-41,-42,-42,-43,-43,-44,-44,-45,-45,-46,-47,-47,-48,-48,-49,-50,-50,-51,-51,-52,-52,-53,-53,-54,-54,-55,-55,-55,-56,-56,-56,-57,-57,-57,-57,-57,-58,-58,-58,-58,-58,-58,-58,-58,-58,-58,-59,-59,-59,-59,-59,-59,-59,-59,-59,-59,-59,-58,-58,-58,-58,-58,-57,-57,-57,-56,-56,-56,-55,-55,-54,-54,-54,-53,-53,-53,-52,-52,-51,-51,-50,-50,-49,-49,-48,-47,-47,-46,-46,-45,-44,-43,-42,-41,-40,-39,-38,-37,-37,-36,-35,-34,-33,-33,-32,-31,-30,-29,-28,-28,-27,-26,-26,-25,-24,-23,-22,-21,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,26,27,28,28,29,30,31,32,33,33,34,35,36,37,37,38,39,40,41,42,43,44,45,46,46,47,47,48,49,49,50,50,51,51,52,52,53,53,53,54,54,54,55,55,56,56,56,57,57,57,58,58,58,58,58,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,58,58,58,58,58,57,57,57,56,56,56,55,55,54,54,54,53,53,53,52,52,51,51,50,50,49,49,48,47,47,46,46,45,44,43,42,41,40,39,38,37,37,36,35,34,33,33,32,31,30,29,28,28,27,26,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-20,-21,-22,-23,-24,-25,-26,-26,-27,-28,-28,-29,-30,-31,-32,-33,-33,-34,-35,-36,-37,-37,-38,-39,-40,-41,-42,-43,-44,-45,-46,-46,-47,-47,-48,-49,-49,-50,-50,-51,-51,-52,-52,-53,-53,-53,-54,-54,-54,-55,-55,-56,-56,-56,-57,-57,-57,-58,-58,-58,-58,-58,-59,-59,-59,-59,-59,-59,-59,-59,-59,-59]; var towards_center = [0,0,0,0,0,0,0,0,0,1,1,2,3,3,3,4,5,5,5,6,7,7,7,8,1,9,9,10,12,13,13,13,14,16,17,17,17,18,20,21,21,21,22,24,9,25,25,26,11,29,30,30,30,31,15,34,35,35,35,36,19,39,40,40,40,41,23,44,25,45,45,46,47,27,48,28,49,50,51,51,51,52,53,32,54,33,55,56,57,57,57,58,59,37,60,38,61,62,63,63,63,64,65,42,66,43,67,68,45,69,69,70,71,73,75,77,78,79,79,79,80,81,83,85,87,88,89,89,89,90,91,93,95,97,98,99,99,99,100,101,103,105,107,108,69,109,109,110,111,72,113,74,114,76,116,117,118,118,118,119,120,82,122,84,123,86,125,126,127,127,127,128,129,92,131,94,132,96,134,135,136,136,136,137,138,102,140,104,141,106,143,144,109,145,145,146,147,148,112,149,151,153,115,154,155,156,157,157,157,158,159,160,121,161,163,165,124,166,167,168,169,169,169,170,171,172,130,173,175,177,133,178,179,180,181,181,181,182,183,184,139,185,187,189,142,190,191,192,145,193,193,194,195,196,198,150,200,152,202,204,205,206,207,207,207,208,209,210,212,162,214,164,216,218,219,220,221,221,221,222,223,224,226,174,228,176,230,232,233,234,235,235,235,236,237,238,240,186,242,188,244,246,247,248,193,249,249,250,251,252,197,254,199,255,257,201,258,203,260,261,262,263,263,263,264,265,266,211,268,213,269,271,215,272,217,274,275,276,277,277,277,278,279,280,225,282,227,283,285,229,286,231,288,289,290,291,291,291,292,293,294,239,296,241,297,299,243,300,245,302,303,304,249,305,305,306,307,308,253,310,312,256,315,317,259,319,320,321,322,322,322,323,324,325,267,327,329,270,332,334,273,336,337,338,339,339,339,340,341,342,281,344,346,284,349,351,287,353,354,355,356,356,356,357,358,359,295,361,363,298,366,368,301,370,371,372,305,373,373,374,375,376,309,378,311,380,313,381,314,382,316,384,318,386,387,388,389,389,389,390,391,392,326,394,328,396,330,397,331,398,333,400,335,402,403,404,405,405,405,406,407,408,343,410,345,412,347,413,348,414,350,416,352,418,419,420,421,421,421,422,423,424,360,426,362,428,364,429,365,430,367,432,369,434,435,436,373,437,437,438,439,440,441,377,442,443,379,444,446,448,450,383,451,452,385,453,454,455,456,457,457,457,458,459,460,461,393,462,463,395,464,466,468,470,399,471,472,401,473,474,475,476,477,477,477,478,479,480,481,409,482,483,411,484,486,488,490,415,491,492,417,493,494,495,496,497,497,497,498,499,500,501,425,502,503,427,504,506,508,510,431,511,512,433,513,514,515,516,437,517,517,518,519,520,521,523,524,526,445,528,447,529,449,531,533,534,536,537,538,539,540,540,540,541,542,543,544,546,547,549,465,551,467,552,469,554,556,557,559,560,561,562,563,563,563,564,565,566,567,569,570,572,485,574,487,575,489,577,579,580,582,583,584,585,586,586,586,587,588,589,590,592,593,595,505,597,507,598,509,600,602,603,605,606,607,608,517,609,609,610,611,612,613,522,615,525,617,527,618,620,622,530,623,532,625,535,627,628,629,630,631,631,631,632,633,634,635,545,637,548,639,550,640,642,644,553,645,555,647,558,649,650,651,652,653,653,653,654,655,656,657,568,659,571,661,573,662,664,666,576,667,578,669,581,671,672,673,674,675,675,675,676,677,678,679,591,681,594,683,596,684,686,688,599,689,601,691,604,693,694,695,696,609,697,697,698,699,700,701,614,703,616,705,707,619,709,621,711,713,624,715,626,717,718,719,720,721,721,721,722,723,724,725,636,727,638,729,731,641,733,643,735,737,646,739,648,741,742,743,744,745,745,745,746,747,748,749,658,751,660,753,755,663,757,665,759,761,668,763,670,765,766,767,768,769,769,769,770,771,772,773,680,775,682,777,779,685,781,687,783,785,690,787,692,789,790,791,792,697,793,793,794,795,796,797,702,799,800,704,801,706,803,708,804,806,710,807,712,809,714,810,811,716,813,814,815,816,817,817,817,818,819,820,821,726,823,824,728,825,730,827,732,828,830,734,831,736,833,738,834,835,740,837,838,839,840,841,841,841,842,843,844,845,750,847,848,752,849,754,851,756,852,854,758,855,760,857,762,858,859,764,861,862,863,864,865,865,865,866,867,868,869,774,871,872,776,873,778,875,780,876,878,782,879,784,881,786,882,883,788,885,886,887,888,793,889,889,890,891,892,893,894,798,895,896,898,899,802,900,902,805,905,907,808,908,909,911,912,812,913,914,915,916,917,918,918,918,919,920,921,922,923,822,924,925,927,928,826,929,931,829,934,936,832,937,938,940,941,836,942,943,944,945,946,947,947,947,948,949,950,951,952,846,953,954,956,957,850,958,960,853,963,965,856,966,967,969,970,860,971,972,973,974,975,976,976,976,977,978,979,980,981,870,982,983,985,986,874,987,989,877,992,994,880,995,996,998,999,884,1000,1001,1002,1003,1004,889,1005,1005,1006,1007,1008,1009,1010,1012,1013,897,1015,1017,901,1019,903,1020,904,1021,906,1023,1025,910,1027,1028,1030,1031,1032,1033,1034,1035,1035,1035,1036,1037,1038,1039,1040,1042,1043,926,1045,1047,930,1049,932,1050,933,1051,935,1053,1055,939,1057,1058,1060,1061,1062,1063,1064,1065,1065,1065,1066,1067,1068,1069,1070,1072,1073,955,1075,1077,959,1079,961,1080,962,1081,964,1083,1085,968,1087,1088,1090,1091,1092,1093,1094,1095,1095,1095,1096,1097,1098,1099,1100,1102,1103,984,1105,1107,988,1109,990,1110,991,1111,993,1113,1115,997,1117,1118,1120,1121,1122,1123,1124,1005,1125,1125,1126,1127,1128,1129,1130,1011,1132,1014,1134,1016,1136,1018,1137,1139,1141,1143,1022,1144,1024,1146,1026,1148,1029,1150,1151,1152,1153,1154,1155,1155,1155,1156,1157,1158,1159,1160,1041,1162,1044,1164,1046,1166,1048,1167,1169,1171,1173,1052,1174,1054,1176,1056,1178,1059,1180,1181,1182,1183,1184,1185,1185,1185,1186,1187,1188,1189,1190,1071,1192,1074,1194,1076,1196,1078,1197,1199,1201,1203,1082,1204,1084,1206,1086,1208,1089,1210,1211,1212,1213,1214,1215,1215,1215,1216,1217,1218,1219,1220,1101,1222,1104,1224,1106,1226,1108,1227,1229,1231,1233,1112,1234,1114,1236,1116,1238,1119,1240,1241,1242,1243,1244,1125,1245,1245,1246,1247,1248,1249,1250,1131,1252,1253,1133,1254,1255,1135,1256,1258,1138,1260,1140,1261,1142,1263,1265,1145,1266,1267,1147,1268,1269,1149,1271,1272,1273,1274,1275,1276,1276,1276,1277,1278,1279,1280,1281,1161,1283,1284,1163,1285,1286,1165,1287,1289,1168,1291,1170,1292,1172,1294,1296,1175,1297,1298,1177,1299,1300,1179,1302,1303,1304,1305,1306,1307,1307,1307,1308,1309,1310,1311,1312,1191,1314,1315,1193,1316,1317,1195,1318,1320,1198,1322,1200,1323,1202,1325,1327,1205,1328,1329,1207,1330,1331,1209,1333,1334,1335,1336,1337,1338,1338,1338,1339,1340,1341,1342,1343,1221,1345,1346,1223,1347,1348,1225,1349,1351,1228,1353,1230,1354,1232,1356,1358,1235,1359,1360,1237,1361,1362,1239,1364,1365,1366,1367,1368,1245,1369,1369,1370,1371,1372,1373,1374,1251,1376,1377,1379,1380,1382,1257,1384,1259,1385,1387,1389,1262,1390,1264,1392,1394,1395,1397,1398,1270,1400,1401,1402,1403,1404,1405,1405,1405,1406,1407,1408,1409,1410,1282,1412,1413,1415,1416,1418,1288,1420,1290,1421,1423,1425,1293,1426,1295,1428,1430,1431,1433,1434,1301,1436,1437,1438,1439,1440,1441,1441,1441,1442,1443,1444,1445,1446,1313,1448,1449,1451,1452,1454,1319,1456,1321,1457,1459,1461,1324,1462,1326,1464,1466,1467,1469,1470,1332,1472,1473,1474,1475,1476,1477,1477,1477,1478,1479,1480,1481,1482,1344,1484,1485,1487,1488,1490,1350,1492,1352,1493,1495,1497,1355,1498,1357,1500,1502,1503,1505,1506,1363,1508,1509,1510,1511,1512,1369,1513,1513,1514,1515,1516,1517,1518,1375,1520,1521,1378,1523,1381,1525,1383,1526,1528,1386,1388,1532,1534,1391,1535,1393,1537,1396,1539,1540,1399,1542,1543,1544,1545,1546,1547,1547,1547,1548,1549,1550,1551,1552,1411,1554,1555,1414,1557,1417,1559,1419,1560,1562,1422,1424,1566,1568,1427,1569,1429,1571,1432,1573,1574,1435,1576,1577,1578,1579,1580,1581,1581,1581,1582,1583,1584,1585,1586,1447,1588,1589,1450,1591,1453,1593,1455,1594,1596,1458,1460,1600,1602,1463,1603,1465,1605,1468,1607,1608,1471,1610,1611,1612,1613,1614,1615,1615,1615,1616,1617,1618,1619,1620,1483,1622,1623,1486,1625,1489,1627,1491,1628,1630,1494,1496,1634,1636,1499,1637,1501,1639,1504,1641,1642,1507,1644,1645,1646,1647,1648,1513,1649,1649,1650,1651,1652,1653,1654,1519,1656,1657,1522,1659,1524,1661,1663,1527,1665,1529,1666,1530,1667,1531,1668,1533,1670,1672,1536,1674,1538,1676,1677,1541,1679,1680,1681,1682,1683,1684,1684,1684,1685,1686,1687,1688,1689,1553,1691,1692,1556,1694,1558,1696,1698,1561,1700,1563,1701,1564,1702,1565,1703,1567,1705,1707,1570,1709,1572,1711,1712,1575,1714,1715,1716,1717,1718,1719,1719,1719,1720,1721,1722,1723,1724,1587,1726,1727,1590,1729,1592,1731,1733,1595,1735,1597,1736,1598,1737,1599,1738,1601,1740,1742,1604,1744,1606,1746,1747,1609,1749,1750,1751,1752,1753,1754,1754,1754,1755,1756,1757,1758,1759,1621,1761,1762,1624,1764,1626,1766,1768,1629,1770,1631,1771,1632,1772,1633,1773,1635,1775,1777,1638,1779,1640,1781,1782,1643,1784,1785,1786,1787,1788,1649,1789,1789,1790,1791,1792,1793,1794,1795,1655,1796,1797,1658,1799,1800,1660,1801,1662,1803,1664,1804,1806,1808,1810,1812,1669,1813,1671,1815,1673,1816,1817,1675,1819,1820,1678,1821,1822,1823,1824,1825,1826,1827,1827,1827,1828,1829,1830,1831,1832,1833,1690,1834,1835,1693,1837,1838,1695,1839,1697,1841,1699,1842,1844,1846,1848,1850,1704,1851,1706,1853,1708,1854,1855,1710,1857,1858,1713,1859,1860,1861,1862,1863,1864,1865,1865,1865,1866,1867,1868,1869,1870,1871,1725,1872,1873,1728,1875,1876,1730,1877,1732,1879,1734,1880,1882,1884,1886,1888,1739,1889,1741,1891,1743,1892,1893,1745,1895,1896,1748,1897,1898,1899,1900,1901,1902,1903,1903,1903,1904,1905,1906,1907,1908,1909,1760,1910,1911,1763,1913,1914,1765,1915,1767,1917,1769,1918,1920,1922,1924,1926,1774,1927,1776,1929,1778,1930,1931,1780,1933,1934,1783,1935,1936,1937,1938,1939,1940,1789,1941,1941,1942,1943,1944,1945,1946,1947,1949,1950,1951,1798,1952,1953,1955,1956,1802,1957,1959,1805,1961,1807,1962,1809,1963,1811,1965,1967,1814,1968,1969,1971,1972,1818,1973,1974,1975,1977,1978,1979,1980,1981,1982,1983,1983,1983,1984,1985,1986,1987,1988,1989,1991,1992,1993,1836,1994,1995,1997,1998,1840,1999,2001,1843,2003,1845,2004,1847,2005,1849,2007,2009,1852,2010,2011,2013,2014,1856,2015,2016,2017,2019,2020,2021,2022,2023,2024,2025,2025,2025,2026,2027,2028,2029,2030,2031,2033,2034,2035,1874,2036,2037,2039,2040,1878,2041,2043,1881,2045,1883,2046,1885,2047,1887,2049,2051,1890,2052,2053,2055,2056,1894,2057,2058,2059,2061,2062,2063,2064,2065,2066,2067,2067,2067,2068,2069,2070,2071,2072,2073,2075,2076,2077,1912,2078,2079,2081,2082,1916,2083,2085,1919,2087,1921,2088,1923,2089,1925,2091,2093,1928,2094,2095,2097,2098,1932,2099,2100,2101,2103,2104,2105,2106,2107,2108,1941,2109,2109,2110,2111,2112,2113,2114,2115,1948,2117,2118,2120,2121,1954,2123,2125,1958,2127,1960,2128,2130,2132,2134,1964,2135,1966,2137,2139,1970,2141,2142,2144,2145,1976,2147,2148,2149,2150,2151,2152,2153,2153,2153,2154,2155,2156,2157,2158,2159,1990,2161,2162,2164,2165,1996,2167,2169,2000,2171,2002,2172,2174,2176,2178,2006,2179,2008,2181,2183,2012,2185,2186,2188,2189,2018,2191,2192,2193,2194,2195,2196,2197,2197,2197,2198,2199,2200,2201,2202,2203,2032,2205,2206,2208,2209,2038,2211,2213,2042,2215,2044,2216,2218,2220,2222,2048,2223,2050,2225,2227,2054,2229,2230,2232,2233,2060,2235,2236,2237,2238,2239,2240,2241,2241,2241,2242,2243,2244,2245,2246,2247,2074,2249,2250,2252,2253,2080,2255,2257,2084,2259,2086,2260,2262,2264,2266,2090,2267,2092,2269,2271,2096,2273,2274,2276,2277,2102,2279,2280,2281,2282,2283,2284,2109,2285,2285,2286,2287,2288,2289,2290,2291,2116,2293,2294,2119,2296,2122,2298,2124,2300,2126,2301,2303,2129,2131,2133,2308,2310,2136,2311,2138,2313,2140,2315,2143,2317,2318,2146,2320,2321,2322,2323,2324,2325,2326,2326,2326,2327,2328,2329,2330,2331,2332,2160,2334,2335,2163,2337,2166,2339,2168,2341,2170,2342,2344,2173,2175,2177,2349,2351,2180,2352,2182,2354,2184,2356,2187,2358,2359,2190,2361,2362,2363,2364,2365,2366,2367,2367,2367,2368,2369,2370,2371,2372,2373,2204,2375,2376,2207,2378,2210,2380,2212,2382,2214,2383,2385,2217,2219,2221,2390,2392,2224,2393,2226,2395,2228,2397,2231,2399,2400,2234,2402,2403,2404,2405,2406,2407,2408,2408,2408,2409,2410,2411,2412,2413,2414,2248,2416,2417,2251,2419,2254,2421,2256,2423,2258,2424,2426,2261,2263,2265,2431,2433,2268,2434,2270,2436,2272,2438,2275,2440,2441,2278,2443,2444,2445,2446,2447,2448,2285,2449,2449,2450,2451,2452,2453,2454,2455,2292,2457,2458,2295,2460,2461,2297,2462,2299,2464,2466,2302,2468,2304,2469,2305,2470,2306,2471,2307,2472,2309,2474,2476,2312,2478,2314,2479,2480,2316,2482,2483,2319,2485,2486,2487,2488,2489,2490,2491,2491,2491,2492,2493,2494,2495,2496,2497,2333,2499,2500,2336,2502,2503,2338,2504,2340,2506,2508,2343,2510,2345,2511,2346,2512,2347,2513,2348,2514,2350,2516,2518,2353,2520,2355,2521,2522,2357,2524,2525,2360,2527,2528,2529,2530,2531,2532,2533,2533,2533,2534,2535,2536,2537,2538,2539,2374,2541,2542,2377,2544,2545,2379,2546,2381,2548,2550,2384,2552,2386,2553,2387,2554,2388,2555,2389,2556,2391,2558,2560,2394,2562,2396,2563,2564,2398,2566,2567,2401,2569,2570,2571,2572,2573,2574,2575,2575,2575,2576,2577,2578,2579,2580,2581,2415,2583,2584,2418,2586,2587,2420,2588,2422,2590,2592,2425,2594,2427,2595,2428,2596,2429,2597,2430,2598,2432,2600,2602,2435,2604,2437,2605,2606,2439,2608,2609,2442,2611,2612,2613,2614,2615,2616,2449,2617,2617,2618,2619,2620,2621,2622,2623,2456,2625,2626,2459,2628,2629,2631,2632,2463,2633,2465,2635,2467,2636,2638,2640,2642,2644,2646,2473,2647,2475,2649,2477,2650,2651,2653,2654,2481,2656,2657,2484,2659,2660,2661,2662,2663,2664,2665,2665,2665,2666,2667,2668,2669,2670,2671,2498,2673,2674,2501,2676,2677,2679,2680,2505,2681,2507,2683,2509,2684,2686,2688,2690,2692,2694,2515,2695,2517,2697,2519,2698,2699,2701,2702,2523,2704,2705,2526,2707,2708,2709,2710,2711,2712,2713,2713,2713,2714,2715,2716,2717,2718,2719,2540,2721,2722,2543,2724,2725,2727,2728,2547,2729,2549,2731,2551,2732,2734,2736,2738,2740,2742,2557,2743,2559,2745,2561,2746,2747,2749,2750,2565,2752,2753,2568,2755,2756,2757,2758,2759,2760,2761,2761,2761,2762,2763,2764,2765,2766,2767,2582,2769,2770,2585,2772,2773,2775,2776,2589,2777,2591,2779,2593,2780,2782,2784,2786,2788,2790,2599,2791,2601,2793,2603,2794,2795,2797,2798,2607,2800,2801,2610,2803,2804,2805,2806,2807,2808,2617,2809,2809,2810,2811,2812,2813,2814,2815,2624,2817,2818,2819,2627,2820,2821,2630,2823,2825,2826,2634,2827,2829,2637,2831,2639,2832,2641,2833,2643,2834,2645,2836,2838,2648,2839,2840,2842,2652,2844,2845,2655,2846,2847,2848,2658,2850,2851,2852,2853,2854,2855,2856,2856,2856,2857,2858,2859,2860,2861,2862,2672,2864,2865,2866,2675,2867,2868,2678,2870,2872,2873,2682,2874,2876,2685,2878,2687,2879,2689,2880,2691,2881,2693,2883,2885,2696,2886,2887,2889,2700,2891,2892,2703,2893,2894,2895,2706,2897,2898,2899,2900,2901,2902,2903,2903,2903,2904,2905,2906,2907,2908,2909,2720,2911,2912,2913,2723,2914,2915,2726,2917,2919,2920,2730,2921,2923,2733,2925,2735,2926,2737,2927,2739,2928,2741,2930,2932,2744,2933,2934,2936,2748,2938,2939,2751,2940,2941,2942,2754,2944,2945,2946,2947,2948,2949,2950,2950,2950,2951,2952,2953,2954,2955,2956,2768,2958,2959,2960,2771,2961,2962,2774,2964,2966,2967,2778,2968,2970,2781,2972,2783,2973,2785,2974,2787,2975,2789,2977,2979,2792,2980,2981,2983,2796,2985,2986,2799,2987,2988,2989,2802,2991,2992,2993,2994,2995,2996,2809,2997,2997,2998,2999,3000,3001,3002,3003,3004,2816,3005,3006,3007,3009,3010,2822,3012,2824,3014,3016,2828,3018,2830,3019,3021,3023,3025,3027,2835,3028,2837,3030,3032,2841,3034,2843,3036,3037,3039,3040,3041,2849,3042,3043,3044,3045,3046,3047,3048,3049,3049,3049,3050,3051,3052,3053,3054,3055,3056,2863,3057,3058,3059,3061,3062,2869,3064,2871,3066,3068,2875,3070,2877,3071,3073,3075,3077,3079,2882,3080,2884,3082,3084,2888,3086,2890,3088,3089,3091,3092,3093,2896,3094,3095,3096,3097,3098,3099,3100,3101,3101,3101,3102,3103,3104,3105,3106,3107,3108,2910,3109,3110,3111,3113,3114,2916,3116,2918,3118,3120,2922,3122,2924,3123,3125,3127,3129,3131,2929,3132,2931,3134,3136,2935,3138,2937,3140,3141,3143,3144,3145,2943,3146,3147,3148,3149,3150,3151,3152,3153,3153,3153,3154,3155,3156,3157,3158,3159,3160,2957,3161,3162,3163,3165,3166,2963,3168,2965,3170,3172,2969,3174,2971,3175,3177,3179,3181,3183,2976,3184,2978,3186,3188,2982,3190,2984,3192,3193,3195,3196,3197,2990,3198,3199,3200,3201,3202,3203,3204,2997,3205,3205,3206,3207,3208,3209,3210,3211,3212,3214,3215,3216,3008,3218,3011,3220,3013,3222,3015,3224,3017,3225,3227,3020,3229,3022,3230,3024,3231,3026,3233,3235,3029,3236,3031,3238,3033,3240,3035,3242,3038,3244,3245,3246,3248,3249,3250,3251,3252,3253,3254,3255,3255,3255,3256,3257,3258,3259,3260,3261,3262,3264,3265,3266,3060,3268,3063,3270,3065,3272,3067,3274,3069,3275,3277,3072,3279,3074,3280,3076,3281,3078,3283,3285,3081,3286,3083,3288,3085,3290,3087,3292,3090,3294,3295,3296,3298,3299,3300,3301,3302,3303,3304,3305,3305,3305,3306,3307,3308,3309,3310,3311,3312,3314,3315,3316,3112,3318,3115,3320,3117,3322,3119,3324,3121,3325,3327,3124,3329,3126,3330,3128,3331,3130,3333,3335,3133,3336,3135,3338,3137,3340,3139,3342,3142,3344,3345,3346,3348,3349,3350,3351,3352,3353,3354,3355,3355,3355,3356,3357,3358,3359,3360,3361,3362,3364,3365,3366,3164,3368,3167,3370,3169,3372,3171,3374,3173,3375,3377,3176,3379,3178,3380,3180,3381,3182,3383,3385,3185,3386,3187,3388,3189,3390,3191,3392,3194,3394,3395,3396,3398,3399,3400,3401,3402,3403,3404,3205,3405,3405,3406,3407,3408,3409,3410,3411,3412,3213,3414,3415,3217,3417,3418,3219,3419,3420,3221,3421,3422,3223,3423,3425,3226,3427,3228,3428,3430,3432,3434,3232,3435,3234,3437,3439,3237,3440,3441,3239,3442,3443,3241,3444,3445,3243,3447,3448,3247,3450,3451,3452,3453,3454,3455,3456,3457,3457,3457,3458,3459,3460,3461,3462,3463,3464,3263,3466,3467,3267,3469,3470,3269,3471,3472,3271,3473,3474,3273,3475,3477,3276,3479,3278,3480,3482,3484,3486,3282,3487,3284,3489,3491,3287,3492,3493,3289,3494,3495,3291,3496,3497,3293,3499,3500,3297,3502,3503,3504,3505,3506,3507,3508,3509,3509,3509,3510,3511,3512,3513,3514,3515,3516,3313,3518,3519,3317,3521,3522,3319,3523,3524,3321,3525,3526,3323,3527,3529,3326,3531,3328,3532,3534,3536,3538,3332,3539,3334,3541,3543,3337,3544,3545,3339,3546,3547,3341,3548,3549,3343,3551,3552,3347,3554,3555,3556,3557,3558,3559,3560,3561,3561,3561,3562,3563,3564,3565,3566,3567,3568,3363,3570,3571,3367,3573,3574,3369,3575,3576,3371,3577,3578,3373,3579,3581,3376,3583,3378,3584,3586,3588,3590,3382,3591,3384,3593,3595,3387,3596,3597,3389,3598,3599,3391,3600,3601,3393,3603,3604,3397,3606,3607,3608,3609,3610,3611,3612,3405,3613,3613,3614,3615,3616,3617,3618,3619,3620,3413,3622,3623,3416,3625,3626,3628,3629,3631,3632,3634,3424,3636,3426,3637,3639,3429,3431,3433,3644,3646,3436,3647,3438,3649,3651,3652,3654,3655,3657,3658,3446,3660,3661,3449,3663,3664,3665,3666,3667,3668,3669,3670,3670,3670,3671,3672,3673,3674,3675,3676,3677,3465,3679,3680,3468,3682,3683,3685,3686,3688,3689,3691,3476,3693,3478,3694,3696,3481,3483,3485,3701,3703,3488,3704,3490,3706,3708,3709,3711,3712,3714,3715,3498,3717,3718,3501,3720,3721,3722,3723,3724,3725,3726,3727,3727,3727,3728,3729,3730,3731,3732,3733,3734,3517,3736,3737,3520,3739,3740,3742,3743,3745,3746,3748,3528,3750,3530,3751,3753,3533,3535,3537,3758,3760,3540,3761,3542,3763,3765,3766,3768,3769,3771,3772,3550,3774,3775,3553,3777,3778,3779,3780,3781,3782,3783,3784,3784,3784,3785,3786,3787,3788,3789,3790,3791,3569,3793,3794,3572,3796,3797,3799,3800,3802,3803,3805,3580,3807,3582,3808,3810,3585,3587,3589,3815,3817,3592,3818,3594,3820,3822,3823,3825,3826,3828,3829,3602,3831,3832,3605,3834,3835,3836,3837,3838,3839,3840,3613,3841,3841,3842,3843,3844,3845,3846,3847,3848,3621,3850,3851,3624,3853,3854,3627,3856,3630,3858,3633,3860,3635,3861,3863,3638,3865,3640,3866,3641,3867,3642,3868,3643,3869,3645,3871,3873,3648,3874,3650,3876,3653,3878,3656,3880,3881,3659,3883,3884,3662,3886,3887,3888,3889,3890,3891,3892,3893,3893,3893,3894,3895,3896,3897,3898,3899,3900,3678,3902,3903,3681,3905,3906,3684,3908,3687,3910,3690,3912,3692,3913,3915,3695,3917,3697,3918,3698,3919,3699,3920,3700,3921,3702,3923,3925,3705,3926,3707,3928,3710,3930,3713,3932,3933,3716,3935,3936,3719,3938,3939,3940,3941,3942,3943,3944,3945,3945,3945,3946,3947,3948,3949,3950,3951,3952,3735,3954,3955,3738,3957,3958,3741,3960,3744,3962,3747,3964,3749,3965,3967,3752,3969,3754,3970,3755,3971,3756,3972,3757,3973,3759,3975,3977,3762,3978,3764,3980,3767,3982,3770,3984,3985,3773,3987,3988,3776,3990,3991,3992,3993,3994,3995,3996,3997,3997,3997,3998,3999,4000,4001,4002,4003,4004,3792,4006,4007,3795,4009,4010,3798,4012,3801,4014,3804,4016,3806,4017,4019,3809,4021,3811,4022,3812,4023,3813,4024,3814,4025,3816,4027,4029,3819,4030,3821,4032,3824,4034,3827,4036,4037,3830,4039,4040,3833,4042,4043,4044,4045,4046,4047,4048,3841,4049,4049,4050,4051,4052,4053,4054,4055,4056,3849,4058,4059,4060,3852,4061,4062,3855,4064,3857,4066,3859,4068,4070,3862,4072,3864,4073,4075,4077,4079,4081,4083,3870,4084,3872,4086,4088,3875,4090,3877,4092,3879,4094,4095,3882,4096,4097,4098,3885,4100,4101,4102,4103,4104,4105,4106,4107,4107,4107,4108,4109,4110,4111,4112,4113,4114,3901,4116,4117,4118,3904,4119,4120,3907,4122,3909,4124,3911,4126,4128,3914,4130,3916,4131,4133,4135,4137,4139,4141,3922,4142,3924,4144,4146,3927,4148,3929,4150,3931,4152,4153,3934,4154,4155,4156,3937,4158,4159,4160,4161,4162,4163,4164,4165,4165,4165,4166,4167,4168,4169,4170,4171,4172,3953,4174,4175,4176,3956,4177,4178,3959,4180,3961,4182,3963,4184,4186,3966,4188,3968,4189,4191,4193,4195,4197,4199,3974,4200,3976,4202,4204,3979,4206,3981,4208,3983,4210,4211,3986,4212,4213,4214,3989,4216,4217,4218,4219,4220,4221,4222,4223,4223,4223,4224,4225,4226,4227,4228,4229,4230,4005,4232,4233,4234,4008,4235,4236,4011,4238,4013,4240,4015,4242,4244,4018,4246,4020,4247,4249,4251,4253,4255,4257,4026,4258,4028,4260,4262,4031,4264,4033,4266,4035,4268,4269,4038,4270,4271,4272,4041,4274,4275,4276,4277,4278,4279,4280,4049,4281,4281,4282,4283,4284,4285,4286,4287,4288,4057,4290,4291,4292,4294,4295,4063,4297,4298,4065,4299,4300,4067,4301,4069,4303,4071,4304,4306,4074,4308,4076,4309,4078,4310,4080,4311,4082,4313,4315,4085,4316,4087,4318,4089,4319,4320,4091,4321,4322,4093,4324,4325,4327,4328,4329,4099,4331,4332,4333,4334,4335,4336,4337,4338,4338,4338,4339,4340,4341,4342,4343,4344,4345,4115,4347,4348,4349,4351,4352,4121,4354,4355,4123,4356,4357,4125,4358,4127,4360,4129,4361,4363,4132,4365,4134,4366,4136,4367,4138,4368,4140,4370,4372,4143,4373,4145,4375,4147,4376,4377,4149,4378,4379,4151,4381,4382,4384,4385,4386,4157,4388,4389,4390,4391,4392,4393,4394,4395,4395,4395,4396,4397,4398,4399,4400,4401,4402,4173,4404,4405,4406,4408,4409,4179,4411,4412,4181,4413,4414,4183,4415,4185,4417,4187,4418,4420,4190,4422,4192,4423,4194,4424,4196,4425,4198,4427,4429,4201,4430,4203,4432,4205,4433,4434,4207,4435,4436,4209,4438,4439,4441,4442,4443,4215,4445,4446,4447,4448,4449,4450,4451,4452,4452,4452,4453,4454,4455,4456,4457,4458,4459,4231,4461,4462,4463,4465,4466,4237,4468,4469,4239,4470,4471,4241,4472,4243,4474,4245,4475,4477,4248,4479,4250,4480,4252,4481,4254,4482,4256,4484,4486,4259,4487,4261,4489,4263,4490,4491,4265,4492,4493,4267,4495,4496,4498,4499,4500,4273,4502,4503,4504,4505,4506,4507,4508,4281,4509,4509,4510,4511,4512,4513,4514,4515,4516,4289,4518,4519,4520,4293,4522,4523,4296,4524,4525,4527,4528,4530,4531,4302,4532,4534,4305,4536,4307,4537,4539,4541,4543,4545,4312,4546,4314,4548,4550,4317,4551,4552,4554,4555,4557,4558,4323,4559,4560,4326,4562,4563,4564,4330,4566,4567,4568,4569,4570,4571,4572,4573,4573,4573,4574,4575,4576,4577,4578,4579,4580,4346,4582,4583,4584,4350,4586,4587,4353,4588,4589,4591,4592,4594,4595,4359,4596,4598,4362,4600,4364,4601,4603,4605,4607,4609,4369,4610,4371,4612,4614,4374,4615,4616,4618,4619,4621,4622,4380,4623,4624,4383,4626,4627,4628,4387,4630,4631,4632,4633,4634,4635,4636,4637,4637,4637,4638,4639,4640,4641,4642,4643,4644,4403,4646,4647,4648,4407,4650,4651,4410,4652,4653,4655,4656,4658,4659,4416,4660,4662,4419,4664,4421,4665,4667,4669,4671,4673,4426,4674,4428,4676,4678,4431,4679,4680,4682,4683,4685,4686,4437,4687,4688,4440,4690,4691,4692,4444,4694,4695,4696,4697,4698,4699,4700,4701,4701,4701,4702,4703,4704,4705,4706,4707,4708,4460,4710,4711,4712,4464,4714,4715,4467,4716,4717,4719,4720,4722,4723,4473,4724,4726,4476,4728,4478,4729,4731,4733,4735,4737,4483,4738,4485,4740,4742,4488,4743,4744,4746,4747,4749,4750,4494,4751,4752,4497,4754,4755,4756,4501,4758,4759,4760,4761,4762,4763,4764,4509,4765,4765,4766,4767,4768,4769,4770,4771,4772,4517,4774,4775,4776,4521,4778,4779,4781,4782,4526,4784,4529,4786,4788,4533,4790,4535,4791,4793,4538,4540,4542,4544,4799,4801,4547,4802,4549,4804,4806,4553,4808,4556,4810,4811,4813,4814,4561,4816,4817,4818,4565,4820,4821,4822,4823,4824,4825,4826,4827,4827,4827,4828,4829,4830,4831,4832,4833,4834,4581,4836,4837,4838,4585,4840,4841,4843,4844,4590,4846,4593,4848,4850,4597,4852,4599,4853,4855,4602,4604,4606,4608,4861,4863,4611,4864,4613,4866,4868,4617,4870,4620,4872,4873,4875,4876,4625,4878,4879,4880,4629,4882,4883,4884,4885,4886,4887,4888,4889,4889,4889,4890,4891,4892,4893,4894,4895,4896,4645,4898,4899,4900,4649,4902,4903,4905,4906,4654,4908,4657,4910,4912,4661,4914,4663,4915,4917,4666,4668,4670,4672,4923,4925,4675,4926,4677,4928,4930,4681,4932,4684,4934,4935,4937,4938,4689,4940,4941,4942,4693,4944,4945,4946,4947,4948,4949,4950,4951,4951,4951,4952,4953,4954,4955,4956,4957,4958,4709,4960,4961,4962,4713,4964,4965,4967,4968,4718,4970,4721,4972,4974,4725,4976,4727,4977,4979,4730,4732,4734,4736,4985,4987,4739,4988,4741,4990,4992,4745,4994,4748,4996,4997,4999,5000,4753,5002,5003,5004,4757,5006,5007,5008,5009,5010,5011,5012,4765,5013,5013,5014,5015,5016,5017,5018,5019,5020,5021,4773,5022,5023,5024,4777,5026,5027,4780,5029,4783,5031,4785,5033,4787,5035,4789,5036,5038,4792,5040,4794,5041,4795,5042,4796,5043,4797,5044,4798,5045,4800,5047,5049,4803,5050,4805,5052,4807,5054,4809,5056,4812,5058,5059,4815,5061,5062,5063,4819,5064,5065,5066,5067,5068,5069,5070,5071,5072,5072,5072,5073,5074,5075,5076,5077,5078,5079,5080,4835,5081,5082,5083,4839,5085,5086,4842,5088,4845,5090,4847,5092,4849,5094,4851,5095,5097,4854,5099,4856,5100,4857,5101,4858,5102,4859,5103,4860,5104,4862,5106,5108,4865,5109,4867,5111,4869,5113,4871,5115,4874,5117,5118,4877,5120,5121,5122,4881,5123,5124,5125,5126,5127,5128,5129,5130,5131,5131,5131,5132,5133,5134,5135,5136,5137,5138,5139,4897,5140,5141,5142,4901,5144,5145,4904,5147,4907,5149,4909,5151,4911,5153,4913,5154,5156,4916,5158,4918,5159,4919,5160,4920,5161,4921,5162,4922,5163,4924,5165,5167,4927,5168,4929,5170,4931,5172,4933,5174,4936,5176,5177,4939,5179,5180,5181,4943,5182,5183,5184,5185,5186,5187,5188,5189,5190,5190,5190,5191,5192,5193,5194,5195,5196,5197,5198,4959,5199,5200,5201,4963,5203,5204,4966,5206,4969,5208,4971,5210,4973,5212,4975,5213,5215,4978,5217,4980,5218,4981,5219,4982,5220,4983,5221,4984,5222,4986,5224,5226,4989,5227,4991,5229,4993,5231,4995,5233,4998,5235,5236,5001,5238,5239,5240,5005,5241,5242,5243,5244,5245,5246,5247,5248,5013,5249,5249,5250,5251,5252,5253,5254,5255,5256,5257,5259,5260,5261,5025,5263,5264,5028,5266,5267,5030,5268,5269,5032,5270,5034,5272,5274,5037,5276,5039,5277,5279,5281,5283,5285,5287,5289,5046,5290,5048,5292,5294,5051,5296,5053,5297,5298,5055,5299,5300,5057,5302,5303,5060,5305,5306,5307,5309,5310,5311,5312,5313,5314,5315,5316,5317,5317,5317,5318,5319,5320,5321,5322,5323,5324,5325,5327,5328,5329,5084,5331,5332,5087,5334,5335,5089,5336,5337,5091,5338,5093,5340,5342,5096,5344,5098,5345,5347,5349,5351,5353,5355,5357,5105,5358,5107,5360,5362,5110,5364,5112,5365,5366,5114,5367,5368,5116,5370,5371,5119,5373,5374,5375,5377,5378,5379,5380,5381,5382,5383,5384,5385,5385,5385,5386,5387,5388,5389,5390,5391,5392,5393,5395,5396,5397,5143,5399,5400,5146,5402,5403,5148,5404,5405,5150,5406,5152,5408,5410,5155,5412,5157,5413,5415,5417,5419,5421,5423,5425,5164,5426,5166,5428,5430,5169,5432,5171,5433,5434,5173,5435,5436,5175,5438,5439,5178,5441,5442,5443,5445,5446,5447,5448,5449,5450,5451,5452,5453,5453,5453,5454,5455,5456,5457,5458,5459,5460,5461,5463,5464,5465,5202,5467,5468,5205,5470,5471,5207,5472,5473,5209,5474,5211,5476,5478,5214,5480,5216,5481,5483,5485,5487,5489,5491,5493,5223,5494,5225,5496,5498,5228,5500,5230,5501,5502,5232,5503,5504,5234,5506,5507,5237,5509,5510,5511,5513,5514,5515,5516,5517,5518,5519,5520,5249,5521,5521,5522,5523,5524,5525,5526,5527,5528,5529,5258,5531,5532,5533,5262,5534,5535,5265,5537,5538,5540,5541,5543,5544,5271,5545,5273,5547,5275,5548,5550,5278,5552,5280,5553,5282,5554,5284,5555,5286,5556,5288,5558,5560,5291,5561,5293,5563,5295,5564,5565,5567,5568,5570,5571,5301,5573,5574,5304,5575,5576,5577,5308,5579,5580,5581,5582,5583,5584,5585,5586,5587,5587,5587,5588,5589,5590,5591,5592,5593,5594,5595,5326,5597,5598,5599,5330,5600,5601,5333,5603,5604,5606,5607,5609,5610,5339,5611,5341,5613,5343,5614,5616,5346,5618,5348,5619,5350,5620,5352,5621,5354,5622,5356,5624,5626,5359,5627,5361,5629,5363,5630,5631,5633,5634,5636,5637,5369,5639,5640,5372,5641,5642,5643,5376,5645,5646,5647,5648,5649,5650,5651,5652,5653,5653,5653,5654,5655,5656,5657,5658,5659,5660,5661,5394,5663,5664,5665,5398,5666,5667,5401,5669,5670,5672,5673,5675,5676,5407,5677,5409,5679,5411,5680,5682,5414,5684,5416,5685,5418,5686,5420,5687,5422,5688,5424,5690,5692,5427,5693,5429,5695,5431,5696,5697,5699,5700,5702,5703,5437,5705,5706,5440,5707,5708,5709,5444,5711,5712,5713,5714,5715,5716,5717,5718,5719,5719,5719,5720,5721,5722,5723,5724,5725,5726,5727,5462,5729,5730,5731,5466,5732,5733,5469,5735,5736,5738,5739,5741,5742,5475,5743,5477,5745,5479,5746,5748,5482,5750,5484,5751,5486,5752,5488,5753,5490,5754,5492,5756,5758,5495,5759,5497,5761,5499,5762,5763,5765,5766,5768,5769,5505,5771,5772,5508,5773,5774,5775,5512,5777,5778,5779,5780,5781,5782,5783,5784,5521,5785,5785,5786,5787,5788,5789,5790,5791,5792,5793,5530,5795,5796,5797,5799,5800,5801,5536,5802,5803,5539,5805,5542,5807,5809,5810,5546,5811,5813,5549,5815,5551,5816,5818,5820,5822,5824,5826,5557,5827,5559,5829,5831,5562,5832,5833,5835,5566,5837,5569,5839,5840,5572,5841,5842,5843,5845,5846,5847,5578,5849,5850,5851,5852,5853,5854,5855,5856,5857,5857,5857,5858,5859,5860,5861,5862,5863,5864,5865,5596,5867,5868,5869,5871,5872,5873,5602,5874,5875,5605,5877,5608,5879,5881,5882,5612,5883,5885,5615,5887,5617,5888,5890,5892,5894,5896,5898,5623,5899,5625,5901,5903,5628,5904,5905,5907,5632,5909,5635,5911,5912,5638,5913,5914,5915,5917,5918,5919,5644,5921,5922,5923,5924,5925,5926,5927,5928,5929,5929,5929,5930,5931,5932,5933,5934,5935,5936,5937,5662,5939,5940,5941,5943,5944,5945,5668,5946,5947,5671,5949,5674,5951,5953,5954,5678,5955,5957,5681,5959,5683,5960,5962,5964,5966,5968,5970,5689,5971,5691,5973,5975,5694,5976,5977,5979,5698,5981,5701,5983,5984,5704,5985,5986,5987,5989,5990,5991,5710,5993,5994,5995,5996,5997,5998,5999,6000,6001,6001,6001,6002,6003,6004,6005,6006,6007,6008,6009,5728,6011,6012,6013,6015,6016,6017,5734,6018,6019,5737,6021,5740,6023,6025,6026,5744,6027,6029,5747,6031,5749,6032,6034,6036,6038,6040,6042,5755,6043,5757,6045,6047,5760,6048,6049,6051,5764,6053,5767,6055,6056,5770,6057,6058,6059,6061,6062,6063,5776,6065,6066,6067,6068,6069,6070,6071,6072,5785,6073,6073,6074,6075,6076,6077,6078,6079,6080,6081,5794,6083,6084,6085,5798,6087,6088,6090,6091,5804,6093,5806,6095,5808,6097,6099,5812,6101,5814,6102,6104,5817,5819,6107,5821,6108,5823,5825,6111,6113,5828,6114,5830,6116,6118,5834,6120,5836,6122,5838,6124,6125,6127,6128,5844,6130,6131,6132,5848,6134,6135,6136,6137,6138,6139,6140,6141,6142,6142,6142,6143,6144,6145,6146,6147,6148,6149,6150,5866,6152,6153,6154,5870,6156,6157,6159,6160,5876,6162,5878,6164,5880,6166,6168,5884,6170,5886,6171,6173,5889,5891,6176,5893,6177,5895,5897,6180,6182,5900,6183,5902,6185,6187,5906,6189,5908,6191,5910,6193,6194,6196,6197,5916,6199,6200,6201,5920,6203,6204,6205,6206,6207,6208,6209,6210,6211,6211,6211,6212,6213,6214,6215,6216,6217,6218,6219,5938,6221,6222,6223,5942,6225,6226,6228,6229,5948,6231,5950,6233,5952,6235,6237,5956,6239,5958,6240,6242,5961,5963,6245,5965,6246,5967,5969,6249,6251,5972,6252,5974,6254,6256,5978,6258,5980,6260,5982,6262,6263,6265,6266,5988,6268,6269,6270,5992,6272,6273,6274,6275,6276,6277,6278,6279,6280,6280,6280,6281,6282,6283,6284,6285,6286,6287,6288,6010,6290,6291,6292,6014,6294,6295,6297,6298,6020,6300,6022,6302,6024,6304,6306,6028,6308,6030,6309,6311,6033,6035,6314,6037,6315,6039,6041,6318,6320,6044,6321,6046,6323,6325,6050,6327,6052,6329,6054,6331,6332,6334,6335,6060,6337,6338,6339,6064,6341,6342,6343,6344,6345,6346,6347,6348,6073,6349,6349,6350,6351,6352,6353,6354,6355,6356,6357,6082,6359,6360,6361,6086,6363,6364,6089,6366,6092,6368,6369,6094,6370,6096,6372,6098,6374,6100,6375,6377,6103,6379,6105,6380,6106,6381,6383,6385,6109,6386,6110,6387,6112,6389,6391,6115,6392,6117,6394,6119,6396,6121,6397,6398,6123,6400,6126,6402,6403,6129,6405,6406,6407,6133,6409,6410,6411,6412,6413,6414,6415,6416,6417,6417,6417,6418,6419,6420,6421,6422,6423,6424,6425,6151,6427,6428,6429,6155,6431,6432,6158,6434,6161,6436,6437,6163,6438,6165,6440,6167,6442,6169,6443,6445,6172,6447,6174,6448,6175,6449,6451,6453,6178,6454,6179,6455,6181,6457,6459,6184,6460,6186,6462,6188,6464,6190,6465,6466,6192,6468,6195,6470,6471,6198,6473,6474,6475,6202,6477,6478,6479,6480,6481,6482,6483,6484,6485,6485,6485,6486,6487,6488,6489,6490,6491,6492,6493,6220,6495,6496,6497,6224,6499,6500,6227,6502,6230,6504,6505,6232,6506,6234,6508,6236,6510,6238,6511,6513,6241,6515,6243,6516,6244,6517,6519,6521,6247,6522,6248,6523,6250,6525,6527,6253,6528,6255,6530,6257,6532,6259,6533,6534,6261,6536,6264,6538,6539,6267,6541,6542,6543,6271,6545,6546,6547,6548,6549,6550,6551,6552,6553,6553,6553,6554,6555,6556,6557,6558,6559,6560,6561,6289,6563,6564,6565,6293,6567,6568,6296,6570,6299,6572,6573,6301,6574,6303,6576,6305,6578,6307,6579,6581,6310,6583,6312,6584,6313,6585,6587,6589,6316,6590,6317,6591,6319,6593,6595,6322,6596,6324,6598,6326,6600,6328,6601,6602,6330,6604,6333,6606,6607,6336,6609,6610,6611,6340,6613,6614,6615,6616,6617,6618,6619,6620,6349,6621,6621,6622,6623,6624,6625,6626,6627,6628,6629,6358,6631,6632,6633,6362,6635,6636,6365,6638,6639,6367,6640,6641,6643,6644,6371,6645,6373,6647,6649,6376,6651,6378,6652,6654,6656,6382,6384,6660,6662,6664,6388,6665,6390,6667,6669,6393,6671,6395,6672,6673,6675,6676,6399,6677,6678,6401,6680,6681,6404,6683,6684,6685,6408,6687,6688,6689,6690,6691,6692,6693,6694,6695,6695,6695,6696,6697,6698,6699,6700,6701,6702,6703,6426,6705,6706,6707,6430,6709,6710,6433,6712,6713,6435,6714,6715,6717,6718,6439,6719,6441,6721,6723,6444,6725,6446,6726,6728,6730,6450,6452,6734,6736,6738,6456,6739,6458,6741,6743,6461,6745,6463,6746,6747,6749,6750,6467,6751,6752,6469,6754,6755,6472,6757,6758,6759,6476,6761,6762,6763,6764,6765,6766,6767,6768,6769,6769,6769,6770,6771,6772,6773,6774,6775,6776,6777,6494,6779,6780,6781,6498,6783,6784,6501,6786,6787,6503,6788,6789,6791,6792,6507,6793,6509,6795,6797,6512,6799,6514,6800,6802,6804,6518,6520,6808,6810,6812,6524,6813,6526,6815,6817,6529,6819,6531,6820,6821,6823,6824,6535,6825,6826,6537,6828,6829,6540,6831,6832,6833,6544,6835,6836,6837,6838,6839,6840,6841,6842,6843,6843,6843,6844,6845,6846,6847,6848,6849,6850,6851,6562,6853,6854,6855,6566,6857,6858,6569,6860,6861,6571,6862,6863,6865,6866,6575,6867,6577,6869,6871,6580,6873,6582,6874,6876,6878,6586,6588,6882,6884,6886,6592,6887,6594,6889,6891,6597,6893,6599,6894,6895,6897,6898,6603,6899,6900,6605,6902,6903,6608,6905,6906,6907,6612,6909,6910,6911,6912,6913,6914,6915,6916,6621,6917,6917,6918,6919,6920,6921,6922,6923,6924,6925,6630,6927,6928,6929,6634,6931,6932,6637,6934,6935,6937,6938,6642,6940,6942,6943,6646,6944,6648,6946,6650,6947,6949,6653,6951,6655,6952,6657,6953,6658,6954,6659,6955,6661,6956,6663,6958,6960,6666,6961,6668,6963,6670,6964,6965,6967,6674,6969,6970,6972,6973,6679,6975,6976,6682,6978,6979,6980,6686,6982,6983,6984,6985,6986,6987,6988,6989,6990,6990,6990,6991,6992,6993,6994,6995,6996,6997,6998,6704,7000,7001,7002,6708,7004,7005,6711,7007,7008,7010,7011,6716,7013,7015,7016,6720,7017,6722,7019,6724,7020,7022,6727,7024,6729,7025,6731,7026,6732,7027,6733,7028,6735,7029,6737,7031,7033,6740,7034,6742,7036,6744,7037,7038,7040,6748,7042,7043,7045,7046,6753,7048,7049,6756,7051,7052,7053,6760,7055,7056,7057,7058,7059,7060,7061,7062,7063,7063,7063,7064,7065,7066,7067,7068,7069,7070,7071,6778,7073,7074,7075,6782,7077,7078,6785,7080,7081,7083,7084,6790,7086,7088,7089,6794,7090,6796,7092,6798,7093,7095,6801,7097,6803,7098,6805,7099,6806,7100,6807,7101,6809,7102,6811,7104,7106,6814,7107,6816,7109,6818,7110,7111,7113,6822,7115,7116,7118,7119,6827,7121,7122,6830,7124,7125,7126,6834,7128,7129,7130,7131,7132,7133,7134,7135,7136,7136,7136,7137,7138,7139,7140,7141,7142,7143,7144,6852,7146,7147,7148,6856,7150,7151,6859,7153,7154,7156,7157,6864,7159,7161,7162,6868,7163,6870,7165,6872,7166,7168,6875,7170,6877,7171,6879,7172,6880,7173,6881,7174,6883,7175,6885,7177,7179,6888,7180,6890,7182,6892,7183,7184,7186,6896,7188,7189,7191,7192,6901,7194,7195,6904,7197,7198,7199,6908,7201,7202,7203,7204,7205,7206,7207,7208,6917,7209,7209,7210,7211,7212,7213,7214,7215,7216,7217,6926,7219,7220,7221,6930,7223,7224,7225,6933,7226,7227,6936,7229,6939,7231,6941,7233,7235,7236,6945,7237,7239,6948,7241,6950,7242,7244,7246,7248,7250,7252,7254,6957,7255,6959,7257,7259,6962,7260,7261,7263,6966,7265,6968,7267,6971,7269,7270,6974,7271,7272,7273,6977,7275,7276,7277,6981,7279,7280,7281,7282,7283,7284,7285,7286,7287,7287,7287,7288,7289,7290,7291,7292,7293,7294,7295,6999,7297,7298,7299,7003,7301,7302,7303,7006,7304,7305,7009,7307,7012,7309,7014,7311,7313,7314,7018,7315,7317,7021,7319,7023,7320,7322,7324,7326,7328,7330,7332,7030,7333,7032,7335,7337,7035,7338,7339,7341,7039,7343,7041,7345,7044,7347,7348,7047,7349,7350,7351,7050,7353,7354,7355,7054,7357,7358,7359,7360,7361,7362,7363,7364,7365,7365,7365,7366,7367,7368,7369,7370,7371,7372,7373,7072,7375,7376,7377,7076,7379,7380,7381,7079,7382,7383,7082,7385,7085,7387,7087,7389,7391,7392,7091,7393,7395,7094,7397,7096,7398,7400,7402,7404,7406,7408,7410,7103,7411,7105,7413,7415,7108,7416,7417,7419,7112,7421,7114,7423,7117,7425,7426,7120,7427,7428,7429,7123,7431,7432,7433,7127,7435,7436,7437,7438,7439,7440,7441,7442,7443,7443,7443,7444,7445,7446,7447,7448,7449,7450,7451,7145,7453,7454,7455,7149,7457,7458,7459,7152,7460,7461,7155,7463,7158,7465,7160,7467,7469,7470,7164,7471,7473,7167,7475,7169,7476,7478,7480,7482,7484,7486,7488,7176,7489,7178,7491,7493,7181,7494,7495,7497,7185,7499,7187,7501,7190,7503,7504,7193,7505,7506,7507,7196,7509,7510,7511,7200,7513,7514,7515,7516,7517,7518,7519,7520,7209,7521,7521,7522,7523,7524,7525,7526,7527,7528,7529,7530,7218,7531,7532,7533,7534,7222,7535,7536,7537,7539,7540,7228,7542,7543,7230,7544,7232,7546,7234,7548,7550,7238,7552,7240,7553,7555,7243,7557,7245,7558,7247,7559,7249,7560,7251,7561,7253,7563,7565,7256,7566,7258,7568,7570,7262,7572,7264,7574,7266,7575,7576,7268,7578,7579,7581,7582,7583,7274,7584,7585,7586,7587,7278,7588,7589,7590,7591,7592,7593,7594,7595,7596,7597,7597,7597,7598,7599,7600,7601,7602,7603,7604,7605,7606,7296,7607,7608,7609,7610,7300,7611,7612,7613,7615,7616,7306,7618,7619,7308,7620,7310,7622,7312,7624,7626,7316,7628,7318,7629,7631,7321,7633,7323,7634,7325,7635,7327,7636,7329,7637,7331,7639,7641,7334,7642,7336,7644,7646,7340,7648,7342,7650,7344,7651,7652,7346,7654,7655,7657,7658,7659,7352,7660,7661,7662,7663,7356,7664,7665,7666,7667,7668,7669,7670,7671,7672,7673,7673,7673,7674,7675,7676,7677,7678,7679,7680,7681,7682,7374,7683,7684,7685,7686,7378,7687,7688,7689,7691,7692,7384,7694,7695,7386,7696,7388,7698,7390,7700,7702,7394,7704,7396,7705,7707,7399,7709,7401,7710,7403,7711,7405,7712,7407,7713,7409,7715,7717,7412,7718,7414,7720,7722,7418,7724,7420,7726,7422,7727,7728,7424,7730,7731,7733,7734,7735,7430,7736,7737,7738,7739,7434,7740,7741,7742,7743,7744,7745,7746,7747,7748,7749,7749,7749,7750,7751,7752,7753,7754,7755,7756,7757,7758,7452,7759,7760,7761,7762,7456,7763,7764,7765,7767,7768,7462,7770,7771,7464,7772,7466,7774,7468,7776,7778,7472,7780,7474,7781,7783,7477,7785,7479,7786,7481,7787,7483,7788,7485,7789,7487,7791,7793,7490,7794,7492,7796,7798,7496,7800,7498,7802,7500,7803,7804,7502,7806,7807,7809,7810,7811,7508,7812,7813,7814,7815,7512,7816,7817,7818,7819,7820,7821,7822,7823,7824,7521,7825,7825,7826,7827,7828,7829,7830,7831,7832,7833,7834,7836,7837,7838,7839,7841,7842,7843,7538,7845,7541,7847,7848,7850,7851,7545,7852,7853,7547,7854,7549,7856,7551,7857,7859,7554,7861,7556,7862,7864,7866,7868,7870,7872,7562,7873,7564,7875,7877,7567,7878,7569,7880,7571,7881,7882,7573,7883,7884,7886,7887,7577,7889,7580,7891,7892,7893,7895,7896,7897,7898,7900,7901,7902,7903,7904,7905,7906,7907,7908,7909,7909,7909,7910,7911,7912,7913,7914,7915,7916,7917,7918,7920,7921,7922,7923,7925,7926,7927,7614,7929,7617,7931,7932,7934,7935,7621,7936,7937,7623,7938,7625,7940,7627,7941,7943,7630,7945,7632,7946,7948,7950,7952,7954,7956,7638,7957,7640,7959,7961,7643,7962,7645,7964,7647,7965,7966,7649,7967,7968,7970,7971,7653,7973,7656,7975,7976,7977,7979,7980,7981,7982,7984,7985,7986,7987,7988,7989,7990,7991,7992,7993,7993,7993,7994,7995,7996,7997,7998,7999,8000,8001,8002,8004,8005,8006,8007,8009,8010,8011,7690,8013,7693,8015,8016,8018,8019,7697,8020,8021,7699,8022,7701,8024,7703,8025,8027,7706,8029,7708,8030,8032,8034,8036,8038,8040,7714,8041,7716,8043,8045,7719,8046,7721,8048,7723,8049,8050,7725,8051,8052,8054,8055,7729,8057,7732,8059,8060,8061,8063,8064,8065,8066,8068,8069,8070,8071,8072,8073,8074,8075,8076,8077,8077,8077,8078,8079,8080,8081,8082,8083,8084,8085,8086,8088,8089,8090,8091,8093,8094,8095,7766,8097,7769,8099,8100,8102,8103,7773,8104,8105,7775,8106,7777,8108,7779,8109,8111,7782,8113,7784,8114,8116,8118,8120,8122,8124,7790,8125,7792,8127,8129,7795,8130,7797,8132,7799,8133,8134,7801,8135,8136,8138,8139,7805,8141,7808,8143,8144,8145,8147,8148,8149,8150,8152,8153,8154,8155,8156,8157,8158,8159,8160,7825,8161,8161,8162,8163,8164,8165,8166,8167,8168,8169,8170,7835,8172,8173,8174,7840,8176,8177,7844,8179,8180,7846,8181,8182,7849,8184,8186,8187,8189,7855,8191,8193,7858,8195,7860,8196,8198,7863,7865,7867,7869,7871,8205,8207,7874,8208,7876,8210,8212,7879,8214,8216,8217,8219,7885,8221,8222,7888,8223,8224,7890,8226,8227,7894,8229,8230,8231,7899,8233,8234,8235,8236,8237,8238,8239,8240,8241,8242,8242,8242,8243,8244,8245,8246,8247,8248,8249,8250,8251,7919,8253,8254,8255,7924,8257,8258,7928,8260,8261,7930,8262,8263,7933,8265,8267,8268,8270,7939,8272,8274,7942,8276,7944,8277,8279,7947,7949,7951,7953,7955,8286,8288,7958,8289,7960,8291,8293,7963,8295,8297,8298,8300,7969,8302,8303,7972,8304,8305,7974,8307,8308,7978,8310,8311,8312,7983,8314,8315,8316,8317,8318,8319,8320,8321,8322,8323,8323,8323,8324,8325,8326,8327,8328,8329,8330,8331,8332,8003,8334,8335,8336,8008,8338,8339,8012,8341,8342,8014,8343,8344,8017,8346,8348,8349,8351,8023,8353,8355,8026,8357,8028,8358,8360,8031,8033,8035,8037,8039,8367,8369,8042,8370,8044,8372,8374,8047,8376,8378,8379,8381,8053,8383,8384,8056,8385,8386,8058,8388,8389,8062,8391,8392,8393,8067,8395,8396,8397,8398,8399,8400,8401,8402,8403,8404,8404,8404,8405,8406,8407,8408,8409,8410,8411,8412,8413,8087,8415,8416,8417,8092,8419,8420,8096,8422,8423,8098,8424,8425,8101,8427,8429,8430,8432,8107,8434,8436,8110,8438,8112,8439,8441,8115,8117,8119,8121,8123,8448,8450,8126,8451,8128,8453,8455,8131,8457,8459,8460,8462,8137,8464,8465,8140,8466,8467,8142,8469,8470,8146,8472,8473,8474,8151,8476,8477,8478,8479,8480,8481,8482,8483,8484,8161,8485,8485,8486,8487,8488,8489,8490,8491,8492,8493,8494,8171,8496,8497,8498,8175,8500,8501,8178,8503,8504,8506,8507,8183,8509,8185,8511,8188,8513,8190,8514,8192,8516,8194,8517,8519,8197,8521,8199,8522,8200,8523,8201,8524,8202,8525,8203,8526,8204,8527,8206,8529,8531,8209,8532,8211,8534,8213,8535,8215,8537,8218,8539,8220,8541,8542,8544,8545,8225,8547,8548,8228,8550,8551,8552,8232,8554,8555,8556,8557,8558,8559,8560,8561,8562,8563,8563,8563,8564,8565,8566,8567,8568,8569,8570,8571,8572,8252,8574,8575,8576,8256,8578,8579,8259,8581,8582,8584,8585,8264,8587,8266,8589,8269,8591,8271,8592,8273,8594,8275,8595,8597,8278,8599,8280,8600,8281,8601,8282,8602,8283,8603,8284,8604,8285,8605,8287,8607,8609,8290,8610,8292,8612,8294,8613,8296,8615,8299,8617,8301,8619,8620,8622,8623,8306,8625,8626,8309,8628,8629,8630,8313,8632,8633,8634,8635,8636,8637,8638,8639,8640,8641,8641,8641,8642,8643,8644,8645,8646,8647,8648,8649,8650,8333,8652,8653,8654,8337,8656,8657,8340,8659,8660,8662,8663,8345,8665,8347,8667,8350,8669,8352,8670,8354,8672,8356,8673,8675,8359,8677,8361,8678,8362,8679,8363,8680,8364,8681,8365,8682,8366,8683,8368,8685,8687,8371,8688,8373,8690,8375,8691,8377,8693,8380,8695,8382,8697,8698,8700,8701,8387,8703,8704,8390,8706,8707,8708,8394,8710,8711,8712,8713,8714,8715,8716,8717,8718,8719,8719,8719,8720,8721,8722,8723,8724,8725,8726,8727,8728,8414,8730,8731,8732,8418,8734,8735,8421,8737,8738,8740,8741,8426,8743,8428,8745,8431,8747,8433,8748,8435,8750,8437,8751,8753,8440,8755,8442,8756,8443,8757,8444,8758,8445,8759,8446,8760,8447,8761,8449,8763,8765,8452,8766,8454,8768,8456,8769,8458,8771,8461,8773,8463,8775,8776,8778,8779,8468,8781,8782,8471,8784,8785,8786,8475,8788,8789,8790,8791,8792,8793,8794,8795,8796,8485,8797,8797,8798,8799,8800,8801,8802,8803,8804,8805,8806,8495,8808,8809,8810,8499,8812,8813,8502,8815,8816,8505,8818,8508,8820,8510,8822,8512,8824,8826,8827,8515,8828,8830,8518,8832,8520,8833,8835,8837,8839,8841,8843,8845,8847,8528,8848,8530,8850,8852,8533,8853,8854,8856,8536,8858,8538,8860,8540,8862,8543,8864,8865,8546,8867,8868,8549,8870,8871,8872,8553,8874,8875,8876,8877,8878,8879,8880,8881,8882,8883,8883,8883,8884,8885,8886,8887,8888,8889,8890,8891,8892,8573,8894,8895,8896,8577,8898,8899,8580,8901,8902,8583,8904,8586,8906,8588,8908,8590,8910,8912,8913,8593,8914,8916,8596,8918,8598,8919,8921,8923,8925,8927,8929,8931,8933,8606,8934,8608,8936,8938,8611,8939,8940,8942,8614,8944,8616,8946,8618,8948,8621,8950,8951,8624,8953,8954,8627,8956,8957,8958,8631,8960,8961,8962,8963,8964,8965,8966,8967,8968,8969,8969,8969,8970,8971,8972,8973,8974,8975,8976,8977,8978,8651,8980,8981,8982,8655,8984,8985,8658,8987,8988,8661,8990,8664,8992,8666,8994,8668,8996,8998,8999,8671,9000,9002,8674,9004,8676,9005,9007,9009,9011,9013,9015,9017,9019,8684,9020,8686,9022,9024,8689,9025,9026,9028,8692,9030,8694,9032,8696,9034,8699,9036,9037,8702,9039,9040,8705,9042,9043,9044,8709,9046,9047,9048,9049,9050,9051,9052,9053,9054,9055,9055,9055,9056,9057,9058,9059,9060,9061,9062,9063,9064,8729,9066,9067,9068,8733,9070,9071,8736,9073,9074,8739,9076,8742,9078,8744,9080,8746,9082,9084,9085,8749,9086,9088,8752,9090,8754,9091,9093,9095,9097,9099,9101,9103,9105,8762,9106,8764,9108,9110,8767,9111,9112,9114,8770,9116,8772,9118,8774,9120,8777,9122,9123,8780,9125,9126,8783,9128,9129,9130,8787,9132,9133,9134,9135,9136,9137,9138,9139,9140,8797,9141,9141,9142,9143,9144,9145,9146,9147,9148,9149,9150,8807,9152,9153,9154,8811,9156,9157,9158,8814,9159,9160,8817,9162,9163,8819,9164,9165,8821,9166,9167,8823,9168,8825,9170,9172,8829,9174,8831,9175,9177,8834,9179,8836,9180,8838,9181,8840,9182,8842,9183,8844,9184,8846,9186,9188,8849,9189,8851,9191,9193,8855,9195,8857,9196,9197,8859,9198,9199,8861,9200,9201,8863,9203,9204,8866,9205,9206,9207,8869,9209,9210,9211,8873,9213,9214,9215,9216,9217,9218,9219,9220,9221,9222,9222,9222,9223,9224,9225,9226,9227,9228,9229,9230,9231,8893,9233,9234,9235,8897,9237,9238,9239,8900,9240,9241,8903,9243,9244,8905,9245,9246,8907,9247,9248,8909,9249,8911,9251,9253,8915,9255,8917,9256,9258,8920,9260,8922,9261,8924,9262,8926,9263,8928,9264,8930,9265,8932,9267,9269,8935,9270,8937,9272,9274,8941,9276,8943,9277,9278,8945,9279,9280,8947,9281,9282,8949,9284,9285,8952,9286,9287,9288,8955,9290,9291,9292,8959,9294,9295,9296,9297,9298,9299,9300,9301,9302,9303,9303,9303,9304,9305,9306,9307,9308,9309,9310,9311,9312,8979,9314,9315,9316,8983,9318,9319,9320,8986,9321,9322,8989,9324,9325,8991,9326,9327,8993,9328,9329,8995,9330,8997,9332,9334,9001,9336,9003,9337,9339,9006,9341,9008,9342,9010,9343,9012,9344,9014,9345,9016,9346,9018,9348,9350,9021,9351,9023,9353,9355,9027,9357,9029,9358,9359,9031,9360,9361,9033,9362,9363,9035,9365,9366,9038,9367,9368,9369,9041,9371,9372,9373,9045,9375,9376,9377,9378,9379,9380,9381,9382,9383,9384,9384,9384,9385,9386,9387,9388,9389,9390,9391,9392,9393,9065,9395,9396,9397,9069,9399,9400,9401,9072,9402,9403,9075,9405,9406,9077,9407,9408,9079,9409,9410,9081,9411,9083,9413,9415,9087,9417,9089,9418,9420,9092,9422,9094,9423,9096,9424,9098,9425,9100,9426,9102,9427,9104,9429,9431,9107,9432,9109,9434,9436,9113,9438,9115,9439,9440,9117,9441,9442,9119,9443,9444,9121,9446,9447,9124,9448,9449,9450,9127,9452,9453,9454,9131,9456,9457,9458,9459,9460,9461,9462,9463,9464,9141,9465,9465,9466,9467,9468,9469,9470,9471,9472,9473,9474,9151,9476,9477,9478,9155,9480,9481,9482,9484,9485,9161,9487,9488,9490,9491,9493,9494,9496,9497,9169,9498,9171,9500,9173,9501,9503,9176,9505,9178,9506,9508,9510,9512,9514,9516,9518,9185,9519,9187,9521,9523,9190,9524,9192,9526,9194,9527,9528,9530,9531,9533,9534,9536,9537,9202,9539,9540,9542,9543,9544,9208,9546,9547,9548,9212,9550,9551,9552,9553,9554,9555,9556,9557,9558,9559,9559,9559,9560,9561,9562,9563,9564,9565,9566,9567,9568,9232,9570,9571,9572,9236,9574,9575,9576,9578,9579,9242,9581,9582,9584,9585,9587,9588,9590,9591,9250,9592,9252,9594,9254,9595,9597,9257,9599,9259,9600,9602,9604,9606,9608,9610,9612,9266,9613,9268,9615,9617,9271,9618,9273,9620,9275,9621,9622,9624,9625,9627,9628,9630,9631,9283,9633,9634,9636,9637,9638,9289,9640,9641,9642,9293,9644,9645,9646,9647,9648,9649,9650,9651,9652,9653,9653,9653,9654,9655,9656,9657,9658,9659,9660,9661,9662,9313,9664,9665,9666,9317,9668,9669,9670,9672,9673,9323,9675,9676,9678,9679,9681,9682,9684,9685,9331,9686,9333,9688,9335,9689,9691,9338,9693,9340,9694,9696,9698,9700,9702,9704,9706,9347,9707,9349,9709,9711,9352,9712,9354,9714,9356,9715,9716,9718,9719,9721,9722,9724,9725,9364,9727,9728,9730,9731,9732,9370,9734,9735,9736,9374,9738,9739,9740,9741,9742,9743,9744,9745,9746,9747,9747,9747,9748,9749,9750,9751,9752,9753,9754,9755,9756,9394,9758,9759,9760,9398,9762,9763,9764,9766,9767,9404,9769,9770,9772,9773,9775,9776,9778,9779,9412,9780,9414,9782,9416,9783,9785,9419,9787,9421,9788,9790,9792,9794,9796,9798,9800,9428,9801,9430,9803,9805,9433,9806,9435,9808,9437,9809,9810,9812,9813,9815,9816,9818,9819,9445,9821,9822,9824,9825,9826,9451,9828,9829,9830,9455,9832,9833,9834,9835,9836,9837,9838,9839,9840,9465,9841,9841,9842,9843,9844,9845,9846,9847,9848,9849,9850,9475,9852,9853,9854,9855,9479,9856,9857,9858,9483,9860,9861,9486,9862,9863,9489,9865,9492,9867,9495,9869,9871,9872,9499,9873,9875,9502,9877,9504,9878,9880,9507,9509,9511,9884,9513,9515,9517,9888,9890,9520,9891,9522,9893,9895,9525,9896,9897,9899,9529,9901,9532,9903,9535,9905,9906,9538,9907,9908,9541,9910,9911,9912,9545,9913,9914,9915,9916,9549,9918,9919,9920,9921,9922,9923,9924,9925,9926,9927,9927,9927,9928,9929,9930,9931,9932,9933,9934,9935,9936,9569,9938,9939,9940,9941,9573,9942,9943,9944,9577,9946,9947,9580,9948,9949,9583,9951,9586,9953,9589,9955,9957,9958,9593,9959,9961,9596,9963,9598,9964,9966,9601,9603,9605,9970,9607,9609,9611,9974,9976,9614,9977,9616,9979,9981,9619,9982,9983,9985,9623,9987,9626,9989,9629,9991,9992,9632,9993,9994,9635,9996,9997,9998,9639,9999,10000,10001,10002,9643,10004,10005,10006,10007,10008,10009,10010,10011,10012,10013,10013,10013,10014,10015,10016,10017,10018,10019,10020,10021,10022,9663,10024,10025,10026,10027,9667,10028,10029,10030,9671,10032,10033,9674,10034,10035,9677,10037,9680,10039,9683,10041,10043,10044,9687,10045,10047,9690,10049,9692,10050,10052,9695,9697,9699,10056,9701,9703,9705,10060,10062,9708,10063,9710,10065,10067,9713,10068,10069,10071,9717,10073,9720,10075,9723,10077,10078,9726,10079,10080,9729,10082,10083,10084,9733,10085,10086,10087,10088,9737,10090,10091,10092,10093,10094,10095,10096,10097,10098,10099,10099,10099,10100,10101,10102,10103,10104,10105,10106,10107,10108,9757,10110,10111,10112,10113,9761,10114,10115,10116,9765,10118,10119,9768,10120,10121,9771,10123,9774,10125,9777,10127,10129,10130,9781,10131,10133,9784,10135,9786,10136,10138,9789,9791,9793,10142,9795,9797,9799,10146,10148,9802,10149,9804,10151,10153,9807,10154,10155,10157,9811,10159,9814,10161,9817,10163,10164,9820,10165,10166,9823,10168,10169,10170,9827,10171,10172,10173,10174,9831,10176,10177,10178,10179,10180,10181,10182,10183,10184,9841,10185,10185,10186,10187,10188,10189,10190,10191,10192,10193,10194,9851,10196,10197,10198,10199,10201,10202,10203,9859,10205,10206,10208,10209,9864,10211,9866,10213,9868,10215,9870,10217,10219,9874,10221,9876,10222,10224,9879,10226,9881,10227,9882,10228,9883,10229,10231,9885,10232,9886,10233,9887,10234,9889,10236,10238,9892,10239,9894,10241,10243,9898,10245,9900,10247,9902,10249,9904,10251,10252,10254,10255,9909,10257,10258,10259,10261,10262,10263,10264,9917,10266,10267,10268,10269,10270,10271,10272,10273,10274,10275,10275,10275,10276,10277,10278,10279,10280,10281,10282,10283,10284,9937,10286,10287,10288,10289,10291,10292,10293,9945,10295,10296,10298,10299,9950,10301,9952,10303,9954,10305,9956,10307,10309,9960,10311,9962,10312,10314,9965,10316,9967,10317,9968,10318,9969,10319,10321,9971,10322,9972,10323,9973,10324,9975,10326,10328,9978,10329,9980,10331,10333,9984,10335,9986,10337,9988,10339,9990,10341,10342,10344,10345,9995,10347,10348,10349,10351,10352,10353,10354,10003,10356,10357,10358,10359,10360,10361,10362,10363,10364,10365,10365,10365,10366,10367,10368,10369,10370,10371,10372,10373,10374,10023,10376,10377,10378,10379,10381,10382,10383,10031,10385,10386,10388,10389,10036,10391,10038,10393,10040,10395,10042,10397,10399,10046,10401,10048,10402,10404,10051,10406,10053,10407,10054,10408,10055,10409,10411,10057,10412,10058,10413,10059,10414,10061,10416,10418,10064,10419,10066,10421,10423,10070,10425,10072,10427,10074,10429,10076,10431,10432,10434,10435,10081,10437,10438,10439,10441,10442,10443,10444,10089,10446,10447,10448,10449,10450,10451,10452,10453,10454,10455,10455,10455,10456,10457,10458,10459,10460,10461,10462,10463,10464,10109,10466,10467,10468,10469,10471,10472,10473,10117,10475,10476,10478,10479,10122,10481,10124,10483,10126,10485,10128,10487,10489,10132,10491,10134,10492,10494,10137,10496,10139,10497,10140,10498,10141,10499,10501,10143,10502,10144,10503,10145,10504,10147,10506,10508,10150,10509,10152,10511,10513,10156,10515,10158,10517,10160,10519,10162,10521,10522,10524,10525,10167,10527,10528,10529,10531,10532,10533,10534,10175,10536,10537,10538,10539,10540,10541,10542,10543,10544,10185,10545,10545,10546,10547,10548,10549,10550,10551,10552,10553,10554,10195,10556,10557,10558,10559,10200,10561,10562,10204,10564,10565,10207,10567,10210,10569,10570,10212,10571,10572,10214,10573,10216,10575,10218,10577,10220,10578,10580,10223,10582,10225,10583,10585,10587,10589,10230,10592,10594,10596,10598,10235,10599,10237,10601,10603,10240,10604,10242,10606,10244,10608,10246,10609,10610,10248,10611,10612,10250,10614,10253,10616,10617,10256,10619,10620,10260,10622,10623,10624,10625,10265,10627,10628,10629,10630,10631,10632,10633,10634,10635,10636,10636,10636,10637,10638,10639,10640,10641,10642,10643,10644,10645,10285,10647,10648,10649,10650,10290,10652,10653,10294,10655,10656,10297,10658,10300,10660,10661,10302,10662,10663,10304,10664,10306,10666,10308,10668,10310,10669,10671,10313,10673,10315,10674,10676,10678,10680,10320,10683,10685,10687,10689,10325,10690,10327,10692,10694,10330,10695,10332,10697,10334,10699,10336,10700,10701,10338,10702,10703,10340,10705,10343,10707,10708,10346,10710,10711,10350,10713,10714,10715,10716,10355,10718,10719,10720,10721,10722,10723,10724,10725,10726,10727,10727,10727,10728,10729,10730,10731,10732,10733,10734,10735,10736,10375,10738,10739,10740,10741,10380,10743,10744,10384,10746,10747,10387,10749,10390,10751,10752,10392,10753,10754,10394,10755,10396,10757,10398,10759,10400,10760,10762,10403,10764,10405,10765,10767,10769,10771,10410,10774,10776,10778,10780,10415,10781,10417,10783,10785,10420,10786,10422,10788,10424,10790,10426,10791,10792,10428,10793,10794,10430,10796,10433,10798,10799,10436,10801,10802,10440,10804,10805,10806,10807,10445,10809,10810,10811,10812,10813,10814,10815,10816,10817,10818,10818,10818,10819,10820,10821,10822,10823,10824,10825,10826,10827,10465,10829,10830,10831,10832,10470,10834,10835,10474,10837,10838,10477,10840,10480,10842,10843,10482,10844,10845,10484,10846,10486,10848,10488,10850,10490,10851,10853,10493,10855,10495,10856,10858,10860,10862,10500,10865,10867,10869,10871,10505,10872,10507,10874,10876,10510,10877,10512,10879,10514,10881,10516,10882,10883,10518,10884,10885,10520,10887,10523,10889,10890,10526,10892,10893,10530,10895,10896,10897,10898,10535,10900,10901,10902,10903,10904,10905,10906,10907,10908,10545]; var count_per_radius = [1,8,16,20,24,40,36,48,56,56,68,64,80,92,88,96,96,116,120,120,124,144,136,140,152,168,176,164,168,192,188,208,200,208,228,208,232,228,256,248,236,272,264,288,276,272,296,292,312,304,336,324,312,344,324,376,344,360,364,368]; var radius_offset = [0,1,9,25,45,69,109,145,193,249,305,373,437,517,609,697,793,889,1005,1125,1245,1369,1513,1649,1789,1941,2109,2285,2449,2617,2809,2997,3205,3405,3613,3841,4049,4281,4509,4765,5013,5249,5521,5785,6073,6349,6621,6917,7209,7521,7825,8161,8485,8797,9141,9465,9841,10185,10545,10909]; // End of generated. var connected = new Array(towards_center.length).fill(true); ================================================ FILE: gesture/index.html ================================================ Depth Capture Based Hand Interaction Demo
Hand physics - depth camera capture demo.
Source code on GitHub
Rotate - head mounted camera:
================================================ FILE: gesture/indexaframe.html ================================================ Depth Capture Based Hand Interaction Demo
Rotate - head mounted camera:
Hand physics - depth camera capture demo.
Source code on GitHub
================================================ FILE: index.html ================================================

Web Depth Camera Capture demos


Moving boxes using hands demo shows live depth captured mesh interaction with scene objects; combining 3D world and depth captured hands (or other objects) rendering and Bullet Physics. Run the live demo here.


Simple background removal implemented as flood-fill of background color to similarly colored pixels. Works only with simple backgrounds - e.g. room walls on the demo gif. Run the live demo here.


Typing in the air tutorial shows how to use depth stream and WebGL transform feedback to do simple gesture recognition. Check the tutorial text and run the live demo here.


3D point cloud rendering demo shows how to render and synchronize depth and color video on GPU. Run the live demo here.


HTML5 Depth Capture tutorial shows how to access depth stream, check the tutorial text or run the live demo here.

================================================ FILE: libs/aframe/LICENSE.txt ================================================ The MIT License Copyright © 2015-2017 A-Frame authors. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: libs/aframe/aframe-v0.7.1.js ================================================ (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.AFRAME = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 0) { this._tweensAddedDuringUpdate = {}; for (var i = 0; i < tweenIds.length; i++) { if (this._tweens[tweenIds[i]].update(time) === false) { this._tweens[tweenIds[i]]._isPlaying = false; if (!preserve) { delete this._tweens[tweenIds[i]]; } } } tweenIds = Object.keys(this._tweensAddedDuringUpdate); } return true; } }; var TWEEN = new _Group(); TWEEN.Group = _Group; TWEEN._nextId = 0; TWEEN.nextId = function () { return TWEEN._nextId++; }; // Include a performance.now polyfill. // In node.js, use process.hrtime. if (typeof (window) === 'undefined' && typeof (process) !== 'undefined') { TWEEN.now = function () { var time = process.hrtime(); // Convert [seconds, nanoseconds] to milliseconds. return time[0] * 1000 + time[1] / 1000000; }; } // In a browser, use window.performance.now if it is available. else if (typeof (window) !== 'undefined' && window.performance !== undefined && window.performance.now !== undefined) { // This must be bound, because directly assigning this function // leads to an invocation exception in Chrome. TWEEN.now = window.performance.now.bind(window.performance); } // Use Date.now if it is available. else if (Date.now !== undefined) { TWEEN.now = Date.now; } // Otherwise, use 'new Date().getTime()'. else { TWEEN.now = function () { return new Date().getTime(); }; } TWEEN.Tween = function (object, group) { this._object = object; this._valuesStart = {}; this._valuesEnd = {}; this._valuesStartRepeat = {}; this._duration = 1000; this._repeat = 0; this._repeatDelayTime = undefined; this._yoyo = false; this._isPlaying = false; this._reversed = false; this._delayTime = 0; this._startTime = null; this._easingFunction = TWEEN.Easing.Linear.None; this._interpolationFunction = TWEEN.Interpolation.Linear; this._chainedTweens = []; this._onStartCallback = null; this._onStartCallbackFired = false; this._onUpdateCallback = null; this._onCompleteCallback = null; this._onStopCallback = null; this._group = group || TWEEN; this._id = TWEEN.nextId(); }; TWEEN.Tween.prototype = { getId: function getId() { return this._id; }, isPlaying: function isPlaying() { return this._isPlaying; }, to: function to(properties, duration) { this._valuesEnd = properties; if (duration !== undefined) { this._duration = duration; } return this; }, start: function start(time) { this._group.add(this); this._isPlaying = true; this._onStartCallbackFired = false; this._startTime = time !== undefined ? time : TWEEN.now(); this._startTime += this._delayTime; for (var property in this._valuesEnd) { // Check if an Array was provided as property value if (this._valuesEnd[property] instanceof Array) { if (this._valuesEnd[property].length === 0) { continue; } // Create a local copy of the Array with the start value at the front this._valuesEnd[property] = [this._object[property]].concat(this._valuesEnd[property]); } // If `to()` specifies a property that doesn't exist in the source object, // we should not set that property in the object if (this._object[property] === undefined) { continue; } // Save the starting value. this._valuesStart[property] = this._object[property]; if ((this._valuesStart[property] instanceof Array) === false) { this._valuesStart[property] *= 1.0; // Ensures we're using numbers, not strings } this._valuesStartRepeat[property] = this._valuesStart[property] || 0; } return this; }, stop: function stop() { if (!this._isPlaying) { return this; } this._group.remove(this); this._isPlaying = false; if (this._onStopCallback !== null) { this._onStopCallback.call(this._object, this._object); } this.stopChainedTweens(); return this; }, end: function end() { this.update(this._startTime + this._duration); return this; }, stopChainedTweens: function stopChainedTweens() { for (var i = 0, numChainedTweens = this._chainedTweens.length; i < numChainedTweens; i++) { this._chainedTweens[i].stop(); } }, delay: function delay(amount) { this._delayTime = amount; return this; }, repeat: function repeat(times) { this._repeat = times; return this; }, repeatDelay: function repeatDelay(amount) { this._repeatDelayTime = amount; return this; }, yoyo: function yoyo(yoyo) { this._yoyo = yoyo; return this; }, easing: function easing(easing) { this._easingFunction = easing; return this; }, interpolation: function interpolation(interpolation) { this._interpolationFunction = interpolation; return this; }, chain: function chain() { this._chainedTweens = arguments; return this; }, onStart: function onStart(callback) { this._onStartCallback = callback; return this; }, onUpdate: function onUpdate(callback) { this._onUpdateCallback = callback; return this; }, onComplete: function onComplete(callback) { this._onCompleteCallback = callback; return this; }, onStop: function onStop(callback) { this._onStopCallback = callback; return this; }, update: function update(time) { var property; var elapsed; var value; if (time < this._startTime) { return true; } if (this._onStartCallbackFired === false) { if (this._onStartCallback !== null) { this._onStartCallback.call(this._object, this._object); } this._onStartCallbackFired = true; } elapsed = (time - this._startTime) / this._duration; elapsed = elapsed > 1 ? 1 : elapsed; value = this._easingFunction(elapsed); for (property in this._valuesEnd) { // Don't update properties that do not exist in the source object if (this._valuesStart[property] === undefined) { continue; } var start = this._valuesStart[property] || 0; var end = this._valuesEnd[property]; if (end instanceof Array) { this._object[property] = this._interpolationFunction(end, value); } else { // Parses relative end values with start as base (e.g.: +10, -3) if (typeof (end) === 'string') { if (end.charAt(0) === '+' || end.charAt(0) === '-') { end = start + parseFloat(end); } else { end = parseFloat(end); } } // Protect against non numeric properties. if (typeof (end) === 'number') { this._object[property] = start + (end - start) * value; } } } if (this._onUpdateCallback !== null) { this._onUpdateCallback.call(this._object, value); } if (elapsed === 1) { if (this._repeat > 0) { if (isFinite(this._repeat)) { this._repeat--; } // Reassign starting values, restart by making startTime = now for (property in this._valuesStartRepeat) { if (typeof (this._valuesEnd[property]) === 'string') { this._valuesStartRepeat[property] = this._valuesStartRepeat[property] + parseFloat(this._valuesEnd[property]); } if (this._yoyo) { var tmp = this._valuesStartRepeat[property]; this._valuesStartRepeat[property] = this._valuesEnd[property]; this._valuesEnd[property] = tmp; } this._valuesStart[property] = this._valuesStartRepeat[property]; } if (this._yoyo) { this._reversed = !this._reversed; } if (this._repeatDelayTime !== undefined) { this._startTime = time + this._repeatDelayTime; } else { this._startTime = time + this._delayTime; } return true; } else { if (this._onCompleteCallback !== null) { this._onCompleteCallback.call(this._object, this._object); } for (var i = 0, numChainedTweens = this._chainedTweens.length; i < numChainedTweens; i++) { // Make the chained tweens start exactly at the time they should, // even if the `update()` method was called way past the duration of the tween this._chainedTweens[i].start(this._startTime + this._duration); } return false; } } return true; } }; TWEEN.Easing = { Linear: { None: function (k) { return k; } }, Quadratic: { In: function (k) { return k * k; }, Out: function (k) { return k * (2 - k); }, InOut: function (k) { if ((k *= 2) < 1) { return 0.5 * k * k; } return - 0.5 * (--k * (k - 2) - 1); } }, Cubic: { In: function (k) { return k * k * k; }, Out: function (k) { return --k * k * k + 1; }, InOut: function (k) { if ((k *= 2) < 1) { return 0.5 * k * k * k; } return 0.5 * ((k -= 2) * k * k + 2); } }, Quartic: { In: function (k) { return k * k * k * k; }, Out: function (k) { return 1 - (--k * k * k * k); }, InOut: function (k) { if ((k *= 2) < 1) { return 0.5 * k * k * k * k; } return - 0.5 * ((k -= 2) * k * k * k - 2); } }, Quintic: { In: function (k) { return k * k * k * k * k; }, Out: function (k) { return --k * k * k * k * k + 1; }, InOut: function (k) { if ((k *= 2) < 1) { return 0.5 * k * k * k * k * k; } return 0.5 * ((k -= 2) * k * k * k * k + 2); } }, Sinusoidal: { In: function (k) { return 1 - Math.cos(k * Math.PI / 2); }, Out: function (k) { return Math.sin(k * Math.PI / 2); }, InOut: function (k) { return 0.5 * (1 - Math.cos(Math.PI * k)); } }, Exponential: { In: function (k) { return k === 0 ? 0 : Math.pow(1024, k - 1); }, Out: function (k) { return k === 1 ? 1 : 1 - Math.pow(2, - 10 * k); }, InOut: function (k) { if (k === 0) { return 0; } if (k === 1) { return 1; } if ((k *= 2) < 1) { return 0.5 * Math.pow(1024, k - 1); } return 0.5 * (- Math.pow(2, - 10 * (k - 1)) + 2); } }, Circular: { In: function (k) { return 1 - Math.sqrt(1 - k * k); }, Out: function (k) { return Math.sqrt(1 - (--k * k)); }, InOut: function (k) { if ((k *= 2) < 1) { return - 0.5 * (Math.sqrt(1 - k * k) - 1); } return 0.5 * (Math.sqrt(1 - (k -= 2) * k) + 1); } }, Elastic: { In: function (k) { if (k === 0) { return 0; } if (k === 1) { return 1; } return -Math.pow(2, 10 * (k - 1)) * Math.sin((k - 1.1) * 5 * Math.PI); }, Out: function (k) { if (k === 0) { return 0; } if (k === 1) { return 1; } return Math.pow(2, -10 * k) * Math.sin((k - 0.1) * 5 * Math.PI) + 1; }, InOut: function (k) { if (k === 0) { return 0; } if (k === 1) { return 1; } k *= 2; if (k < 1) { return -0.5 * Math.pow(2, 10 * (k - 1)) * Math.sin((k - 1.1) * 5 * Math.PI); } return 0.5 * Math.pow(2, -10 * (k - 1)) * Math.sin((k - 1.1) * 5 * Math.PI) + 1; } }, Back: { In: function (k) { var s = 1.70158; return k * k * ((s + 1) * k - s); }, Out: function (k) { var s = 1.70158; return --k * k * ((s + 1) * k + s) + 1; }, InOut: function (k) { var s = 1.70158 * 1.525; if ((k *= 2) < 1) { return 0.5 * (k * k * ((s + 1) * k - s)); } return 0.5 * ((k -= 2) * k * ((s + 1) * k + s) + 2); } }, Bounce: { In: function (k) { return 1 - TWEEN.Easing.Bounce.Out(1 - k); }, Out: function (k) { if (k < (1 / 2.75)) { return 7.5625 * k * k; } else if (k < (2 / 2.75)) { return 7.5625 * (k -= (1.5 / 2.75)) * k + 0.75; } else if (k < (2.5 / 2.75)) { return 7.5625 * (k -= (2.25 / 2.75)) * k + 0.9375; } else { return 7.5625 * (k -= (2.625 / 2.75)) * k + 0.984375; } }, InOut: function (k) { if (k < 0.5) { return TWEEN.Easing.Bounce.In(k * 2) * 0.5; } return TWEEN.Easing.Bounce.Out(k * 2 - 1) * 0.5 + 0.5; } } }; TWEEN.Interpolation = { Linear: function (v, k) { var m = v.length - 1; var f = m * k; var i = Math.floor(f); var fn = TWEEN.Interpolation.Utils.Linear; if (k < 0) { return fn(v[0], v[1], f); } if (k > 1) { return fn(v[m], v[m - 1], m - f); } return fn(v[i], v[i + 1 > m ? m : i + 1], f - i); }, Bezier: function (v, k) { var b = 0; var n = v.length - 1; var pw = Math.pow; var bn = TWEEN.Interpolation.Utils.Bernstein; for (var i = 0; i <= n; i++) { b += pw(1 - k, n - i) * pw(k, i) * v[i] * bn(n, i); } return b; }, CatmullRom: function (v, k) { var m = v.length - 1; var f = m * k; var i = Math.floor(f); var fn = TWEEN.Interpolation.Utils.CatmullRom; if (v[0] === v[m]) { if (k < 0) { i = Math.floor(f = m * (1 + k)); } return fn(v[(i - 1 + m) % m], v[i], v[(i + 1) % m], v[(i + 2) % m], f - i); } else { if (k < 0) { return v[0] - (fn(v[0], v[0], v[1], v[1], -f) - v[0]); } if (k > 1) { return v[m] - (fn(v[m], v[m], v[m - 1], v[m - 1], f - m) - v[m]); } return fn(v[i ? i - 1 : 0], v[i], v[m < i + 1 ? m : i + 1], v[m < i + 2 ? m : i + 2], f - i); } }, Utils: { Linear: function (p0, p1, t) { return (p1 - p0) * t + p0; }, Bernstein: function (n, i) { var fc = TWEEN.Interpolation.Utils.Factorial; return fc(n) / fc(i) / fc(n - i); }, Factorial: (function () { var a = [1]; return function (n) { var s = 1; if (a[n]) { return a[n]; } for (var i = n; i > 1; i--) { s *= i; } a[n] = s; return s; }; })(), CatmullRom: function (p0, p1, p2, p3, t) { var v0 = (p2 - p0) * 0.5; var v1 = (p3 - p1) * 0.5; var t2 = t * t; var t3 = t * t2; return (2 * p1 - 2 * p2 + v0 + v1) * t3 + (- 3 * p1 + 3 * p2 - 2 * v0 - v1) * t2 + v0 * t + p1; } } }; // UMD (Universal Module Definition) (function (root) { if (typeof define === 'function' && define.amd) { // AMD define([], function () { return TWEEN; }); } else if (typeof module !== 'undefined' && typeof exports === 'object') { // Node.js module.exports = TWEEN; } else if (root !== undefined) { // Global variable root.TWEEN = TWEEN; } })(this); }).call(this,_dereq_('_process')) },{"_process":6}],2:[function(_dereq_,module,exports){ var str = Object.prototype.toString module.exports = anArray function anArray(arr) { return ( arr.BYTES_PER_ELEMENT && str.call(arr.buffer) === '[object ArrayBuffer]' || Array.isArray(arr) ) } },{}],3:[function(_dereq_,module,exports){ module.exports = function numtype(num, def) { return typeof num === 'number' ? num : (typeof def === 'number' ? def : 0) } },{}],4:[function(_dereq_,module,exports){ 'use strict' exports.byteLength = byteLength exports.toByteArray = toByteArray exports.fromByteArray = fromByteArray var lookup = [] var revLookup = [] var Arr = typeof Uint8Array !== 'undefined' ? Uint8Array : Array var code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' for (var i = 0, len = code.length; i < len; ++i) { lookup[i] = code[i] revLookup[code.charCodeAt(i)] = i } revLookup['-'.charCodeAt(0)] = 62 revLookup['_'.charCodeAt(0)] = 63 function placeHoldersCount (b64) { var len = b64.length if (len % 4 > 0) { throw new Error('Invalid string. Length must be a multiple of 4') } // the number of equal signs (place holders) // if there are two placeholders, than the two characters before it // represent one byte // if there is only one, then the three characters before it represent 2 bytes // this is just a cheap hack to not do indexOf twice return b64[len - 2] === '=' ? 2 : b64[len - 1] === '=' ? 1 : 0 } function byteLength (b64) { // base64 is 4/3 + up to two characters of the original data return (b64.length * 3 / 4) - placeHoldersCount(b64) } function toByteArray (b64) { var i, l, tmp, placeHolders, arr var len = b64.length placeHolders = placeHoldersCount(b64) arr = new Arr((len * 3 / 4) - placeHolders) // if there are placeholders, only get up to the last complete 4 chars l = placeHolders > 0 ? len - 4 : len var L = 0 for (i = 0; i < l; i += 4) { tmp = (revLookup[b64.charCodeAt(i)] << 18) | (revLookup[b64.charCodeAt(i + 1)] << 12) | (revLookup[b64.charCodeAt(i + 2)] << 6) | revLookup[b64.charCodeAt(i + 3)] arr[L++] = (tmp >> 16) & 0xFF arr[L++] = (tmp >> 8) & 0xFF arr[L++] = tmp & 0xFF } if (placeHolders === 2) { tmp = (revLookup[b64.charCodeAt(i)] << 2) | (revLookup[b64.charCodeAt(i + 1)] >> 4) arr[L++] = tmp & 0xFF } else if (placeHolders === 1) { tmp = (revLookup[b64.charCodeAt(i)] << 10) | (revLookup[b64.charCodeAt(i + 1)] << 4) | (revLookup[b64.charCodeAt(i + 2)] >> 2) arr[L++] = (tmp >> 8) & 0xFF arr[L++] = tmp & 0xFF } return arr } function tripletToBase64 (num) { return lookup[num >> 18 & 0x3F] + lookup[num >> 12 & 0x3F] + lookup[num >> 6 & 0x3F] + lookup[num & 0x3F] } function encodeChunk (uint8, start, end) { var tmp var output = [] for (var i = start; i < end; i += 3) { tmp = (uint8[i] << 16) + (uint8[i + 1] << 8) + (uint8[i + 2]) output.push(tripletToBase64(tmp)) } return output.join('') } function fromByteArray (uint8) { var tmp var len = uint8.length var extraBytes = len % 3 // if we have 1 byte left, pad 2 bytes var output = '' var parts = [] var maxChunkLength = 16383 // must be multiple of 3 // go through the array every three bytes, we'll deal with trailing stuff later for (var i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) { parts.push(encodeChunk(uint8, i, (i + maxChunkLength) > len2 ? len2 : (i + maxChunkLength))) } // pad the end with zeros, but make sure to not forget the extra bytes if (extraBytes === 1) { tmp = uint8[len - 1] output += lookup[tmp >> 2] output += lookup[(tmp << 4) & 0x3F] output += '==' } else if (extraBytes === 2) { tmp = (uint8[len - 2] << 8) + (uint8[len - 1]) output += lookup[tmp >> 10] output += lookup[(tmp >> 4) & 0x3F] output += lookup[(tmp << 2) & 0x3F] output += '=' } parts.push(output) return parts.join('') } },{}],5:[function(_dereq_,module,exports){ 'use strict'; // For more information about browser field, check out the browser field at https://github.com/substack/browserify-handbook#browser-field. module.exports = { // Create a tag with optional data attributes createLink: function(href, attributes) { var head = document.head || document.getElementsByTagName('head')[0]; var link = document.createElement('link'); link.href = href; link.rel = 'stylesheet'; for (var key in attributes) { if ( ! attributes.hasOwnProperty(key)) { continue; } var value = attributes[key]; link.setAttribute('data-' + key, value); } head.appendChild(link); }, // Create a ================================================ FILE: nn/using-deeplab/tfjs/LICENSE ================================================ Copyright 2016 The TensorFlow Authors. All rights reserved. Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 2016, The Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: nn/using-deeplab/tfjs/tf-core.js ================================================ /** * @license * Copyright 2019 Google LLC. All Rights Reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================= */ (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : (factory((global.tf = global.tf || {}))); }(this, (function (exports) { 'use strict'; /*! ***************************************************************************** Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT. See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ /* global Reflect, Promise */ var extendStatics = function(d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return extendStatics(d, b); }; function __extends(d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); } var __assign = function() { __assign = Object.assign || function __assign(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; function __awaiter(thisArg, _arguments, P, generator) { return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); } function __generator(thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (_) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } } var contexts = {}; var WEBGL_ATTRIBUTES = { alpha: false, antialias: false, premultipliedAlpha: false, preserveDrawingBuffer: false, depth: false, stencil: false, failIfMajorPerformanceCaveat: true }; function getWebGLContext(webGLVersion) { if (!(webGLVersion in contexts)) { var canvas = document.createElement('canvas'); canvas.addEventListener('webglcontextlost', function (ev) { ev.preventDefault(); delete contexts[webGLVersion]; }, false); contexts[webGLVersion] = getWebGLRenderingContext(webGLVersion); } var gl = contexts[webGLVersion]; if (gl.isContextLost()) { delete contexts[webGLVersion]; return getWebGLContext(webGLVersion); } gl.disable(gl.DEPTH_TEST); gl.disable(gl.STENCIL_TEST); gl.disable(gl.BLEND); gl.disable(gl.DITHER); gl.disable(gl.POLYGON_OFFSET_FILL); gl.disable(gl.SAMPLE_COVERAGE); gl.enable(gl.SCISSOR_TEST); gl.enable(gl.CULL_FACE); gl.cullFace(gl.BACK); return contexts[webGLVersion]; } function getWebGLRenderingContext(webGLVersion) { if (webGLVersion !== 1 && webGLVersion !== 2) { throw new Error('Cannot get WebGL rendering context, WebGL is disabled.'); } var canvas = document.createElement('canvas'); if (webGLVersion === 1) { return (canvas.getContext('webgl', WEBGL_ATTRIBUTES) || canvas.getContext('experimental-webgl', WEBGL_ATTRIBUTES)); } return canvas.getContext('webgl2', WEBGL_ATTRIBUTES); } function isMobile() { var a = navigator.userAgent || navigator.vendor || window.opera; return /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i .test(a) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i .test(a.substr(0, 4)); } function shuffle(array) { var counter = array.length; var temp = 0; var index = 0; while (counter > 0) { index = (Math.random() * counter) | 0; counter--; temp = array[counter]; array[counter] = array[index]; array[index] = temp; } } function clamp(min, x, max) { return Math.max(min, Math.min(x, max)); } function nearestLargerEven(val) { return val % 2 === 0 ? val : val + 1; } function sum(arr) { var sum = 0; for (var i = 0; i < arr.length; i++) { sum += arr[i]; } return sum; } function randUniform(a, b) { var r = Math.random(); return (b * r) + (1 - r) * a; } function distSquared(a, b) { var result = 0; for (var i = 0; i < a.length; i++) { var diff = Number(a[i]) - Number(b[i]); result += diff * diff; } return result; } function assert(expr, msg) { if (!expr) { throw new Error(typeof msg === 'string' ? msg : msg()); } } function assertShapesMatch(shapeA, shapeB, errorMessagePrefix) { if (errorMessagePrefix === void 0) { errorMessagePrefix = ''; } assert(arraysEqual(shapeA, shapeB), errorMessagePrefix + (" Shapes " + shapeA + " and " + shapeB + " must match")); } function assertNonNull(a) { assert(a != null, "The input to the tensor constructor must be a non-null value."); } function flatten(arr, ret) { if (ret === void 0) { ret = []; } if (Array.isArray(arr) || isTypedArray(arr)) { for (var i = 0; i < arr.length; ++i) { flatten(arr[i], ret); } } else { ret.push(arr); } return ret; } function sizeFromShape(shape) { if (shape.length === 0) { return 1; } var size = shape[0]; for (var i = 1; i < shape.length; i++) { size *= shape[i]; } return size; } function isScalarShape(shape) { return shape.length === 0; } function arraysEqual(n1, n2) { if (n1 === n2) { return true; } if (n1 == null || n2 == null) { return false; } if (n1.length !== n2.length) { return false; } for (var i = 0; i < n1.length; i++) { if (n1[i] !== n2[i]) { return false; } } return true; } function isInt(a) { return a % 1 === 0; } function tanh(x) { if (Math.tanh != null) { return Math.tanh(x); } if (x === Infinity) { return 1; } else if (x === -Infinity) { return -1; } else { var e2x = Math.exp(2 * x); return (e2x - 1) / (e2x + 1); } } function sizeToSquarishShape(size) { var width = Math.ceil(Math.sqrt(size)); return [width, Math.ceil(size / width)]; } function createShuffledIndices(n) { var shuffledIndices = new Uint32Array(n); for (var i = 0; i < n; ++i) { shuffledIndices[i] = i; } shuffle(shuffledIndices); return shuffledIndices; } function rightPad(a, size) { if (size <= a.length) { return a; } return a + ' '.repeat(size - a.length); } function repeatedTry(checkFn, delayFn, maxCounter) { if (delayFn === void 0) { delayFn = function (counter) { return 0; }; } return new Promise(function (resolve, reject) { var tryCount = 0; var tryFn = function () { if (checkFn()) { resolve(); return; } tryCount++; var nextBackoff = delayFn(tryCount); if (maxCounter != null && tryCount >= maxCounter) { reject(); return; } setTimeout(tryFn, nextBackoff); }; tryFn(); }); } function inferFromImplicitShape(shape, size) { var shapeProd = 1; var implicitIdx = -1; for (var i = 0; i < shape.length; ++i) { if (shape[i] >= 0) { shapeProd *= shape[i]; } else if (shape[i] === -1) { if (implicitIdx !== -1) { throw Error("Shapes can only have 1 implicit size. " + ("Found -1 at dim " + implicitIdx + " and dim " + i)); } implicitIdx = i; } else if (shape[i] < 0) { throw Error("Shapes can not be < 0. Found " + shape[i] + " at dim " + i); } } if (implicitIdx === -1) { if (size > 0 && size !== shapeProd) { throw Error("Size(" + size + ") must match the product of shape " + shape); } return shape; } if (shapeProd === 0) { throw Error("Cannot infer the missing size in [" + shape + "] when " + "there are 0 elements"); } if (size % shapeProd !== 0) { throw Error("The implicit shape can't be a fractional number. " + ("Got " + size + " / " + shapeProd)); } var newShape = shape.slice(); newShape[implicitIdx] = size / shapeProd; return newShape; } function parseAxisParam(axis, shape) { var rank = shape.length; axis = axis == null ? shape.map(function (s, i) { return i; }) : [].concat(axis); assert(axis.every(function (ax) { return ax >= -rank && ax < rank; }), "All values in axis param must be in range [-" + rank + ", " + rank + ") but " + ("got axis " + axis)); assert(axis.every(function (ax) { return isInt(ax); }), "All values in axis param must be integers but " + ("got axis " + axis)); return axis.map(function (a) { return a < 0 ? rank + a : a; }); } function squeezeShape(shape, axis) { var newShape = []; var keptDims = []; var axes = axis == null ? null : parseAxisParam(axis, shape).sort(); var j = 0; for (var i = 0; i < shape.length; ++i) { if (axes != null) { if (axes[j] === i && shape[i] !== 1) { throw new Error("Can't squeeze axis " + i + " since its dim '" + shape[i] + "' is not 1"); } if ((axes[j] == null || axes[j] > i) && shape[i] === 1) { newShape.push(shape[i]); keptDims.push(i); } if (axes[j] <= i) { j++; } } if (shape[i] !== 1) { newShape.push(shape[i]); keptDims.push(i); } } return { newShape: newShape, keptDims: keptDims }; } function getTypedArrayFromDType(dtype, size) { var values = null; if (dtype == null || dtype === 'float32') { values = new Float32Array(size); } else if (dtype === 'int32') { values = new Int32Array(size); } else if (dtype === 'bool') { values = new Uint8Array(size); } else { throw new Error("Unknown data type " + dtype); } return values; } function getArrayFromDType(dtype, size) { var values = null; if (dtype == null || dtype === 'float32') { values = new Float32Array(size); } else if (dtype === 'int32') { values = new Int32Array(size); } else if (dtype === 'bool') { values = new Uint8Array(size); } else if (dtype === 'string') { values = new Array(size); } else { throw new Error("Unknown data type " + dtype); } return values; } function checkComputationForErrors(vals, dtype, name) { if (dtype !== 'float32') { return; } for (var i = 0; i < vals.length; i++) { var num = vals[i]; if (isNaN(num) || !isFinite(num)) { throw Error("The result of the '" + name + "' is " + num + "."); } } } function checkConversionForErrors(vals, dtype) { for (var i = 0; i < vals.length; i++) { var num = vals[i]; if (isNaN(num) || !isFinite(num)) { throw Error("A tensor of type " + dtype + " being uploaded contains " + num + "."); } } } function hasEncodingLoss(oldType, newType) { if (newType === 'complex64') { return false; } if (newType === 'float32' && oldType !== 'complex64') { return false; } if (newType === 'int32' && oldType !== 'float32' && oldType !== 'complex64') { return false; } if (newType === 'bool' && oldType === 'bool') { return false; } return true; } function isTypedArray(a) { return a instanceof Float32Array || a instanceof Int32Array || a instanceof Uint8Array; } function bytesPerElement(dtype) { if (dtype === 'float32' || dtype === 'int32') { return 4; } else if (dtype === 'complex64') { return 8; } else if (dtype === 'bool') { return 1; } else { throw new Error("Unknown dtype " + dtype); } } function bytesFromStringArray(arr) { if (arr == null) { return 0; } var bytes = 0; arr.forEach(function (x) { return bytes += x.length * 2; }); return bytes; } function isString(value) { return typeof value === 'string' || value instanceof String; } function isBoolean(value) { return typeof value === 'boolean'; } function isNumber(value) { return typeof value === 'number'; } function inferDtype(values) { if (Array.isArray(values)) { return inferDtype(values[0]); } if (values instanceof Float32Array) { return 'float32'; } else if (values instanceof Int32Array || values instanceof Uint8Array) { return 'int32'; } else if (isNumber(values)) { return 'float32'; } else if (isString(values)) { return 'string'; } else if (isBoolean(values)) { return 'bool'; } return 'float32'; } function isFunction(f) { return !!(f && f.constructor && f.call && f.apply); } function nearestDivisor(size, start) { for (var i = start; i < size; ++i) { if (size % i === 0) { return i; } } return size; } function computeStrides(shape) { var rank = shape.length; if (rank < 2) { return []; } var strides = new Array(rank - 1); strides[rank - 2] = shape[rank - 1]; for (var i = rank - 3; i >= 0; --i) { strides[i] = strides[i + 1] * shape[i + 1]; } return strides; } function toTypedArray(a, dtype, debugMode) { if (dtype === 'string') { throw new Error('Cannot convert a string[] to a TypedArray'); } if (Array.isArray(a)) { a = flatten(a); } if (debugMode) { checkConversionForErrors(a, dtype); } if (noConversionNeeded(a, dtype)) { return a; } if (dtype == null || dtype === 'float32' || dtype === 'complex64') { return new Float32Array(a); } else if (dtype === 'int32') { return new Int32Array(a); } else if (dtype === 'bool') { var bool = new Uint8Array(a.length); for (var i = 0; i < bool.length; ++i) { if (Math.round(a[i]) !== 0) { bool[i] = 1; } } return bool; } else { throw new Error("Unknown data type " + dtype); } } function createNestedArray(offset, shape, a) { var ret = new Array(); if (shape.length === 1) { var d = shape[0]; for (var i = 0; i < d; i++) { ret[i] = a[offset + i]; } } else { var d = shape[0]; var rest = shape.slice(1); var len = rest.reduce(function (acc, c) { return acc * c; }); for (var i = 0; i < d; i++) { ret[i] = createNestedArray(offset + i * len, rest, a); } } return ret; } function toNestedArray(shape, a) { if (shape.length === 0) { return []; } var size = shape.reduce(function (acc, c) { return acc * c; }); if (size === 0) { return []; } if (size !== a.length) { throw new Error("[" + shape + "] does not match the input size."); } return createNestedArray(0, shape, a); } function noConversionNeeded(a, dtype) { return (a instanceof Float32Array && dtype === 'float32') || (a instanceof Int32Array && dtype === 'int32') || (a instanceof Uint8Array && dtype === 'bool'); } function makeOnesTypedArray(size, dtype) { var array = makeZerosTypedArray(size, dtype); for (var i = 0; i < array.length; i++) { array[i] = 1; } return array; } function makeZerosTypedArray(size, dtype) { if (dtype == null || dtype === 'float32' || dtype === 'complex64') { return new Float32Array(size); } else if (dtype === 'int32') { return new Int32Array(size); } else if (dtype === 'bool') { return new Uint8Array(size); } else { throw new Error("Unknown data type " + dtype); } } function now() { if (typeof performance !== 'undefined') { return performance.now(); } else if (typeof process !== 'undefined') { var time = process.hrtime(); return time[0] * 1000 + time[1] / 1000000; } else { throw new Error('Cannot measure time in this environment. You should run tf.js ' + 'in the browser or in Node.js'); } } function monitorPromisesProgress(promises, onProgress, startFraction, endFraction) { checkPromises(promises); startFraction = startFraction == null ? 0 : startFraction; endFraction = endFraction == null ? 1 : endFraction; checkFraction(startFraction, endFraction); var resolvedPromise = 0; function registerMonitor(promise) { promise.then(function (value) { var fraction = startFraction + ++resolvedPromise / promises.length * (endFraction - startFraction); onProgress(fraction); return value; }); return promise; } function checkPromises(promises) { assert(promises != null && Array.isArray(promises) && promises.length > 0, 'promises must be a none empty array'); } function checkFraction(startFraction, endFraction) { assert(startFraction >= 0 && startFraction <= 1, "Progress fraction must be in range [0, 1], but " + ("got startFraction " + startFraction)); assert(endFraction >= 0 && endFraction <= 1, "Progress fraction must be in range [0, 1], but " + ("got endFraction " + endFraction)); assert(endFraction >= startFraction, "startFraction must be no more than endFraction, but " + ("got startFraction " + startFraction + " and endFraction " + endFraction)); } return Promise.all(promises.map(registerMonitor)); } var util = /*#__PURE__*/Object.freeze({ shuffle: shuffle, clamp: clamp, nearestLargerEven: nearestLargerEven, sum: sum, randUniform: randUniform, distSquared: distSquared, assert: assert, assertShapesMatch: assertShapesMatch, assertNonNull: assertNonNull, flatten: flatten, sizeFromShape: sizeFromShape, isScalarShape: isScalarShape, arraysEqual: arraysEqual, isInt: isInt, tanh: tanh, sizeToSquarishShape: sizeToSquarishShape, createShuffledIndices: createShuffledIndices, rightPad: rightPad, repeatedTry: repeatedTry, inferFromImplicitShape: inferFromImplicitShape, parseAxisParam: parseAxisParam, squeezeShape: squeezeShape, getTypedArrayFromDType: getTypedArrayFromDType, getArrayFromDType: getArrayFromDType, checkComputationForErrors: checkComputationForErrors, checkConversionForErrors: checkConversionForErrors, hasEncodingLoss: hasEncodingLoss, isTypedArray: isTypedArray, bytesPerElement: bytesPerElement, bytesFromStringArray: bytesFromStringArray, isString: isString, isBoolean: isBoolean, isNumber: isNumber, inferDtype: inferDtype, isFunction: isFunction, nearestDivisor: nearestDivisor, computeStrides: computeStrides, toTypedArray: toTypedArray, toNestedArray: toNestedArray, makeOnesTypedArray: makeOnesTypedArray, makeZerosTypedArray: makeZerosTypedArray, now: now, monitorPromisesProgress: monitorPromisesProgress }); var Profiler = (function () { function Profiler(backendTimer, logger) { this.backendTimer = backendTimer; this.logger = logger; if (logger == null) { this.logger = new Logger(); } } Profiler.prototype.profileKernel = function (name, f) { var _this = this; var result; var holdResultWrapperFn = function () { result = f(); }; var timer = this.backendTimer.time(holdResultWrapperFn); var results = Array.isArray(result) ? result : [result]; results.forEach(function (r) { var vals = r.dataSync(); checkComputationForErrors(vals, r.dtype, name); timer.then(function (timing) { var extraInfo = ''; if (timing.getExtraProfileInfo != null) { extraInfo = timing.getExtraProfileInfo(); } _this.logger.logKernelProfile(name, r, vals, timing.kernelMs, extraInfo); }); }); return result; }; return Profiler; }()); var Logger = (function () { function Logger() { } Logger.prototype.logKernelProfile = function (name, result, vals, timeMs, extraInfo) { var time = rightPad(timeMs + "ms", 9); var paddedName = rightPad(name, 25); var rank = result.rank; var size = result.size; var shape = rightPad(result.shape.toString(), 14); console.log("%c" + paddedName + "\t%c" + time + "\t%c" + rank + "D " + shape + "\t%c" + size + "\t%c" + extraInfo, 'font-weight:bold', 'color:red', 'color:blue', 'color: orange', 'color: green'); }; return Logger; }()); var FORMAT_LIMIT_NUM_VALS = 20; var FORMAT_NUM_FIRST_LAST_VALS = 3; var FORMAT_NUM_SIG_DIGITS = 7; function tensorToString(vals, shape, dtype, verbose) { var strides = computeStrides(shape); var padPerCol = computeMaxSizePerColumn(vals, shape, dtype, strides); var rank = shape.length; var valsLines = subTensorToString(vals, shape, dtype, strides, padPerCol); var lines = ['Tensor']; if (verbose) { lines.push(" dtype: " + dtype); lines.push(" rank: " + rank); lines.push(" shape: [" + shape + "]"); lines.push(" values:"); } lines.push(valsLines.map(function (l) { return ' ' + l; }).join('\n')); return lines.join('\n'); } function computeMaxSizePerColumn(vals, shape, dtype, strides) { var n = sizeFromShape(shape); var numCols = strides[strides.length - 1]; var padPerCol = new Array(numCols).fill(0); var rank = shape.length; var valuesOrTuples = dtype === 'complex64' ? createComplexTuples(vals) : vals; if (rank > 1) { for (var row = 0; row < n / numCols; row++) { var offset = row * numCols; for (var j = 0; j < numCols; j++) { padPerCol[j] = Math.max(padPerCol[j], valToString(valuesOrTuples[offset + j], 0).length); } } } return padPerCol; } function valToString(val, pad) { var valStr; if (Array.isArray(val)) { valStr = parseFloat(val[0].toFixed(FORMAT_NUM_SIG_DIGITS)) + " + " + (parseFloat(val[1].toFixed(FORMAT_NUM_SIG_DIGITS)) + "j"); } else if (isString(val)) { valStr = "'" + val + "'"; } else { valStr = parseFloat(val.toFixed(FORMAT_NUM_SIG_DIGITS)).toString(); } return rightPad(valStr, pad); } function subTensorToString(vals, shape, dtype, strides, padPerCol, isLast) { if (isLast === void 0) { isLast = true; } var storagePerElement = dtype === 'complex64' ? 2 : 1; var size = shape[0]; var rank = shape.length; if (rank === 0) { if (dtype === 'complex64') { var complexTuple = createComplexTuples(vals); return [valToString(complexTuple[0], 0)]; } return [vals[0].toString()]; } if (rank === 1) { if (size > FORMAT_LIMIT_NUM_VALS) { var firstValsSize = FORMAT_NUM_FIRST_LAST_VALS * storagePerElement; var firstVals = Array.from(vals.slice(0, firstValsSize)); var lastVals = Array.from(vals.slice(size - FORMAT_NUM_FIRST_LAST_VALS * storagePerElement, size)); if (dtype === 'complex64') { firstVals = createComplexTuples(firstVals); lastVals = createComplexTuples(lastVals); } return [ '[' + firstVals.map(function (x, i) { return valToString(x, padPerCol[i]); }).join(', ') + ', ..., ' + lastVals .map(function (x, i) { return valToString(x, padPerCol[size - FORMAT_NUM_FIRST_LAST_VALS + i]); }) .join(', ') + ']' ]; } var displayVals = dtype === 'complex64' ? createComplexTuples(vals) : Array.from(vals); return [ '[' + displayVals.map(function (x, i) { return valToString(x, padPerCol[i]); }).join(', ') + ']' ]; } var subshape = shape.slice(1); var substrides = strides.slice(1); var stride = strides[0] * storagePerElement; var lines = []; if (size > FORMAT_LIMIT_NUM_VALS) { for (var i = 0; i < FORMAT_NUM_FIRST_LAST_VALS; i++) { var start = i * stride; var end = start + stride; lines.push.apply(lines, subTensorToString(vals.slice(start, end), subshape, dtype, substrides, padPerCol, false)); } lines.push('...'); for (var i = size - FORMAT_NUM_FIRST_LAST_VALS; i < size; i++) { var start = i * stride; var end = start + stride; lines.push.apply(lines, subTensorToString(vals.slice(start, end), subshape, dtype, substrides, padPerCol, i === size - 1)); } } else { for (var i = 0; i < size; i++) { var start = i * stride; var end = start + stride; lines.push.apply(lines, subTensorToString(vals.slice(start, end), subshape, dtype, substrides, padPerCol, i === size - 1)); } } var sep = rank === 2 ? ',' : ''; lines[0] = '[' + lines[0] + sep; for (var i = 1; i < lines.length - 1; i++) { lines[i] = ' ' + lines[i] + sep; } var newLineSep = ',\n'; for (var i = 2; i < rank; i++) { newLineSep += '\n'; } lines[lines.length - 1] = ' ' + lines[lines.length - 1] + ']' + (isLast ? '' : newLineSep); return lines; } function createComplexTuples(vals) { var complexTuples = []; for (var i = 0; i < vals.length; i += 2) { complexTuples.push([vals[i], vals[i + 1]]); } return complexTuples; } var TensorBuffer = (function () { function TensorBuffer(shape, dtype, values) { this.dtype = dtype; this.shape = shape.slice(); this.size = sizeFromShape(shape); if (values != null) { var n = values.length; assert(n === this.size, "Length of values '" + n + "' does not match the size " + ("inferred by the shape '" + this.size + "'.")); } if (dtype === 'complex64') { throw new Error("complex64 dtype TensorBuffers are not supported. Please create " + "a TensorBuffer for the real and imaginary parts separately and " + "call tf.complex(real, imag)."); } this.values = values || getArrayFromDType(dtype, sizeFromShape(this.shape)); this.strides = computeStrides(shape); } TensorBuffer.prototype.set = function (value) { var locs = []; for (var _i = 1; _i < arguments.length; _i++) { locs[_i - 1] = arguments[_i]; } if (locs.length === 0) { locs = [0]; } assert(locs.length === this.rank, "The number of provided coordinates (" + locs.length + ") must " + ("match the rank (" + this.rank + ")")); var index = this.locToIndex(locs); this.values[index] = value; }; TensorBuffer.prototype.get = function () { var locs = []; for (var _i = 0; _i < arguments.length; _i++) { locs[_i] = arguments[_i]; } if (locs.length === 0) { locs = [0]; } var index = locs[locs.length - 1]; for (var i = 0; i < locs.length - 1; ++i) { index += this.strides[i] * locs[i]; } return this.values[index]; }; TensorBuffer.prototype.locToIndex = function (locs) { if (this.rank === 0) { return 0; } else if (this.rank === 1) { return locs[0]; } var index = locs[locs.length - 1]; for (var i = 0; i < locs.length - 1; ++i) { index += this.strides[i] * locs[i]; } return index; }; TensorBuffer.prototype.indexToLoc = function (index) { if (this.rank === 0) { return []; } else if (this.rank === 1) { return [index]; } var locs = new Array(this.shape.length); for (var i = 0; i < locs.length - 1; ++i) { locs[i] = Math.floor(index / this.strides[i]); index -= locs[i] * this.strides[i]; } locs[locs.length - 1] = index; return locs; }; Object.defineProperty(TensorBuffer.prototype, "rank", { get: function () { return this.shape.length; }, enumerable: true, configurable: true }); TensorBuffer.prototype.toTensor = function () { return Tensor.make(this.shape, { values: this.values }, this.dtype); }; return TensorBuffer; }()); var trackerFn = null; var opHandler = null; function setTensorTracker(fn) { trackerFn = fn; } function setOpHandler(handler) { opHandler = handler; } var Tensor = (function () { function Tensor(shape, dtype, values, dataId) { this.isDisposedInternal = false; this.shape = shape.slice(); this.dtype = dtype || 'float32'; this.size = sizeFromShape(shape); this.strides = computeStrides(shape); this.dataId = dataId != null ? dataId : {}; this.id = trackerFn().nextTensorId(); this.rankType = (this.rank < 5 ? this.rank.toString() : 'higher'); trackerFn().registerTensor(this); if (values != null) { trackerFn().write(this.dataId, values); } } Tensor.make = function (shape, data, dtype) { return new Tensor(shape, dtype, data.values, data.dataId); }; Tensor.prototype.flatten = function () { this.throwIfDisposed(); return this.as1D(); }; Tensor.prototype.asScalar = function () { this.throwIfDisposed(); assert(this.size === 1, 'The array must have only 1 element.'); return this.reshape([]); }; Tensor.prototype.as1D = function () { this.throwIfDisposed(); return this.reshape([this.size]); }; Tensor.prototype.as2D = function (rows, columns) { this.throwIfDisposed(); return this.reshape([rows, columns]); }; Tensor.prototype.as3D = function (rows, columns, depth) { this.throwIfDisposed(); return this.reshape([rows, columns, depth]); }; Tensor.prototype.as4D = function (rows, columns, depth, depth2) { this.throwIfDisposed(); return this.reshape([rows, columns, depth, depth2]); }; Tensor.prototype.as5D = function (rows, columns, depth, depth2, depth3) { this.throwIfDisposed(); return this.reshape([rows, columns, depth, depth2, depth3]); }; Tensor.prototype.asType = function (dtype) { this.throwIfDisposed(); return opHandler.cast(this, dtype); }; Object.defineProperty(Tensor.prototype, "rank", { get: function () { return this.shape.length; }, enumerable: true, configurable: true }); Tensor.prototype.get = function () { var locs = []; for (var _i = 0; _i < arguments.length; _i++) { locs[_i] = arguments[_i]; } assert(locs.length === this.rank, 'Number of coordinates in get() must match the rank of the tensor'); assert(this.dtype !== 'complex64', 'Tensor.get() is not supported for complex64 tensors yet.'); this.throwIfDisposed(); if (locs.length === 0) { locs = [0]; } var index = locs[locs.length - 1]; for (var i = 0; i < locs.length - 1; ++i) { index += this.strides[i] * locs[i]; } return this.dataSync()[index]; }; Tensor.prototype.buffer = function () { return opHandler.buffer(this.shape, this.dtype, this.dataSync()); }; Tensor.prototype.data = function () { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { this.throwIfDisposed(); return [2, trackerFn().read(this.dataId)]; }); }); }; Tensor.prototype.dataSync = function () { this.throwIfDisposed(); return trackerFn().readSync(this.dataId); }; Tensor.prototype.dispose = function () { if (this.isDisposed) { return; } trackerFn().disposeTensor(this); this.isDisposedInternal = true; }; Object.defineProperty(Tensor.prototype, "isDisposed", { get: function () { return this.isDisposedInternal; }, enumerable: true, configurable: true }); Tensor.prototype.throwIfDisposed = function () { if (this.isDisposed) { throw new Error("Tensor is disposed."); } }; Tensor.prototype.toFloat = function () { return this.asType('float32'); }; Tensor.prototype.toInt = function () { return this.asType('int32'); }; Tensor.prototype.toBool = function () { return this.asType('bool'); }; Tensor.prototype.print = function (verbose) { if (verbose === void 0) { verbose = false; } return opHandler.print(this, verbose); }; Tensor.prototype.reshape = function (newShape) { this.throwIfDisposed(); return opHandler.reshape(this, newShape); }; Tensor.prototype.reshapeAs = function (x) { this.throwIfDisposed(); return this.reshape(x.shape); }; Tensor.prototype.expandDims = function (axis) { if (axis === void 0) { axis = 0; } return opHandler.expandDims(this, axis); }; Tensor.prototype.cumsum = function (axis, exclusive, reverse) { if (axis === void 0) { axis = 0; } if (exclusive === void 0) { exclusive = false; } if (reverse === void 0) { reverse = false; } return opHandler.cumsum(this, axis, exclusive, reverse); }; Tensor.prototype.squeeze = function (axis) { this.throwIfDisposed(); return opHandler.squeeze(this, axis); }; Tensor.prototype.clone = function () { this.throwIfDisposed(); return opHandler.clone(this); }; Tensor.prototype.oneHot = function (depth, onValue, offValue) { this.throwIfDisposed(); return opHandler.oneHot(this, depth, onValue, offValue); }; Tensor.prototype.toString = function (verbose) { if (verbose === void 0) { verbose = false; } var vals = this.dataSync(); return tensorToString(vals, this.shape, this.dtype, verbose); }; Tensor.prototype.tile = function (reps) { this.throwIfDisposed(); return opHandler.tile(this, reps); }; Tensor.prototype.gather = function (indices, axis) { if (axis === void 0) { axis = 0; } this.throwIfDisposed(); return opHandler.gather(this, indices, axis); }; Tensor.prototype.matMul = function (b, transposeA, transposeB) { if (transposeA === void 0) { transposeA = false; } if (transposeB === void 0) { transposeB = false; } this.throwIfDisposed(); return opHandler.matMul(this, b, transposeA, transposeB); }; Tensor.prototype.dot = function (b) { this.throwIfDisposed(); return opHandler.dot(this, b); }; Tensor.prototype.norm = function (ord, axis, keepDims) { if (ord === void 0) { ord = 'euclidean'; } if (axis === void 0) { axis = null; } if (keepDims === void 0) { keepDims = false; } this.throwIfDisposed(); return opHandler.norm(this, ord, axis, keepDims); }; Tensor.prototype.slice = function (begin, size) { this.throwIfDisposed(); return opHandler.slice(this, begin, size); }; Tensor.prototype.reverse = function (axis) { this.throwIfDisposed(); return opHandler.reverse(this, axis); }; Tensor.prototype.concat = function (x, axis) { if (axis === void 0) { axis = 0; } this.throwIfDisposed(); if (x instanceof Tensor) { x = [x]; } return opHandler.concat([this].concat(x), axis); }; Tensor.prototype.split = function (numOrSizeSplits, axis) { if (axis === void 0) { axis = 0; } this.throwIfDisposed(); return opHandler.split(this, numOrSizeSplits, axis); }; Tensor.prototype.stack = function (x, axis) { if (axis === void 0) { axis = 0; } return opHandler.stack([this, x], axis); }; Tensor.prototype.unstack = function (x, axis) { if (axis === void 0) { axis = 0; } return opHandler.unstack(this, axis); }; Tensor.prototype.pad = function (paddings, constantValue) { if (constantValue === void 0) { constantValue = 0; } return opHandler.pad(this, paddings, constantValue); }; Tensor.prototype.batchNormalization = function (mean, variance, varianceEpsilon, scale, offset) { if (varianceEpsilon === void 0) { varianceEpsilon = .001; } this.throwIfDisposed(); return opHandler.batchNormalization(this, mean, variance, varianceEpsilon, scale, offset); }; Tensor.prototype.all = function (axis, keepDims) { if (axis === void 0) { axis = null; } if (keepDims === void 0) { keepDims = false; } this.throwIfDisposed(); return opHandler.all(this, axis, keepDims); }; Tensor.prototype.any = function (axis, keepDims) { if (axis === void 0) { axis = null; } if (keepDims === void 0) { keepDims = false; } this.throwIfDisposed(); return opHandler.any(this, axis, keepDims); }; Tensor.prototype.logSumExp = function (axis, keepDims) { if (axis === void 0) { axis = null; } if (keepDims === void 0) { keepDims = false; } this.throwIfDisposed(); return opHandler.logSumExp(this, axis, keepDims); }; Tensor.prototype.sum = function (axis, keepDims) { if (axis === void 0) { axis = null; } if (keepDims === void 0) { keepDims = false; } this.throwIfDisposed(); return opHandler.sum(this, axis, keepDims); }; Tensor.prototype.prod = function (axis, keepDims) { if (axis === void 0) { axis = null; } if (keepDims === void 0) { keepDims = false; } this.throwIfDisposed(); return opHandler.prod(this, axis, keepDims); }; Tensor.prototype.mean = function (axis, keepDims) { if (axis === void 0) { axis = null; } if (keepDims === void 0) { keepDims = false; } this.throwIfDisposed(); return opHandler.mean(this, axis, keepDims); }; Tensor.prototype.min = function (axis, keepDims) { if (axis === void 0) { axis = null; } if (keepDims === void 0) { keepDims = false; } this.throwIfDisposed(); return opHandler.min(this, axis, keepDims); }; Tensor.prototype.max = function (axis, keepDims) { if (axis === void 0) { axis = null; } if (keepDims === void 0) { keepDims = false; } this.throwIfDisposed(); return opHandler.max(this, axis, keepDims); }; Tensor.prototype.argMin = function (axis) { if (axis === void 0) { axis = null; } this.throwIfDisposed(); return opHandler.argMin(this, axis); }; Tensor.prototype.argMax = function (axis) { if (axis === void 0) { axis = null; } this.throwIfDisposed(); return opHandler.argMax(this, axis); }; Tensor.prototype.cast = function (dtype) { this.throwIfDisposed(); return opHandler.cast(this, dtype); }; Tensor.prototype.add = function (x) { this.throwIfDisposed(); return opHandler.add(this, x); }; Tensor.prototype.addStrict = function (x) { this.throwIfDisposed(); return opHandler.addStrict(this, x); }; Tensor.prototype.atan2 = function (x) { this.throwIfDisposed(); return opHandler.atan2(this, x); }; Tensor.prototype.sub = function (x) { this.throwIfDisposed(); return opHandler.sub(this, x); }; Tensor.prototype.subStrict = function (x) { this.throwIfDisposed(); return opHandler.subStrict(this, x); }; Tensor.prototype.pow = function (exp) { this.throwIfDisposed(); return opHandler.pow(this, exp); }; Tensor.prototype.powStrict = function (exp) { this.throwIfDisposed(); return opHandler.powStrict(this, exp); }; Tensor.prototype.mul = function (x) { this.throwIfDisposed(); return opHandler.mul(this, x); }; Tensor.prototype.mulStrict = function (x) { this.throwIfDisposed(); return opHandler.mulStrict(this, x); }; Tensor.prototype.div = function (x) { this.throwIfDisposed(); return opHandler.div(this, x); }; Tensor.prototype.floorDiv = function (x) { this.throwIfDisposed(); return opHandler.floorDiv(this, x); }; Tensor.prototype.divStrict = function (x) { this.throwIfDisposed(); return opHandler.divStrict(this, x); }; Tensor.prototype.minimum = function (x) { this.throwIfDisposed(); return opHandler.minimum(this, x); }; Tensor.prototype.minimumStrict = function (x) { this.throwIfDisposed(); return opHandler.minimumStrict(this, x); }; Tensor.prototype.maximum = function (x) { this.throwIfDisposed(); return opHandler.maximum(this, x); }; Tensor.prototype.maximumStrict = function (x) { this.throwIfDisposed(); return opHandler.maximumStrict(this, x); }; Tensor.prototype.mod = function (x) { this.throwIfDisposed(); return opHandler.mod(this, x); }; Tensor.prototype.modStrict = function (x) { this.throwIfDisposed(); return opHandler.modStrict(this, x); }; Tensor.prototype.squaredDifference = function (x) { this.throwIfDisposed(); return opHandler.squaredDifference(this, x); }; Tensor.prototype.squaredDifferenceStrict = function (x) { this.throwIfDisposed(); return opHandler.squaredDifferenceStrict(this, x); }; Tensor.prototype.transpose = function (perm) { this.throwIfDisposed(); return opHandler.transpose(this, perm); }; Tensor.prototype.notEqual = function (x) { this.throwIfDisposed(); return opHandler.notEqual(this, x); }; Tensor.prototype.notEqualStrict = function (x) { this.throwIfDisposed(); return opHandler.notEqualStrict(this, x); }; Tensor.prototype.less = function (x) { this.throwIfDisposed(); return opHandler.less(this, x); }; Tensor.prototype.lessStrict = function (x) { this.throwIfDisposed(); return opHandler.lessStrict(this, x); }; Tensor.prototype.equal = function (x) { this.throwIfDisposed(); return opHandler.equal(this, x); }; Tensor.prototype.equalStrict = function (x) { this.throwIfDisposed(); return opHandler.equalStrict(this, x); }; Tensor.prototype.lessEqual = function (x) { this.throwIfDisposed(); return opHandler.lessEqual(this, x); }; Tensor.prototype.lessEqualStrict = function (x) { this.throwIfDisposed(); return opHandler.lessEqualStrict(this, x); }; Tensor.prototype.greater = function (x) { this.throwIfDisposed(); return opHandler.greater(this, x); }; Tensor.prototype.greaterStrict = function (x) { this.throwIfDisposed(); return opHandler.greaterStrict(this, x); }; Tensor.prototype.greaterEqual = function (x) { this.throwIfDisposed(); return opHandler.greaterEqual(this, x); }; Tensor.prototype.greaterEqualStrict = function (x) { this.throwIfDisposed(); return opHandler.greaterEqualStrict(this, x); }; Tensor.prototype.logicalAnd = function (x) { this.throwIfDisposed(); return opHandler.logicalAnd(this, x); }; Tensor.prototype.logicalOr = function (x) { this.throwIfDisposed(); return opHandler.logicalOr(this, x); }; Tensor.prototype.logicalNot = function () { this.throwIfDisposed(); return opHandler.logicalNot(this); }; Tensor.prototype.logicalXor = function (x) { this.throwIfDisposed(); return opHandler.logicalXor(this, x); }; Tensor.prototype.where = function (condition, x) { this.throwIfDisposed(); return opHandler.where(condition, this, x); }; Tensor.prototype.neg = function () { this.throwIfDisposed(); return opHandler.neg(this); }; Tensor.prototype.ceil = function () { this.throwIfDisposed(); return opHandler.ceil(this); }; Tensor.prototype.floor = function () { this.throwIfDisposed(); return opHandler.floor(this); }; Tensor.prototype.sign = function () { this.throwIfDisposed(); return opHandler.sign(this); }; Tensor.prototype.exp = function () { this.throwIfDisposed(); return opHandler.exp(this); }; Tensor.prototype.expm1 = function () { this.throwIfDisposed(); return opHandler.expm1(this); }; Tensor.prototype.log = function () { this.throwIfDisposed(); return opHandler.log(this); }; Tensor.prototype.log1p = function () { this.throwIfDisposed(); return opHandler.log1p(this); }; Tensor.prototype.sqrt = function () { this.throwIfDisposed(); return opHandler.sqrt(this); }; Tensor.prototype.rsqrt = function () { this.throwIfDisposed(); return opHandler.rsqrt(this); }; Tensor.prototype.square = function () { this.throwIfDisposed(); return opHandler.square(this); }; Tensor.prototype.reciprocal = function () { this.throwIfDisposed(); return opHandler.reciprocal(this); }; Tensor.prototype.abs = function () { this.throwIfDisposed(); return opHandler.abs(this); }; Tensor.prototype.clipByValue = function (min, max) { this.throwIfDisposed(); return opHandler.clipByValue(this, min, max); }; Tensor.prototype.relu = function () { this.throwIfDisposed(); return opHandler.relu(this); }; Tensor.prototype.elu = function () { this.throwIfDisposed(); return opHandler.elu(this); }; Tensor.prototype.selu = function () { this.throwIfDisposed(); return opHandler.selu(this); }; Tensor.prototype.leakyRelu = function (alpha) { if (alpha === void 0) { alpha = 0.2; } this.throwIfDisposed(); return opHandler.leakyRelu(this, alpha); }; Tensor.prototype.prelu = function (alpha) { this.throwIfDisposed(); return opHandler.prelu(this, alpha); }; Tensor.prototype.sigmoid = function () { this.throwIfDisposed(); return opHandler.sigmoid(this); }; Tensor.prototype.logSigmoid = function () { this.throwIfDisposed(); return opHandler.logSigmoid(this); }; Tensor.prototype.softplus = function () { this.throwIfDisposed(); return opHandler.softplus(this); }; Tensor.prototype.zerosLike = function () { this.throwIfDisposed(); return opHandler.zerosLike(this); }; Tensor.prototype.onesLike = function () { this.throwIfDisposed(); return opHandler.onesLike(this); }; Tensor.prototype.sin = function () { this.throwIfDisposed(); return opHandler.sin(this); }; Tensor.prototype.cos = function () { this.throwIfDisposed(); return opHandler.cos(this); }; Tensor.prototype.tan = function () { this.throwIfDisposed(); return opHandler.tan(this); }; Tensor.prototype.asin = function () { this.throwIfDisposed(); return opHandler.asin(this); }; Tensor.prototype.acos = function () { this.throwIfDisposed(); return opHandler.acos(this); }; Tensor.prototype.atan = function () { this.throwIfDisposed(); return opHandler.atan(this); }; Tensor.prototype.sinh = function () { this.throwIfDisposed(); return opHandler.sinh(this); }; Tensor.prototype.cosh = function () { this.throwIfDisposed(); return opHandler.cosh(this); }; Tensor.prototype.tanh = function () { this.throwIfDisposed(); return opHandler.tanh(this); }; Tensor.prototype.asinh = function () { this.throwIfDisposed(); return opHandler.asinh(this); }; Tensor.prototype.acosh = function () { this.throwIfDisposed(); return opHandler.acosh(this); }; Tensor.prototype.atanh = function () { this.throwIfDisposed(); return opHandler.atanh(this); }; Tensor.prototype.erf = function () { this.throwIfDisposed(); return opHandler.erf(this); }; Tensor.prototype.round = function () { this.throwIfDisposed(); return opHandler.round(this); }; Tensor.prototype.step = function (alpha) { if (alpha === void 0) { alpha = 0.0; } this.throwIfDisposed(); return opHandler.step(this, alpha); }; Tensor.prototype.softmax = function (dim) { if (dim === void 0) { dim = -1; } this.throwIfDisposed(); return opHandler.softmax(this, dim); }; Tensor.prototype.logSoftmax = function (axis) { if (axis === void 0) { axis = -1; } this.throwIfDisposed(); return opHandler.logSoftmax(this, axis); }; Tensor.prototype.resizeBilinear = function (newShape2D, alignCorners) { if (alignCorners === void 0) { alignCorners = false; } this.throwIfDisposed(); return opHandler.image.resizeBilinear(this, newShape2D, alignCorners); }; Tensor.prototype.resizeNearestNeighbor = function (newShape2D, alignCorners) { if (alignCorners === void 0) { alignCorners = false; } this.throwIfDisposed(); return opHandler.image.resizeNearestNeighbor(this, newShape2D, alignCorners); }; Tensor.prototype.conv1d = function (filter, stride, pad, dataFormat, dilation, dimRoundingMode) { if (dataFormat === void 0) { dataFormat = 'NWC'; } if (dilation === void 0) { dilation = 1; } this.throwIfDisposed(); return opHandler.conv1d(this, filter, stride, pad, dataFormat, dilation, dimRoundingMode); }; Tensor.prototype.conv2d = function (filter, strides, pad, dataFormat, dilations, dimRoundingMode) { if (dataFormat === void 0) { dataFormat = 'NHWC'; } if (dilations === void 0) { dilations = [1, 1]; } this.throwIfDisposed(); return opHandler.conv2d(this, filter, strides, pad, dataFormat, dilations, dimRoundingMode); }; Tensor.prototype.conv2dTranspose = function (filter, outputShape, strides, pad, dimRoundingMode) { this.throwIfDisposed(); return opHandler.conv2dTranspose(this, filter, outputShape, strides, pad, dimRoundingMode); }; Tensor.prototype.depthwiseConv2D = function (filter, strides, pad, dataFormat, dilations, dimRoundingMode) { if (dataFormat === void 0) { dataFormat = 'NHWC'; } if (dilations === void 0) { dilations = [1, 1]; } this.throwIfDisposed(); return opHandler.depthwiseConv2d(this, filter, strides, pad, dataFormat, dilations, dimRoundingMode); }; Tensor.prototype.separableConv2d = function (depthwiseFilter, pointwiseFilter, strides, pad, dilation, dataFormat) { if (dilation === void 0) { dilation = [1, 1]; } if (dataFormat === void 0) { dataFormat = 'NHWC'; } this.throwIfDisposed(); return opHandler.separableConv2d(this, depthwiseFilter, pointwiseFilter, strides, pad, dilation, dataFormat); }; Tensor.prototype.avgPool = function (filterSize, strides, pad, dimRoundingMode) { this.throwIfDisposed(); return opHandler.avgPool(this, filterSize, strides, pad, dimRoundingMode); }; Tensor.prototype.maxPool = function (filterSize, strides, pad, dimRoundingMode) { this.throwIfDisposed(); return opHandler.maxPool(this, filterSize, strides, pad, dimRoundingMode); }; Tensor.prototype.localResponseNormalization = function (radius, bias, alpha, beta) { if (radius === void 0) { radius = 5; } if (bias === void 0) { bias = 1; } if (alpha === void 0) { alpha = 1; } if (beta === void 0) { beta = 0.5; } return opHandler.localResponseNormalization(this, radius, bias, alpha, beta); }; Tensor.prototype.pool = function (windowShape, poolingType, padding, dilationRate, strides) { this.throwIfDisposed(); return opHandler.pool(this, windowShape, poolingType, padding, dilationRate, strides); }; Tensor.prototype.variable = function (trainable, name, dtype) { if (trainable === void 0) { trainable = true; } this.throwIfDisposed(); return Variable.variable(this, trainable, name, dtype); }; Tensor.prototype.unsortedSegmentSum = function (segmentIds, numSegments) { this.throwIfDisposed(); return opHandler.unsortedSegmentSum(this, segmentIds, numSegments); }; Tensor.prototype.batchToSpaceND = function (blockShape, crops) { this.throwIfDisposed(); return opHandler.batchToSpaceND(this, blockShape, crops); }; Tensor.prototype.spaceToBatchND = function (blockShape, paddings) { this.throwIfDisposed(); return opHandler.spaceToBatchND(this, blockShape, paddings); }; Tensor.prototype.topk = function (k, sorted) { if (k === void 0) { k = 1; } if (sorted === void 0) { sorted = true; } this.throwIfDisposed(); return opHandler.topk(this, k, sorted); }; Tensor.prototype.stridedSlice = function (begin, end, strides, beginMask, endMask, ellipsisMask, newAxisMask, shrinkAxisMask) { if (beginMask === void 0) { beginMask = 0; } if (endMask === void 0) { endMask = 0; } if (ellipsisMask === void 0) { ellipsisMask = 0; } if (newAxisMask === void 0) { newAxisMask = 0; } if (shrinkAxisMask === void 0) { shrinkAxisMask = 0; } this.throwIfDisposed(); return opHandler.stridedSlice(this, begin, end, strides, beginMask, endMask, ellipsisMask, newAxisMask, shrinkAxisMask); }; Tensor.prototype.depthToSpace = function (blockSize, dataFormat) { this.throwIfDisposed(); return opHandler.depthToSpace(this, blockSize, dataFormat); }; Tensor.prototype.fft = function () { this.throwIfDisposed(); return opHandler.spectral.fft(this); }; Tensor.prototype.ifft = function () { this.throwIfDisposed(); return opHandler.spectral.ifft(this); }; Tensor.prototype.rfft = function () { this.throwIfDisposed(); return opHandler.spectral.rfft(this); }; Tensor.prototype.irfft = function () { this.throwIfDisposed(); return opHandler.spectral.irfft(this); }; return Tensor; }()); Object.defineProperty(Tensor, Symbol.hasInstance, { value: function (instance) { return !!instance && instance.dataId != null && instance.shape != null && instance.dtype != null; } }); var Variable = (function (_super) { __extends(Variable, _super); function Variable(initialValue, trainable, name) { if (trainable === void 0) { trainable = true; } var _this = _super.call(this, initialValue.shape, initialValue.dtype, null, initialValue.dataId) || this; _this.trainable = trainable; _this.name = name; if (_this.name == null) { _this.name = trackerFn().nextVariableId().toString(); } try { trackerFn().registerVariable(_this); } catch (ex) { trackerFn().disposeTensor(_this); throw ex; } return _this; } Variable.variable = function (initialValue, trainable, name, dtype) { if (trainable === void 0) { trainable = true; } if (dtype != null && dtype !== initialValue.dtype) { initialValue = initialValue.asType(dtype); } return new Variable(initialValue, trainable, name); }; Variable.prototype.assign = function (newValue) { if (newValue.dtype !== this.dtype) { throw new Error("dtype of the new value (" + newValue.dtype + ") and " + ("previous value (" + this.dtype + ") must match")); } if (!arraysEqual(newValue.shape, this.shape)) { throw new Error("shape of the new value (" + newValue.shape + ") and " + ("previous value (" + this.shape + ") must match")); } trackerFn().disposeTensor(this); this.dataId = newValue.dataId; trackerFn().registerTensor(this); }; return Variable; }(Tensor)); Object.defineProperty(Variable, Symbol.hasInstance, { value: function (instance) { return instance instanceof Tensor && instance.assign != null && instance.assign instanceof Function; } }); var variable = Variable.variable; function getFilteredNodesXToY(tape, xs, y) { var tensorsFromX = {}; var nodesFromX = {}; for (var i = 0; i < xs.length; i++) { tensorsFromX[xs[i].id] = true; } for (var i = 0; i < tape.length; i++) { var node = tape[i]; var nodeInputs = node.inputs; for (var inputName in nodeInputs) { var input = nodeInputs[inputName]; var anyInputFromX = false; for (var j = 0; j < xs.length; j++) { if (tensorsFromX[input.id]) { node.outputs.forEach(function (output) { return tensorsFromX[output.id] = true; }); anyInputFromX = true; nodesFromX[node.id] = true; break; } } if (anyInputFromX) { break; } } } var tensorsLeadToY = {}; tensorsLeadToY[y.id] = true; var nodesToY = {}; for (var i = tape.length - 1; i >= 0; i--) { var node = tape[i]; var nodeInputs = node.inputs; for (var j = 0; j < node.outputs.length; j++) { if (tensorsLeadToY[node.outputs[j].id]) { for (var inputName in nodeInputs) { tensorsLeadToY[nodeInputs[inputName].id] = true; nodesToY[node.id] = true; } break; } } } var filteredTape = []; for (var i = 0; i < tape.length; i++) { var node = tape[i]; if (nodesFromX[node.id] && nodesToY[node.id]) { var prunedInputs = {}; for (var inputName in node.inputs) { var nodeInput = node.inputs[inputName]; if (tensorsFromX[nodeInput.id]) { prunedInputs[inputName] = nodeInput; } } var prunedNode = Object.assign({}, node); prunedNode.inputs = prunedInputs; prunedNode.outputs = node.outputs; filteredTape.push(prunedNode); } } return filteredTape; } function backpropagateGradients(tensorAccumulatedGradientMap, filteredTape) { var _loop_1 = function (i) { var node = filteredTape[i]; var dys = []; node.outputs.forEach(function (o) { var gradTensor = tensorAccumulatedGradientMap[o.id]; if (gradTensor != null) { dys.push(gradTensor); } else { var dy = Tensor.make(o.shape, { values: makeZerosTypedArray(o.size, o.dtype) }, o.dtype); dys.push(dy); } }); if (node.gradient == null) { throw new Error("Cannot compute gradient: gradient function not found " + ("for " + node.name + ".")); } var inputGradients = node.gradient(node.outputs.length === 1 ? dys[0] : dys); for (var inputName in node.inputs) { if (!(inputName in inputGradients)) { throw new Error("Cannot backprop through input " + inputName + ". " + ("Available gradients found: " + Object.keys(inputGradients) + ".")); } var dx = inputGradients[inputName](); if (dx.dtype !== 'float32') { throw new Error("Error in gradient for op " + node.name + ". The gradient of input " + (inputName + " must have 'float32' dtype, but has '" + dx.dtype + "'")); } var x = node.inputs[inputName]; if (!arraysEqual(dx.shape, x.shape)) { throw new Error("Error in gradient for op " + node.name + ". The gradient of input " + ("'" + inputName + "' has shape '" + dx.shape + "', which does not match ") + ("the shape of the input '" + x.shape + "'")); } if (tensorAccumulatedGradientMap[x.id] == null) { tensorAccumulatedGradientMap[x.id] = dx; } else { var curGradient = tensorAccumulatedGradientMap[x.id]; tensorAccumulatedGradientMap[x.id] = curGradient.add(dx); curGradient.dispose(); } } }; for (var i = filteredTape.length - 1; i >= 0; i--) { _loop_1(i); } } (function (Rank) { Rank["R0"] = "R0"; Rank["R1"] = "R1"; Rank["R2"] = "R2"; Rank["R3"] = "R3"; Rank["R4"] = "R4"; Rank["R5"] = "R5"; Rank["R6"] = "R6"; })(exports.Rank || (exports.Rank = {})); var UpcastInt32AndMap; (function (UpcastInt32AndMap) { UpcastInt32AndMap["float32"] = "float32"; UpcastInt32AndMap["int32"] = "int32"; UpcastInt32AndMap["bool"] = "int32"; UpcastInt32AndMap["complex64"] = "complex64"; })(UpcastInt32AndMap || (UpcastInt32AndMap = {})); var UpcastBoolAndMap; (function (UpcastBoolAndMap) { UpcastBoolAndMap["float32"] = "float32"; UpcastBoolAndMap["int32"] = "int32"; UpcastBoolAndMap["bool"] = "bool"; UpcastBoolAndMap["complex64"] = "complex64"; })(UpcastBoolAndMap || (UpcastBoolAndMap = {})); var UpcastFloat32AndMap; (function (UpcastFloat32AndMap) { UpcastFloat32AndMap["float32"] = "float32"; UpcastFloat32AndMap["int32"] = "float32"; UpcastFloat32AndMap["bool"] = "float32"; UpcastFloat32AndMap["complex64"] = "complex64"; })(UpcastFloat32AndMap || (UpcastFloat32AndMap = {})); var UpcastComplex64AndMap; (function (UpcastComplex64AndMap) { UpcastComplex64AndMap["float32"] = "complex64"; UpcastComplex64AndMap["int32"] = "complex64"; UpcastComplex64AndMap["bool"] = "complex64"; UpcastComplex64AndMap["complex64"] = "complex64"; })(UpcastComplex64AndMap || (UpcastComplex64AndMap = {})); var upcastTypeMap = { 'float32': UpcastFloat32AndMap, 'int32': UpcastInt32AndMap, 'bool': UpcastBoolAndMap, 'complex64': UpcastComplex64AndMap }; function upcastType(typeA, typeB) { if (typeA === 'string' || typeB === 'string') { if (typeA === 'string' && typeB === 'string') { return 'string'; } throw new Error("Can not upcast " + typeA + " with " + typeB); } return upcastTypeMap[typeA][typeB]; } function sumOutType(type) { return upcastType(type, 'int32'); } function makeTypesMatch(a, b) { if (a.dtype === b.dtype) { return [a, b]; } var dtype = upcastType(a.dtype, b.dtype); return [a.cast(dtype), b.cast(dtype)]; } function assertTypesMatch(a, b) { assert(a.dtype === b.dtype, "The dtypes of the first(" + a.dtype + ") and" + (" second(" + b.dtype + ") input must match")); } function isTensorInList(tensor, tensorList) { for (var i = 0; i < tensorList.length; i++) { if (tensorList[i].id === tensor.id) { return true; } } return false; } function getTensorsInContainer(result) { var list = []; var seen = new Set(); walkTensorContainer(result, list, seen); return list; } function walkTensorContainer(container, list, seen) { if (container == null) { return; } if (container instanceof Tensor) { list.push(container); return; } if (!isIterable(container)) { return; } var iterable = container; for (var k in iterable) { var val = iterable[k]; if (!seen.has(val)) { seen.add(val); walkTensorContainer(val, list, seen); } } } function isIterable(obj) { return Array.isArray(obj) || typeof obj === 'object'; } var Engine = (function () { function Engine(backend, safeMode, debugMode) { this.backend = backend; this.safeMode = safeMode; this.debugMode = debugMode; this.registeredVariables = {}; this.nextTapeNodeId = 0; this.numBytes = 0; this.numTensors = 0; this.numStringTensors = 0; this.numDataBuffers = 0; this.profiling = false; this.gradientScopeCount = 0; this.customGradientDepth = 0; this.scopeStack = []; this.keepTensors = new Set(); this.tensorInfo = new WeakMap(); this.profiler = new Profiler(backend); this.activeProfile = { newBytes: 0, newTensors: 0, peakBytes: 0, kernels: [], result: null }; } Engine.prototype.moveData = function (dataId) { this.write(dataId, this.readSync(dataId)); }; Engine.prototype.tidy = function (nameOrFn, fn, gradMode) { var _this = this; if (gradMode === void 0) { gradMode = false; } var name = null; if (fn == null) { if (typeof nameOrFn !== 'function') { throw new Error('Please provide a function to tidy()'); } fn = nameOrFn; } else { if (typeof nameOrFn !== 'string' && !(nameOrFn instanceof String)) { throw new Error('When calling with two arguments, the first argument ' + 'to tidy() must be a string'); } if (typeof fn !== 'function') { throw new Error('When calling with two arguments, the 2nd argument ' + 'to tidy() must be a function'); } name = nameOrFn; } var result; return this.scopedRun(function () { return _this.startScope(name, gradMode); }, function () { return _this.endScope(result, gradMode); }, function () { result = fn(); if (result instanceof Promise) { console.error('Cannot return a Promise inside of tidy.'); } return result; }); }; Engine.prototype.scopedRun = function (start, end, f) { start(); try { var res = f(); end(); return res; } catch (ex) { end(); throw ex; } }; Engine.prototype.nextTensorId = function () { return Engine.nextTensorId++; }; Engine.prototype.nextVariableId = function () { return Engine.nextVariableId++; }; Engine.prototype.runKernel = function (forwardFunc, inputs, backwardsFunc) { var _this = this; var result; var saved = []; var saveFunc = function (x) { saved.push(x); return x; }; var scopeName = this.activeScope != null ? this.activeScope.name : ''; var startingBytecount = this.numBytes; var startingNumTensors = this.numTensors; this.scopedRun(function () { return _this.customGradientDepth++; }, function () { return _this.customGradientDepth--; }, function () { if (!_this.debugMode()) { result = forwardFunc(_this.backend, saveFunc); } else { result = _this.profiler.profileKernel(scopeName, function () { return forwardFunc(_this.backend, saveFunc); }); } }); if (this.shouldRecord()) { var tapeNode = { id: this.nextTapeNodeId++, name: scopeName, inputs: inputs, outputs: Array.isArray(result) ? result : [result] }; if (backwardsFunc != null) { tapeNode.gradient = (function (dy) { return backwardsFunc(dy, saved); }); } this.activeTape.push(tapeNode); } if (this.profiling) { this.activeProfile.kernels.push({ name: scopeName, bytesAdded: this.numBytes - startingBytecount, totalBytesSnapshot: this.numBytes, tensorsAdded: this.numTensors - startingNumTensors, totalTensorsSnapshot: this.numTensors, inputShapes: Object.keys(inputs).map(function (key) { return inputs[key].shape; }), outputShape: Array.isArray(result) ? result.map(function (item) { return item.shape; }) : result.shape }); } return result; }; Engine.prototype.registerTensor = function (a) { var refCount = this.tensorInfo.has(a.dataId) ? this.tensorInfo.get(a.dataId).refCount : 0; this.numTensors++; if (a.dtype === 'string') { this.numStringTensors++; } if (refCount === 0) { this.numDataBuffers++; var bytes = 0; if (a.dtype !== 'complex64' && a.dtype !== 'string') { bytes = sizeFromShape(a.shape) * bytesPerElement(a.dtype); } this.tensorInfo.set(a.dataId, { backend: this.backend, dtype: a.dtype, shape: a.shape, bytes: bytes, refCount: 0 }); this.numBytes += bytes; this.backend.register(a.dataId, a.shape, a.dtype); } this.tensorInfo.get(a.dataId).refCount++; if (!(a instanceof Variable)) { this.track(a); } }; Engine.prototype.registerVariable = function (v) { if (this.registeredVariables[v.name] != null) { throw new Error("Variable with name " + v.name + " was already registered"); } this.registeredVariables[v.name] = v; }; Engine.prototype.disposeTensor = function (a) { if (!this.tensorInfo.has(a.dataId)) { return; } if (this.keepTensors.has(a.id)) { this.keepTensors.delete(a.id); } this.numTensors--; if (a.dtype === 'string') { this.numStringTensors--; } var info = this.tensorInfo.get(a.dataId); var refCount = info.refCount; if (refCount <= 1) { if (a.dtype !== 'complex64') { this.numBytes -= info.bytes; } this.numDataBuffers--; info.backend.disposeData(a.dataId); this.tensorInfo.delete(a.dataId); } else { this.tensorInfo.get(a.dataId).refCount--; } }; Engine.prototype.disposeVariables = function () { for (var varName in this.registeredVariables) { var v = this.registeredVariables[varName]; this.disposeTensor(v); delete this.registeredVariables[varName]; } }; Engine.prototype.memory = function () { var info = this.backend.memory(); info.numTensors = this.numTensors; info.numDataBuffers = this.numDataBuffers; info.numBytes = this.numBytes; if (this.numStringTensors > 0) { info.unreliable = true; if (info.reasons == null) { info.reasons = []; } info.reasons.push('Memory usage by string tensors is approximate ' + '(2 bytes per character)'); } return info; }; Engine.prototype.profile = function (query) { return __awaiter(this, void 0, void 0, function () { var startBytes, startNumTensors; return __generator(this, function (_a) { this.profiling = true; startBytes = this.numBytes; startNumTensors = this.numTensors; this.activeProfile.kernels = []; this.activeProfile.result = query(); this.profiling = false; this.activeProfile.peakBytes = Math.max.apply(Math, this.activeProfile.kernels.map(function (d) { return d.totalBytesSnapshot; })); this.activeProfile.newBytes = this.numBytes - startBytes; this.activeProfile.newTensors = this.numTensors - startNumTensors; return [2, this.activeProfile]; }); }); }; Engine.prototype.shouldRecord = function () { return this.activeTape != null && this.customGradientDepth === 0; }; Engine.prototype.addTapeNode = function (inputs, result, gradientsFunc) { var inputsMap = {}; inputs.forEach(function (input, idx) { inputsMap[idx] = input; }); var gradient = function (dy) { var res = gradientsFunc(dy); var resMap = {}; res.forEach(function (r, idx) { resMap[idx] = function () { return r; }; }); return resMap; }; var tapeNode = { id: this.nextTapeNodeId++, name: this.activeScope.name, inputs: inputsMap, outputs: [result], gradient: gradient }; this.activeTape.push(tapeNode); }; Engine.prototype.keep = function (result) { if (this.scopeStack.length === 1 && this.safeMode) { throw new Error('Safe mode is ON. Enclose all tensor operations inside tf.tidy(): ' + 'tf.tidy(() => {...}) to avoid memory leaks.'); } this.keepTensors.add(result.id); return result; }; Engine.prototype.startScope = function (name, gradientsMode) { if (gradientsMode === void 0) { gradientsMode = false; } if (gradientsMode && this.gradientScopeCount === 0) { this.activeTape = []; } if (gradientsMode) { this.gradientScopeCount++; } var scopeInfo = { track: [], name: 'unnamed scope' }; if (name) { scopeInfo.name = name; } this.scopeStack.push(scopeInfo); this.activeScope = scopeInfo; }; Engine.prototype.endScope = function (result, gradientsMode) { var _this = this; if (gradientsMode === void 0) { gradientsMode = false; } if (gradientsMode) { this.gradientScopeCount--; if (this.gradientScopeCount === 0) { this.activeTape = null; } } var tensorsToKeep = new Set(this.keepTensors); var tensorsToTrackInParent = getTensorsInContainer(result); tensorsToTrackInParent.forEach(function (tensor) { return tensorsToKeep.add(tensor.id); }); for (var i = 0; i < this.activeScope.track.length; i++) { var tensor = this.activeScope.track[i]; if (tensorsToKeep.has(tensor.id)) { continue; } if (this.activeTape != null) { tensorsToTrackInParent.push(tensor); } else { tensor.dispose(); } } var oldScope = this.scopeStack.pop(); this.activeScope = this.scopeStack.length === 0 ? null : this.scopeStack[this.scopeStack.length - 1]; tensorsToTrackInParent.forEach(function (tensor) { if (!_this.keepTensors.has(tensor.id) && isTensorInList(tensor, oldScope.track)) { _this.track(tensor); } }); }; Engine.prototype.gradients = function (f, xs, dy, allowNoGradients) { var _this = this; if (allowNoGradients === void 0) { allowNoGradients = false; } assert(xs.length > 0, 'gradients() received an empty list of xs.'); if (dy != null && dy.dtype !== 'float32') { throw new Error("dy must have 'float32' dtype, but has '" + dy.dtype + "'"); } return this.tidy('gradients', function () { var y = f(); assert(y instanceof Tensor, 'The result y returned by f() must be a tensor.'); var filteredTape = getFilteredNodesXToY(_this.activeTape, xs, y); if (!allowNoGradients && filteredTape.length === 0 && xs.length > 0) { throw new Error('Cannot compute gradient of y=f(x) with respect to x. Make sure ' + 'that the f you passed encloses all operations that lead from x ' + 'to y.'); } var accumulatedGradientMap = {}; accumulatedGradientMap[y.id] = (dy == null) ? ones(y.shape) : dy; backpropagateGradients(accumulatedGradientMap, filteredTape); var grads = xs.map(function (x) { return accumulatedGradientMap[x.id]; }); return { value: y, grads: grads }; }, true); }; Engine.prototype.customGrad = function (f) { var _this = this; assert(isFunction(f), 'The f passed in customGrad(f) must be a function.'); return function () { var inputs = []; for (var _i = 0; _i < arguments.length; _i++) { inputs[_i] = arguments[_i]; } assert(inputs.every(function (t) { return t instanceof Tensor; }), 'The args passed in customGrad(f)(x1, x2,...) must all be tensors'); var gradientsFunc; var result; _this.scopedRun(function () { return _this.customGradientDepth++; }, function () { return _this.customGradientDepth--; }, function () { var gradientsMode = true; result = _this.tidy(f.name, function () { var _a = f.apply(void 0, inputs), value = _a.value, gradFunc = _a.gradFunc; assert(value instanceof Tensor, 'The function f passed in customGrad(f) must return an ' + 'object where `obj.value` is a tensor'); assert(isFunction(gradFunc), 'The function f passed in customGrad(f) must return an ' + 'object where `obj.gradFunc` is a function.'); gradientsFunc = gradFunc; return value; }, gradientsMode); }); if (_this.shouldRecord()) { var gradFunc = function (dy) { var res = gradientsFunc(dy); var grads = Array.isArray(res) ? res : [res]; assert(grads.length === inputs.length, 'The function f passed in customGrad(f) must return an object ' + 'where `obj.gradFunc` is a function that returns the same ' + 'number of tensors as inputs passed to f(...).'); assert(grads.every(function (t) { return t instanceof Tensor; }), 'The function f passed in customGrad(f) must return an object ' + 'where `obj.gradFunc` is a function that returns a list of ' + 'only tensors.'); return grads; }; _this.addTapeNode(inputs, result, gradFunc); } return result; }; }; Engine.prototype.write = function (dataId, values) { var info = this.tensorInfo.get(dataId); if (info.dtype === 'string') { var newBytes = bytesFromStringArray(values); this.numBytes += newBytes - info.bytes; info.bytes = newBytes; } if (this.backend !== info.backend) { info.backend.disposeData(dataId); info.backend = this.backend; this.backend.register(dataId, info.shape, info.dtype); } this.backend.write(dataId, values); }; Engine.prototype.readSync = function (dataId) { var info = this.tensorInfo.get(dataId); return info.backend.readSync(dataId); }; Engine.prototype.read = function (dataId) { var info = this.tensorInfo.get(dataId); return info.backend.read(dataId); }; Engine.prototype.fromPixels = function (pixels, numChannels) { return this.backend.fromPixels(pixels, numChannels); }; Engine.prototype.time = function (query) { return __awaiter(this, void 0, void 0, function () { var start, timingInfo; return __generator(this, function (_a) { switch (_a.label) { case 0: start = now(); return [4, this.backend.time(query)]; case 1: timingInfo = _a.sent(); timingInfo.wallMs = now() - start; return [2, timingInfo]; } }); }); }; Engine.prototype.track = function (result) { if (this.scopeStack.length === 1 && this.safeMode) { throw new Error('Safe mode is ON. Enclose all tensor operations inside tf.tidy(): ' + 'tf.tidy(() => {op();...}); to avoid memory leaks.'); } if (this.activeScope != null) { this.activeScope.track.push(result); } return result; }; Engine.nextTensorId = 0; Engine.nextVariableId = 0; return Engine; }()); function ones(shape) { var values = makeOnesTypedArray(sizeFromShape(shape), 'float32'); return Tensor.make(shape, { values: values }); } var Type; (function (Type) { Type[Type["NUMBER"] = 0] = "NUMBER"; Type[Type["BOOLEAN"] = 1] = "BOOLEAN"; Type[Type["STRING"] = 2] = "STRING"; })(Type || (Type = {})); var URL_PROPERTIES = [ { name: 'DEBUG', type: Type.BOOLEAN }, { name: 'IS_BROWSER', type: Type.BOOLEAN }, { name: 'WEBGL_LAZILY_UNPACK', type: Type.BOOLEAN }, { name: 'WEBGL_CPU_FORWARD', type: Type.BOOLEAN }, { name: 'WEBGL_PACK', type: Type.BOOLEAN }, { name: 'WEBGL_PACK_BATCHNORMALIZATION', type: Type.BOOLEAN }, { name: 'WEBGL_PACK_CLIP', type: Type.BOOLEAN }, { name: 'WEBGL_PACK_DEPTHWISECONV', type: Type.BOOLEAN }, { name: 'WEBGL_PACK_BINARY_OPERATIONS', type: Type.BOOLEAN }, { name: 'WEBGL_PACK_ARRAY_OPERATIONS', type: Type.BOOLEAN }, { name: 'WEBGL_CONV_IM2COL', type: Type.BOOLEAN }, { name: 'WEBGL_MAX_TEXTURE_SIZE', type: Type.NUMBER }, { name: 'WEBGL_NUM_MB_BEFORE_PAGING', type: Type.NUMBER }, { name: 'WEBGL_MAX_TEXTURES_IN_SHADER', type: Type.NUMBER }, { name: 'WEBGL_DISJOINT_QUERY_TIMER_EXTENSION_VERSION', type: Type.NUMBER }, { name: 'WEBGL_DISJOINT_QUERY_TIMER_EXTENSION_RELIABLE', type: Type.BOOLEAN }, { name: 'WEBGL_VERSION', type: Type.NUMBER }, { name: 'WEBGL_RENDER_FLOAT32_ENABLED', type: Type.BOOLEAN }, { name: 'WEBGL_DOWNLOAD_FLOAT_ENABLED', type: Type.BOOLEAN }, { name: 'WEBGL_FENCE_API_ENABLED', type: Type.BOOLEAN }, { name: 'WEBGL_SIZE_UPLOAD_UNIFORM', type: Type.NUMBER }, { name: 'BACKEND', type: Type.STRING }, { name: 'EPSILON', type: Type.NUMBER }, { name: 'PROD', type: Type.BOOLEAN }, { name: 'TENSORLIKE_CHECK_SHAPE_CONSISTENCY', type: Type.BOOLEAN }, ]; function isWebGLVersionEnabled(webGLVersion) { try { var gl = getWebGLContext(webGLVersion); if (gl != null) { return true; } } catch (e) { return false; } return false; } var MAX_TEXTURE_SIZE; var MAX_TEXTURES_IN_SHADER; function getWebGLMaxTextureSize(webGLVersion) { if (MAX_TEXTURE_SIZE == null) { var gl = getWebGLContext(webGLVersion); MAX_TEXTURE_SIZE = gl.getParameter(gl.MAX_TEXTURE_SIZE); } return MAX_TEXTURE_SIZE; } function getMaxTexturesInShader(webGLVersion) { if (MAX_TEXTURES_IN_SHADER == null) { var gl = getWebGLContext(webGLVersion); MAX_TEXTURES_IN_SHADER = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS); } return Math.min(16, MAX_TEXTURES_IN_SHADER); } function getWebGLDisjointQueryTimerVersion(webGLVersion) { if (webGLVersion === 0) { return 0; } var queryTimerVersion; var gl = getWebGLContext(webGLVersion); if (hasExtension(gl, 'EXT_disjoint_timer_query_webgl2') && webGLVersion === 2) { queryTimerVersion = 2; } else if (hasExtension(gl, 'EXT_disjoint_timer_query')) { queryTimerVersion = 1; } else { queryTimerVersion = 0; } return queryTimerVersion; } function isRenderToFloatTextureEnabled(webGLVersion) { if (webGLVersion === 0) { return false; } var gl = getWebGLContext(webGLVersion); if (webGLVersion === 1) { if (!hasExtension(gl, 'OES_texture_float')) { return false; } } else { if (!hasExtension(gl, 'EXT_color_buffer_float')) { return false; } } var isFrameBufferComplete = createFloatTextureAndBindToFramebuffer(gl, webGLVersion); return isFrameBufferComplete; } function isDownloadFloatTextureEnabled(webGLVersion) { if (webGLVersion === 0) { return false; } var gl = getWebGLContext(webGLVersion); if (webGLVersion === 1) { if (!hasExtension(gl, 'OES_texture_float')) { return false; } if (!hasExtension(gl, 'WEBGL_color_buffer_float')) { return false; } } else { if (!hasExtension(gl, 'EXT_color_buffer_float')) { return false; } } var isFrameBufferComplete = createFloatTextureAndBindToFramebuffer(gl, webGLVersion); return isFrameBufferComplete; } function isWebGLFenceEnabled(webGLVersion) { if (webGLVersion !== 2) { return false; } var gl = getWebGLContext(webGLVersion); var isEnabled = gl.fenceSync != null; return isEnabled; } function isChrome() { return typeof navigator !== 'undefined' && navigator != null && navigator.userAgent != null && /Chrome/.test(navigator.userAgent) && /Google Inc/.test(navigator.vendor); } var TENSORFLOWJS_FLAGS_PREFIX = 'tfjsflags'; function getFeaturesFromURL() { var features = {}; if (typeof window === 'undefined' || typeof window.location === 'undefined' || typeof window.location.search === 'undefined') { return features; } var urlParams = getQueryParams(window.location.search); if (TENSORFLOWJS_FLAGS_PREFIX in urlParams) { var urlFlags_1 = {}; var keyValues = urlParams[TENSORFLOWJS_FLAGS_PREFIX].split(','); keyValues.forEach(function (keyValue) { var _a = keyValue.split(':'), key = _a[0], value = _a[1]; urlFlags_1[key] = value; }); URL_PROPERTIES.forEach(function (urlProperty) { if (urlProperty.name in urlFlags_1) { console.log("Setting feature override from URL " + urlProperty.name + ": " + ("" + urlFlags_1[urlProperty.name])); if (urlProperty.type === Type.NUMBER) { features[urlProperty.name] = +urlFlags_1[urlProperty.name]; } else if (urlProperty.type === Type.BOOLEAN) { features[urlProperty.name] = urlFlags_1[urlProperty.name] === 'true'; } else if (urlProperty.type === Type.STRING) { features[urlProperty.name] = urlFlags_1[urlProperty.name]; } else { console.warn("Unknown URL param: " + urlProperty.name + "."); } } }); } return features; } function hasExtension(gl, extensionName) { var ext = gl.getExtension(extensionName); return ext != null; } function createFloatTextureAndBindToFramebuffer(gl, webGLVersion) { var frameBuffer = gl.createFramebuffer(); var texture = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, texture); var internalFormat = webGLVersion === 2 ? gl.RGBA32F : gl.RGBA; gl.texImage2D(gl.TEXTURE_2D, 0, internalFormat, 1, 1, 0, gl.RGBA, gl.FLOAT, null); gl.bindFramebuffer(gl.FRAMEBUFFER, frameBuffer); gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0); var isFrameBufferComplete = gl.checkFramebufferStatus(gl.FRAMEBUFFER) === gl.FRAMEBUFFER_COMPLETE; gl.bindTexture(gl.TEXTURE_2D, null); gl.bindFramebuffer(gl.FRAMEBUFFER, null); gl.deleteTexture(texture); gl.deleteFramebuffer(frameBuffer); return isFrameBufferComplete; } function getQueryParams(queryString) { var params = {}; queryString.replace(/[?&]([^=?&]+)(?:=([^&]*))?/g, function (s) { var t = []; for (var _i = 1; _i < arguments.length; _i++) { t[_i - 1] = arguments[_i]; } decodeParam(params, t[0], t[1]); return t.join('='); }); return params; } function decodeParam(params, name, value) { params[decodeURIComponent(name)] = decodeURIComponent(value || ''); } var BEFORE_PAGING_CONSTANT = 600; function getNumMBBeforePaging() { return (window.screen.height * window.screen.width * window.devicePixelRatio) * BEFORE_PAGING_CONSTANT / 1024; } var EPSILON_FLOAT16 = 1e-4; var TEST_EPSILON_FLOAT16 = 1e-1; var EPSILON_FLOAT32 = 1e-7; var TEST_EPSILON_FLOAT32 = 1e-3; var Environment = (function () { function Environment(features) { this.features = {}; this.registry = {}; if (features != null) { this.features = features; } if (this.get('DEBUG')) { console.warn('Debugging mode is ON. The output of every math call will ' + 'be downloaded to CPU and checked for NaNs. ' + 'This significantly impacts performance.'); } } Environment.setBackend = function (backendName, safeMode) { if (safeMode === void 0) { safeMode = false; } if (!(backendName in ENV.registry)) { throw new Error("Backend name '" + backendName + "' not found in registry"); } ENV.engine.backend = ENV.findBackend(backendName); ENV.backendName = backendName; }; Environment.getBackend = function () { ENV.initEngine(); return ENV.backendName; }; Environment.disposeVariables = function () { ENV.engine.disposeVariables(); }; Environment.memory = function () { return ENV.engine.memory(); }; Environment.profile = function (f) { return ENV.engine.profile(f); }; Environment.tidy = function (nameOrFn, fn) { return ENV.engine.tidy(nameOrFn, fn); }; Environment.dispose = function (container) { var tensors = getTensorsInContainer(container); tensors.forEach(function (tensor) { return tensor.dispose(); }); }; Environment.keep = function (result) { return ENV.engine.keep(result); }; Environment.time = function (f) { return ENV.engine.time(f); }; Environment.prototype.get = function (feature) { if (feature in this.features) { return this.features[feature]; } this.features[feature] = this.evaluateFeature(feature); return this.features[feature]; }; Environment.prototype.getFeatures = function () { return this.features; }; Environment.prototype.set = function (feature, value) { this.features[feature] = value; }; Environment.prototype.getBestBackendName = function () { var _this = this; if (Object.keys(this.registry).length === 0) { throw new Error('No backend found in registry.'); } var sortedBackends = Object.keys(this.registry) .map(function (name) { return { name: name, entry: _this.registry[name] }; }) .sort(function (a, b) { return b.entry.priority - a.entry.priority; }); return sortedBackends[0].name; }; Environment.prototype.evaluateFeature = function (feature) { if (feature === 'DEBUG') { return false; } else if (feature === 'IS_BROWSER') { return typeof window !== 'undefined'; } else if (feature === 'IS_NODE') { return (typeof process !== 'undefined') && (typeof process.versions !== 'undefined') && (typeof process.versions.node !== 'undefined'); } else if (feature === 'IS_CHROME') { return isChrome(); } else if (feature === 'WEBGL_CPU_FORWARD') { return true; } else if (feature === 'WEBGL_PACK') { return false; } else if (feature === 'WEBGL_PACK_BATCHNORMALIZATION') { return this.get('WEBGL_PACK'); } else if (feature === 'WEBGL_PACK_CLIP') { return this.get('WEBGL_PACK'); } else if (feature === 'WEBGL_PACK_DEPTHWISECONV') { return this.get('WEBGL_PACK'); } else if (feature === 'WEBGL_PACK_BINARY_OPERATIONS') { return this.get('WEBGL_PACK'); } else if (feature === 'WEBGL_PACK_ARRAY_OPERATIONS') { return this.get('WEBGL_PACK'); } else if (feature === 'WEBGL_LAZILY_UNPACK') { return this.get('WEBGL_PACK'); } else if (feature === 'WEBGL_CONV_IM2COL') { return this.get('WEBGL_PACK'); } else if (feature === 'WEBGL_NUM_MB_BEFORE_PAGING') { if (this.get('PROD') || !this.get('IS_BROWSER')) { return Number.POSITIVE_INFINITY; } return getNumMBBeforePaging(); } else if (feature === 'WEBGL_MAX_TEXTURE_SIZE') { return getWebGLMaxTextureSize(this.get('WEBGL_VERSION')); } else if (feature === 'WEBGL_MAX_TEXTURES_IN_SHADER') { return getMaxTexturesInShader(this.get('WEBGL_VERSION')); } else if (feature === 'IS_TEST') { return false; } else if (feature === 'BACKEND') { return this.getBestBackendName(); } else if (feature === 'WEBGL_DISJOINT_QUERY_TIMER_EXTENSION_VERSION') { var webGLVersion = this.get('WEBGL_VERSION'); if (webGLVersion === 0) { return 0; } return getWebGLDisjointQueryTimerVersion(webGLVersion); } else if (feature === 'WEBGL_DISJOINT_QUERY_TIMER_EXTENSION_RELIABLE') { return this.get('WEBGL_DISJOINT_QUERY_TIMER_EXTENSION_VERSION') > 0 && !isMobile(); } else if (feature === 'HAS_WEBGL') { return this.get('WEBGL_VERSION') > 0; } else if (feature === 'WEBGL_VERSION') { if (isWebGLVersionEnabled(2)) { return 2; } else if (isWebGLVersionEnabled(1)) { return 1; } return 0; } else if (feature === 'WEBGL_RENDER_FLOAT32_ENABLED') { return isRenderToFloatTextureEnabled(this.get('WEBGL_VERSION')); } else if (feature === 'WEBGL_DOWNLOAD_FLOAT_ENABLED') { return isDownloadFloatTextureEnabled(this.get('WEBGL_VERSION')); } else if (feature === 'WEBGL_FENCE_API_ENABLED') { return isWebGLFenceEnabled(this.get('WEBGL_VERSION')); } else if (feature === 'WEBGL_SIZE_UPLOAD_UNIFORM') { var useUniforms = this.get('WEBGL_RENDER_FLOAT32_ENABLED'); return useUniforms ? 4 : 0; } else if (feature === 'TEST_EPSILON') { return this.backend.floatPrecision() === 32 ? TEST_EPSILON_FLOAT32 : TEST_EPSILON_FLOAT16; } else if (feature === 'EPSILON') { return this.backend.floatPrecision() === 32 ? EPSILON_FLOAT32 : EPSILON_FLOAT16; } else if (feature === 'PROD') { return false; } else if (feature === 'TENSORLIKE_CHECK_SHAPE_CONSISTENCY') { return !this.get('PROD'); } throw new Error("Unknown feature " + feature + "."); }; Environment.prototype.setFeatures = function (features) { this.features = Object.assign({}, features); }; Environment.prototype.reset = function () { this.features = getFeaturesFromURL(); if (this.globalEngine != null) { this.globalEngine = null; } }; Object.defineProperty(Environment.prototype, "backend", { get: function () { return this.engine.backend; }, enumerable: true, configurable: true }); Environment.prototype.findBackend = function (name) { if (!(name in this.registry)) { return null; } return this.registry[name].backend; }; Environment.prototype.registerBackend = function (name, factory, priority, setTensorTrackerFn) { var _this = this; if (priority === void 0) { priority = 1; } if (name in this.registry) { console.warn(name + " backend was already registered. Reusing existing backend"); if (setTensorTrackerFn != null) { setTensorTrackerFn(function () { return _this.engine; }); } return false; } try { var backend = factory(); backend.setDataMover({ moveData: function (dataId) { return _this.engine.moveData(dataId); } }); this.registry[name] = { backend: backend, priority: priority }; return true; } catch (err) { console.warn("Registration of backend " + name + " failed"); console.warn(err.stack || err.message); return false; } }; Environment.prototype.removeBackend = function (name) { if (!(name in this.registry)) { throw new Error(name + " backend not found in registry"); } this.registry[name].backend.dispose(); delete this.registry[name]; }; Object.defineProperty(Environment.prototype, "engine", { get: function () { this.initEngine(); return this.globalEngine; }, enumerable: true, configurable: true }); Environment.prototype.initEngine = function () { var _this = this; if (this.globalEngine == null) { this.backendName = this.get('BACKEND'); var backend = this.findBackend(this.backendName); this.globalEngine = new Engine(backend, false, function () { return _this.get('DEBUG'); }); } }; return Environment; }()); function getGlobalNamespace() { var ns; if (typeof (window) !== 'undefined') { ns = window; } else if (typeof (process) !== 'undefined') { ns = process; } else { throw new Error('Could not find a global object'); } return ns; } function getOrMakeEnvironment() { var ns = getGlobalNamespace(); if (ns.ENV == null) { ns.ENV = new Environment(getFeaturesFromURL()); setTensorTracker(function () { return ns.ENV.engine; }); } return ns.ENV; } function enableProdMode() { ENV.set('PROD', true); } var ENV = getOrMakeEnvironment(); var environment = /*#__PURE__*/Object.freeze({ EPSILON_FLOAT16: EPSILON_FLOAT16, EPSILON_FLOAT32: EPSILON_FLOAT32, Environment: Environment, enableProdMode: enableProdMode, ENV: ENV }); function grad(f) { assert(isFunction(f), 'The f passed in grad(f) must be a function'); return function (x, dy) { assert(x instanceof Tensor, 'The x passed in grad(f)(x) must be a tensor'); assert(dy == null || dy instanceof Tensor, 'The dy passed in grad(f)(x, dy) must be a tensor'); return ENV.engine.tidy(function () { var _a = ENV.engine.gradients(function () { return f(x); }, [x], dy), value = _a.value, grads = _a.grads; if (dy != null) { assertShapesMatch(value.shape, dy.shape, 'The shape of dy passed in grad(f)(x, dy) must match the shape ' + 'returned by f(x)'); } checkGrads(grads); return grads[0]; }); }; } function grads(f) { assert(isFunction(f), 'The f passed in grads(f) must be a function'); return function (args, dy) { assert(Array.isArray(args) && args.every(function (arg) { return arg instanceof Tensor; }), 'The args passed in grads(f)(args) must be an array of tensors'); assert(dy == null || dy instanceof Tensor, 'The dy passed in grads(f)(args, dy) must be a tensor'); return ENV.engine.tidy(function () { var _a = ENV.engine.gradients(function () { return f.apply(void 0, args); }, args, dy), value = _a.value, grads = _a.grads; if (dy != null) { assertShapesMatch(value.shape, dy.shape, 'The shape of dy passed in grads(f)([x1,...], dy) must ' + 'match the shape returned by f([x1,...])'); } checkGrads(grads); return grads; }); }; } function valueAndGrad(f) { assert(isFunction(f), 'The f passed in valueAndGrad(f) must be a function'); return function (x, dy) { assert(x instanceof Tensor, 'The x passed in valueAndGrad(f)(x) must be a tensor'); assert(dy == null || dy instanceof Tensor, 'The dy passed in valueAndGrad(f)(x, dy) must be a tensor'); var _a = ENV.engine.gradients(function () { return f(x); }, [x], dy), grads = _a.grads, value = _a.value; checkGrads(grads); return { grad: grads[0], value: value }; }; } function valueAndGrads(f) { assert(isFunction(f), 'The f passed in valueAndGrads(f) must be a function'); return function (args, dy) { assert(Array.isArray(args) && args.every(function (arg) { return arg instanceof Tensor; }), 'The args passed in valueAndGrads(f)(args) must be array of tensors'); assert(dy == null || dy instanceof Tensor, 'The dy passed in valueAndGrads(f)(args, dy) must be a tensor'); var res = ENV.engine.gradients(function () { return f.apply(void 0, args); }, args, dy); if (dy != null) { assertShapesMatch(res.value.shape, dy.shape, 'The shape of dy passed in valueAndGrads(f)([x1,...], dy) must ' + 'match the shape returned by f([x1,...])'); } checkGrads(res.grads); return res; }; } function variableGrads(f, varList) { assert(isFunction(f), 'The f passed in variableGrads(f) must be a function'); assert(varList == null || Array.isArray(varList) && varList.every(function (v) { return v instanceof Variable; }), 'The varList passed in variableGrads(f, varList) must be an array ' + 'of variables'); if (varList == null) { varList = []; for (var varName in ENV.engine.registeredVariables) { varList.push(ENV.engine.registeredVariables[varName]); } } var originalVarCount = varList.length; varList = varList.filter(function (variable$$1) { return variable$$1.trainable; }); assert(varList.length > 0, "variableGrads() expects at least one of the input variables to be " + ("trainable, but none of the " + originalVarCount + " variables is ") + "trainable."); var allowNoGradients = true; var _a = ENV.engine.gradients(f, varList, null, allowNoGradients), value = _a.value, grads = _a.grads; assert(grads.some(function (g) { return g != null; }), 'Cannot find a connection between any variable and the result of the ' + 'loss function y=f(x). Please make sure the operations that use ' + 'variables are inside the function f passed to minimize().'); assert(value.rank === 0, "The f passed in variableGrads(f) must return a scalar, but it " + ("returned a rank-" + value.rank + " tensor")); var namedGrads = {}; varList.forEach(function (v, i) { if (grads[i] != null) { namedGrads[v.name] = grads[i]; } }); return { value: value, grads: namedGrads }; } function customGrad(f) { return ENV.engine.customGrad(f); } function checkGrads(grads) { var numNullGradients = grads.filter(function (g) { return g == null; }).length; if (numNullGradients > 0) { throw new Error("Cannot compute gradient of y=f(x) with respect to x. Make sure that\n the f you passed encloses all operations that lead from x to y."); } } var tidy = Environment.tidy; var keep = Environment.keep; var dispose = Environment.dispose; var time = Environment.time; var profile = Environment.profile; function warn() { var msg = []; for (var _i = 0; _i < arguments.length; _i++) { msg[_i] = arguments[_i]; } if (!ENV.get('IS_TEST')) { console.warn.apply(console, msg); } } function getReshaped(inputShape, blockShape, prod, batchToSpace) { if (batchToSpace === void 0) { batchToSpace = true; } var reshaped = []; if (batchToSpace) { reshaped = reshaped.concat(blockShape.slice(0)); reshaped.push(inputShape[0] / prod); reshaped = reshaped.concat(inputShape.slice(1)); } else { reshaped = reshaped.concat(inputShape[0]); var spatialLength = blockShape.length; for (var i = 0; i < spatialLength; ++i) { reshaped = reshaped.concat([inputShape[i + 1] / blockShape[i], blockShape[i]]); } reshaped = reshaped.concat(inputShape.slice(spatialLength + 1)); } return reshaped; } function getPermuted(reshapedRank, blockShapeRank, batchToSpace) { if (batchToSpace === void 0) { batchToSpace = true; } var permuted = []; if (batchToSpace) { permuted.push(blockShapeRank); for (var i = blockShapeRank + 1; i < reshapedRank; ++i) { if (i <= 2 * blockShapeRank) { permuted.push(i); permuted.push(i - (blockShapeRank + 1)); } else { permuted.push(i); } } } else { var permutedBeforeBatch = []; var permutedAfterBatch = []; for (var i = 1; i < reshapedRank; ++i) { if (i >= blockShapeRank * 2 + 1 || i % 2 === 1) { permutedAfterBatch.push(i); } else { permutedBeforeBatch.push(i); } } permuted.push.apply(permuted, permutedBeforeBatch); permuted.push(0); permuted.push.apply(permuted, permutedAfterBatch); } return permuted; } function getReshapedPermuted(inputShape, blockShape, prod, batchToSpace) { if (batchToSpace === void 0) { batchToSpace = true; } var reshapedPermuted = []; if (batchToSpace) { reshapedPermuted.push(inputShape[0] / prod); } else { reshapedPermuted.push(inputShape[0] * prod); } for (var i = 1; i < inputShape.length; ++i) { if (i <= blockShape.length) { if (batchToSpace) { reshapedPermuted.push(blockShape[i - 1] * inputShape[i]); } else { reshapedPermuted.push(inputShape[i] / blockShape[i - 1]); } } else { reshapedPermuted.push(inputShape[i]); } } return reshapedPermuted; } function getSliceBeginCoords(crops, blockShape) { var sliceBeginCoords = [0]; for (var i = 0; i < blockShape; ++i) { sliceBeginCoords.push(crops[i][0]); } return sliceBeginCoords; } function getSliceSize(uncroppedShape, crops, blockShape) { var sliceSize = uncroppedShape.slice(0, 1); for (var i = 0; i < blockShape; ++i) { sliceSize.push(uncroppedShape[i + 1] - crops[i][0] - crops[i][1]); } return sliceSize; } function axesAreInnerMostDims(axes, rank) { for (var i = 0; i < axes.length; ++i) { if (axes[axes.length - i - 1] !== rank - 1 - i) { return false; } } return true; } function combineLocations(outputLoc, reduceLoc, axes) { var rank = outputLoc.length + reduceLoc.length; var loc = []; var outIdx = 0; var reduceIdx = 0; for (var dim = 0; dim < rank; dim++) { if (axes.indexOf(dim) === -1) { loc.push(outputLoc[outIdx++]); } else { loc.push(reduceLoc[reduceIdx++]); } } return loc; } function computeOutAndReduceShapes(aShape, axes) { var outShape = []; var rank = aShape.length; for (var dim = 0; dim < rank; dim++) { if (axes.indexOf(dim) === -1) { outShape.push(aShape[dim]); } } var reduceShape = axes.map(function (dim) { return aShape[dim]; }); return [outShape, reduceShape]; } function expandShapeToKeepDim(shape, axes) { var reduceSubShape = axes.map(function (x) { return 1; }); return combineLocations(shape, reduceSubShape, axes); } function assertAxesAreInnerMostDims(msg, axes, rank) { assert(axesAreInnerMostDims(axes, rank), msg + " supports only inner-most axes for now. " + ("Got axes " + axes + " and rank-" + rank + " input.")); } function getAxesPermutation(axes, rank) { if (axesAreInnerMostDims(axes, rank)) { return null; } var result = []; for (var i = 0; i < rank; ++i) { if (axes.indexOf(i) === -1) { result.push(i); } } axes.forEach(function (axis) { return result.push(axis); }); return result; } function getUndoAxesPermutation(axes) { return axes.map(function (axis, i) { return [i, axis]; }) .sort(function (a, b) { return a[1] - b[1]; }) .map(function (x) { return x[0]; }); } function getInnerMostAxes(numAxes, rank) { var res = []; for (var i = rank - numAxes; i < rank; ++i) { res.push(i); } return res; } function getBroadcastDims(inShape, outShape) { var inRank = inShape.length; var dims = []; for (var i = 0; i < inRank; i++) { var dim = inRank - 1 - i; var a = inShape[dim] || 1; var b = outShape[outShape.length - 1 - i] || 1; if (b > 1 && a === 1) { dims.unshift(dim); } } return dims; } function getReductionAxes(inShape, outShape) { var result = []; for (var i = 0; i < outShape.length; i++) { var inDim = inShape[inShape.length - i - 1]; var outAxis = outShape.length - i - 1; var outDim = outShape[outAxis]; if (inDim == null || (inDim === 1 && outDim > 1)) { result.unshift(outAxis); } } return result; } function assertAndGetBroadcastShape(shapeA, shapeB) { var result = []; var l = Math.max(shapeA.length, shapeB.length); for (var i = 0; i < l; i++) { var a = shapeA[shapeA.length - i - 1]; if (a == null) { a = 1; } var b = shapeB[shapeB.length - i - 1]; if (b == null) { b = 1; } if (a === 1) { result.unshift(b); } else if (b === 1) { result.unshift(a); } else if (a !== b) { var errMsg = "Operands could not be broadcast together with shapes " + (shapeA + " and " + shapeB + "."); throw Error(errMsg); } else { result.unshift(a); } } return result; } function assertParamsConsistent(shapes, axis) { var rank = shapes[0].length; shapes.forEach(function (shape, i) { assert(shape.length === rank, "Error in concat" + rank + "D: rank of tensors[" + i + "] must be the same " + ("as the rank of the rest (" + rank + ")")); }); assert(axis >= 0 && axis < rank, "Error in concat" + rank + "D: axis must be between 0 and " + (rank - 1) + "."); var firstShape = shapes[0]; shapes.forEach(function (shape, i) { for (var r = 0; r < rank; r++) { assert((r === axis) || (shape[r] === firstShape[r]), "Error in concat" + rank + "D: Shape of tensors[" + i + "] (" + shape + ") " + ("does not match the shape of the rest (" + firstShape + ") ") + ("along the non-concatenated axis " + i + ".")); } }); } function computeOutShape(shapes, axis) { var outputShape = shapes[0].slice(); for (var i = 1; i < shapes.length; i++) { outputShape[axis] += shapes[i][axis]; } return outputShape; } function prepareAndValidate(tensor, indices) { if (tensor.rank < 1) { throw new Error('tf.gatherND() expects the input to be rank 1 or higher,' + (" but the rank was " + tensor.rank + ".")); } if (indices.rank < 1) { throw new Error('tf.gatherND() expects the indices to be rank 1 or higher,' + (" but the rank was " + indices.rank + ".")); } if (indices.dtype !== 'int32') { throw new Error('tf.gatherND() expects the indices to be int32 type,' + (" but the dtype was " + indices.dtype + ".")); } if (indices.shape[indices.rank - 1] > tensor.rank) { throw new Error('index innermost dimension length must be <= tensor rank; saw: ' + (indices.shape[indices.rank - 1] + " vs. " + tensor.rank)); } if (tensor.size === 0) { throw new Error('Requested more than 0 entries, but input is empty.' + (" Input shape: " + tensor.shape + ".")); } var indicesShape = indices.shape; var sliceRank = indicesShape[indicesShape.length - 1]; var nResult = 1; for (var i = 0; i < indicesShape.length - 1; ++i) { nResult *= indicesShape[i]; } var inputShape = tensor.shape; var resultShape = indicesShape.slice(); resultShape.pop(); var sliceSize = 1; for (var i = sliceRank; i < tensor.rank; ++i) { sliceSize *= inputShape[i]; resultShape.push(inputShape[i]); } var strides = computeStrides(tensor.shape).map(function (stride) { return stride / sliceSize; }).concat([1]).slice(0, sliceRank); return [resultShape, nResult, sliceSize, strides]; } var PARALLELIZE_THRESHOLD = 30; function computeOptimalWindowSize(inSize) { if (inSize <= PARALLELIZE_THRESHOLD) { return inSize; } return nearestDivisor(inSize, Math.floor(Math.sqrt(inSize))); } function validateUpdateShape(shape, indices, updates) { var sliceDim = (indices.rank > 1) ? indices.shape[indices.rank - 1] : 1; var batchDim = (indices.rank > 1) ? indices.rank - 1 : 1; var shapeError = 'Must have updates.shape = indices.shape[:batchDim] + ' + ("shape[sliceDim:], got updates.shape: " + updates.shape) + (", indices.shape: " + indices.shape + ", shape: " + shape) + (", sliceDim: " + sliceDim + ", and batchDim: " + batchDim + "."); if (updates.rank < batchDim) { throw new Error(shapeError + (" update.rank < " + batchDim + ". ")); } if (shape.length < sliceDim + (updates.rank - batchDim)) { throw new Error(shapeError + (" Output shape length < " + (sliceDim + (updates.rank - batchDim)))); } if (updates.rank !== batchDim + shape.length - sliceDim) { throw new Error(shapeError + (" update.rank != " + (batchDim + shape.length - sliceDim))); } for (var d = 0; d < batchDim; ++d) { if (updates.shape[d] !== indices.shape[d]) { throw new Error(shapeError + (" updates.shape[" + d + "] (" + updates.shape[d] + ") != indices.shape[" + d + "] (" + indices.shape[d] + ").")); } } for (var d = 0; d < updates.rank - batchDim; ++d) { if (updates.shape[d + batchDim] !== shape[d + sliceDim]) { throw new Error(shapeError + (" updates.shape[" + (d + batchDim) + "] (" + updates.shape[d + batchDim] + ") != shape[" + (d + batchDim) + "] (" + shape[d + batchDim] + ")")); } } } function validateInput(updates, indices, shape) { if (indices.rank < 1) { throw new Error('tf.scatterND() expects the indices to be rank 1 or higher,' + (" but the rank was " + indices.rank + ".")); } if (updates.rank < 1) { throw new Error('tf.scatterND() expects the updates to be rank 1 or higher,' + (" but the rank was " + updates.rank + ".")); } if (indices.dtype !== 'int32') { throw new Error("The dtype of 'indices' should be int32, but got dtype: " + indices.dtype); } if (shape.length < 1) { throw new Error("Output rank must be greater or equal to 1, but got shape: " + shape); } if (shape.length === 0) { if (indices.size === 0) { throw new Error("Indices specified for empty output. indices shape: " + indices.shape); } if (updates.size === 0) { throw new Error("Updates specified for empty output. updates shape: " + updates.shape); } } validateUpdateShape(shape, indices, updates); } function calculateShapes(updates, indices, shape) { var sliceRank = (indices.rank > 1) ? indices.shape[indices.rank - 1] : 1; var totalNd = shape.length; var sliceSize = 1; for (var i = sliceRank; i < totalNd; ++i) { sliceSize *= shape[i]; } var safeSliceDim = (sliceRank < 1) ? 1 : sliceRank; var numUpdates = indices.size / safeSliceDim; var strides = computeStrides(shape.slice(0, sliceRank)).concat([1]); var outputSize = sizeFromShape(shape); return { sliceRank: sliceRank, numUpdates: numUpdates, sliceSize: sliceSize, strides: strides, outputSize: outputSize }; } function segOpComputeOptimalWindowSize(inSize, numSegments) { var done = false; var res; if (inSize <= PARALLELIZE_THRESHOLD) { res = inSize; done = true; } else { res = nearestDivisor(inSize, Math.floor(Math.sqrt(inSize))); } while (!done) { if (res > numSegments || res === inSize) { done = true; break; } else { res = nearestDivisor(inSize, res + 1); } } return res; } function computeOutShape$1(aShape, axis, numSegments) { var outShape = []; var rank = aShape.length; for (var dim = 0; dim < rank; dim++) { if (dim !== axis) { outShape.push(aShape[dim]); } else { outShape.push(numSegments); } } return outShape; } function collectGatherOpShapeInfo(x, indices, axis) { var dimSize = x.shape[axis]; var outputShape = []; var batchSize = 1; var sliceSize = 1; for (var i = 0; i < axis; i++) { outputShape.push(x.shape[i]); batchSize *= x.shape[i]; } for (var i = 0; i < indices.rank; i++) { outputShape.push(indices.shape[i]); } for (var i = axis + 1; i < x.rank; i++) { outputShape.push(x.shape[i]); sliceSize *= x.shape[i]; } return { batchSize: batchSize, sliceSize: sliceSize, dimSize: dimSize, outputShape: outputShape }; } function assertParamsValid(input, begin, size) { assert(input.rank === begin.length, "Error in slice" + input.rank + "D: Length of begin " + begin + " must " + ("match the rank of the array (" + input.rank + ").")); assert(input.rank === size.length, "Error in slice" + input.rank + "D: Length of size " + size + " must " + ("match the rank of the array (" + input.rank + ").")); for (var i = 0; i < input.rank; ++i) { assert(begin[i] + size[i] <= input.shape[i], "Error in slice" + input.rank + "D: begin[" + i + "] + size[" + i + "] " + ("(" + (begin[i] + size[i]) + ") would overflow input.shape[" + i + "] (" + input.shape[i] + ")")); } } function getStridedSlicedInfo(shape, begin, end, strides, beginMask, endMask, ellipsisMask, newAxisMask, shrinkAxisMask) { if (beginMask === void 0) { beginMask = 0; } if (endMask === void 0) { endMask = 0; } if (ellipsisMask === void 0) { ellipsisMask = 0; } if (newAxisMask === void 0) { newAxisMask = 0; } if (shrinkAxisMask === void 0) { shrinkAxisMask = 0; } if (ellipsisMask !== 0) { throw new Error('ellipsis mask is not yet supported'); } if (newAxisMask !== 0) { throw new Error('new axis mask is not yet supported'); } var startIndex = []; var endIndex = []; var shrinkAxis = []; for (var i = 0; i < shape.length; i++) { startIndex[i] = startForAxis(beginMask, begin, strides, shape, i); endIndex[i] = stopForAxis(endMask, end, strides, shape, i); if (shrinkAxisMask & 1 << i) { endIndex[i] = startIndex[i] + 1; shrinkAxis.push(i); } } var size = new Array(shape.length).fill(0); size = size.map(function (d, i) { var count = 0; for (var start = startIndex[i]; !(strides[i] > 0 ? start >= endIndex[i] : start <= endIndex[i]); start += strides[i]) { count += 1; } return count; }); return [startIndex, size, shrinkAxis]; } function startForAxis(beginMask, startIndices, strides, inputShape, axis) { var start = startIndices[axis]; if (beginMask & 1 << axis) { if (strides[axis] > 0) { start = Number.MIN_SAFE_INTEGER; } else { start = Number.MAX_SAFE_INTEGER; } } var axisSize = inputShape[axis]; if (start < 0) { start += axisSize; } start = clamp(0, start, axisSize - 1); return start; } function stopForAxis(endMask, stopIndices, strides, inputShape, axis) { var stop = stopIndices[axis]; if (endMask & (1 << axis)) { if (strides[axis] > 0) { stop = Number.MAX_SAFE_INTEGER; } else { stop = Number.MIN_SAFE_INTEGER; } } var axisSize = inputShape[axis]; if (stop < 0) { stop += axisSize; } if (strides[axis] > 0) { stop = clamp(0, stop, axisSize); } else { stop = clamp(-1, stop, axisSize - 1); } return stop; } function isSliceContinous(shape, begin, size) { var firstNonOneAxis = size.length; for (var i = 0; i < size.length; i++) { if (size[i] > 1) { firstNonOneAxis = i; break; } } for (var i = firstNonOneAxis + 1; i < size.length; i++) { if (begin[i] > 0 || size[i] !== shape[i]) { return false; } } return true; } function computeFlatOffset(begin, strides) { var flatOffset = begin.length > 0 ? begin[begin.length - 1] : 1; for (var i = 0; i < begin.length - 1; i++) { flatOffset += begin[i] * strides[i]; } return flatOffset; } function inferShape(val) { var firstElem = val; if (isTypedArray(val)) { return [val.length]; } if (!Array.isArray(val)) { return []; } var shape = []; while (Array.isArray(firstElem) || isTypedArray(firstElem)) { shape.push(firstElem.length); firstElem = firstElem[0]; } if (Array.isArray(val) && ENV.get('TENSORLIKE_CHECK_SHAPE_CONSISTENCY')) { deepAssertShapeConsistency(val, shape, []); } return shape; } function deepAssertShapeConsistency(val, shape, indices) { indices = indices || []; if (!(Array.isArray(val)) && !isTypedArray(val)) { assert(shape.length === 0, function () { return "Element arr[" + indices.join('][') + "] is a primitive, " + ("but should be an array/TypedArray of " + shape[0] + " elements"); }); return; } assert(shape.length > 0, function () { return "Element arr[" + indices.join('][') + "] should be a primitive, " + ("but is an array of " + val.length + " elements"); }); assert(val.length === shape[0], function () { return "Element arr[" + indices.join('][') + "] should have " + shape[0] + " " + ("elements, but has " + val.length + " elements"); }); var subShape = shape.slice(1); for (var i = 0; i < val.length; ++i) { deepAssertShapeConsistency(val[i], subShape, indices.concat(i)); } } function assertDtype(expectedDtype, actualDType, argName, functionName) { if (expectedDtype == null) { return; } if (expectedDtype !== 'numeric' && expectedDtype !== actualDType || expectedDtype === 'numeric' && actualDType === 'string') { throw new Error("Argument '" + argName + "' passed to '" + functionName + "' must " + ("be " + expectedDtype + " tensor, but got " + actualDType + " tensor")); } } function convertToTensor(x, argName, functionName, parseAsDtype) { if (parseAsDtype === void 0) { parseAsDtype = 'numeric'; } if (x instanceof Tensor) { assertDtype(parseAsDtype, x.dtype, argName, functionName); return x; } var inferredDtype = inferDtype(x); if (inferredDtype !== 'string' && ['bool', 'int32', 'float32'].indexOf(parseAsDtype) >= 0) { inferredDtype = parseAsDtype; } assertDtype(parseAsDtype, inferredDtype, argName, functionName); if (!isTypedArray(x) && !Array.isArray(x) && typeof x !== 'number' && typeof x !== 'boolean' && typeof x !== 'string') { throw new Error("Argument '" + argName + "' passed to '" + functionName + "' must be a " + ("Tensor or TensorLike, but got '" + x.constructor.name + "'")); } var inferredShape = inferShape(x); if (!isTypedArray(x) && !Array.isArray(x)) { x = [x]; } var values = inferredDtype !== 'string' ? toTypedArray(x, inferredDtype, ENV.get('DEBUG')) : flatten(x); return Tensor.make(inferredShape, { values: values }, inferredDtype); } function convertToTensorArray(arg, argName, functionName) { if (!Array.isArray(arg)) { throw new Error("Argument " + argName + " passed to " + functionName + " must be a " + '`Tensor[]` or `TensorLike[]`'); } var tensors = arg; return tensors.map(function (t, i) { return convertToTensor(t, argName + "[" + i + "]", functionName); }); } function op(f) { var keys = Object.keys(f); if (keys.length !== 1) { throw new Error("Please provide an object with a single key " + "(operation name) mapping to a function. Got an object with " + (keys.length + " keys.")); } var opName = keys[0]; var fn = f[opName]; if (opName.endsWith('_')) { opName = opName.substring(0, opName.length - 1); } var f2 = function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } ENV.engine.startScope(opName); try { var result = fn.apply(void 0, args); if (result instanceof Promise) { console.error('Cannot return a Promise inside of tidy.'); } ENV.engine.endScope(result); return result; } catch (ex) { ENV.engine.endScope(null); throw ex; } }; Object.defineProperty(f2, 'name', { value: opName, configurable: true }); return f2; } function softmax_(logits, dim) { if (dim === void 0) { dim = -1; } var $logits = convertToTensor(logits, 'logits', 'softmax'); if (dim === -1) { dim = $logits.rank - 1; } if (dim !== $logits.rank - 1) { throw Error('Softmax along a non-last dimension is not yet supported. ' + ("Logits was rank " + $logits.rank + " and dim was " + dim)); } var customOp = customGrad(function (logits) { var keepDims = true; var lse = logits.logSumExp([dim], keepDims); var logResult = logits.toFloat().sub(lse); var y = logResult.exp(); var gradFunc = function (dy) { var dyTimesY = dy.mul(y); var keepDims = true; return dyTimesY.sub(dyTimesY.sum([dim], keepDims).mul(y)); }; return { value: y, gradFunc: gradFunc }; }); return customOp($logits); } function logSoftmax_(logits, axis) { if (axis === void 0) { axis = -1; } var $logits = convertToTensor(logits, 'logits', 'logSoftmax'); if (axis === -1) { axis = $logits.rank - 1; } if (axis !== $logits.rank - 1) { throw Error('Log Softmax along a non-last dimension is not yet supported. ' + ("Logits was rank " + $logits.rank + " and axis was " + axis)); } var customOp = customGrad(function (logits) { var keepDims = true; var xMax = logits.max(axis, true); var shifted = logits.sub(xMax); var value = shifted.toFloat().sub(shifted.exp().sum(axis, keepDims).log()); var gradFunc = function (dy) { var softmax = value.exp(); return dy.sub(dy.sum(axis, keepDims).mul(softmax)); }; return { value: value, gradFunc: gradFunc }; }); return customOp($logits); } var softmax = op({ softmax_: softmax_ }); var logSoftmax = op({ logSoftmax_: logSoftmax_ }); function complex_(real, imag) { var $real = convertToTensor(real, 'real', 'complex'); var $imag = convertToTensor(imag, 'imag', 'complex'); assertShapesMatch($real.shape, $imag.shape, "real and imag shapes, " + $real.shape + " and " + $imag.shape + ", " + "must match in call to tf.complex()."); return ENV.engine.runKernel(function (backend) { return backend.complex($real, $imag); }, { $real: $real, $imag: $imag }); } function real_(input) { var $input = convertToTensor(input, 'input', 'real'); return ENV.engine.runKernel(function (backend) { return backend.real($input); }, { $input: $input }); } function imag_(input) { var $input = convertToTensor(input, 'input', 'imag'); return ENV.engine.runKernel(function (backend) { return backend.imag($input); }, { $input: $input }); } var complex = op({ complex_: complex_ }); var real = op({ real_: real_ }); var imag = op({ imag_: imag_ }); function tensor(values, shape, dtype) { if (dtype == null) { dtype = inferDtype(values); } if (dtype === 'complex64') { throw new Error("Cannot construct a complex64 tensor directly. " + "Please use tf.complex(real, imag)."); } if (!isTypedArray(values) && !Array.isArray(values) && typeof values !== 'number' && typeof values !== 'boolean' && typeof values !== 'string') { throw new Error('values passed to tensor(values) must be a number/boolean/string or ' + 'an array of numbers/booleans/strings, or a TypedArray'); } var inferredShape = inferShape(values); if (shape != null) { var providedSize_1 = sizeFromShape(shape); var inferredSize_1 = sizeFromShape(inferredShape); assert(providedSize_1 === inferredSize_1, function () { return "Based on the provided shape, [" + shape + "], the tensor should have " + (providedSize_1 + " values but has " + inferredSize_1); }); for (var i = 0; i < inferredShape.length; ++i) { var inferred = inferredShape[i]; var flatDimsDontMatch = i === inferredShape.length - 1 ? inferred !== sizeFromShape(shape.slice(i)) : true; assert(inferredShape[i] === shape[i] || !flatDimsDontMatch, function () { return "Error creating a new Tensor. Inferred shape " + ("(" + inferredShape + ") does not match the provided ") + ("shape (" + shape + "). "); }); } } if (!isTypedArray(values) && !Array.isArray(values)) { values = [values]; } shape = shape || inferredShape; values = dtype !== 'string' ? toTypedArray(values, dtype, ENV.get('DEBUG')) : flatten(values); return Tensor.make(shape, { values: values }, dtype); } function scalar(value, dtype) { if ((isTypedArray(value) || Array.isArray(value)) && dtype !== 'complex64') { throw new Error('Error creating a new Scalar: value must be a primitive ' + '(number|boolean|string)'); } return tensor(value, [], dtype); } function tensor1d(values, dtype) { assertNonNull(values); var inferredShape = inferShape(values); if (inferredShape.length !== 1) { throw new Error('tensor1d() requires values to be a flat/TypedArray'); } return tensor(values, inferredShape, dtype); } function tensor2d(values, shape, dtype) { assertNonNull(values); if (shape != null && shape.length !== 2) { throw new Error('tensor2d() requires shape to have two numbers'); } var inferredShape = inferShape(values); if (inferredShape.length !== 2 && inferredShape.length !== 1) { throw new Error('tensor2d() requires values to be number[][] or flat/TypedArray'); } if (inferredShape.length === 1 && shape == null) { throw new Error('tensor2d() requires shape to be provided when `values` ' + 'are a flat/TypedArray'); } shape = shape || inferredShape; return tensor(values, shape, dtype); } function tensor3d(values, shape, dtype) { assertNonNull(values); if (shape != null && shape.length !== 3) { throw new Error('tensor3d() requires shape to have three numbers'); } var inferredShape = inferShape(values); if (inferredShape.length !== 3 && inferredShape.length !== 1) { throw new Error('tensor3d() requires values to be number[][][] or flat/TypedArray'); } if (inferredShape.length === 1 && shape == null) { throw new Error('tensor3d() requires shape to be provided when `values` ' + 'are a flat array'); } shape = shape || inferredShape; return tensor(values, shape, dtype); } function tensor4d(values, shape, dtype) { assertNonNull(values); if (shape != null && shape.length !== 4) { throw new Error('tensor4d() requires shape to have four numbers'); } var inferredShape = inferShape(values); if (inferredShape.length !== 4 && inferredShape.length !== 1) { throw new Error('tensor4d() requires values to be number[][][][] or flat/TypedArray'); } if (inferredShape.length === 1 && shape == null) { throw new Error('tensor4d() requires shape to be provided when `values` ' + 'are a flat array'); } shape = shape || inferredShape; return tensor(values, shape, dtype); } function tensor5d(values, shape, dtype) { assertNonNull(values); if (shape != null && shape.length !== 5) { throw new Error('tensor5d() requires shape to have five numbers'); } var inferredShape = inferShape(values); if (inferredShape.length !== 5 && inferredShape.length !== 1) { throw new Error('tensor5d() requires values to be ' + 'number[][][][][] or flat/TypedArray'); } if (inferredShape.length === 1 && shape == null) { throw new Error('tensor5d() requires shape to be provided when `values` ' + 'are a flat array'); } shape = shape || inferredShape; return tensor(values, shape, dtype); } function tensor6d(values, shape, dtype) { assertNonNull(values); if (shape != null && shape.length !== 6) { throw new Error('tensor6d() requires shape to have six numbers'); } var inferredShape = inferShape(values); if (inferredShape.length !== 6 && inferredShape.length !== 1) { throw new Error('tensor6d() requires values to be number[][][][][][] or ' + 'flat/TypedArray'); } if (inferredShape.length === 1 && shape == null) { throw new Error('tensor6d() requires shape to be provided when `values` ' + 'are a flat array'); } shape = shape || inferredShape; return tensor(values, shape, dtype); } function ones$1(shape, dtype) { if (dtype === void 0) { dtype = 'float32'; } if (dtype === 'complex64') { var real$$1 = ones$1(shape, 'float32'); var imag$$1 = ones$1(shape, 'float32'); return complex(real$$1, imag$$1); } var values = makeOnesTypedArray(sizeFromShape(shape), dtype); return Tensor.make(shape, { values: values }, dtype); } function zeros(shape, dtype) { if (dtype === void 0) { dtype = 'float32'; } if (dtype === 'complex64') { var real$$1 = zeros(shape, 'float32'); var imag$$1 = zeros(shape, 'float32'); return complex(real$$1, imag$$1); } var values = makeZerosTypedArray(sizeFromShape(shape), dtype); return Tensor.make(shape, { values: values }, dtype); } function fill(shape, value, dtype) { dtype = dtype || inferDtype(value); var values = getArrayFromDType(dtype, sizeFromShape(shape)); values.fill(value); return Tensor.make(shape, { values: values }, dtype); } function onesLike_(x) { var $x = convertToTensor(x, 'x', 'onesLike'); return ones$1($x.shape, $x.dtype); } function zerosLike_(x) { var $x = convertToTensor(x, 'x', 'zerosLike'); return zeros($x.shape, $x.dtype); } function linspace(start, stop, num) { if (num === 0) { throw new Error('Cannot request zero samples'); } var step = (stop - start) / (num - 1); var values = makeZerosTypedArray(num, 'float32'); values[0] = start; for (var i = 1; i < values.length; i++) { values[i] = values[i - 1] + step; } return tensor1d(values, 'float32'); } function range(start, stop, step, dtype) { if (step === void 0) { step = 1; } if (dtype === void 0) { dtype = 'float32'; } if (step === 0) { throw new Error('Cannot have a step of zero'); } var sameStartStop = start === stop; var increasingRangeNegativeStep = start < stop && step < 0; var decreasingRangePositiveStep = stop < start && step > 1; if (sameStartStop || increasingRangeNegativeStep || decreasingRangePositiveStep) { return zeros([0], dtype); } var numElements = Math.abs(Math.ceil((stop - start) / step)); var values = makeZerosTypedArray(numElements, dtype); if (stop < start && step === 1) { step = -1; } values[0] = start; for (var i = 1; i < values.length; i++) { values[i] = values[i - 1] + step; } return tensor1d(values, dtype); } var onesLike = op({ onesLike_: onesLike_ }); var zerosLike = op({ zerosLike_: zerosLike_ }); var DataStorage = (function () { function DataStorage(dataMover) { this.dataMover = dataMover; this.data = new WeakMap(); } DataStorage.prototype.get = function (dataId) { if (!this.data.has(dataId)) { this.dataMover.moveData(dataId); } return this.data.get(dataId); }; DataStorage.prototype.set = function (dataId, value) { this.data.set(dataId, value); }; DataStorage.prototype.has = function (dataId) { return this.data.has(dataId); }; DataStorage.prototype.delete = function (dataId) { return this.data.delete(dataId); }; return DataStorage; }()); var KernelBackend = (function () { function KernelBackend() { } KernelBackend.prototype.time = function (f) { throw new Error('Not yet implemented.'); }; KernelBackend.prototype.read = function (dataId) { throw new Error('Not yet implemented.'); }; KernelBackend.prototype.readSync = function (dataId) { throw new Error('Not yet implemented.'); }; KernelBackend.prototype.disposeData = function (dataId) { throw new Error('Not yet implemented.'); }; KernelBackend.prototype.write = function (dataId, values) { throw new Error('Not yet implemented.'); }; KernelBackend.prototype.fromPixels = function (pixels, numChannels) { throw new Error('Not yet implemented.'); }; KernelBackend.prototype.register = function (dataId, shape, dtype) { throw new Error('Not yet implemented.'); }; KernelBackend.prototype.memory = function () { throw new Error('Not yet implemented.'); }; KernelBackend.prototype.floatPrecision = function () { throw new Error('Not yet implemented'); }; KernelBackend.prototype.batchMatMul = function (a, b, transposeA, transposeB) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.fusedBatchMatMul = function (a, b, transposeA, transposeB, bias, activation) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.slice = function (x, begin, size) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.stridedSlice = function (x, begin, end, strides, beginMask, endMask, ellipsisMask, newAxisMask, shrinkAxisMask) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.unstack = function (x, axis) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.reverse = function (a, axis) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.concat = function (tensors, axis) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.neg = function (a) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.add = function (a, b) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.addN = function (tensors) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.subtract = function (a, b) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.multiply = function (a, b) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.realDivide = function (a, b) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.floorDiv = function (a, b) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.sum = function (x, axes) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.prod = function (x, axes) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.unsortedSegmentSum = function (x, segmentIds, numSegments) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.argMin = function (x, axis) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.argMax = function (x, axis) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.equal = function (a, b) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.notEqual = function (a, b) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.less = function (a, b) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.lessEqual = function (a, b) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.greater = function (a, b) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.greaterEqual = function (a, b) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.logicalNot = function (a) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.logicalAnd = function (a, b) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.logicalOr = function (a, b) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.where = function (condition) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.select = function (condition, a, b) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.topk = function (x, k, sorted) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.min = function (x, axes) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.minimum = function (a, b) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.mod = function (a, b) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.max = function (x, axes) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.maximum = function (a, b) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.all = function (x, axes) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.any = function (x, axes) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.squaredDifference = function (a, b) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.ceil = function (x) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.floor = function (x) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.round = function (x) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.sign = function (x) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.pow = function (a, b) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.exp = function (x) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.expm1 = function (x) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.log = function (x) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.log1p = function (x) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.sqrt = function (x) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.rsqrt = function (x) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.square = function (x) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.reciprocal = function (x) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.relu = function (x) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.prelu = function (x, a) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.elu = function (x) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.eluDer = function (dy, y) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.selu = function (x) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.int = function (x) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.clip = function (x, min, max) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.abs = function (x) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.complexAbs = function (x) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.sigmoid = function (x) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.softplus = function (x) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.sin = function (x) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.cos = function (x) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.tan = function (x) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.asin = function (x) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.acos = function (x) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.atan = function (x) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.atan2 = function (a, b) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.sinh = function (x) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.cosh = function (x) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.tanh = function (x) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.asinh = function (x) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.acosh = function (x) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.atanh = function (x) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.erf = function (x) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.step = function (x, alpha) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.conv2d = function (x, filter, convInfo) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.conv2dDerInput = function (dy, filter, convInfo) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.conv2dDerFilter = function (x, dY, convInfo) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.depthwiseConv2D = function (input, filter, convInfo) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.depthwiseConv2DDerInput = function (dy, filter, convInfo) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.depthwiseConv2DDerFilter = function (x, dY, convInfo) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.conv3d = function (x, filter, convInfo) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.conv3dDerInput = function (dy, filter, convInfo) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.conv3dDerFilter = function (x, dY, convInfo) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.maxPool = function (x, convInfo) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.maxPoolBackprop = function (dy, x, y, convInfo) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.avgPool = function (x, convInfo) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.avgPoolBackprop = function (dy, x, convInfo) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.reshape = function (x, shape) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.cast = function (x, dtype) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.tile = function (x, reps) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.pad = function (x, paddings, constantValue) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.transpose = function (x, perm) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.gather = function (x, indices, axis) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.gatherND = function (x, indices) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.scatterND = function (indices, updates, shape) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.batchToSpaceND = function (x, blockShape, crops) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.spaceToBatchND = function (x, blockShape, paddings) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.resizeBilinear = function (x, newHeight, newWidth, alignCorners) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.resizeBilinearBackprop = function (dy, x, alignCorners) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.resizeNearestNeighbor = function (x, newHEight, newWidth, alignCorners) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.resizeNearestNeighborBackprop = function (dy, x, alignCorners) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.batchNormalization = function (x, mean, variance, varianceEpsilon, scale, offset) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.localResponseNormalization4D = function (x, radius, bias, alpha, beta) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.LRNGrad = function (dy, inputImage, outputImage, radius, bias, alpha, beta) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.multinomial = function (logits, normalized, numSamples, seed) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.oneHot = function (indices, depth, onValue, offValue) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.cumsum = function (x, axis, exclusive, reverse) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.nonMaxSuppression = function (boxes, scores, maxOutputSize, iouThreshold, scoreThreshold) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.fft = function (x) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.ifft = function (x) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.complex = function (real, imag) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.real = function (input) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.imag = function (input) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.cropAndResize = function (image, boxes, boxIndex, cropSize, method, extrapolationValue) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.depthToSpace = function (x, blockSize, dataFormat) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.split = function (value, sizeSplits, axis) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.sparseToDense = function (sparseIndices, sparseValues, outputShape, defaultValue) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.setDataMover = function (dataMover) { throw new Error('Not yet implemented'); }; KernelBackend.prototype.dispose = function () { throw new Error('Not yet implemented'); }; return KernelBackend; }()); function castTensor(x, dtype, backend) { if (dtype === 'complex64') { if (x.dtype === 'complex64') { return x.clone(); } var zerosTensor = zeros(x.shape); var floatX = x.toFloat(); var result = backend.complex(floatX, zerosTensor); zerosTensor.dispose(); floatX.dispose(); return result; } if (!hasEncodingLoss(x.dtype, dtype)) { return Tensor.make(x.shape, { dataId: x.dataId }, dtype); } if (x.dtype === 'complex64') { var real = backend.real(x); var result = real.cast(dtype); real.dispose(); return result; } if (dtype === 'int32') { return backend.int(x); } else if (dtype === 'bool') { var zero = scalar(0, x.dtype); var result = backend.notEqual(x, zero); zero.dispose(); return result; } else { throw new Error("Error in Cast: unknown dtype argument (" + dtype + ")"); } } function reshapeTensor(x, shape) { return Tensor.make(shape, { dataId: x.dataId }, x.dtype); } function mergeRealAndImagArrays(real, imag) { if (real.length !== imag.length) { throw new Error("Cannot merge real and imag arrays of different lengths. real:" + (real.length + ", imag: " + imag.length + ".")); } var result = new Float32Array(real.length * 2); for (var i = 0; i < result.length; i += 2) { result[i] = real[i / 2]; result[i + 1] = imag[i / 2]; } return result; } function splitRealAndImagArrays(complex) { var real = new Float32Array(complex.length / 2); var imag = new Float32Array(complex.length / 2); for (var i = 0; i < complex.length; i += 2) { real[i / 2] = complex[i]; imag[i / 2] = complex[i + 1]; } return { real: real, imag: imag }; } function complexWithEvenIndex(complex) { var len = Math.ceil(complex.length / 4); var real = new Float32Array(len); var imag = new Float32Array(len); for (var i = 0; i < complex.length; i += 4) { real[Math.floor(i / 4)] = complex[i]; imag[Math.floor(i / 4)] = complex[i + 1]; } return { real: real, imag: imag }; } function complexWithOddIndex(complex) { var len = Math.floor(complex.length / 4); var real = new Float32Array(len); var imag = new Float32Array(len); for (var i = 2; i < complex.length; i += 4) { real[Math.floor(i / 4)] = complex[i]; imag[Math.floor(i / 4)] = complex[i + 1]; } return { real: real, imag: imag }; } function getComplexWithIndex(complex, index) { var real = complex[index * 2]; var imag = complex[index * 2 + 1]; return { real: real, imag: imag }; } function assignToTypedArray(data, real, imag, index) { data[index * 2] = real; data[index * 2 + 1] = imag; } function exponents(n, inverse) { var real = new Float32Array(n / 2); var imag = new Float32Array(n / 2); for (var i = 0; i < Math.ceil(n / 2); i++) { var x = (inverse ? 2 : -2) * Math.PI * (i / n); real[i] = Math.cos(x); imag[i] = Math.sin(x); } return { real: real, imag: imag }; } function exponent(k, n, inverse) { var x = (inverse ? 2 : -2) * Math.PI * (k / n); var real = Math.cos(x); var imag = Math.sin(x); return { real: real, imag: imag }; } function nonMaxSuppressionImpl(boxes, scores, maxOutputSize, iouThreshold, scoreThreshold) { var candidates = Array.from(scores) .map(function (score, boxIndex) { return ({ score: score, boxIndex: boxIndex }); }) .filter(function (c) { return c.score > scoreThreshold; }) .sort(function (c1, c2) { return c2.score - c1.score; }); var selected = []; for (var i = 0; i < candidates.length; i++) { var _a = candidates[i], score = _a.score, boxIndex = _a.boxIndex; if (score < scoreThreshold) { break; } var ignoreCandidate = false; for (var j = selected.length - 1; j >= 0; --j) { var iou = intersectionOverUnion(boxes, boxIndex, selected[j]); if (iou >= iouThreshold) { ignoreCandidate = true; break; } } if (!ignoreCandidate) { selected.push(boxIndex); if (selected.length >= maxOutputSize) { break; } } } return tensor1d(selected, 'int32'); } function intersectionOverUnion(boxes, i, j) { var iCoord = boxes.subarray(i * 4, i * 4 + 4); var jCoord = boxes.subarray(j * 4, j * 4 + 4); var yminI = Math.min(iCoord[0], iCoord[2]); var xminI = Math.min(iCoord[1], iCoord[3]); var ymaxI = Math.max(iCoord[0], iCoord[2]); var xmaxI = Math.max(iCoord[1], iCoord[3]); var yminJ = Math.min(jCoord[0], jCoord[2]); var xminJ = Math.min(jCoord[1], jCoord[3]); var ymaxJ = Math.max(jCoord[0], jCoord[2]); var xmaxJ = Math.max(jCoord[1], jCoord[3]); var areaI = (ymaxI - yminI) * (xmaxI - xminI); var areaJ = (ymaxJ - yminJ) * (xmaxJ - xminJ); if (areaI <= 0 || areaJ <= 0) { return 0.0; } var intersectionYmin = Math.max(yminI, yminJ); var intersectionXmin = Math.max(xminI, xminJ); var intersectionYmax = Math.min(ymaxI, ymaxJ); var intersectionXmax = Math.min(xmaxI, xmaxJ); var intersectionArea = Math.max(intersectionYmax - intersectionYmin, 0.0) * Math.max(intersectionXmax - intersectionXmin, 0.0); return intersectionArea / (areaI + areaJ - intersectionArea); } function split(x, sizeSplits, axis) { var begin = new Array(x.rank).fill(0); var size = x.shape.slice(); return sizeSplits.map(function (s) { size[axis] = s; var slice = x.slice(begin, size); begin[axis] += s; return slice; }); } function topkImpl(x, xShape, xDtype, k, sorted) { var lastDim = xShape[xShape.length - 1]; var _a = [x.length / lastDim, lastDim], batch = _a[0], size = _a[1]; var allTopKVals = getTypedArrayFromDType(xDtype, batch * k); var allTopKIndices = getTypedArrayFromDType('int32', batch * k); for (var b = 0; b < batch; b++) { var offset = b * size; var vals = x.subarray(offset, offset + size); var valAndInd = []; for (var i = 0; i < vals.length; i++) { valAndInd.push({ value: vals[i], index: i }); } valAndInd.sort(function (a, b) { return b.value - a.value; }); var outOffset = b * k; var topKVals = allTopKVals.subarray(outOffset, outOffset + k); var topKIndices = allTopKIndices.subarray(outOffset, outOffset + k); for (var i = 0; i < k; i++) { topKVals[i] = valAndInd[i].value; topKIndices[i] = valAndInd[i].index; } } var outputShape = xShape.slice(); outputShape[outputShape.length - 1] = k; return [ tensor(allTopKVals, outputShape, xDtype), tensor(allTopKIndices, outputShape, 'int32') ]; } var ArgMinMaxProgram = (function () { function ArgMinMaxProgram(reduceInfo, op, firstPass) { this.variableNames = ['A']; var windowSize = reduceInfo.windowSize; var batchSize = reduceInfo.batchSize; var inSize = reduceInfo.inSize; var outSize = Math.ceil(inSize / windowSize); if (!firstPass) { this.variableNames.push('bestIndicesA'); } this.outputShape = [batchSize, outSize]; var compOp = (op === 'max') ? '>' : '<'; var indexSnippet = firstPass ? 'inOffset + i;' : 'round(getBestIndicesA(batch, inOffset + i));'; this.userCode = "\n void main() {\n ivec2 coords = getOutputCoords();\n int batch = coords[0];\n int outIdx = coords[1];\n int inOffset = outIdx * " + windowSize + ";\n\n int bestIndex = inOffset;\n float bestValue = getA(batch, bestIndex);\n\n for (int i = 0; i < " + windowSize + "; i++) {\n int inIdx = " + indexSnippet + ";\n float candidate = getA(batch, inIdx);\n if (candidate " + compOp + " bestValue) {\n bestValue = candidate;\n bestIndex = inIdx;\n }\n }\n setOutput(float(bestIndex));\n }\n "; } return ArgMinMaxProgram; }()); var AvgPool2DBackpropProgram = (function () { function AvgPool2DBackpropProgram(convInfo) { this.variableNames = ['dy']; this.outputShape = convInfo.inShape; var filterHeight = convInfo.filterHeight; var filterWidth = convInfo.filterWidth; var strideHeight = convInfo.strideHeight; var strideWidth = convInfo.strideWidth; var dilationHeight = convInfo.dilationHeight; var dilationWidth = convInfo.dilationWidth; var effectiveFilterHeight = convInfo.effectiveFilterHeight; var effectiveFilterWidth = convInfo.effectiveFilterWidth; var padTop = effectiveFilterHeight - 1 - convInfo.padInfo.top; var padLeft = effectiveFilterWidth - 1 - convInfo.padInfo.left; var avgMultiplier = 1 / (filterHeight * filterWidth); this.userCode = "\n const ivec2 pads = ivec2(" + padTop + ", " + padLeft + ");\n const float avgMultiplier = float(" + avgMultiplier + ");\n\n void main() {\n ivec4 coords = getOutputCoords();\n int b = coords[0];\n int d = coords[3];\n\n ivec2 dyRCCorner = coords.yz - pads;\n int dyRCorner = dyRCCorner.x;\n int dyCCorner = dyRCCorner.y;\n\n // Convolve dy(?, ?, d) with pos mask(:, :, d) to get dx(xR, xC, d).\n // ? = to be determined. : = across all values in that axis.\n float dotProd = 0.0;\n for (int wR = 0; wR < " + effectiveFilterHeight + ";\n wR += " + dilationHeight + ") {\n float dyR = float(dyRCorner + wR) / " + strideHeight + ".0;\n\n if (dyR < 0.0 || dyR >= " + convInfo.outHeight + ".0 || fract(dyR) > 0.0) {\n continue;\n }\n int idyR = int(dyR);\n\n for (int wC = 0; wC < " + effectiveFilterWidth + ";\n wC+= " + dilationWidth + ") {\n float dyC = float(dyCCorner + wC) / " + strideWidth + ".0;\n\n if (dyC < 0.0 || dyC >= " + convInfo.outWidth + ".0 ||\n fract(dyC) > 0.0) {\n continue;\n }\n int idyC = int(dyC);\n\n float dyValue = getDy(b, idyR, idyC, d);\n\n dotProd += dyValue * avgMultiplier;\n }\n }\n setOutput(dotProd);\n }\n "; } return AvgPool2DBackpropProgram; }()); var BatchNormProgram = (function () { function BatchNormProgram(xShape, meanShape, varianceShape, offsetShape, scaleShape, varianceEpsilon) { this.outputShape = []; this.variableNames = ['x', 'mean', 'variance']; assertAndGetBroadcastShape(xShape, meanShape); assertAndGetBroadcastShape(xShape, varianceShape); var offsetSnippet = '0.0'; if (offsetShape != null) { assertAndGetBroadcastShape(xShape, offsetShape); this.variableNames.push('offset'); offsetSnippet = 'getOffsetAtOutCoords()'; } var scaleSnippet = '1.0'; if (scaleShape != null) { assertAndGetBroadcastShape(xShape, scaleShape); this.variableNames.push('scale'); scaleSnippet = 'getScaleAtOutCoords()'; } this.outputShape = xShape; this.userCode = "\n void main() {\n float x = getXAtOutCoords();\n float mean = getMeanAtOutCoords();\n float variance = getVarianceAtOutCoords();\n float offset = " + offsetSnippet + ";\n float scale = " + scaleSnippet + ";\n float inv = scale * inversesqrt(variance + float(" + varianceEpsilon + "));\n setOutput(dot(vec3(x, -mean, offset), vec3(inv, inv, 1)));\n }\n "; } return BatchNormProgram; }()); var BatchNormPackedProgram = (function () { function BatchNormPackedProgram(xShape, meanShape, varianceShape, offsetShape, scaleShape, varianceEpsilon) { this.usesPackedTextures = true; this.variableNames = ['x', 'mean', 'variance']; assertAndGetBroadcastShape(xShape, meanShape); assertAndGetBroadcastShape(xShape, varianceShape); var offsetSnippet = 'vec4(0.0)'; if (offsetShape != null) { assertAndGetBroadcastShape(xShape, offsetShape); this.variableNames.push('offset'); offsetSnippet = 'getOffsetAtOutCoords()'; } var scaleSnippet = 'vec4(1.0)'; if (scaleShape != null) { assertAndGetBroadcastShape(xShape, scaleShape); this.variableNames.push('scale'); scaleSnippet = 'getScaleAtOutCoords()'; } this.outputShape = xShape; this.userCode = "\n void main() {\n vec4 offset = " + offsetSnippet + ";\n vec4 scale = " + scaleSnippet + ";\n\n vec4 x = getXAtOutCoords();\n vec4 mean = getMeanAtOutCoords();\n vec4 variance = getVarianceAtOutCoords();\n\n vec4 inv = scale * inversesqrt(variance + vec4(" + varianceEpsilon + "));\n\n setOutput((x - mean) * inv + offset);\n }\n "; } return BatchNormPackedProgram; }()); var COMPLEX_MULTIPLY = { REAL: 'return areal * breal - aimag * bimag;', IMAG: 'return areal * bimag + aimag * breal;' }; var BinaryOpComplexProgram = (function () { function BinaryOpComplexProgram(op, aShape, bShape) { this.variableNames = ['AReal', 'AImag', 'BReal', 'BImag']; this.outputShape = assertAndGetBroadcastShape(aShape, bShape); this.userCode = "\n float binaryOpComplex(\n float areal, float aimag, float breal, float bimag) {\n " + op + "\n }\n\n void main() {\n float areal = getARealAtOutCoords();\n float aimag = getAImagAtOutCoords();\n float breal = getBRealAtOutCoords();\n float bimag = getBImagAtOutCoords();\n setOutput(binaryOpComplex(areal, aimag, breal, bimag));\n }\n "; } return BinaryOpComplexProgram; }()); var CHECK_NAN_SNIPPET = "\n if (isNaN(a)) return a;\n if (isNaN(b)) return b;\n"; var ADD = 'return a + b;'; var SUB = 'return a - b;'; var MUL = 'return a * b;'; var DIV = "if (a == b) return 1.0;\n return a / b;"; var INT_DIV = "\n float resultSign = sign(a) * sign(b);\n int ia = round(a);\n int ib = round(b);\n int result = ia / ib;\n int amodb = ia - ib * result;\n\n if (resultSign < 0.0 && amodb != 0) {\n result -= 1;\n }\n return float(result);\n"; var POW = "\nif(a < 0.0 && floor(b) < b){\n return NAN;\n}\nreturn (round(mod(b, 2.0)) != 1) ?\n pow(abs(a), b) : sign(a) * pow(abs(a), b);\n"; var SQUARED_DIFFERENCE = 'return (a - b) * (a - b);'; var EQUAL = "return float(a == b);"; var NOT_EQUAL = "return float(a != b);"; var LESS = "return float(a < b);"; var LESS_EQUAL = "return float(a <= b);"; var GREATER = "return float(a > b);"; var GREATER_EQUAL = "return float(a >= b);"; var LOGICAL_AND = "return float(a >= 1.0 && b >= 1.0);"; var LOGICAL_OR = "return float(a >= 1.0 || b >= 1.0);"; var MAX = CHECK_NAN_SNIPPET + "\n return max(a, b);\n"; var MIN = CHECK_NAN_SNIPPET + "\n return min(a, b);\n"; var MOD = "if (b == 0.0) return NAN;\n return mod(a, b);"; var ATAN2 = CHECK_NAN_SNIPPET + "\n return atan(a, b);\n"; var ELU_DER = "return (b >= 1.0) ? a : a * (b + 1.0);"; var PRELU = "return (a < 0.) ? b * a : a;"; var BinaryOpProgram = (function () { function BinaryOpProgram(op, aShape, bShape) { this.variableNames = ['A', 'B']; this.outputShape = assertAndGetBroadcastShape(aShape, bShape); this.userCode = "\n uniform float NAN;\n float binaryOperation(float a, float b) {\n " + op + "\n }\n\n void main() {\n float a = getAAtOutCoords();\n float b = getBAtOutCoords();\n setOutput(binaryOperation(a, b));\n }\n "; } BinaryOpProgram.prototype.getCustomSetupFunc = function () { var _this = this; return function (gpgpu, webGLProgram) { if (_this.startLoc == null) { _this.startLoc = gpgpu.getUniformLocationNoThrow(webGLProgram, 'NAN'); if (_this.startLoc == null) { return; } } gpgpu.gl.uniform1f(_this.startLoc, NaN); }; }; return BinaryOpProgram; }()); var PACKED_DIV = "\n vec4 one = vec4(equal(a, b));\n return one + (vec4(1.0) - one) * a / b;\n"; var PACKED_INT_DIV = "\n vec4 resultSign = sign(a) * sign(b);\n ivec4 ia = round(a);\n ivec4 ib = round(b);\n ivec4 result = ia / ib;\n ivec4 amodb = ia - ib * result;\n \n // Vectorize INT_DIV\n // if (resultSign < 0.0 && amodb != 0) result -= 1;\n // return float(result);\n return vec4(result -\n ivec4(lessThan(resultSign, vec4(0.0))) * ivec4(notEqual(amodb, ivec4(0))));\n"; var PACKED_POW = "\n // isModRound1 has 1 for components with round(mod(b, 2.0)) == 1, 0 otherwise.\n vec4 isModRound1 = vec4(equal(round(mod(b, 2.0)), ivec4(1)));\n vec4 multiplier = sign(a) * isModRound1 + (vec4(1.0) - isModRound1);\n vec4 result = multiplier * pow(abs(a), b);\n\n vec4 isNaN = vec4(lessThan(a, vec4(0.0))) * vec4(lessThan(floor(b), b));\n result.r = isNaN.r == 1.0 ? NAN : result.r;\n result.g = isNaN.g == 1.0 ? NAN : result.g;\n result.b = isNaN.b == 1.0 ? NAN : result.b;\n result.a = isNaN.a == 1.0 ? NAN : result.a;\n \n return result;\n"; var BinaryOpPackedProgram = (function () { function BinaryOpPackedProgram(op, aShape, bShape) { this.variableNames = ['A', 'B']; this.supportsBroadcasting = true; this.usesPackedTextures = true; this.outputShape = assertAndGetBroadcastShape(aShape, bShape); this.userCode = "\n uniform float NAN;\n vec4 binaryOperation(vec4 a, vec4 b) {\n " + op + "\n }\n\n void main() {\n vec4 a = getAAtOutCoords();\n vec4 b = getBAtOutCoords();\n setOutput(binaryOperation(a, b));\n }\n "; } BinaryOpPackedProgram.prototype.getCustomSetupFunc = function () { var _this = this; return function (gpgpu, webGLProgram) { if (_this.startLoc == null) { _this.startLoc = gpgpu.getUniformLocationNoThrow(webGLProgram, 'NAN'); if (_this.startLoc == null) { return; } } gpgpu.gl.uniform1f(_this.startLoc, NaN); }; }; return BinaryOpPackedProgram; }()); var ClipProgram = (function () { function ClipProgram(aShape) { this.variableNames = ['A']; this.outputShape = aShape; this.userCode = "\n uniform float min;\n uniform float max;\n\n void main() {\n float value = getAAtOutCoords();\n if (isNaN(value)) {\n setOutput(value);\n return;\n }\n\n setOutput(clamp(value, min, max));\n }\n "; } ClipProgram.prototype.getCustomSetupFunc = function (min, max) { var _this = this; return function (gpgpu, webGLProgram) { if (_this.minLoc == null) { _this.minLoc = gpgpu.getUniformLocationNoThrow(webGLProgram, 'min'); _this.maxLoc = gpgpu.getUniformLocationNoThrow(webGLProgram, 'max'); } gpgpu.gl.uniform1f(_this.minLoc, min); gpgpu.gl.uniform1f(_this.maxLoc, max); }; }; return ClipProgram; }()); var ClipPackedProgram = (function () { function ClipPackedProgram(aShape) { this.variableNames = ['A']; this.usesPackedTextures = true; this.outputShape = aShape; this.userCode = "\n uniform float min;\n uniform float max;\n\n void main() {\n vec4 value = getAAtOutCoords();\n\n if (hasNaN(value)) {\n setOutput(value);\n return;\n }\n\n setOutput(clamp(value, vec4(min), vec4(max)));\n }\n "; } ClipPackedProgram.prototype.getCustomSetupFunc = function (min, max) { var _this = this; return function (gpgpu, webGLProgram) { if (_this.minLoc == null) { _this.minLoc = gpgpu.getUniformLocationNoThrow(webGLProgram, 'min'); _this.maxLoc = gpgpu.getUniformLocationNoThrow(webGLProgram, 'max'); } gpgpu.gl.uniform1f(_this.minLoc, min); gpgpu.gl.uniform1f(_this.maxLoc, max); }; }; return ClipPackedProgram; }()); var ComplexAbsProgram = (function () { function ComplexAbsProgram(shape) { this.variableNames = ['real', 'imag']; this.outputShape = shape; this.userCode = "\n void main() {\n float re = abs(getRealAtOutCoords());\n float im = abs(getImagAtOutCoords());\n float mx = max(re, im);\n\n // sadly the length function in glsl is not underflow-safe\n // (at least not on Intel GPUs). So the safe solution is\n // to ensure underflow-safety in all cases.\n setOutput(\n mx == 0.0 ? 0.0 : mx * length(vec2(1, min(re, im)/mx))\n );\n }\n "; } return ComplexAbsProgram; }()); var ConcatProgram = (function () { function ConcatProgram(shapes) { this.outputShape = []; this.outputShape = computeOutShape(shapes, 1); this.variableNames = shapes.map(function (_, i) { return "T" + i; }); var offsets = new Array(shapes.length - 1); offsets[0] = shapes[0][1]; for (var i = 1; i < offsets.length; i++) { offsets[i] = offsets[i - 1] + shapes[i][1]; } var snippets = ["if (yC < " + offsets[0] + ") setOutput(getT0(yR, yC));"]; for (var i = 1; i < offsets.length; i++) { var shift = offsets[i - 1]; snippets.push("else if (yC < " + offsets[i] + ") " + ("setOutput(getT" + i + "(yR, yC-" + shift + "));")); } var lastIndex = offsets.length; var lastShift = offsets[offsets.length - 1]; snippets.push("else setOutput(getT" + lastIndex + "(yR, yC-" + lastShift + "));"); this.userCode = "\n void main() {\n ivec2 coords = getOutputCoords();\n int yR = coords.x;\n int yC = coords.y;\n\n " + snippets.join('\n ') + "\n }\n "; } return ConcatProgram; }()); var Conv2DDerFilterProgram = (function () { function Conv2DDerFilterProgram(convInfo) { this.variableNames = ['x', 'dy']; this.outputShape = convInfo.filterShape; var strideHeight = convInfo.strideHeight; var strideWidth = convInfo.strideWidth; var padTop = convInfo.padInfo.top; var padLeft = convInfo.padInfo.left; this.userCode = "\n void main() {\n ivec4 coords = getOutputCoords();\n int wR = coords.x;\n int wC = coords.y;\n int d1 = coords.z;\n int d2 = coords.w;\n\n // Convolve x(?, ?, d1) with dy(:, :, d2) to get dw(wR, wC, d1, d2).\n // ? = to be determined. : = across all values in that axis.\n float dotProd = 0.0;\n\n for (int b = 0; b < " + convInfo.batchSize + "; b++) {\n for (int yR = 0; yR < " + convInfo.outHeight + "; yR++) {\n int xR = wR + yR * " + strideHeight + " - " + padTop + ";\n\n if (xR < 0 || xR >= " + convInfo.inHeight + ") {\n continue;\n }\n\n for (int yC = 0; yC < " + convInfo.outWidth + "; yC++) {\n int xC = wC + yC * " + strideWidth + " - " + padLeft + ";\n\n if (xC < 0 || xC >= " + convInfo.inWidth + ") {\n continue;\n }\n\n float dyValue = getDy(b, yR, yC, d2);\n float xValue = getX(b, xR, xC, d1);\n dotProd += (xValue * dyValue);\n }\n }\n }\n setOutput(dotProd);\n }\n "; } return Conv2DDerFilterProgram; }()); var Conv2DDerInputProgram = (function () { function Conv2DDerInputProgram(convInfo) { this.variableNames = ['dy', 'W']; this.outputShape = convInfo.inShape; var filterHeight = convInfo.filterHeight; var filterWidth = convInfo.filterWidth; var strideHeight = convInfo.strideHeight; var strideWidth = convInfo.strideWidth; var padTop = filterHeight - 1 - convInfo.padInfo.top; var padLeft = filterWidth - 1 - convInfo.padInfo.left; this.userCode = "\n const ivec2 pads = ivec2(" + padTop + ", " + padLeft + ");\n\n void main() {\n ivec4 coords = getOutputCoords();\n int batch = coords[0];\n int d1 = coords[3];\n\n ivec2 dyCorner = coords.yz - pads;\n int dyRCorner = dyCorner.x;\n int dyCCorner = dyCorner.y;\n\n // Convolve dy(?, ?, d2) with w(:, :, d1, d2) to compute dx(xR, xC, d1).\n // ? = to be determined. : = across all values in that axis.\n float dotProd = 0.0;\n for (int wR = 0; wR < " + filterHeight + "; wR++) {\n float dyR = float(dyRCorner + wR) / " + strideHeight + ".0;\n\n if (dyR < 0.0 || dyR >= " + convInfo.outHeight + ".0 || fract(dyR) > 0.0) {\n continue;\n }\n int idyR = int(dyR);\n\n int wRPerm = " + filterHeight + " - 1 - wR;\n\n for (int wC = 0; wC < " + filterWidth + "; wC++) {\n float dyC = float(dyCCorner + wC) / " + strideWidth + ".0;\n\n if (dyC < 0.0 || dyC >= " + convInfo.outWidth + ".0 ||\n fract(dyC) > 0.0) {\n continue;\n }\n int idyC = int(dyC);\n\n int wCPerm = " + filterWidth + " - 1 - wC;\n\n for (int d2 = 0; d2 < " + convInfo.outChannels + "; d2++) {\n float xValue = getDy(batch, idyR, idyC, d2);\n float wValue = getW(wRPerm, wCPerm, d1, d2);\n dotProd += xValue * wValue;\n }\n }\n }\n setOutput(dotProd);\n }\n "; } return Conv2DDerInputProgram; }()); var Conv3DDerFilterProgram = (function () { function Conv3DDerFilterProgram(convInfo) { this.variableNames = ['x', 'dy']; this.outputShape = convInfo.filterShape; var strideDepth = convInfo.strideDepth; var strideHeight = convInfo.strideHeight; var strideWidth = convInfo.strideWidth; var padFront = convInfo.padInfo.front; var padTop = convInfo.padInfo.top; var padLeft = convInfo.padInfo.left; this.userCode = "\n void main() {\n ivec5 coords = getOutputCoords();\n int wF = coords.x;\n int wR = coords.y;\n int wC = coords.z;\n int d1 = coords.w;\n int d2 = coords.u;\n\n float dotProd = 0.0;\n\n for (int b = 0; b < " + convInfo.batchSize + "; b++) {\n for (int yF = 0; yF < " + convInfo.outDepth + "; yF++) {\n int xF = wF + yF * " + strideDepth + " - " + padFront + ";\n\n if (xF < 0 || xF >= " + convInfo.inDepth + ") {\n continue;\n }\n\n for (int yR = 0; yR < " + convInfo.outHeight + "; yR++) {\n int xR = wR + yR * " + strideHeight + " - " + padTop + ";\n\n if (xR < 0 || xR >= " + convInfo.inHeight + ") {\n continue;\n }\n\n for (int yC = 0; yC < " + convInfo.outWidth + "; yC++) {\n int xC = wC + yC * " + strideWidth + " - " + padLeft + ";\n\n if (xC < 0 || xC >= " + convInfo.inWidth + ") {\n continue;\n }\n\n float dyValue = getDy(b, yF, yR, yC, d2);\n float xValue = getX(b, xF, xR, xC, d1);\n dotProd += (xValue * dyValue);\n }\n }\n }\n }\n setOutput(dotProd);\n }\n "; } return Conv3DDerFilterProgram; }()); var Conv3DDerInputProgram = (function () { function Conv3DDerInputProgram(convInfo) { this.variableNames = ['dy', 'W']; this.outputShape = convInfo.inShape; var filterDepth = convInfo.filterDepth; var filterHeight = convInfo.filterHeight; var filterWidth = convInfo.filterWidth; var strideDepth = convInfo.strideDepth; var strideHeight = convInfo.strideHeight; var strideWidth = convInfo.strideWidth; var padFront = filterDepth - 1 - convInfo.padInfo.front; var padTop = filterHeight - 1 - convInfo.padInfo.top; var padLeft = filterWidth - 1 - convInfo.padInfo.left; this.userCode = "\n const ivec3 pads = ivec3(" + padFront + ", " + padTop + ", " + padLeft + ");\n\n void main() {\n ivec5 coords = getOutputCoords();\n int batch = coords.x;\n int d1 = coords.u;\n\n\n ivec3 dyCorner = ivec3(coords.y, coords.z, coords.w) - pads;\n int dyFCorner = dyCorner.x;\n int dyRCorner = dyCorner.y;\n int dyCCorner = dyCorner.z;\n\n float dotProd = 0.0;\n for (int wF = 0; wF < " + filterDepth + "; wF++) {\n float dyF = float(dyFCorner + wF) / " + strideDepth + ".0;\n\n if (dyF < 0.0 || dyF >= " + convInfo.outDepth + ".0 || fract(dyF) > 0.0) {\n continue;\n }\n int idyF = int(dyF);\n\n int wFPerm = " + filterDepth + " - 1 - wF;\n\n for (int wR = 0; wR < " + filterHeight + "; wR++) {\n float dyR = float(dyRCorner + wR) / " + strideHeight + ".0;\n\n if (dyR < 0.0 || dyR >= " + convInfo.outHeight + ".0 ||\n fract(dyR) > 0.0) {\n continue;\n }\n int idyR = int(dyR);\n\n int wRPerm = " + filterHeight + " - 1 - wR;\n\n for (int wC = 0; wC < " + filterWidth + "; wC++) {\n float dyC = float(dyCCorner + wC) / " + strideWidth + ".0;\n\n if (dyC < 0.0 || dyC >= " + convInfo.outWidth + ".0 ||\n fract(dyC) > 0.0) {\n continue;\n }\n int idyC = int(dyC);\n\n int wCPerm = " + filterWidth + " - 1 - wC;\n\n for (int d2 = 0; d2 < " + convInfo.outChannels + "; d2++) {\n float xValue = getDy(batch, idyF, idyR, idyC, d2);\n float wValue = getW(wFPerm, wRPerm, wCPerm, d1, d2);\n dotProd += xValue * wValue;\n }\n }\n }\n }\n setOutput(dotProd);\n }\n "; } return Conv3DDerInputProgram; }()); var DepthwiseConv2DDerFilterProgram = (function () { function DepthwiseConv2DDerFilterProgram(convInfo) { this.variableNames = ['x', 'dy']; this.outputShape = convInfo.filterShape; var strideHeight = convInfo.strideHeight; var strideWidth = convInfo.strideWidth; var padTop = convInfo.padInfo.top; var padLeft = convInfo.padInfo.left; var channelMul = convInfo.outChannels / convInfo.inChannels; this.userCode = "\n void main() {\n ivec4 coords = getOutputCoords();\n int wR = coords.x;\n int wC = coords.y;\n int d1 = coords.z;\n int dm = coords.w;\n int d2 = d1 * " + channelMul + " + dm;\n\n float dotProd = 0.0;\n\n // TODO: Vec4 over the batch size\n for (int b = 0; b < " + convInfo.batchSize + "; b++) {\n for (int yR = 0; yR < " + convInfo.outHeight + "; yR++) {\n int xR = wR + yR * " + strideHeight + " - " + padTop + ";\n\n if (xR < 0 || xR >= " + convInfo.inHeight + ") {\n continue;\n }\n\n for (int yC = 0; yC < " + convInfo.outWidth + "; yC++) {\n int xC = wC + yC * " + strideWidth + " - " + padLeft + ";\n\n if (xC < 0 || xC >= " + convInfo.inWidth + ") {\n continue;\n }\n\n float dyValue = getDy(b, yR, yC, d2);\n float xValue = getX(b, xR, xC, d1);\n dotProd += (xValue * dyValue);\n }\n }\n }\n setOutput(dotProd);\n }\n "; } return DepthwiseConv2DDerFilterProgram; }()); var DepthwiseConv2DDerInputProgram = (function () { function DepthwiseConv2DDerInputProgram(convInfo) { this.variableNames = ['dy', 'W']; this.outputShape = convInfo.inShape; var filterHeight = convInfo.filterHeight; var filterWidth = convInfo.filterWidth; var strideHeight = convInfo.strideHeight; var strideWidth = convInfo.strideWidth; var padTop = filterHeight - 1 - convInfo.padInfo.top; var padLeft = filterWidth - 1 - convInfo.padInfo.left; var channelMul = convInfo.outChannels / convInfo.inChannels; this.userCode = "\n const ivec2 pads = ivec2(" + padTop + ", " + padLeft + ");\n\n void main() {\n ivec4 coords = getOutputCoords();\n int batch = coords[0];\n int d1 = coords[3];\n ivec2 dyCorner = coords.yz - pads;\n int dyRCorner = dyCorner.x;\n int dyCCorner = dyCorner.y;\n\n float dotProd = 0.0;\n\n for (int wR = 0; wR < " + filterHeight + "; wR++) {\n float dyR = float(dyRCorner + wR) / " + strideHeight + ".0;\n\n if (dyR < 0.0 || dyR >= " + convInfo.outHeight + ".0 || fract(dyR) > 0.0) {\n continue;\n }\n int idyR = int(dyR);\n\n int wRPerm = " + filterHeight + " - 1 - wR;\n\n for (int wC = 0; wC < " + filterWidth + "; wC++) {\n float dyC = float(dyCCorner + wC) / " + strideWidth + ".0;\n\n if (dyC < 0.0 || dyC >= " + convInfo.outWidth + ".0 ||\n fract(dyC) > 0.0) {\n continue;\n }\n int idyC = int(dyC);\n\n int wCPerm = " + filterWidth + " - 1 - wC;\n\n // TODO: Vec4 over the channelMul\n for (int dm = 0; dm < " + channelMul + "; dm++) {\n int d2 = d1 * " + channelMul + " + dm;\n float xValue = getDy(batch, idyR, idyC, d2);\n float wValue = getW(wRPerm, wCPerm, d1, dm);\n dotProd += xValue * wValue;\n }\n }\n }\n setOutput(dotProd);\n }\n "; } return DepthwiseConv2DDerInputProgram; }()); var Conv2DProgram = (function () { function Conv2DProgram(convInfo) { this.variableNames = ['x', 'W']; this.outputShape = convInfo.outShape; var padTop = convInfo.padInfo.top; var padLeft = convInfo.padInfo.left; var strideHeight = convInfo.strideHeight; var strideWidth = convInfo.strideWidth; var dilationHeight = convInfo.dilationHeight; var dilationWidth = convInfo.dilationWidth; var filterHeight = convInfo.filterHeight; var filterWidth = convInfo.filterWidth; var inputDepthNearestVec4 = Math.floor(convInfo.inChannels / 4) * 4; var inputDepthVec4Remainder = convInfo.inChannels % 4; this.userCode = "\n const ivec2 strides = ivec2(" + strideHeight + ", " + strideWidth + ");\n const ivec2 pads = ivec2(" + padTop + ", " + padLeft + ");\n\n void main() {\n ivec4 coords = getOutputCoords();\n int batch = coords[0];\n int d2 = coords[3];\n\n ivec2 xRCCorner = coords.yz * strides - pads;\n int xRCorner = xRCCorner.x;\n int xCCorner = xRCCorner.y;\n\n // Convolve x(?, ?, d1) with w(:, :, d1, d2) to get y(yR, yC, d2).\n // ? = to be determined. : = across all values in that axis.\n float dotProd = 0.0;\n for (int wR = 0; wR < " + filterHeight + "; wR++) {\n int xR = xRCorner + wR * " + dilationHeight + ";\n\n if (xR < 0 || xR >= " + convInfo.inHeight + ") {\n continue;\n }\n\n for (int wC = 0; wC < " + filterWidth + "; wC++) {\n int xC = xCCorner + wC * " + dilationWidth + ";\n\n if (xC < 0 || xC >= " + convInfo.inWidth + ") {\n continue;\n }\n\n for (int d1 = 0; d1 < " + inputDepthNearestVec4 + "; d1 += 4) {\n vec4 xValues = vec4(\n getX(batch, xR, xC, d1),\n getX(batch, xR, xC, d1 + 1),\n getX(batch, xR, xC, d1 + 2),\n getX(batch, xR, xC, d1 + 3)\n );\n vec4 wValues = vec4(\n getW(wR, wC, d1, d2),\n getW(wR, wC, d1 + 1, d2),\n getW(wR, wC, d1 + 2, d2),\n getW(wR, wC, d1 + 3, d2)\n );\n\n dotProd += dot(xValues, wValues);\n }\n\n if (" + (inputDepthVec4Remainder === 1) + ") {\n dotProd +=\n getX(batch, xR, xC, " + inputDepthNearestVec4 + ") *\n getW(wR, wC, " + inputDepthNearestVec4 + ", d2);\n } else if (" + (inputDepthVec4Remainder === 2) + ") {\n vec2 xValues = vec2(\n getX(batch, xR, xC, " + inputDepthNearestVec4 + "),\n getX(batch, xR, xC, " + inputDepthNearestVec4 + " + 1)\n );\n vec2 wValues = vec2(\n getW(wR, wC, " + inputDepthNearestVec4 + ", d2),\n getW(wR, wC, " + inputDepthNearestVec4 + " + 1, d2)\n );\n dotProd += dot(xValues, wValues);\n } else if (" + (inputDepthVec4Remainder === 3) + ") {\n vec3 xValues = vec3(\n getX(batch, xR, xC, " + inputDepthNearestVec4 + "),\n getX(batch, xR, xC, " + inputDepthNearestVec4 + " + 1),\n getX(batch, xR, xC, " + inputDepthNearestVec4 + " + 2)\n );\n vec3 wValues = vec3(\n getW(wR, wC, " + inputDepthNearestVec4 + ", d2),\n getW(wR, wC, " + inputDepthNearestVec4 + " + 1, d2),\n getW(wR, wC, " + inputDepthNearestVec4 + " + 2, d2)\n );\n dotProd += dot(xValues, wValues);\n }\n }\n }\n setOutput(dotProd);\n }\n "; } return Conv2DProgram; }()); var Conv3DProgram = (function () { function Conv3DProgram(convInfo) { this.variableNames = ['x', 'W']; this.outputShape = convInfo.outShape; var padFront = convInfo.padInfo.front; var padTop = convInfo.padInfo.top; var padLeft = convInfo.padInfo.left; var strideDepth = convInfo.strideDepth; var strideHeight = convInfo.strideHeight; var strideWidth = convInfo.strideWidth; var dilationDepth = convInfo.dilationDepth; var dilationHeight = convInfo.dilationHeight; var dilationWidth = convInfo.dilationWidth; var filterDepth = convInfo.filterDepth; var filterHeight = convInfo.filterHeight; var filterWidth = convInfo.filterWidth; var inputDepthNearestVec4 = Math.floor(convInfo.inChannels / 4) * 4; var inputDepthVec4Remainder = convInfo.inChannels % 4; this.userCode = "\n const ivec3 strides = ivec3(" + strideDepth + ", " + strideHeight + ", " + strideWidth + ");\n const ivec3 pads = ivec3(" + padFront + ", " + padTop + ", " + padLeft + ");\n\n void main() {\n ivec5 coords = getOutputCoords();\n int batch = coords.x;\n int d2 = coords.u;\n\n ivec3 xFRCCorner = ivec3(coords.y, coords.z, coords.w) * strides - pads;\n int xFCorner = xFRCCorner.x;\n int xRCorner = xFRCCorner.y;\n int xCCorner = xFRCCorner.z;\n\n // Convolve x(?, ?, ?, d1) with w(:, :, :, d1, d2) to get\n // y(yF, yR, yC, d2). ? = to be determined. : = across all\n // values in that axis.\n float dotProd = 0.0;\n for (int wF = 0; wF < " + filterDepth + "; wF++) {\n int xF = xFCorner + wF * " + dilationDepth + ";\n\n if (xF < 0 || xF >= " + convInfo.inDepth + ") {\n continue;\n }\n\n for (int wR = 0; wR < " + filterHeight + "; wR++) {\n int xR = xRCorner + wR * " + dilationHeight + ";\n\n if (xR < 0 || xR >= " + convInfo.inHeight + ") {\n continue;\n }\n\n for (int wC = 0; wC < " + filterWidth + "; wC++) {\n int xC = xCCorner + wC * " + dilationWidth + ";\n\n if (xC < 0 || xC >= " + convInfo.inWidth + ") {\n continue;\n }\n\n for (int d1 = 0; d1 < " + inputDepthNearestVec4 + "; d1 += 4) {\n vec4 xValues = vec4(\n getX(batch, xF, xR, xC, d1),\n getX(batch, xF, xR, xC, d1 + 1),\n getX(batch, xF, xR, xC, d1 + 2),\n getX(batch, xF, xR, xC, d1 + 3)\n );\n vec4 wValues = vec4(\n getW(wF, wR, wC, d1, d2),\n getW(wF, wR, wC, d1 + 1, d2),\n getW(wF, wR, wC, d1 + 2, d2),\n getW(wF, wR, wC, d1 + 3, d2)\n );\n\n dotProd += dot(xValues, wValues);\n }\n\n if (" + (inputDepthVec4Remainder === 1) + ") {\n dotProd +=\n getX(batch, xF, xR, xC, " + inputDepthNearestVec4 + ") *\n getW(wF, wR, wC, " + inputDepthNearestVec4 + ", d2);\n } else if (" + (inputDepthVec4Remainder === 2) + ") {\n vec2 xValues = vec2(\n getX(batch, xF, xR, xC, " + inputDepthNearestVec4 + "),\n getX(batch, xF, xR, xC, " + inputDepthNearestVec4 + " + 1)\n );\n vec2 wValues = vec2(\n getW(wF, wR, wC, " + inputDepthNearestVec4 + ", d2),\n getW(wF, wR, wC, " + inputDepthNearestVec4 + " + 1, d2)\n );\n dotProd += dot(xValues, wValues);\n } else if (" + (inputDepthVec4Remainder === 3) + ") {\n vec3 xValues = vec3(\n getX(batch, xF, xR, xC, " + inputDepthNearestVec4 + "),\n getX(batch, xF, xR, xC, " + inputDepthNearestVec4 + " + 1),\n getX(batch, xF, xR, xC, " + inputDepthNearestVec4 + " + 2)\n );\n vec3 wValues = vec3(\n getW(wF, wR, wC, " + inputDepthNearestVec4 + ", d2),\n getW(wF, wR, wC, " + inputDepthNearestVec4 + " + 1, d2),\n getW(wF, wR, wC, " + inputDepthNearestVec4 + " + 2, d2)\n );\n dotProd += dot(xValues, wValues);\n }\n }\n }\n }\n setOutput(dotProd);\n }\n "; } return Conv3DProgram; }()); var DepthwiseConv2DProgram = (function () { function DepthwiseConv2DProgram(convInfo) { this.variableNames = ['x', 'W']; this.outputShape = convInfo.outShape; var xNumRows = convInfo.inHeight; var xNumCols = convInfo.inWidth; var padTop = convInfo.padInfo.top; var padLeft = convInfo.padInfo.left; var strideHeight = convInfo.strideHeight; var strideWidth = convInfo.strideWidth; var dilationHeight = convInfo.dilationHeight; var dilationWidth = convInfo.dilationWidth; var filterHeight = convInfo.filterHeight; var filterWidth = convInfo.filterWidth; var channelMul = convInfo.outChannels / convInfo.inChannels; this.userCode = "\n const ivec2 strides = ivec2(" + strideHeight + ", " + strideWidth + ");\n const ivec2 pads = ivec2(" + padTop + ", " + padLeft + ");\n\n void main() {\n ivec4 coords = getOutputCoords();\n int batch = coords.x;\n ivec2 xRCCorner = coords.yz * strides - pads;\n int d2 = coords.w;\n int d1 = d2 / " + channelMul + ";\n int q = d2 - d1 * " + channelMul + ";\n\n int xRCorner = xRCCorner.x;\n int xCCorner = xRCCorner.y;\n\n // Convolve x(?, ?, d1) with w(:, :, d1, q) to get y(yR, yC, d2).\n // ? = to be determined. : = across all values in that axis.\n float dotProd = 0.0;\n // TODO(dsmilkov): Flatten the two for loops and vec4 the operations.\n for (int wR = 0; wR < " + filterHeight + "; wR++) {\n int xR = xRCorner + wR * " + dilationHeight + ";\n\n if (xR < 0 || xR >= " + xNumRows + ") {\n continue;\n }\n\n for (int wC = 0; wC < " + filterWidth + "; wC++) {\n int xC = xCCorner + wC * " + dilationWidth + ";\n\n if (xC < 0 || xC >= " + xNumCols + ") {\n continue;\n }\n\n float xVal = getX(batch, xR, xC, d1);\n float wVal = getW(wR, wC, d1, q);\n dotProd += xVal * wVal;\n }\n }\n setOutput(dotProd);\n }\n "; } return DepthwiseConv2DProgram; }()); var DepthwiseConvPacked2DProgram = (function () { function DepthwiseConvPacked2DProgram(convInfo) { this.variableNames = ['x', 'W']; this.usesPackedTextures = true; this.outputShape = convInfo.outShape; var xNumRows = convInfo.inHeight; var xNumCols = convInfo.inWidth; var padTop = convInfo.padInfo.top; var padLeft = convInfo.padInfo.left; var strideHeight = convInfo.strideHeight; var strideWidth = convInfo.strideWidth; var dilationHeight = convInfo.dilationHeight; var dilationWidth = convInfo.dilationWidth; var filterHeight = convInfo.filterHeight; var filterWidth = convInfo.filterWidth; var texelsAcross = filterWidth; var mainLoop = "int xR; int xC; int xCOffset;"; for (var r = 0; r < filterHeight; r++) { for (var c = 0; c < filterWidth; c++) { mainLoop += "\n vec4 xTexelR" + r + "C" + c * 2 + " = vec4(0.);\n vec4 wR" + r + "C" + c + " = vec4(0.);\n vec4 xR" + r + "C" + c + " = vec4(0.);"; } } for (var r = 0; r < filterHeight; r++) { for (var texelC = 0; texelC < texelsAcross; texelC++) { var c = texelC * 2; mainLoop += "\n xR = xRCorner + " + r * dilationHeight + ";\n xC = xCCorner + " + c * dilationWidth + ";\n "; if (strideWidth === 1) { if (c < filterWidth) { if (padLeft % 2 === 1) { mainLoop += "\n xCOffset = xC + 1;\n if(xR >= 0 && xR < " + xNumRows + " && xCOffset >= 0 && xCOffset < " + xNumCols + ") {\n xTexelR" + r + "C" + c + " = getX(batch, xR, xCOffset, d1);\n } else {\n xTexelR" + r + "C" + c + " = vec4(0.);\n }\n\n xCOffset = xC + 1 - 2;\n if(xR >= 0 && xR < " + xNumRows + " && xCOffset >= 0 && xCOffset < " + xNumCols + ") {\n vec4 previous = getX(batch, xR, xCOffset, d1);\n xR" + r + "C" + c + " = vec4(previous.zw, xTexelR" + r + "C" + c + ".xy);\n } else {\n xR" + r + "C" + c + " = vec4(0, 0, xTexelR" + r + "C" + c + ".xy);\n }\n "; } else { mainLoop += "\n if(xR >= 0 && xR < " + xNumRows + " && xC >= 0 && xC < " + xNumCols + ") {\n xTexelR" + r + "C" + c + " = getX(batch, xR, xC, d1);\n } else {\n xTexelR" + r + "C" + c + " = vec4(0.);\n }\n\n xR" + r + "C" + c + " = xTexelR" + r + "C" + c + ";\n "; } if (c + 1 < filterWidth) { var nextTexelOffset = padLeft % 2 === 0 ? nearestLargerEven(dilationWidth) : dilationWidth; if ((dilationWidth % 2 === 0 && padLeft % 2 === 1) || (dilationWidth % 2 !== 0 && padLeft % 2 !== 1)) { mainLoop += "\n xCOffset = xC + " + padLeft % 2 + " + " + nextTexelOffset + ";\n\n if(xR >= 0 && xR < " + xNumRows + " &&\n xCOffset >= 0 && xCOffset < " + xNumCols + ") {\n xTexelR" + r + "C" + (c + 2) + " = getX(batch, xR, xCOffset, d1);\n }\n "; if (dilationWidth > 1) { mainLoop += "\n xCOffset -= 2;\n if(xR >= 0 && xR < " + xNumRows + " &&\n xCOffset >= 0 && xCOffset < " + xNumCols + ") {\n xTexelR" + r + "C" + c + " = getX(batch, xR, xCOffset, d1);\n } else {\n xTexelR" + r + "C" + c + " = vec4(0.);\n }\n "; } mainLoop += "\n xR" + r + "C" + (c + 1) + " = vec4(\n xTexelR" + r + "C" + c + ".zw, xTexelR" + r + "C" + (c + 2) + ".xy);\n "; } else { mainLoop += "\n xCOffset = xC + " + nextTexelOffset + ";\n\n if(xR >= 0 && xR < " + xNumRows + " &&\n xCOffset >= 0 && xCOffset < " + xNumCols + ") {\n xTexelR" + r + "C" + (c + 2) + " = getX(batch, xR, xCOffset, d1);\n }\n\n xR" + r + "C" + (c + 1) + " = xTexelR" + r + "C" + (c + 2) + ";\n "; } } } } else { if (c < filterWidth) { mainLoop += "\n if(xR >= 0 && xR < " + xNumRows + ") {\n "; if (padLeft % 2 === 1) { mainLoop += "\n xCOffset = xC + 1 - " + strideWidth + ";\n if(xCOffset >= 0 && xCOffset < " + xNumCols + ") {\n xTexelR" + r + "C" + c + " = getX(batch, xR, xCOffset, d1);\n } else {\n xTexelR" + r + "C" + c + " = vec4(0.);\n }\n\n if(xC + 1 >= 0 && xC + 1 < " + xNumCols + ") {\n xTexelR" + r + "C" + (c + 2) + " = getX(batch, xR, xC + 1, d1);\n } else {\n xTexelR" + r + "C" + (c + 2) + " = vec4(0.);\n }\n\n xR" + r + "C" + c + " = vec4(\n xTexelR" + r + "C" + c + ".zw, xTexelR" + r + "C" + (c + 2) + ".zw);\n "; if (c + 1 < filterWidth) { mainLoop += "\n vec4 final = vec4(0.);\n xCOffset = xC + 1 + " + strideWidth + ";\n if(xCOffset >= 0 && xCOffset < " + xNumCols + ") {\n final = getX(batch, xR, xCOffset, d1);\n }\n xR" + r + "C" + (c + 1) + " = vec4(xTexelR" + r + "C" + (c + 2) + ".xy, final.xy);\n "; } } else { mainLoop += "\n if(xC >= 0 && xC < " + xNumCols + ") {\n xTexelR" + r + "C" + c + " = getX(batch, xR, xC, d1);\n } else {\n xTexelR" + r + "C" + c + " = vec4(0.);\n }\n\n xCOffset = xC + " + strideWidth + ";\n if(xCOffset >= 0 && xCOffset < " + xNumCols + ") {\n xTexelR" + r + "C" + (c + 2) + " = getX(batch, xR, xCOffset, d1);\n } else {\n xTexelR" + r + "C" + (c + 2) + " = vec4(0.);\n }\n\n xR" + r + "C" + c + " = vec4(\n xTexelR" + r + "C" + c + ".xy, xTexelR" + r + "C" + (c + 2) + ".xy);\n "; if (c + 1 < filterWidth) { mainLoop += "\n xR" + r + "C" + (c + 1) + " = vec4(\n xTexelR" + r + "C" + c + ".zw, xTexelR" + r + "C" + (c + 2) + ".zw);\n "; } } mainLoop += "}"; } } if (c < filterWidth) { mainLoop += "\n vec4 wTexelR" + r + "C" + c + " = getW(" + r + ", " + c + ", d1, q);\n wR" + r + "C" + c + " = vec4(wTexelR" + r + "C" + c + ".xz, wTexelR" + r + "C" + c + ".xz);\n "; if (c + 1 < filterWidth) { mainLoop += "\n vec4 wTexelR" + r + "C" + (c + 1) + " = getW(" + r + ", " + (c + 1) + ", d1, q);\n wR" + r + "C" + (c + 1) + " =\n vec4(wTexelR" + r + "C" + (c + 1) + ".xz, wTexelR" + r + "C" + (c + 1) + ".xz);"; } } } } for (var r = 0; r < filterHeight; r++) { for (var c = 0; c < filterWidth; c++) { mainLoop += "result += xR" + r + "C" + c + " * wR" + r + "C" + c + ";"; } } this.userCode = "\n const ivec2 strides = ivec2(" + strideHeight + ", " + strideWidth + ");\n const ivec2 pads = ivec2(" + padTop + ", " + padLeft + ");\n\n void main() {\n\n ivec4 coords = getOutputCoords();\n int batch = coords.x;\n ivec2 xRCCorner = coords.yz * strides - pads;\n int d2 = coords.w;\n int d1 = d2;\n int q = 0;\n int xRCorner = xRCCorner.x;\n int xCCorner = xRCCorner.y;\n\n vec4 result = vec4(0.);\n\n " + mainLoop + "\n\n setOutput(result);\n }\n "; } return DepthwiseConvPacked2DProgram; }()); var CropAndResizeProgram = (function () { function CropAndResizeProgram(imageShape, boxShape, cropSize, method, extrapolationValue) { this.variableNames = ['Image', 'Boxes', 'BoxInd']; this.outputShape = []; var batch = imageShape[0], imageHeight = imageShape[1], imageWidth = imageShape[2], depth = imageShape[3]; var numBoxes = boxShape[0]; var cropHeight = cropSize[0], cropWidth = cropSize[1]; this.outputShape = [numBoxes, cropHeight, cropWidth, depth]; var methodId = method === 'bilinear' ? 1 : 0; var _a = [imageHeight - 1 + ".0", imageWidth - 1 + ".0"], inputHeightFloat = _a[0], inputWidthFloat = _a[1]; var _b = cropHeight > 1 ? [ "" + (imageHeight - 1) / (cropHeight - 1), '(y2-y1) * height_ratio', "y1*" + inputHeightFloat + " + float(y)*(height_scale)", ] : [ '0.0', '0.0', "0.5 * (y1+y2) * " + inputHeightFloat, ], heightRatio = _b[0], heightScale = _b[1], inY = _b[2]; var _c = cropWidth > 1 ? [ "" + (imageWidth - 1) / (cropWidth - 1), '(x2-x1) * width_ratio', "x1*" + inputWidthFloat + " + float(x)*(width_scale)", ] : [ '0.0', '0.0', "0.5 * (x1+x2) * " + inputWidthFloat, ], widthRatio = _c[0], widthScale = _c[1], inX = _c[2]; this.userCode = "\n const float height_ratio = float(" + heightRatio + ");\n const float width_ratio = float(" + widthRatio + ");\n void main() {\n ivec4 coords = getOutputCoords();\n int b = coords[0];\n int y = coords[1];\n int x = coords[2];\n int d = coords[3];\n\n // get box vals\n float y1 = getBoxes(b,0);\n float x1 = getBoxes(b,1);\n float y2 = getBoxes(b,2);\n float x2 = getBoxes(b,3);\n\n // get image in batch index\n int bInd = round(getBoxInd(b));\n if(bInd < 0 || bInd >= " + batch + ") {\n return;\n }\n\n float height_scale = " + heightScale + ";\n float width_scale = " + widthScale + ";\n\n float in_y = " + inY + ";\n if( in_y < 0.0 || in_y > " + inputHeightFloat + " ) {\n setOutput(float(" + extrapolationValue + "));\n return;\n }\n float in_x = " + inX + ";\n if( in_x < 0.0 || in_x > " + inputWidthFloat + " ) {\n setOutput(float(" + extrapolationValue + "));\n return;\n }\n\n vec2 sourceFracIndexRC = vec2(in_y,in_x);\n if(" + methodId + " == 1) {\n // Compute the four integer indices.\n ivec2 sourceFloorRC = ivec2(sourceFracIndexRC);\n ivec2 sourceCeilRC = ivec2(ceil(sourceFracIndexRC));\n\n float topLeft = getImage(b, sourceFloorRC.x, sourceFloorRC.y, d);\n float bottomLeft = getImage(b, sourceCeilRC.x, sourceFloorRC.y, d);\n float topRight = getImage(b, sourceFloorRC.x, sourceCeilRC.y, d);\n float bottomRight = getImage(b, sourceCeilRC.x, sourceCeilRC.y, d);\n\n vec2 fracRC = sourceFracIndexRC - vec2(sourceFloorRC);\n\n float top = topLeft + (topRight - topLeft) * fracRC.y;\n float bottom = bottomLeft + (bottomRight - bottomLeft) * fracRC.y;\n float newValue = top + (bottom - top) * fracRC.x;\n setOutput(newValue);\n } else {\n // Compute the coordinators of nearest neighbor point.\n ivec2 sourceNearestRC = ivec2(floor(\n sourceFracIndexRC + vec2(0.5,0.5)));\n float newValue = getImage(b, sourceNearestRC.x, sourceNearestRC.y, d);\n setOutput(newValue);\n }\n }\n "; } return CropAndResizeProgram; }()); function getGlslDifferences() { var version; var attribute; var varyingVs; var varyingFs; var texture2D; var output; var defineOutput; var defineRound; if (ENV.get('WEBGL_VERSION') === 2) { version = '#version 300 es'; attribute = 'in'; varyingVs = 'out'; varyingFs = 'in'; texture2D = 'texture'; output = 'outputColor'; defineOutput = 'out vec4 outputColor;'; defineRound = "\n #define round(value) newRound(value)\n int newRound(float value) {\n return int(floor(value + 0.5));\n }\n\n ivec4 newRound(vec4 value) {\n return ivec4(floor(value + vec4(0.5)));\n }\n "; } else { version = ''; attribute = 'attribute'; varyingVs = 'varying'; varyingFs = 'varying'; texture2D = 'texture2D'; output = 'gl_FragColor'; defineOutput = ''; defineRound = "\n int round(float value) {\n return int(floor(value + 0.5));\n }\n\n ivec4 round(vec4 value) {\n return ivec4(floor(value + vec4(0.5)));\n }\n "; } return { version: version, attribute: attribute, varyingVs: varyingVs, varyingFs: varyingFs, texture2D: texture2D, output: output, defineOutput: defineOutput, defineRound: defineRound }; } function getLogicalCoordinatesFromFlatIndex(coords, shape, index) { if (index === void 0) { index = 'index'; } var strides = computeStrides(shape); return strides .map(function (stride, i) { var line1 = "int " + coords[i] + " = " + index + " / " + stride; var line2 = i === strides.length - 1 ? "int " + coords[i + 1] + " = " + index + " - " + coords[i] + " * " + stride : "index -= " + coords[i] + " * " + stride; return line1 + "; " + line2 + ";"; }) .join(''); } function buildVec(x) { if (x.length === 1) { return "" + x[0]; } return "vec" + x.length + "(" + x.join(',') + ")"; } function dotify(x, y) { if (x.length !== y.length) { throw new Error("Vectors to be dotted must be of the same length -" + ("got " + x.length + " and " + y.length)); } var slices = []; var nearestVec4 = Math.floor(x.length / 4); var nearestVec4Remainder = x.length % 4; for (var i = 0; i < nearestVec4; i++) { var xSlice = x.slice(i * 4, i * 4 + 4); var ySlice = y.slice(i * 4, i * 4 + 4); slices.push(buildVec(xSlice) + ", " + buildVec(ySlice)); } if (nearestVec4Remainder !== 0) { var xSlice = x.slice(nearestVec4 * 4); var ySlice = y.slice(nearestVec4 * 4); if (xSlice.length === 1) { xSlice = xSlice.map(function (d) { return "float(" + d + ")"; }); ySlice = ySlice.map(function (d) { return "float(" + d + ")"; }); } slices.push(buildVec(xSlice) + ", " + buildVec(ySlice)); } return slices.map(function (d, i) { return "dot(" + d + ")"; }).join('+'); } function makeShader(inputsInfo, outputShape, userCode, usesPackedTextures) { var prefixSnippets = []; inputsInfo.forEach(function (x) { var size = sizeFromShape(x.shapeInfo.logicalShape); if (x.shapeInfo.isUniform) { prefixSnippets.push("uniform float " + x.name + (size > 1 ? "[" + size + "]" : '') + ";"); } else { prefixSnippets.push("uniform sampler2D " + x.name + ";"); prefixSnippets.push("uniform int offset" + x.name + ";"); } }); var inputPrefixSnippet = prefixSnippets.join('\n'); var inputSamplingSnippet = inputsInfo .map(function (x) { return getInputSamplingSnippet(x, outputShape, usesPackedTextures); }) .join('\n'); var outTexShape = outputShape.texShape; var glsl = getGlslDifferences(); var floatTextureSampleSnippet = getFloatTextureSampleSnippet(glsl); var outputSamplingSnippet; var floatTextureSetOutputSnippet; var shaderPrefix = getShaderPrefix(glsl); if (outputShape.isPacked) { outputSamplingSnippet = getPackedOutputSamplingSnippet(outputShape.logicalShape, outTexShape); floatTextureSetOutputSnippet = getFloatTextureSetRGBASnippet(glsl); } else { outputSamplingSnippet = getOutputSamplingSnippet(outputShape.logicalShape, outTexShape); floatTextureSetOutputSnippet = getFloatTextureSetRSnippet(glsl); } if (usesPackedTextures) { shaderPrefix += SHADER_PACKED_PREFIX; } var source = [ shaderPrefix, floatTextureSampleSnippet, floatTextureSetOutputSnippet, inputPrefixSnippet, outputSamplingSnippet, inputSamplingSnippet, userCode ].join('\n'); return source; } function getSamplerFromInInfo(inInfo) { var shape = inInfo.shapeInfo.logicalShape; switch (shape.length) { case 0: return getSamplerScalar(inInfo); case 1: return getSampler1D(inInfo); case 2: return getSampler2D(inInfo); case 3: return getSampler3D(inInfo); case 4: return getSampler4D(inInfo); case 5: return getSampler5D(inInfo); case 6: return getSampler6D(inInfo); default: throw new Error(shape.length + "-D input sampling" + " is not yet supported"); } } function getPackedSamplerFromInInfo(inInfo) { var shape = inInfo.shapeInfo.logicalShape; switch (shape.length) { case 0: return getPackedSamplerScalar(inInfo); case 1: return getPackedSampler1D(inInfo); case 2: return getPackedSampler2D(inInfo); case 3: return getPackedSampler3D(inInfo); default: return getPackedSamplerND(inInfo); } } function getInputSamplingSnippet(inInfo, outShapeInfo, usesPackedTextures) { if (usesPackedTextures === void 0) { usesPackedTextures = false; } var res = ''; if (usesPackedTextures) { res += getPackedSamplerFromInInfo(inInfo); } else { res += getSamplerFromInInfo(inInfo); } var inShape = inInfo.shapeInfo.logicalShape; var outShape = outShapeInfo.logicalShape; if (inShape.length <= outShape.length) { if (usesPackedTextures) { if (getBroadcastDims(inShape, outShape).length === 0) { res += getPackedSamplerAtOutputCoords(inInfo, outShapeInfo); } } else { res += getSamplerAtOutputCoords(inInfo, outShapeInfo); } } return res; } function getPackedOutputSamplingSnippet(outShape, outTexShape) { switch (outShape.length) { case 0: return getOutputScalarCoords(); case 1: return getOutputPacked1DCoords(outShape, outTexShape); case 2: return getOutputPacked2DCoords(outShape, outTexShape); case 3: return getOutputPacked3DCoords(outShape, outTexShape); default: return getOutputPackedNDCoords(outShape, outTexShape); } } function getOutputSamplingSnippet(outShape, outTexShape) { switch (outShape.length) { case 0: return getOutputScalarCoords(); case 1: return getOutput1DCoords(outShape, outTexShape); case 2: return getOutput2DCoords(outShape, outTexShape); case 3: return getOutput3DCoords(outShape, outTexShape); case 4: return getOutput4DCoords(outShape, outTexShape); case 5: return getOutput5DCoords(outShape, outTexShape); case 6: return getOutput6DCoords(outShape, outTexShape); default: throw new Error(outShape.length + "-D output sampling is not yet supported"); } } function getFloatTextureSampleSnippet(glsl) { return "\n float sampleTexture(sampler2D textureSampler, vec2 uv) {\n return " + glsl.texture2D + "(textureSampler, uv).r;\n }\n "; } function getFloatTextureSetRSnippet(glsl) { return "\n void setOutput(float val) {\n " + glsl.output + " = vec4(val, 0, 0, 0);\n }\n "; } function getFloatTextureSetRGBASnippet(glsl) { return "\n void setOutput(vec4 val) {\n " + glsl.output + " = val;\n }\n "; } function getShaderPrefix(glsl) { var NAN_CHECKS = ''; if (ENV.get('PROD')) { NAN_CHECKS = "\n bool isNaN(float val) {\n return false;\n }\n\n bool hasNaN(vec4 values) {\n return false;\n }\n "; } else { NAN_CHECKS = "\n bool isNaN(float val) {\n return (val < 1.0 || 0.0 < val || val == 0.0) ? false : true;\n }\n\n bool hasNaN(vec4 values) {\n return any(bvec4(\n isNaN(values.x),\n isNaN(values.y),\n isNaN(values.z),\n isNaN(values.w)\n ));\n }\n "; } var SHADER_PREFIX = glsl.version + "\n precision highp float;\n precision highp int;\n precision highp sampler2D;\n " + glsl.varyingFs + " vec2 resultUV;\n " + glsl.defineOutput + "\n const vec2 halfCR = vec2(0.5, 0.5);\n\n struct ivec5\n {\n int x;\n int y;\n int z;\n int w;\n int u;\n };\n\n struct ivec6\n {\n int x;\n int y;\n int z;\n int w;\n int u;\n int v;\n };\n\n " + NAN_CHECKS + "\n\n float getNaN(vec4 values) {\n return dot(vec4(1), values);\n }\n\n " + glsl.defineRound + "\n\n int imod(int x, int y) {\n return x - y * (x / y);\n }\n\n //Based on the work of Dave Hoskins\n //https://www.shadertoy.com/view/4djSRW\n #define HASHSCALE1 443.8975\n float random(float seed){\n vec2 p = resultUV * seed;\n vec3 p3 = fract(vec3(p.xyx) * HASHSCALE1);\n p3 += dot(p3, p3.yzx + 19.19);\n return fract((p3.x + p3.y) * p3.z);\n }\n\n " + SAMPLE_1D_SNIPPET + "\n " + SAMPLE_2D_SNIPPET + "\n " + SAMPLE_3D_SNIPPET + "\n " + SAMPLE_5D_SNIPPET + "\n " + SAMPLE_6D_SNIPPET + "\n "; return SHADER_PREFIX; } var SAMPLE_1D_SNIPPET = "\nvec2 uvFromFlat(int texNumR, int texNumC, int index) {\n int texR = index / texNumC;\n int texC = index - texR * texNumC;\n return (vec2(texC, texR) + halfCR) / vec2(texNumC, texNumR);\n}\nvec2 packedUVfrom1D(int texNumR, int texNumC, int index) {\n int texelIndex = index / 2;\n int texR = texelIndex / texNumC;\n int texC = texelIndex - texR * texNumC;\n return (vec2(texC, texR) + halfCR) / vec2(texNumC, texNumR);\n}\n"; var SAMPLE_2D_SNIPPET = "\nvec2 packedUVfrom2D(int texelsInLogicalRow, int texNumR,\n int texNumC, int row, int col) {\n int texelIndex = (row / 2) * texelsInLogicalRow + (col / 2);\n int texR = texelIndex / texNumC;\n int texC = texelIndex - texR * texNumC;\n return (vec2(texC, texR) + halfCR) / vec2(texNumC, texNumR);\n}\n"; var SAMPLE_3D_SNIPPET = "\nvec2 packedUVfrom3D(int texNumR, int texNumC,\n int texelsInBatch, int texelsInLogicalRow, int b,\n int row, int col) {\n int index = b * texelsInBatch + (row / 2) * texelsInLogicalRow + (col / 2);\n int texR = index / texNumC;\n int texC = index - texR * texNumC;\n return (vec2(texC, texR) + halfCR) / vec2(texNumC, texNumR);\n}\n"; var SAMPLE_5D_SNIPPET = "\nvec2 UVfrom5D(int texNumR, int texNumC, int stride0,\n int stride1, int stride2, int stride3, int row, int col, int depth,\n int depth2, int depth3) {\n // Explicitly use integer operations as dot() only works on floats.\n int index = row * stride0 + col * stride1 +\n depth * stride2 + depth2 * stride3 + depth3;\n int texR = index / texNumC;\n int texC = index - texR * texNumC;\n return (vec2(texC, texR) + halfCR) / vec2(texNumC, texNumR);\n}\n"; var SAMPLE_6D_SNIPPET = "\nvec2 UVfrom6D(int texNumR, int texNumC, int stride0,\n int stride1, int stride2, int stride3, int stride4,\n int row, int col, int depth, int depth2, int depth3, int depth4) {\n // Explicitly use integer operations as dot() only works on floats.\n int index = row * stride0 + col * stride1 + depth * stride2 + depth2 *\n stride3 + depth3 * stride4 + depth4;\n int texR = index / texNumC;\n int texC = index - texR * texNumC;\n return (vec2(texC, texR) + halfCR) / vec2(texNumC, texNumR);\n}\n"; var SHADER_PACKED_PREFIX = "\n float getChannel(vec4 frag, vec2 innerDims) {\n vec2 modCoord = mod(innerDims, 2.);\n return modCoord.x == 0. ?\n (modCoord.y == 0. ? frag.r : frag.g) :\n (modCoord.y == 0. ? frag.b : frag.a);\n }\n float getChannel(vec4 frag, int dim) {\n float modCoord = mod(float(dim), 2.);\n return modCoord == 0. ? frag.r : frag.g;\n }\n"; function getOutputScalarCoords() { return "\n int getOutputCoords() {\n return 0;\n }\n "; } function getOutputPacked1DCoords(shape, texShape) { var packedTexShape = [Math.ceil(texShape[0] / 2), Math.ceil(texShape[1] / 2)]; if (packedTexShape[0] === 1) { return "\n int getOutputCoords() {\n return 2 * int(resultUV.x * " + packedTexShape[1] + ".0);\n }\n "; } if (packedTexShape[1] === 1) { return "\n int getOutputCoords() {\n return 2 * int(resultUV.y * " + packedTexShape[0] + ".0);\n }\n "; } return "\n int getOutputCoords() {\n ivec2 resTexRC = ivec2(resultUV.yx *\n vec2(" + packedTexShape[0] + ", " + packedTexShape[1] + "));\n return resTexRC.x * " + packedTexShape[1] + " + resTexRC.y;\n }\n "; } function getOutput1DCoords(shape, texShape) { if (texShape[0] === 1) { return "\n int getOutputCoords() {\n return int(resultUV.x * " + texShape[1] + ".0);\n }\n "; } if (texShape[1] === 1) { return "\n int getOutputCoords() {\n return int(resultUV.y * " + texShape[0] + ".0);\n }\n "; } return "\n int getOutputCoords() {\n ivec2 resTexRC = ivec2(resultUV.yx *\n vec2(" + texShape[0] + ", " + texShape[1] + "));\n return resTexRC.x * " + texShape[1] + " + resTexRC.y;\n }\n "; } function getOutputPacked3DCoords(shape, texShape) { var packedTexShape = [Math.ceil(texShape[0] / 2), Math.ceil(texShape[1] / 2)]; var texelsInLogicalRow = Math.ceil(shape[2] / 2); var texelsInBatch = texelsInLogicalRow * Math.ceil(shape[1] / 2); return "\n ivec3 getOutputCoords() {\n ivec2 resTexRC = ivec2(resultUV.yx *\n vec2(" + packedTexShape[0] + ", " + packedTexShape[1] + "));\n int index = resTexRC.x * " + packedTexShape[1] + " + resTexRC.y;\n\n int b = index / " + texelsInBatch + ";\n index -= b * " + texelsInBatch + ";\n\n int r = 2 * (index / " + texelsInLogicalRow + ");\n int c = imod(index, " + texelsInLogicalRow + ") * 2;\n\n return ivec3(b, r, c);\n }\n "; } function getOutput3DCoords(shape, texShape) { var coordsFromIndexSnippet = getLogicalCoordinatesFromFlatIndex(['r', 'c', 'd'], shape); return "\n ivec3 getOutputCoords() {\n ivec2 resTexRC = ivec2(resultUV.yx *\n vec2(" + texShape[0] + ", " + texShape[1] + "));\n int index = resTexRC.x * " + texShape[1] + " + resTexRC.y;\n " + coordsFromIndexSnippet + "\n return ivec3(r, c, d);\n }\n "; } function getOutputPackedNDCoords(shape, texShape) { var packedTexShape = [Math.ceil(texShape[0] / 2), Math.ceil(texShape[1] / 2)]; var texelsInLogicalRow = Math.ceil(shape[shape.length - 1] / 2); var texelsInBatch = texelsInLogicalRow * Math.ceil(shape[shape.length - 2] / 2); var texelsInBatchN = texelsInBatch; var batches = ""; var coords = 'b, r, c'; for (var b = 2; b < shape.length - 1; b++) { texelsInBatchN *= shape[shape.length - b - 1]; batches = "\n int b" + b + " = index / " + texelsInBatchN + ";\n index -= b" + b + " * " + texelsInBatchN + ";\n " + batches; coords = "b" + b + ", " + coords; } return "\n ivec" + shape.length + " getOutputCoords() {\n ivec2 resTexRC = ivec2(resultUV.yx *\n vec2(" + packedTexShape[0] + ", " + packedTexShape[1] + "));\n int index = resTexRC.x * " + packedTexShape[1] + " + resTexRC.y;\n\n " + batches + "\n\n int b = index / " + texelsInBatch + ";\n index -= b * " + texelsInBatch + ";\n\n int r = 2 * (index / " + texelsInLogicalRow + ");\n int c = imod(index, " + texelsInLogicalRow + ") * 2;\n\n return ivec" + shape.length + "(" + coords + ");\n }\n "; } function getOutput4DCoords(shape, texShape) { var coordsFromIndexSnippet = getLogicalCoordinatesFromFlatIndex(['r', 'c', 'd', 'd2'], shape); return "\n ivec4 getOutputCoords() {\n ivec2 resTexRC = ivec2(resultUV.yx *\n vec2(" + texShape[0] + ", " + texShape[1] + "));\n int index = resTexRC.x * " + texShape[1] + " + resTexRC.y;\n " + coordsFromIndexSnippet + "\n return ivec4(r, c, d, d2);\n }\n "; } function getOutput5DCoords(shape, texShape) { var coordsFromIndexSnippet = getLogicalCoordinatesFromFlatIndex(['r', 'c', 'd', 'd2', 'd3'], shape); return "\n ivec5 getOutputCoords() {\n ivec2 resTexRC = ivec2(resultUV.yx * vec2(" + texShape[0] + ",\n " + texShape[1] + "));\n\n int index = resTexRC.x * " + texShape[1] + " + resTexRC.y;\n\n " + coordsFromIndexSnippet + "\n\n ivec5 outShape = ivec5(r, c, d, d2, d3);\n return outShape;\n }\n "; } function getOutput6DCoords(shape, texShape) { var coordsFromIndexSnippet = getLogicalCoordinatesFromFlatIndex(['r', 'c', 'd', 'd2', 'd3', 'd4'], shape); return "\n ivec6 getOutputCoords() {\n ivec2 resTexRC = ivec2(resultUV.yx *\n vec2(" + texShape[0] + ", " + texShape[1] + "));\n int index = resTexRC.x * " + texShape[1] + " + resTexRC.y;\n\n " + coordsFromIndexSnippet + "\n\n ivec6 result = ivec6(r, c, d, d2, d3, d4);\n return result;\n }\n "; } function getOutputPacked2DCoords(shape, texShape) { var packedTexShape = [Math.ceil(texShape[0] / 2), Math.ceil(texShape[1] / 2)]; if (arraysEqual(shape, texShape)) { return "\n ivec2 getOutputCoords() {\n return 2 * ivec2(resultUV.yx * vec2(" + packedTexShape[0] + ", " + packedTexShape[1] + "));\n }\n "; } var texelsInLogicalRow = Math.ceil(shape[1] / 2); return "\n ivec2 getOutputCoords() {\n ivec2 resTexRC = ivec2(resultUV.yx *\n vec2(" + packedTexShape[0] + ", " + packedTexShape[1] + "));\n\n int index = resTexRC.x * " + packedTexShape[1] + " + resTexRC.y;\n int r = 2 * (index / " + texelsInLogicalRow + ");\n int c = imod(index, " + texelsInLogicalRow + ") * 2;\n\n return ivec2(r, c);\n }\n "; } function getOutput2DCoords(shape, texShape) { if (arraysEqual(shape, texShape)) { return "\n ivec2 getOutputCoords() {\n return ivec2(resultUV.yx * vec2(" + texShape[0] + ", " + texShape[1] + "));\n }\n "; } if (shape[1] === 1) { return "\n ivec2 getOutputCoords() {\n ivec2 resTexRC = ivec2(resultUV.yx *\n vec2(" + texShape[0] + ", " + texShape[1] + "));\n int index = resTexRC.x * " + texShape[1] + " + resTexRC.y;\n return ivec2(index, 0);\n }\n "; } if (shape[0] === 1) { return "\n ivec2 getOutputCoords() {\n ivec2 resTexRC = ivec2(resultUV.yx *\n vec2(" + texShape[0] + ", " + texShape[1] + "));\n int index = resTexRC.x * " + texShape[1] + " + resTexRC.y;\n return ivec2(0, index);\n }\n "; } return "\n ivec2 getOutputCoords() {\n ivec2 resTexRC = ivec2(resultUV.yx *\n vec2(" + texShape[0] + ", " + texShape[1] + "));\n int index = resTexRC.x * " + texShape[1] + " + resTexRC.y;\n int r = index / " + shape[1] + ";\n int c = index - r * " + shape[1] + ";\n return ivec2(r, c);\n }\n "; } function getFlatOffsetUniformName(texName) { return "offset" + texName; } function getPackedSamplerScalar(inputInfo) { var texName = inputInfo.name; var funcName = 'get' + texName.charAt(0).toUpperCase() + texName.slice(1); var glsl = getGlslDifferences(); return "\n vec4 " + funcName + "() {\n return " + glsl.texture2D + "(" + texName + ", halfCR);\n }\n "; } function getSamplerScalar(inputInfo) { var texName = inputInfo.name; var funcName = 'get' + texName.charAt(0).toUpperCase() + texName.slice(1); if (inputInfo.shapeInfo.isUniform) { return "float " + funcName + "() {return " + texName + ";}"; } var _a = inputInfo.shapeInfo.texShape, texNumR = _a[0], texNumC = _a[1]; if (texNumR === 1 && texNumC === 1) { return "\n float " + funcName + "() {\n return sampleTexture(" + texName + ", halfCR);\n }\n "; } var _b = inputInfo.shapeInfo.texShape, tNumR = _b[0], tNumC = _b[1]; var offset = getFlatOffsetUniformName(texName); return "\n float " + funcName + "() {\n vec2 uv = uvFromFlat(" + tNumR + ", " + tNumC + ", " + offset + ");\n return sampleTexture(" + texName + ", uv);\n }\n "; } function getPackedSampler1D(inputInfo) { var texName = inputInfo.name; var funcName = 'get' + texName.charAt(0).toUpperCase() + texName.slice(1); var texShape = inputInfo.shapeInfo.texShape; var packedTexShape = [Math.ceil(texShape[0] / 2), Math.ceil(texShape[1] / 2)]; var glsl = getGlslDifferences(); return "\n vec4 " + funcName + "(int index) {\n vec2 uv = packedUVfrom1D(\n " + packedTexShape[0] + ", " + packedTexShape[1] + ", index);\n return " + glsl.texture2D + "(" + texName + ", uv);\n }\n "; } function getSampler1D(inputInfo) { var texName = inputInfo.name; var funcName = 'get' + texName.charAt(0).toUpperCase() + texName.slice(1); if (inputInfo.shapeInfo.isUniform) { return "\n float " + funcName + "(int index) {\n " + getUniformSampler(inputInfo) + "\n }\n "; } var texShape = inputInfo.shapeInfo.texShape; var tNumR = texShape[0]; var tNumC = texShape[1]; if (tNumC === 1 && tNumR === 1) { return "\n float " + funcName + "(int index) {\n return sampleTexture(" + texName + ", halfCR);\n }\n "; } var offset = getFlatOffsetUniformName(texName); if (tNumC === 1) { return "\n float " + funcName + "(int index) {\n vec2 uv = vec2(0.5, (float(index + " + offset + ") + 0.5) / " + tNumR + ".0);\n return sampleTexture(" + texName + ", uv);\n }\n "; } if (tNumR === 1) { return "\n float " + funcName + "(int index) {\n vec2 uv = vec2((float(index + " + offset + ") + 0.5) / " + tNumC + ".0, 0.5);\n return sampleTexture(" + texName + ", uv);\n }\n "; } return "\n float " + funcName + "(int index) {\n vec2 uv = uvFromFlat(" + tNumR + ", " + tNumC + ", index + " + offset + ");\n return sampleTexture(" + texName + ", uv);\n }\n "; } function getPackedSampler2D(inputInfo) { var shape = inputInfo.shapeInfo.logicalShape; var texName = inputInfo.name; var funcName = 'get' + texName.charAt(0).toUpperCase() + texName.slice(1); var texShape = inputInfo.shapeInfo.texShape; var texNumR = texShape[0]; var texNumC = texShape[1]; var glsl = getGlslDifferences(); if (texShape != null && arraysEqual(shape, texShape)) { return "\n vec4 " + funcName + "(int row, int col) {\n vec2 uv = (vec2(col, row) + halfCR) / vec2(" + texNumC + ".0, " + texNumR + ".0);\n\n return " + glsl.texture2D + "(" + texName + ", uv);\n }\n "; } var packedTexShape = [Math.ceil(texShape[0] / 2), Math.ceil(texShape[1] / 2)]; var valuesPerRow = Math.ceil(shape[1] / 2); return "\n vec4 " + funcName + "(int row, int col) {\n vec2 uv = packedUVfrom2D(" + valuesPerRow + ", " + packedTexShape[0] + ", " + packedTexShape[1] + ", row, col);\n return " + glsl.texture2D + "(" + texName + ", uv);\n }\n "; } function getSampler2D(inputInfo) { var shape = inputInfo.shapeInfo.logicalShape; var texName = inputInfo.name; var funcName = 'get' + texName.charAt(0).toUpperCase() + texName.slice(1); var texShape = inputInfo.shapeInfo.texShape; if (texShape != null && arraysEqual(shape, texShape)) { var texNumR_1 = texShape[0]; var texNumC_1 = texShape[1]; return "\n float " + funcName + "(int row, int col) {\n vec2 uv = (vec2(col, row) + halfCR) / vec2(" + texNumC_1 + ".0, " + texNumR_1 + ".0);\n return sampleTexture(" + texName + ", uv);\n }\n "; } var _a = squeezeShape(shape), newShape = _a.newShape, keptDims = _a.keptDims; var squeezedShape = newShape; if (squeezedShape.length < shape.length) { var newInputInfo = squeezeInputInfo(inputInfo, squeezedShape); var params = ['row', 'col']; return "\n " + getSamplerFromInInfo(newInputInfo) + "\n float " + funcName + "(int row, int col) {\n return " + funcName + "(" + getSqueezedParams(params, keptDims) + ");\n }\n "; } if (inputInfo.shapeInfo.isUniform) { return "\n float " + funcName + "(int row, int col) {\n int index = round(dot(vec2(row, col), vec2(" + shape[1] + ", 1)));\n " + getUniformSampler(inputInfo) + "\n }\n "; } var texNumR = texShape[0]; var texNumC = texShape[1]; var offset = getFlatOffsetUniformName(texName); if (texNumC === 1) { return "\n float " + funcName + "(int row, int col) {\n float index = dot(vec3(row, col, " + offset + "), vec3(" + shape[1] + ", 1, 1));\n vec2 uv = vec2(0.5, (index + 0.5) / " + texNumR + ".0);\n return sampleTexture(" + texName + ", uv);\n }\n "; } if (texNumR === 1) { return "\n float " + funcName + "(int row, int col) {\n float index = dot(vec3(row, col, " + offset + "), vec3(" + shape[1] + ", 1, 1));\n vec2 uv = vec2((index + 0.5) / " + texNumC + ".0, 0.5);\n return sampleTexture(" + texName + ", uv);\n }\n "; } return "\n float " + funcName + "(int row, int col) {\n // Explicitly use integer operations as dot() only works on floats.\n int index = row * " + shape[1] + " + col + " + offset + ";\n vec2 uv = uvFromFlat(" + texNumR + ", " + texNumC + ", index);\n return sampleTexture(" + texName + ", uv);\n }\n"; } function getPackedSampler3D(inputInfo) { var shape = inputInfo.shapeInfo.logicalShape; var texName = inputInfo.name; var funcName = 'get' + texName.charAt(0).toUpperCase() + texName.slice(1); var texShape = inputInfo.shapeInfo.texShape; var packedTexShape = [Math.ceil(texShape[0] / 2), Math.ceil(texShape[1] / 2)]; if (shape[0] === 1) { var squeezedShape = shape.slice(1); var keptDims = [1, 2]; var newInputInfo = squeezeInputInfo(inputInfo, squeezedShape); var params = ['b', 'row', 'col']; return "\n " + getPackedSamplerFromInInfo(newInputInfo) + "\n vec4 " + funcName + "(int b, int row, int col) {\n return " + funcName + "(" + getSqueezedParams(params, keptDims) + ");\n }\n "; } var texNumR = packedTexShape[0]; var texNumC = packedTexShape[1]; var valuesPerRow = Math.ceil(shape[2] / 2); var texelsInBatch = valuesPerRow * Math.ceil(shape[1] / 2); var glsl = getGlslDifferences(); return "\n vec4 " + funcName + "(int b, int row, int col) {\n vec2 uv = packedUVfrom3D(\n " + texNumR + ", " + texNumC + ", " + texelsInBatch + ", " + valuesPerRow + ", b, row, col);\n return " + glsl.texture2D + "(" + texName + ", uv);\n }\n "; } function getSampler3D(inputInfo) { var shape = inputInfo.shapeInfo.logicalShape; var texName = inputInfo.name; var funcName = 'get' + texName.charAt(0).toUpperCase() + texName.slice(1); var stride0 = shape[1] * shape[2]; var stride1 = shape[2]; var _a = squeezeShape(shape), newShape = _a.newShape, keptDims = _a.keptDims; var squeezedShape = newShape; if (squeezedShape.length < shape.length) { var newInputInfo = squeezeInputInfo(inputInfo, squeezedShape); var params = ['row', 'col', 'depth']; return "\n " + getSamplerFromInInfo(newInputInfo) + "\n float " + funcName + "(int row, int col, int depth) {\n return " + funcName + "(" + getSqueezedParams(params, keptDims) + ");\n }\n "; } if (inputInfo.shapeInfo.isUniform) { return "\n float " + funcName + "(int row, int col, int depth) {\n int index = round(dot(vec3(row, col, depth),\n vec3(" + stride0 + ", " + stride1 + ", 1)));\n " + getUniformSampler(inputInfo) + "\n }\n "; } var texShape = inputInfo.shapeInfo.texShape; var texNumR = texShape[0]; var texNumC = texShape[1]; var flatOffset = inputInfo.shapeInfo.flatOffset; if (texNumC === stride0 && flatOffset == null) { return "\n float " + funcName + "(int row, int col, int depth) {\n float texR = float(row);\n float texC = dot(vec2(col, depth), vec2(" + stride1 + ", 1));\n vec2 uv = (vec2(texC, texR) + halfCR) /\n vec2(" + texNumC + ".0, " + texNumR + ".0);\n return sampleTexture(" + texName + ", uv);\n }\n "; } if (texNumC === stride1 && flatOffset == null) { return "\n float " + funcName + "(int row, int col, int depth) {\n float texR = dot(vec2(row, col), vec2(" + shape[1] + ", 1));\n float texC = float(depth);\n vec2 uv = (vec2(texC, texR) + halfCR) / vec2(" + texNumC + ".0, " + texNumR + ".0);\n return sampleTexture(" + texName + ", uv);\n }\n "; } var offset = getFlatOffsetUniformName(texName); return "\n float " + funcName + "(int row, int col, int depth) {\n // Explicitly use integer operations as dot() only works on floats.\n int index = row * " + stride0 + " + col * " + stride1 + " + depth + " + offset + ";\n vec2 uv = uvFromFlat(" + texNumR + ", " + texNumC + ", index);\n return sampleTexture(" + texName + ", uv);\n }\n "; } function getPackedSamplerND(inputInfo) { var shape = inputInfo.shapeInfo.logicalShape; var rank = shape.length; var texName = inputInfo.name; var funcName = 'get' + texName.charAt(0).toUpperCase() + texName.slice(1); var texShape = inputInfo.shapeInfo.texShape; var packedTexShape = [Math.ceil(texShape[0] / 2), Math.ceil(texShape[1] / 2)]; var texNumR = packedTexShape[0]; var texNumC = packedTexShape[1]; var valuesPerRow = Math.ceil(shape[rank - 1] / 2); var texelsInBatch = valuesPerRow * Math.ceil(shape[rank - 2] / 2); var params = "int b, int row, int col"; var index = "b * " + texelsInBatch + " + (row / 2) * " + valuesPerRow + " + (col / 2)"; for (var b = 2; b < rank - 1; b++) { params = "int b" + b + ", " + params; texelsInBatch *= shape[rank - b - 1]; index = "b" + b + " * " + texelsInBatch + " + " + index; } var glsl = getGlslDifferences(); return "\n vec4 " + funcName + "(" + params + ") {\n int index = " + index + ";\n int texR = index / " + texNumC + ";\n int texC = index - texR * " + texNumC + ";\n vec2 uv = (vec2(texC, texR) + halfCR) / vec2(" + texNumC + ", " + texNumR + ");\n return " + glsl.texture2D + "(" + texName + ", uv);\n }\n "; } function getSampler4D(inputInfo) { var shape = inputInfo.shapeInfo.logicalShape; var texName = inputInfo.name; var funcName = 'get' + texName.charAt(0).toUpperCase() + texName.slice(1); var stride2 = shape[3]; var stride1 = shape[2] * stride2; var stride0 = shape[1] * stride1; var _a = squeezeShape(shape), newShape = _a.newShape, keptDims = _a.keptDims; if (newShape.length < shape.length) { var newInputInfo = squeezeInputInfo(inputInfo, newShape); var params = ['row', 'col', 'depth', 'depth2']; return "\n " + getSamplerFromInInfo(newInputInfo) + "\n float " + funcName + "(int row, int col, int depth, int depth2) {\n return " + funcName + "(" + getSqueezedParams(params, keptDims) + ");\n }\n "; } if (inputInfo.shapeInfo.isUniform) { return "\n float " + funcName + "(int row, int col, int depth, int depth2) {\n int index = round(dot(vec4(row, col, depth, depth2),\n vec4(" + stride0 + ", " + stride1 + ", " + stride2 + ", 1)));\n " + getUniformSampler(inputInfo) + "\n }\n "; } var flatOffset = inputInfo.shapeInfo.flatOffset; var texShape = inputInfo.shapeInfo.texShape; var texNumR = texShape[0]; var texNumC = texShape[1]; if (texNumC === stride0 && flatOffset == null) { return "\n float " + funcName + "(int row, int col, int depth, int depth2) {\n float texR = float(row);\n float texC =\n dot(vec3(col, depth, depth2),\n vec3(" + stride1 + ", " + stride2 + ", 1));\n vec2 uv = (vec2(texC, texR) + halfCR) /\n vec2(" + texNumC + ".0, " + texNumR + ".0);\n return sampleTexture(" + texName + ", uv);\n }\n "; } if (texNumC === stride2 && flatOffset == null) { return "\n float " + funcName + "(int row, int col, int depth, int depth2) {\n float texR = dot(vec3(row, col, depth),\n vec3(" + shape[1] * shape[2] + ", " + shape[2] + ", 1));\n float texC = float(depth2);\n vec2 uv = (vec2(texC, texR) + halfCR) /\n vec2(" + texNumC + ".0, " + texNumR + ".0);\n return sampleTexture(" + texName + ", uv);\n }\n "; } var offset = getFlatOffsetUniformName(texName); return "\n float " + funcName + "(int row, int col, int depth, int depth2) {\n // Explicitly use integer operations as dot() only works on floats.\n int index = row * " + stride0 + " + col * " + stride1 + " +\n depth * " + stride2 + " + depth2;\n vec2 uv = uvFromFlat(" + texNumR + ", " + texNumC + ", index + " + offset + ");\n return sampleTexture(" + texName + ", uv);\n }\n "; } function getSampler5D(inputInfo) { var shape = inputInfo.shapeInfo.logicalShape; var texName = inputInfo.name; var funcName = 'get' + texName.charAt(0).toUpperCase() + texName.slice(1); var stride3 = shape[4]; var stride2 = shape[3] * stride3; var stride1 = shape[2] * stride2; var stride0 = shape[1] * stride1; var _a = squeezeShape(shape), newShape = _a.newShape, keptDims = _a.keptDims; if (newShape.length < shape.length) { var newInputInfo = squeezeInputInfo(inputInfo, newShape); var params = ['row', 'col', 'depth', 'depth2', 'depth3']; return "\n " + getSamplerFromInInfo(newInputInfo) + "\n float " + funcName + "(int row, int col, int depth, int depth2, int depth3) {\n return " + funcName + "(" + getSqueezedParams(params, keptDims) + ");\n }\n "; } if (inputInfo.shapeInfo.isUniform) { return "\n float " + funcName + "(int row, int col, int depth, int depth2, int depth3) {\n float index = dot(\n vec4(row, col, depth, depth2),\n vec4(" + stride0 + ", " + stride1 + ", " + stride2 + ", " + stride3 + ")) +\n depth3;\n " + getUniformSampler(inputInfo) + "\n }\n "; } var flatOffset = inputInfo.shapeInfo.flatOffset; var texShape = inputInfo.shapeInfo.texShape; var texNumR = texShape[0]; var texNumC = texShape[1]; if (texNumC === stride0 && flatOffset == null) { return "\n float " + funcName + "(int row, int col, int depth, int depth2, int depth3) {\n int texR = row;\n float texC = dot(vec4(col, depth, depth2, depth3),\n vec4(" + stride1 + ", " + stride2 + ", " + stride3 + ", 1));\n vec2 uv = (vec2(texC, texR) + halfCR) /\n vec2(" + texNumC + ".0, " + texNumR + ".0);\n return sampleTexture(" + texName + ", uv);\n }\n "; } if (texNumC === stride3 && flatOffset == null) { return "\n float " + funcName + "(int row, int col, int depth, int depth2, int depth3) {\n float texR = dot(\n vec4(row, col, depth, depth2),\n vec4(" + shape[1] * shape[2] * shape[3] + ",\n " + shape[2] * shape[3] + ", " + shape[3] + ", 1));\n int texC = depth3;\n vec2 uv = (vec2(texC, texR) + halfCR) /\n vec2(" + texNumC + ".0, " + texNumR + ".0);\n return sampleTexture(" + texName + ", uv);\n }\n "; } var offset = getFlatOffsetUniformName(texName); return "\n float " + funcName + "(int row, int col, int depth, int depth2, int depth3) {\n // Explicitly use integer operations as dot() only works on floats.\n int index = row * " + stride0 + " + col * " + stride1 + " + depth * " + stride2 + " +\n depth2 * " + stride3 + " + depth3 + " + offset + ";\n vec2 uv = uvFromFlat(" + texNumR + ", " + texNumC + ", index);\n return sampleTexture(" + texName + ", uv);\n }\n "; } function getSampler6D(inputInfo) { var shape = inputInfo.shapeInfo.logicalShape; var texName = inputInfo.name; var funcName = 'get' + texName.charAt(0).toUpperCase() + texName.slice(1); var _a = squeezeShape(shape), newShape = _a.newShape, keptDims = _a.keptDims; if (newShape.length < shape.length) { var newInputInfo = squeezeInputInfo(inputInfo, newShape); var params = ['row', 'col', 'depth', 'depth2', 'depth3', 'depth4']; return "\n " + getSamplerFromInInfo(newInputInfo) + "\n float " + funcName + "(int row, int col, int depth,\n int depth2, int depth3, int depth4) {\n return " + funcName + "(" + getSqueezedParams(params, keptDims) + ");\n }\n "; } var stride4 = shape[5]; var stride3 = shape[4] * stride4; var stride2 = shape[3] * stride3; var stride1 = shape[2] * stride2; var stride0 = shape[1] * stride1; if (inputInfo.shapeInfo.isUniform) { return "\n float " + funcName + "(int row, int col, int depth,\n int depth2, int depth3, int depth4) {\n int index = round(dot(\n vec4(row, col, depth, depth2),\n vec4(" + stride0 + ", " + stride1 + ", " + stride2 + ", " + stride3 + ")) +\n dot(\n vec2(depth3, depth4),\n vec2(" + stride4 + ", 1)));\n " + getUniformSampler(inputInfo) + "\n }\n "; } var flatOffset = inputInfo.shapeInfo.flatOffset; var texShape = inputInfo.shapeInfo.texShape; var texNumR = texShape[0]; var texNumC = texShape[1]; if (texNumC === stride0 && flatOffset == null) { return "\n float " + funcName + "(int row, int col, int depth,\n int depth2, int depth3, int depth4) {\n int texR = row;\n float texC = dot(vec4(col, depth, depth2, depth3),\n vec4(" + stride1 + ", " + stride2 + ", " + stride3 + ", " + stride4 + ")) +\n float(depth4);\n vec2 uv = (vec2(texC, texR) + halfCR) /\n vec2(" + texNumC + ".0, " + texNumR + ".0);\n return sampleTexture(" + texName + ", uv);\n }\n "; } if (texNumC === stride4 && flatOffset == null) { return "\n float " + funcName + "(int row, int col, int depth,\n int depth2, int depth3, int depth4) {\n float texR = dot(vec4(row, col, depth, depth2),\n vec4(" + shape[1] * shape[2] * shape[3] * shape[4] + ",\n " + shape[2] * shape[3] * shape[4] + ",\n " + shape[3] * shape[4] + ",\n " + shape[4] + ")) + float(depth3);\n int texC = depth4;\n vec2 uv = (vec2(texC, texR) + halfCR) /\n vec2(" + texNumC + ".0, " + texNumR + ".0);\n return sampleTexture(" + texName + ", uv);\n }\n "; } var offset = getFlatOffsetUniformName(texName); return "\n float " + funcName + "(int row, int col, int depth,\n int depth2, int depth3, int depth4) {\n // Explicitly use integer operations as dot() only works on floats.\n int index = row * " + stride0 + " + col * " + stride1 + " + depth * " + stride2 + " +\n depth2 * " + stride3 + " + depth3 * " + stride4 + " + depth4 + " + offset + ";\n vec2 uv = uvFromFlat(" + texNumR + ", " + texNumC + ", index);\n return sampleTexture(" + texName + ", uv);\n }\n "; } function getUniformSampler(inputInfo) { var texName = inputInfo.name; var inSize = sizeFromShape(inputInfo.shapeInfo.logicalShape); if (inSize === 1) { return "return " + texName + ";"; } return "\n for (int i = 0; i < " + inSize + "; i++) {\n if (i == index) {\n return " + texName + "[i];\n }\n }\n "; } function getPackedSamplerAtOutputCoords(inputInfo, outShapeInfo) { var texName = inputInfo.name; var texFuncSnippet = texName.charAt(0).toUpperCase() + texName.slice(1); var funcName = 'get' + texFuncSnippet + 'AtOutCoords'; var outTexShape = outShapeInfo.texShape; var inTexShape = inputInfo.shapeInfo.texShape; var glsl = getGlslDifferences(); var inRank = inputInfo.shapeInfo.logicalShape.length; var outRank = outShapeInfo.logicalShape.length; if (!inputInfo.shapeInfo.isUniform && inRank === outRank && inputInfo.shapeInfo.flatOffset == null && arraysEqual(inTexShape, outTexShape)) { return "\n vec4 " + funcName + "() {\n return " + glsl.texture2D + "(" + texName + ", resultUV);\n }\n "; } var type = getCoordsDataType(outRank); var broadcastDims = getBroadcastDims(inputInfo.shapeInfo.logicalShape, outShapeInfo.logicalShape); var rankDiff = outRank - inRank; var coordsSnippet; var fields = ['x', 'y', 'z', 'w', 'u', 'v']; if (broadcastDims.length) { throw Error('Packed broadcast sampling is not implemented yet.'); } if (inRank === 0) { coordsSnippet = ''; } else if (outRank < 2 && broadcastDims.length >= 1) { coordsSnippet = 'coords = 0;'; } else { coordsSnippet = broadcastDims.map(function (d) { return "coords." + fields[d + rankDiff] + " = 0;"; }) .join('\n'); } var unpackedCoordsSnippet = ''; if (outRank < 2 && inRank > 0) { unpackedCoordsSnippet = 'coords'; } else { unpackedCoordsSnippet = inputInfo.shapeInfo.logicalShape .map(function (s, i) { return "coords." + fields[i + rankDiff]; }) .join(', '); } var output = "return outputValue;"; if (inRank === 1 && outRank > 1) { output = "\n return vec4(outputValue.xy, outputValue.xy);\n "; } else if (inRank === 0 && outRank > 0) { if (outRank === 1) { output = "\n return vec4(outputValue.x, outputValue.x, 0., 0.);\n "; } else { output = "\n return vec4(outputValue.x);\n "; } } return "\n vec4 " + funcName + "() {\n " + type + " coords = getOutputCoords();\n " + coordsSnippet + "\n vec4 outputValue = get" + texFuncSnippet + "(" + unpackedCoordsSnippet + ");\n " + output + "\n }\n "; } function getSamplerAtOutputCoords(inputInfo, outShapeInfo) { var texName = inputInfo.name; var texFuncSnippet = texName.charAt(0).toUpperCase() + texName.slice(1); var funcName = 'get' + texFuncSnippet + 'AtOutCoords'; var outTexShape = outShapeInfo.texShape; var inTexShape = inputInfo.shapeInfo.texShape; var inRank = inputInfo.shapeInfo.logicalShape.length; var outRank = outShapeInfo.logicalShape.length; if (!inputInfo.shapeInfo.isUniform && inRank === outRank && inputInfo.shapeInfo.flatOffset == null && arraysEqual(inTexShape, outTexShape)) { return "\n float " + funcName + "() {\n return sampleTexture(" + texName + ", resultUV);\n }\n "; } var type = getCoordsDataType(outRank); var broadcastDims = getBroadcastDims(inputInfo.shapeInfo.logicalShape, outShapeInfo.logicalShape); var rankDiff = outRank - inRank; var coordsSnippet; var fields = ['x', 'y', 'z', 'w', 'u', 'v']; if (inRank === 0) { coordsSnippet = ''; } else if (outRank < 2 && broadcastDims.length >= 1) { coordsSnippet = 'coords = 0;'; } else { coordsSnippet = broadcastDims.map(function (d) { return "coords." + fields[d + rankDiff] + " = 0;"; }) .join('\n'); } var unpackedCoordsSnippet = ''; if (outRank < 2 && inRank > 0) { unpackedCoordsSnippet = 'coords'; } else { unpackedCoordsSnippet = inputInfo.shapeInfo.logicalShape .map(function (s, i) { return "coords." + fields[i + rankDiff]; }) .join(', '); } return "\n float " + funcName + "() {\n " + type + " coords = getOutputCoords();\n " + coordsSnippet + "\n return get" + texFuncSnippet + "(" + unpackedCoordsSnippet + ");\n }\n "; } function getCoordsDataType(rank) { if (rank <= 1) { return 'int'; } else if (rank === 2) { return 'ivec2'; } else if (rank === 3) { return 'ivec3'; } else if (rank === 4) { return 'ivec4'; } else if (rank === 5) { return 'ivec5'; } else if (rank === 6) { return 'ivec6'; } else { throw Error("GPU for rank " + rank + " is not yet supported"); } } function squeezeInputInfo(inInfo, squeezedShape) { var newInputInfo = JSON.parse(JSON.stringify(inInfo)); newInputInfo.shapeInfo.logicalShape = squeezedShape; return newInputInfo; } function getSqueezedParams(params, keptDims) { return keptDims.map(function (d) { return params[d]; }).join(', '); } var CumSumProgram = (function () { function CumSumProgram(shape, exclusive, reverse) { this.variableNames = ['x']; this.outputShape = shape; var rank = shape.length; var finalDim = shape[shape.length - 1]; var comparator = reverse ? '<' : '>'; this.userCode = "\n int getIndex(int i) {\n " + (reverse ? "return " + finalDim + " -i - 1;" : 'return i;') + "\n }\n\n void main() {\n " + getCoordsDataType(rank) + " coords = getOutputCoords();\n int end = " + getFinalCoord(rank, 'coords') + ";\n float val = 0.0;\n for (int i = " + finalDim + " - 1; i >= 0; i -= 1) {\n int idx = getIndex(i);\n if (idx " + comparator + " end) {\n continue;\n }\n if (idx == end && " + exclusive + ") {\n continue;\n }\n " + getFinalCoord(rank, 'coords') + " = idx;\n val += getX(" + getCoords(rank, 'coords') + ");\n }\n setOutput(val);\n }\n "; } return CumSumProgram; }()); function getCoords(rank, name) { if (rank === 1) { return "" + name; } else if (rank === 2) { return name + ".x, " + name + ".y"; } else if (rank === 3) { return name + ".x, " + name + ".y, " + name + ".z"; } else if (rank === 4) { return name + ".x, " + name + ".y, " + name + ".z, " + name + ".w"; } else { throw Error("Cumulative sum for rank " + rank + " is not yet supported"); } } function getFinalCoord(rank, name) { if (rank === 1) { return "" + name; } else if (rank === 2) { return name + ".y"; } else if (rank === 3) { return name + ".z"; } else if (rank === 4) { return name + ".w"; } else { throw Error("Cumulative sum for rank " + rank + " is not yet supported"); } } var DepthToSpaceProgram = (function () { function DepthToSpaceProgram(outputShape, blockSize, dataFormat) { this.variableNames = ['x']; this.outputShape = []; this.outputShape = outputShape; this.blockSize = blockSize; this.dataFormat = dataFormat; this.userCode = "\n void main() {\n ivec4 coords = getOutputCoords();\n int b = coords[0];\n int h = " + this.getHeightCoordString() + ";\n int w = " + this.getWidthCoordString() + ";\n int d = " + this.getDepthCoordString() + ";\n\n int in_h = h / " + blockSize + ";\n int offset_h = imod(h, " + blockSize + ");\n int in_w = w / " + blockSize + ";\n int offset_w = imod(w, " + blockSize + ");\n int offset_d = (offset_h * " + blockSize + " + offset_w) *\n " + this.getOutputDepthSize() + ";\n int in_d = d + offset_d;\n\n float result = " + this.getInputSamplingString() + ";\n setOutput(result);\n }\n "; } DepthToSpaceProgram.prototype.getHeightCoordString = function () { if (this.dataFormat === 'NHWC') { return "coords[1]"; } else { return "coords[2]"; } }; DepthToSpaceProgram.prototype.getWidthCoordString = function () { if (this.dataFormat === 'NHWC') { return "coords[2]"; } else { return "coords[3]"; } }; DepthToSpaceProgram.prototype.getDepthCoordString = function () { if (this.dataFormat === 'NHWC') { return "coords[3]"; } else { return "coords[1]"; } }; DepthToSpaceProgram.prototype.getOutputDepthSize = function () { if (this.dataFormat === 'NHWC') { return this.outputShape[3]; } else { return this.outputShape[1]; } }; DepthToSpaceProgram.prototype.getInputSamplingString = function () { if (this.dataFormat === 'NHWC') { return "getX(b, in_h, in_w, in_d)"; } else { return "getX(b, in_d, in_h, in_w)"; } }; return DepthToSpaceProgram; }()); var EncodeFloatProgram = (function () { function EncodeFloatProgram(outputShape) { this.variableNames = ['A']; var glsl = getGlslDifferences(); this.outputShape = outputShape; this.userCode = "\n const float FLOAT_MAX = 1.70141184e38;\n const float FLOAT_MIN = 1.17549435e-38;\n\n lowp vec4 encode_float(highp float v) {\n if (isNaN(v)) {\n return vec4(255, 255, 255, 255);\n }\n\n highp float av = abs(v);\n\n if(av < FLOAT_MIN) {\n return vec4(0.0, 0.0, 0.0, 0.0);\n } else if(v > FLOAT_MAX) {\n return vec4(0.0, 0.0, 128.0, 127.0) / 255.0;\n } else if(v < -FLOAT_MAX) {\n return vec4(0.0, 0.0, 128.0, 255.0) / 255.0;\n }\n\n highp vec4 c = vec4(0,0,0,0);\n\n highp float e = floor(log2(av));\n highp float m = exp2(fract(log2(av))) - 1.0;\n\n c[2] = floor(128.0 * m);\n m -= c[2] / 128.0;\n c[1] = floor(32768.0 * m);\n m -= c[1] / 32768.0;\n c[0] = floor(8388608.0 * m);\n\n highp float ebias = e + 127.0;\n c[3] = floor(ebias / 2.0);\n ebias -= c[3] * 2.0;\n c[2] += floor(ebias) * 128.0;\n\n c[3] += 128.0 * step(0.0, -v);\n\n return c / 255.0;\n }\n\n void main() {\n float x = getAAtOutCoords();\n " + glsl.output + " = encode_float(x);\n }\n "; } return EncodeFloatProgram; }()); var COMPLEX_FFT = { REAL: 'return real * expR - imag * expI;', IMAG: 'return real * expI + imag * expR;' }; var FFTProgram = (function () { function FFTProgram(op, inputShape, inverse) { this.variableNames = ['real', 'imag']; var innerDim = inputShape[1]; this.outputShape = inputShape; var exponentMultiplierSnippet = inverse ? "2.0 * " + Math.PI : "-2.0 * " + Math.PI; var resultDenominator = inverse ? innerDim + ".0" : '1.0'; this.userCode = "\n const float exponentMultiplier = " + exponentMultiplierSnippet + ";\n\n float unaryOpComplex(float real, float expR, float imag, float expI) {\n " + op + "\n }\n\n float mulMatDFT(int batch, int index) {\n float indexRatio = float(index) / float(" + innerDim + ");\n float exponentMultiplierTimesIndexRatio =\n exponentMultiplier * indexRatio;\n\n float result = 0.0;\n\n for (int i = 0; i < " + innerDim + "; i++) {\n // x = (-2|2 * PI / N) * index * i;\n float x = exponentMultiplierTimesIndexRatio * float(i);\n float expR = cos(x);\n float expI = sin(x);\n float real = getReal(batch, i);\n float imag = getImag(batch, i);\n\n result +=\n unaryOpComplex(real, expR, imag, expI) / " + resultDenominator + ";\n }\n\n return result;\n }\n\n void main() {\n ivec2 coords = getOutputCoords();\n setOutput(mulMatDFT(coords[0], coords[1]));\n }\n "; } return FFTProgram; }()); var FromPixelsProgram = (function () { function FromPixelsProgram(outputShape) { this.variableNames = ['A']; var glsl = getGlslDifferences(); var height = outputShape[0], width = outputShape[1]; this.outputShape = outputShape; this.userCode = "\n void main() {\n ivec3 coords = getOutputCoords();\n int texR = coords[0];\n int texC = coords[1];\n int depth = coords[2];\n vec2 uv = (vec2(texC, texR) + halfCR) / vec2(" + width + ".0, " + height + ".0);\n\n vec4 values = " + glsl.texture2D + "(A, uv);\n float value;\n if (depth == 0) {\n value = values.r;\n } else if (depth == 1) {\n value = values.g;\n } else if (depth == 2) {\n value = values.b;\n } else if (depth == 3) {\n value = values.a;\n }\n\n setOutput(floor(value * 255.0 + 0.5));\n }\n "; } return FromPixelsProgram; }()); var GatherProgram = (function () { function GatherProgram(aShape, indicesLength, axis) { this.variableNames = ['A', 'indices']; var outputShape = aShape.slice(); outputShape[axis] = indicesLength; this.outputShape = outputShape; this.rank = outputShape.length; var dtype = getCoordsDataType(this.rank); var sourceCoords = getSourceCoords(aShape, axis); this.userCode = "\n void main() {\n " + dtype + " resRC = getOutputCoords();\n setOutput(getA(" + sourceCoords + "));\n }\n "; } return GatherProgram; }()); function getSourceCoords(aShape, axis) { var rank = aShape.length; if (rank > 4) { throw Error("Gather for rank " + rank + " is not yet supported"); } if (rank === 1) { return "int(getIndices(resRC))"; } var currentCoords = ['resRC.x', 'resRC.y', 'resRC.z', 'resRC.w']; var sourceCoords = []; for (var i = 0; i < aShape.length; i++) { if (i === axis) { sourceCoords.push("int(getIndices(" + currentCoords[i] + "))"); } else { sourceCoords.push("" + currentCoords[i]); } } return sourceCoords.join(); } var GatherNDProgram = (function () { function GatherNDProgram(sliceDim, strides, shape) { this.sliceDim = sliceDim; this.strides = strides; this.variableNames = ['x', 'indices']; this.outputShape = shape; var stridesType = getCoordsDataType(strides.length); var dtype = getCoordsDataType(shape.length); var strideString = this.sliceDim > 1 ? 'strides[j]' : 'strides'; this.userCode = "\n " + stridesType + " strides = " + stridesType + "(" + this.strides + ");\n void main() {\n " + dtype + " coords = getOutputCoords();\n int flattenIndex = 0;\n for (int j = 0; j < " + this.sliceDim + "; j++) {\n int index = round(getIndices(coords[0], j));\n flattenIndex += index * " + strideString + ";\n }\n setOutput(getX(flattenIndex, coords[1]));\n }\n "; } return GatherNDProgram; }()); var TextureUsage; (function (TextureUsage) { TextureUsage[TextureUsage["RENDER"] = 0] = "RENDER"; TextureUsage[TextureUsage["UPLOAD"] = 1] = "UPLOAD"; TextureUsage[TextureUsage["PIXELS"] = 2] = "PIXELS"; TextureUsage[TextureUsage["DOWNLOAD"] = 3] = "DOWNLOAD"; })(TextureUsage || (TextureUsage = {})); var PhysicalTextureType; (function (PhysicalTextureType) { PhysicalTextureType[PhysicalTextureType["UNPACKED_FLOAT16"] = 0] = "UNPACKED_FLOAT16"; PhysicalTextureType[PhysicalTextureType["UNPACKED_FLOAT32"] = 1] = "UNPACKED_FLOAT32"; PhysicalTextureType[PhysicalTextureType["PACKED_4X1_UNSIGNED_BYTE"] = 2] = "PACKED_4X1_UNSIGNED_BYTE"; PhysicalTextureType[PhysicalTextureType["PACKED_2X2_FLOAT32"] = 3] = "PACKED_2X2_FLOAT32"; PhysicalTextureType[PhysicalTextureType["PACKED_2X2_FLOAT16"] = 4] = "PACKED_2X2_FLOAT16"; })(PhysicalTextureType || (PhysicalTextureType = {})); function getUnpackedMatrixTextureShapeWidthHeight(rows, columns) { return [columns, rows]; } function getUnpackedArraySizeFromMatrixSize(matrixSize, channelsPerTexture) { return matrixSize * channelsPerTexture; } function getMatrixSizeFromUnpackedArraySize(unpackedSize, channelsPerTexture) { if (unpackedSize % channelsPerTexture !== 0) { throw new Error("unpackedSize (" + unpackedSize + ") must be a multiple of " + ("" + channelsPerTexture)); } return unpackedSize / channelsPerTexture; } function encodeMatrixToUnpackedArray(matrix, unpackedArray, channelsPerTexture) { var requiredSize = getUnpackedArraySizeFromMatrixSize(matrix.length, channelsPerTexture); if (unpackedArray.length < requiredSize) { throw new Error("unpackedArray length (" + unpackedArray.length + ") must be >= " + ("" + requiredSize)); } var dst = 0; for (var src = 0; src < matrix.length; ++src) { unpackedArray[dst] = matrix[src]; dst += channelsPerTexture; } } function decodeMatrixFromUnpackedArray(unpackedArray, matrix, channelsPerTexture) { var requiredSize = getMatrixSizeFromUnpackedArraySize(unpackedArray.length, channelsPerTexture); if (matrix.length < requiredSize) { throw new Error("matrix length (" + matrix.length + ") must be >= " + requiredSize); } var dst = 0; for (var src = 0; src < unpackedArray.length; src += channelsPerTexture) { matrix[dst++] = unpackedArray[src]; } } function getPackedMatrixTextureShapeWidthHeight(rows, columns) { return [ Math.max(1, Math.ceil(columns / 2)), Math.max(1, Math.ceil(rows / 2)) ]; } function getPackedRGBAArraySizeFromMatrixShape(rows, columns) { var _a = getPackedMatrixTextureShapeWidthHeight(rows, columns), w = _a[0], h = _a[1]; return w * h * 4; } function encodeMatrixToPackedRGBA(matrix, batches, rows, columns, packedRGBA) { var oddWidth = (columns % 2) === 1; var oddHeight = (rows % 2) === 1; var widthInFullBlocks = Math.floor(columns / 2); var heightInFullBlocks = Math.floor(rows / 2); var texelsPerRow = Math.ceil(columns / 2); var texelsPerBatch = texelsPerRow * Math.ceil(rows / 2); var flattenedMatrixSize = nearestLargerEven(rows) * nearestLargerEven(columns); for (var batch = 0; batch < batches; batch++) { var sourceOffset = batch * rows * columns; var batchOffset = batch * flattenedMatrixSize; { var dstStride = (oddWidth ? 4 : 0); var oneRow = columns; var dst = batchOffset; for (var blockY = 0; blockY < heightInFullBlocks; ++blockY) { var matrixSrcRow = (blockY * 2 * columns); for (var blockX = 0; blockX < widthInFullBlocks; ++blockX) { var matrixSrcCol = blockX * 2; var src = sourceOffset + matrixSrcRow + matrixSrcCol; packedRGBA[dst] = matrix[src]; packedRGBA[dst + 1] = matrix[src + 1]; packedRGBA[dst + 2] = matrix[src + oneRow]; packedRGBA[dst + 3] = matrix[src + oneRow + 1]; dst += 4; } dst += dstStride; } } if (oddWidth) { var src = sourceOffset + columns - 1; var dst = batchOffset + (texelsPerRow - 1) * 4; var srcStride = 2 * columns; var dstStride = texelsPerRow * 4; for (var blockY = 0; blockY < heightInFullBlocks; ++blockY) { packedRGBA[dst] = matrix[src]; packedRGBA[dst + 2] = matrix[src + columns]; src += srcStride; dst += dstStride; } } if (oddHeight) { var src = sourceOffset + (rows - 1) * columns; var dst = batchOffset + (texelsPerBatch - texelsPerRow) * 4; for (var blockX = 0; blockX < widthInFullBlocks; ++blockX) { packedRGBA[dst++] = matrix[src++]; packedRGBA[dst++] = matrix[src++]; dst += 2; } if (oddWidth && oddHeight) { packedRGBA[batchOffset + flattenedMatrixSize - 4] = matrix[src]; } } } return packedRGBA; } function decodeMatrixFromPackedRGBA(packedRGBA, batches, rows, columns, matrix) { var requiredSize = rows * columns; if (matrix.length < requiredSize) { throw new Error("matrix length (" + matrix.length + ") must be >= " + requiredSize); } var oddWidth = (columns % 2) === 1; var oddHeight = (rows % 2) === 1; var widthInFullBlocks = Math.floor(columns / 2); var heightInFullBlocks = Math.floor(rows / 2); var texelsPerRow = Math.ceil(columns / 2); var texelsPerBatch = texelsPerRow * Math.ceil(rows / 2); var flattenedMatrixSize = nearestLargerEven(rows) * nearestLargerEven(columns); for (var batch = 0; batch < batches; batch++) { var batchOffset = batch * rows * columns; var sourceOffset = batch * flattenedMatrixSize; { var srcStride = oddWidth ? 4 : 0; var dstStride = columns + (oddWidth ? 1 : 0); var src = sourceOffset; var dstRow1 = batchOffset; var dstRow2 = batchOffset + columns; for (var blockY = 0; blockY < heightInFullBlocks; ++blockY) { for (var blockX = 0; blockX < widthInFullBlocks; ++blockX) { matrix[dstRow1++] = packedRGBA[src++]; matrix[dstRow1++] = packedRGBA[src++]; matrix[dstRow2++] = packedRGBA[src++]; matrix[dstRow2++] = packedRGBA[src++]; } src += srcStride; dstRow1 += dstStride; dstRow2 += dstStride; } } if (oddWidth) { var src = sourceOffset + (texelsPerRow - 1) * 4; var dst = batchOffset + columns - 1; var srcStride = texelsPerRow * 4; var dstStride = 2 * columns; for (var blockY = 0; blockY < heightInFullBlocks; ++blockY) { matrix[dst] = packedRGBA[src]; matrix[dst + columns] = packedRGBA[src + 2]; src += srcStride; dst += dstStride; } } if (oddHeight) { var src = sourceOffset + (texelsPerBatch - texelsPerRow) * 4; var dst = batchOffset + (rows - 1) * columns; for (var blockX = 0; blockX < widthInFullBlocks; ++blockX) { matrix[dst++] = packedRGBA[src++]; matrix[dst++] = packedRGBA[src++]; src += 2; } if (oddWidth) { matrix[batchOffset + (rows * columns) - 1] = packedRGBA[src]; } } } return matrix; } function callAndCheck(gl, func) { var returnValue = func(); checkWebGLError(gl); return returnValue; } var webGLDebugErrorCheckingEnabled = false; function enableDebugWebGLErrorChecking(enabled) { webGLDebugErrorCheckingEnabled = enabled; } function checkWebGLError(gl) { if (webGLDebugErrorCheckingEnabled) { var error = gl.getError(); if (error !== gl.NO_ERROR) { throw new Error('WebGL Error: ' + getWebGLErrorMessage(gl, error)); } } } var MIN_FLOAT16 = 5.96e-8; var MAX_FLOAT16 = 65504; function canBeRepresented(num) { if (ENV.get('WEBGL_RENDER_FLOAT32_ENABLED') || num === 0 || (MIN_FLOAT16 < Math.abs(num) && Math.abs(num) < MAX_FLOAT16)) { return true; } return false; } function getWebGLErrorMessage(gl, status) { switch (status) { case gl.NO_ERROR: return 'NO_ERROR'; case gl.INVALID_ENUM: return 'INVALID_ENUM'; case gl.INVALID_VALUE: return 'INVALID_VALUE'; case gl.INVALID_OPERATION: return 'INVALID_OPERATION'; case gl.INVALID_FRAMEBUFFER_OPERATION: return 'INVALID_FRAMEBUFFER_OPERATION'; case gl.OUT_OF_MEMORY: return 'OUT_OF_MEMORY'; case gl.CONTEXT_LOST_WEBGL: return 'CONTEXT_LOST_WEBGL'; default: return "Unknown error code " + status; } } function getExtensionOrThrow(gl, extensionName) { return throwIfNull(gl, function () { return gl.getExtension(extensionName); }, 'Extension "' + extensionName + '" not supported on this browser.'); } function createVertexShader(gl, vertexShaderSource) { var vertexShader = throwIfNull(gl, function () { return gl.createShader(gl.VERTEX_SHADER); }, 'Unable to create vertex WebGLShader.'); callAndCheck(gl, function () { return gl.shaderSource(vertexShader, vertexShaderSource); }); callAndCheck(gl, function () { return gl.compileShader(vertexShader); }); if (gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS) === false) { console.log(gl.getShaderInfoLog(vertexShader)); throw new Error('Failed to compile vertex shader.'); } return vertexShader; } function createFragmentShader(gl, fragmentShaderSource) { var fragmentShader = throwIfNull(gl, function () { return gl.createShader(gl.FRAGMENT_SHADER); }, 'Unable to create fragment WebGLShader.'); callAndCheck(gl, function () { return gl.shaderSource(fragmentShader, fragmentShaderSource); }); callAndCheck(gl, function () { return gl.compileShader(fragmentShader); }); if (gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS) === false) { logShaderSourceAndInfoLog(fragmentShaderSource, gl.getShaderInfoLog(fragmentShader)); throw new Error('Failed to compile fragment shader.'); } return fragmentShader; } var lineNumberRegex = /ERROR: [0-9]+:([0-9]+):/g; function logShaderSourceAndInfoLog(shaderSource, shaderInfoLog) { var lineNumberRegexResult = lineNumberRegex.exec(shaderInfoLog); if (lineNumberRegexResult == null) { console.log("Couldn't parse line number in error: " + shaderInfoLog); console.log(shaderSource); return; } var lineNumber = +lineNumberRegexResult[1]; var shaderLines = shaderSource.split('\n'); var pad = shaderLines.length.toString().length + 2; var linesWithLineNumbers = shaderLines.map(function (line, lineNumber) { return rightPad((lineNumber + 1).toString(), pad) + line; }); var maxLineLength = 0; for (var i = 0; i < linesWithLineNumbers.length; i++) { maxLineLength = Math.max(linesWithLineNumbers[i].length, maxLineLength); } var beforeErrorLines = linesWithLineNumbers.slice(0, lineNumber - 1); var errorLine = linesWithLineNumbers.slice(lineNumber - 1, lineNumber); var afterErrorLines = linesWithLineNumbers.slice(lineNumber); console.log(beforeErrorLines.join('\n')); console.log(shaderInfoLog.split('\n')[0]); console.log("%c " + rightPad(errorLine[0], maxLineLength), 'border:1px solid red; background-color:#e3d2d2; color:#a61717'); console.log(afterErrorLines.join('\n')); } function createProgram(gl) { return throwIfNull(gl, function () { return gl.createProgram(); }, 'Unable to create WebGLProgram.'); } function linkProgram(gl, program) { callAndCheck(gl, function () { return gl.linkProgram(program); }); if (gl.getProgramParameter(program, gl.LINK_STATUS) === false) { console.log(gl.getProgramInfoLog(program)); throw new Error('Failed to link vertex and fragment shaders.'); } } function validateProgram(gl, program) { callAndCheck(gl, function () { return gl.validateProgram(program); }); if (gl.getProgramParameter(program, gl.VALIDATE_STATUS) === false) { console.log(gl.getProgramInfoLog(program)); throw new Error('Shader program validation failed.'); } } function createStaticVertexBuffer(gl, data) { var buffer = throwIfNull(gl, function () { return gl.createBuffer(); }, 'Unable to create WebGLBuffer'); callAndCheck(gl, function () { return gl.bindBuffer(gl.ARRAY_BUFFER, buffer); }); callAndCheck(gl, function () { return gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW); }); return buffer; } function createStaticIndexBuffer(gl, data) { var buffer = throwIfNull(gl, function () { return gl.createBuffer(); }, 'Unable to create WebGLBuffer'); callAndCheck(gl, function () { return gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffer); }); callAndCheck(gl, function () { return gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, data, gl.STATIC_DRAW); }); return buffer; } function getNumChannels() { if (ENV.get('WEBGL_VERSION') === 2) { return 1; } return 4; } function createTexture(gl) { return throwIfNull(gl, function () { return gl.createTexture(); }, 'Unable to create WebGLTexture.'); } function validateTextureSize(width, height) { var maxTextureSize = ENV.get('WEBGL_MAX_TEXTURE_SIZE'); if ((width <= 0) || (height <= 0)) { var requested = "[" + width + "x" + height + "]"; throw new Error('Requested texture size ' + requested + ' is invalid.'); } if ((width > maxTextureSize) || (height > maxTextureSize)) { var requested = "[" + width + "x" + height + "]"; var max = "[" + maxTextureSize + "x" + maxTextureSize + "]"; throw new Error('Requested texture size ' + requested + ' greater than WebGL maximum on this browser / GPU ' + max + '.'); } } function createFramebuffer(gl) { return throwIfNull(gl, function () { return gl.createFramebuffer(); }, 'Unable to create WebGLFramebuffer.'); } function bindVertexBufferToProgramAttribute(gl, program, attribute, buffer, arrayEntriesPerItem, itemStrideInBytes, itemOffsetInBytes) { var loc = gl.getAttribLocation(program, attribute); if (loc === -1) { return false; } callAndCheck(gl, function () { return gl.bindBuffer(gl.ARRAY_BUFFER, buffer); }); callAndCheck(gl, function () { return gl.vertexAttribPointer(loc, arrayEntriesPerItem, gl.FLOAT, false, itemStrideInBytes, itemOffsetInBytes); }); callAndCheck(gl, function () { return gl.enableVertexAttribArray(loc); }); return true; } function bindTextureUnit(gl, texture, textureUnit) { validateTextureUnit(gl, textureUnit); callAndCheck(gl, function () { return gl.activeTexture(gl.TEXTURE0 + textureUnit); }); callAndCheck(gl, function () { return gl.bindTexture(gl.TEXTURE_2D, texture); }); } function unbindTextureUnit(gl, textureUnit) { validateTextureUnit(gl, textureUnit); callAndCheck(gl, function () { return gl.activeTexture(gl.TEXTURE0 + textureUnit); }); callAndCheck(gl, function () { return gl.bindTexture(gl.TEXTURE_2D, null); }); } function getProgramUniformLocationOrThrow(gl, program, uniformName) { return throwIfNull(gl, function () { return gl.getUniformLocation(program, uniformName); }, 'uniform "' + uniformName + '" not present in program.'); } function getProgramUniformLocation(gl, program, uniformName) { return gl.getUniformLocation(program, uniformName); } function bindTextureToProgramUniformSampler(gl, program, texture, uniformSamplerLocation, textureUnit) { callAndCheck(gl, function () { return bindTextureUnit(gl, texture, textureUnit); }); callAndCheck(gl, function () { return gl.uniform1i(uniformSamplerLocation, textureUnit); }); } function bindCanvasToFramebuffer(gl) { callAndCheck(gl, function () { return gl.bindFramebuffer(gl.FRAMEBUFFER, null); }); callAndCheck(gl, function () { return gl.viewport(0, 0, gl.canvas.width, gl.canvas.height); }); callAndCheck(gl, function () { return gl.scissor(0, 0, gl.canvas.width, gl.canvas.height); }); } function bindColorTextureToFramebuffer(gl, texture, framebuffer) { callAndCheck(gl, function () { return gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer); }); callAndCheck(gl, function () { return gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0); }); } function unbindColorTextureFromFramebuffer(gl, framebuffer) { callAndCheck(gl, function () { return gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer); }); callAndCheck(gl, function () { return gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, null, 0); }); } function validateFramebuffer(gl) { var status = gl.checkFramebufferStatus(gl.FRAMEBUFFER); if (status !== gl.FRAMEBUFFER_COMPLETE) { throw new Error('Error binding framebuffer: ' + getFramebufferErrorMessage(gl, status)); } } function getFramebufferErrorMessage(gl, status) { switch (status) { case gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT: return 'FRAMEBUFFER_INCOMPLETE_ATTACHMENT'; case gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: return 'FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT'; case gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS: return 'FRAMEBUFFER_INCOMPLETE_DIMENSIONS'; case gl.FRAMEBUFFER_UNSUPPORTED: return 'FRAMEBUFFER_UNSUPPORTED'; default: return "unknown error " + status; } } function throwIfNull(gl, returnTOrNull, failureMessage) { var tOrNull = callAndCheck(gl, function () { return returnTOrNull(); }); if (tOrNull == null) { throw new Error(failureMessage); } return tOrNull; } function validateTextureUnit(gl, textureUnit) { var maxTextureUnit = gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS - 1; var glTextureUnit = textureUnit + gl.TEXTURE0; if (glTextureUnit < gl.TEXTURE0 || glTextureUnit > maxTextureUnit) { var textureUnitRange = "[gl.TEXTURE0, gl.TEXTURE" + maxTextureUnit + "]"; throw new Error("textureUnit must be in " + textureUnitRange + "."); } } function getBatchDim(shape, dimsToSkip) { if (dimsToSkip === void 0) { dimsToSkip = 2; } return sizeFromShape(shape.slice(0, shape.length - dimsToSkip)); } function getRowsCols(shape) { if (shape.length === 0) { throw Error('Cannot get rows and columns of an empty shape array.'); } return [ shape.length > 1 ? shape[shape.length - 2] : 1, shape[shape.length - 1] ]; } function getTextureShapeFromLogicalShape(logShape, isPacked) { if (isPacked === void 0) { isPacked = false; } var _a; var maxTexSize = ENV.get('WEBGL_MAX_TEXTURE_SIZE'); if (isPacked) { maxTexSize = maxTexSize * 2; logShape = logShape.map(function (d, i) { return i >= logShape.length - 2 ? nearestLargerEven(logShape[i]) : logShape[i]; }); if (logShape.length === 1) { logShape = [2, logShape[0]]; } } if (logShape.length !== 2) { var squeezeResult = squeezeShape(logShape); logShape = squeezeResult.newShape; } var size = sizeFromShape(logShape); if (logShape.length <= 1 && size <= maxTexSize) { return [1, size]; } else if (logShape.length === 2 && logShape[0] <= maxTexSize && logShape[1] <= maxTexSize) { return logShape; } else if (logShape.length === 3 && logShape[0] * logShape[1] <= maxTexSize && logShape[2] <= maxTexSize) { return [logShape[0] * logShape[1], logShape[2]]; } else if (logShape.length === 3 && logShape[0] <= maxTexSize && logShape[1] * logShape[2] <= maxTexSize) { return [logShape[0], logShape[1] * logShape[2]]; } else if (logShape.length === 4 && logShape[0] * logShape[1] * logShape[2] <= maxTexSize && logShape[3] <= maxTexSize) { return [logShape[0] * logShape[1] * logShape[2], logShape[3]]; } else if (logShape.length === 4 && logShape[0] <= maxTexSize && logShape[1] * logShape[2] * logShape[3] <= maxTexSize) { return [logShape[0], logShape[1] * logShape[2] * logShape[3]]; } else { if (isPacked) { var batchDim = getBatchDim(logShape); var rows = 2, cols = 2; if (logShape.length) { _a = getRowsCols(logShape), rows = _a[0], cols = _a[1]; } size = batchDim * (rows / 2) * (cols / 2); return sizeToSquarishShape(size).map(function (d) { return d * 2; }); } return sizeToSquarishShape(size); } } function isEven(n) { return n % 2 === 0; } function isReshapeFree(shape1, shape2) { shape1 = shape1.slice(-2); shape2 = shape2.slice(-2); if (arraysEqual(shape1, shape2)) { return true; } if (!shape1.length || !shape2.length) { return true; } if (shape1[0] === 0 || shape1[1] === 0 || shape2[0] === 0 || shape2[1] === 0) { return true; } if (shape1.length !== shape2.length) { var shape1Cols = shape1.slice(-1)[0]; var shape2Cols = shape2.slice(-1)[0]; if (shape1Cols === shape2Cols) { return true; } if (isEven(shape1Cols) && isEven(shape2Cols) && (shape1[0] === 1 || shape2[0] === 1)) { return true; } } return shape1[1] === shape2[1] && isEven(shape1[0]) && isEven(shape2[0]); } var webgl_util = /*#__PURE__*/Object.freeze({ callAndCheck: callAndCheck, enableDebugWebGLErrorChecking: enableDebugWebGLErrorChecking, checkWebGLError: checkWebGLError, canBeRepresented: canBeRepresented, getWebGLErrorMessage: getWebGLErrorMessage, getExtensionOrThrow: getExtensionOrThrow, createVertexShader: createVertexShader, createFragmentShader: createFragmentShader, createProgram: createProgram, linkProgram: linkProgram, validateProgram: validateProgram, createStaticVertexBuffer: createStaticVertexBuffer, createStaticIndexBuffer: createStaticIndexBuffer, getNumChannels: getNumChannels, createTexture: createTexture, validateTextureSize: validateTextureSize, createFramebuffer: createFramebuffer, bindVertexBufferToProgramAttribute: bindVertexBufferToProgramAttribute, bindTextureUnit: bindTextureUnit, unbindTextureUnit: unbindTextureUnit, getProgramUniformLocationOrThrow: getProgramUniformLocationOrThrow, getProgramUniformLocation: getProgramUniformLocation, bindTextureToProgramUniformSampler: bindTextureToProgramUniformSampler, bindCanvasToFramebuffer: bindCanvasToFramebuffer, bindColorTextureToFramebuffer: bindColorTextureToFramebuffer, unbindColorTextureFromFramebuffer: unbindColorTextureFromFramebuffer, validateFramebuffer: validateFramebuffer, getFramebufferErrorMessage: getFramebufferErrorMessage, getBatchDim: getBatchDim, getRowsCols: getRowsCols, getTextureShapeFromLogicalShape: getTextureShapeFromLogicalShape, isReshapeFree: isReshapeFree }); function createVertexShader$1(gl) { var glsl = getGlslDifferences(); var vertexShaderSource = glsl.version + "\n precision highp float;\n " + glsl.attribute + " vec3 clipSpacePos;\n " + glsl.attribute + " vec2 uv;\n " + glsl.varyingVs + " vec2 resultUV;\n\n void main() {\n gl_Position = vec4(clipSpacePos, 1);\n resultUV = uv;\n }"; return createVertexShader(gl, vertexShaderSource); } function createVertexBuffer(gl) { var vertexArray = new Float32Array([-1, 1, 0, 0, 1, -1, -1, 0, 0, 0, 1, 1, 0, 1, 1, 1, -1, 0, 1, 0]); return createStaticVertexBuffer(gl, vertexArray); } function createIndexBuffer(gl) { var triangleVertexIndices = new Uint16Array([0, 1, 2, 2, 1, 3]); return createStaticIndexBuffer(gl, triangleVertexIndices); } function getTextureConfig(gl, textureHalfFloatExtension) { var glany = gl; var internalFormatFloat; var internalFormatHalfFloat; var internalFormatPackedFloat; var textureFormatFloat; var downloadTextureFormat; var downloadUnpackNumChannels; var defaultNumChannels; var textureTypeHalfFloat; if (ENV.get('WEBGL_VERSION') === 2) { internalFormatFloat = glany.R32F; internalFormatHalfFloat = glany.R16F; internalFormatPackedFloat = glany.RGBA32F; textureFormatFloat = glany.RED; downloadUnpackNumChannels = 4; defaultNumChannels = 1; textureTypeHalfFloat = glany.HALF_FLOAT; } else { internalFormatFloat = gl.RGBA; internalFormatHalfFloat = gl.RGBA; internalFormatPackedFloat = glany.RGBA; textureFormatFloat = gl.RGBA; downloadUnpackNumChannels = 4; defaultNumChannels = 4; textureTypeHalfFloat = textureHalfFloatExtension != null ? textureHalfFloatExtension.HALF_FLOAT_OES : null; } downloadTextureFormat = gl.RGBA; return { internalFormatFloat: internalFormatFloat, internalFormatHalfFloat: internalFormatHalfFloat, internalFormatPackedFloat: internalFormatPackedFloat, textureFormatFloat: textureFormatFloat, downloadTextureFormat: downloadTextureFormat, downloadUnpackNumChannels: downloadUnpackNumChannels, defaultNumChannels: defaultNumChannels, textureTypeHalfFloat: textureTypeHalfFloat }; } function createAndConfigureTexture(gl, width, height, internalFormat, textureFormat, textureType) { validateTextureSize(width, height); var texture = createTexture(gl); var tex2d = gl.TEXTURE_2D; callAndCheck(gl, function () { return gl.bindTexture(tex2d, texture); }); callAndCheck(gl, function () { return gl.texParameteri(tex2d, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); }); callAndCheck(gl, function () { return gl.texParameteri(tex2d, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); }); callAndCheck(gl, function () { return gl.texParameteri(tex2d, gl.TEXTURE_MIN_FILTER, gl.NEAREST); }); callAndCheck(gl, function () { return gl.texParameteri(tex2d, gl.TEXTURE_MAG_FILTER, gl.NEAREST); }); callAndCheck(gl, function () { return gl.texImage2D(tex2d, 0, internalFormat, width, height, 0, textureFormat, textureType, null); }); callAndCheck(gl, function () { return gl.bindTexture(gl.TEXTURE_2D, null); }); return texture; } function createFloat32MatrixTexture(gl, rows, columns, textureConfig) { var _a = getUnpackedMatrixTextureShapeWidthHeight(rows, columns), width = _a[0], height = _a[1]; return createAndConfigureTexture(gl, width, height, textureConfig.internalFormatFloat, textureConfig.textureFormatFloat, gl.FLOAT); } function createFloat16MatrixTexture(gl, rows, columns, textureConfig) { var _a = getUnpackedMatrixTextureShapeWidthHeight(rows, columns), width = _a[0], height = _a[1]; return createAndConfigureTexture(gl, width, height, textureConfig.internalFormatFloat, textureConfig.textureFormatFloat, textureConfig.textureTypeHalfFloat); } function createUnsignedBytesMatrixTexture(gl, rows, columns, textureConfig) { var _a = getUnpackedMatrixTextureShapeWidthHeight(rows, columns), width = _a[0], height = _a[1]; return createAndConfigureTexture(gl, width, height, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE); } function createPackedMatrixTexture(gl, rows, columns, textureConfig) { var _a = getPackedMatrixTextureShapeWidthHeight(rows, columns), width = _a[0], height = _a[1]; return createAndConfigureTexture(gl, width, height, textureConfig.internalFormatPackedFloat, gl.RGBA, gl.FLOAT); } function createFloat16PackedMatrixTexture(gl, rows, columns, textureConfig) { var _a = getPackedMatrixTextureShapeWidthHeight(rows, columns), width = _a[0], height = _a[1]; return createAndConfigureTexture(gl, width, height, textureConfig.internalFormatHalfFloat, gl.RGBA, textureConfig.textureTypeHalfFloat); } function bindVertexProgramAttributeStreams(gl, program, vertexBuffer) { var posOffset = 0; var uvOffset = 3 * 4; var stride = (3 * 4) + (2 * 4); callAndCheck(gl, function () { return gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); }); var success = bindVertexBufferToProgramAttribute(gl, program, 'clipSpacePos', vertexBuffer, 3, stride, posOffset); return success && bindVertexBufferToProgramAttribute(gl, program, 'uv', vertexBuffer, 2, stride, uvOffset); } function uploadPixelDataToTexture(gl, texture, pixels) { callAndCheck(gl, function () { return gl.bindTexture(gl.TEXTURE_2D, texture); }); callAndCheck(gl, function () { return gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, pixels); }); callAndCheck(gl, function () { return gl.bindTexture(gl.TEXTURE_2D, null); }); } function uploadDataToTexture(gl, texture, width, height, data, textureFormat) { validateTextureSize(width, height); callAndCheck(gl, function () { return gl.bindTexture(gl.TEXTURE_2D, texture); }); callAndCheck(gl, function () { return gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width, height, textureFormat, gl.FLOAT, data); }); callAndCheck(gl, function () { return gl.bindTexture(gl.TEXTURE_2D, null); }); } function uploadMatrixToTexture(gl, texture, rows, columns, matrix, numChannels, textureConfig) { var _a = getUnpackedMatrixTextureShapeWidthHeight(rows, columns), w = _a[0], h = _a[1]; var unpackedArray; var numTexels = rows * columns; if (textureConfig.defaultNumChannels === 1 && numTexels === matrix.length) { unpackedArray = matrix; } else { unpackedArray = new Float32Array(numTexels * numChannels); encodeMatrixToUnpackedArray(matrix, unpackedArray, numChannels); } uploadDataToTexture(gl, texture, w, h, unpackedArray, textureConfig.textureFormatFloat); } function uploadMatrixToPackedTexture(gl, texture, batch, rows, columns, physicalRows, physicalCols, matrix, textureConfig) { var _a = getPackedMatrixTextureShapeWidthHeight(physicalRows, physicalCols), w = _a[0], h = _a[1]; var packedRGBA = new Float32Array(getPackedRGBAArraySizeFromMatrixShape(physicalRows, physicalCols)); encodeMatrixToPackedRGBA(matrix, batch, rows, columns, packedRGBA); uploadDataToTexture(gl, texture, w, h, packedRGBA, gl.RGBA); } function maybeCreateBufferFromOutputTexture(gl, texture, rows, columns, textureConfig) { var bufferOrTexture = texture; if (ENV.get('WEBGL_VERSION') === 2) { var gl2_1 = gl; var buffer_1 = gl2_1.createBuffer(); callAndCheck(gl, function () { return gl.bindBuffer(gl2_1.PIXEL_PACK_BUFFER, buffer_1); }); var bytesPerFloat = 4; var bufferSizeBytes_1 = bytesPerFloat * getUnpackedArraySizeFromMatrixSize(rows * columns, textureConfig.downloadUnpackNumChannels); callAndCheck(gl, function () { return gl.bufferData(gl2_1.PIXEL_PACK_BUFFER, bufferSizeBytes_1, gl2_1.STREAM_READ); }); callAndCheck(gl, function () { return gl2_1.readPixels(0, 0, columns, rows, gl.RGBA, gl.FLOAT, 0); }); callAndCheck(gl, function () { return gl.bindBuffer(gl2_1.PIXEL_PACK_BUFFER, null); }); bufferOrTexture = buffer_1; } return bufferOrTexture; } function downloadFloat32MatrixFromBuffer(gl, buffer, rows, columns, textureConfig) { var gl2 = gl; var downloadTarget = new Float32Array(getUnpackedArraySizeFromMatrixSize(rows * columns, textureConfig.downloadUnpackNumChannels)); gl2.bindBuffer(gl2.PIXEL_PACK_BUFFER, buffer); gl2.getBufferSubData(gl2.PIXEL_PACK_BUFFER, 0, downloadTarget); gl2.bindBuffer(gl2.PIXEL_PACK_BUFFER, null); var matrix = new Float32Array(rows * columns); decodeMatrixFromUnpackedArray(downloadTarget, matrix, textureConfig.downloadUnpackNumChannels); return matrix; } function downloadFloat32MatrixFromOutputTexture(gl, rows, columns, textureConfig) { var _a = getUnpackedMatrixTextureShapeWidthHeight(rows, columns), w = _a[0], h = _a[1]; var downloadTarget = new Float32Array(getUnpackedArraySizeFromMatrixSize(rows * columns, textureConfig.downloadUnpackNumChannels)); callAndCheck(gl, function () { return gl.readPixels(0, 0, w, h, textureConfig.downloadTextureFormat, gl.FLOAT, downloadTarget); }); var matrix = new Float32Array(rows * columns); decodeMatrixFromUnpackedArray(downloadTarget, matrix, textureConfig.downloadUnpackNumChannels); return matrix; } function downloadByteEncodedFloatMatrixFromOutputTexture(gl, rows, columns, textureConfig) { var _a = getUnpackedMatrixTextureShapeWidthHeight(rows, columns), w = _a[0], h = _a[1]; var numChannels = 4; var downloadTarget = new Uint8Array(getUnpackedArraySizeFromMatrixSize(rows * columns, numChannels)); callAndCheck(gl, function () { return gl.readPixels(0, 0, w, h, textureConfig.downloadTextureFormat, gl.UNSIGNED_BYTE, downloadTarget); }); return new Float32Array(downloadTarget.buffer); } function downloadPackedMatrixFromBuffer(gl, buffer, batch, rows, cols, physicalRows, physicalCols, textureConfig) { var gl2 = gl; var downloadTarget = new Float32Array(getPackedRGBAArraySizeFromMatrixShape(physicalRows, physicalCols)); gl2.bindBuffer(gl2.PIXEL_PACK_BUFFER, buffer); gl2.getBufferSubData(gl2.PIXEL_PACK_BUFFER, 0, downloadTarget); gl2.bindBuffer(gl2.PIXEL_PACK_BUFFER, null); var matrix = new Float32Array(sizeFromShape([batch, rows, cols])); decodeMatrixFromPackedRGBA(downloadTarget, batch, rows, cols, matrix); return matrix; } function downloadMatrixFromPackedOutputTexture(gl, batch, rows, cols, physicalRows, physicalCols, textureConfig) { var _a = getPackedMatrixTextureShapeWidthHeight(physicalRows, physicalCols), w = _a[0], h = _a[1]; var packedRGBA = new Float32Array(getPackedRGBAArraySizeFromMatrixShape(physicalRows, physicalCols)); callAndCheck(gl, function () { return gl.readPixels(0, 0, w, h, gl.RGBA, gl.FLOAT, packedRGBA); }); var matrix = new Float32Array(sizeFromShape([batch, rows, cols])); return decodeMatrixFromPackedRGBA(packedRGBA, batch, rows, cols, matrix); } var gpgpu_util = /*#__PURE__*/Object.freeze({ createVertexShader: createVertexShader$1, createVertexBuffer: createVertexBuffer, createIndexBuffer: createIndexBuffer, getTextureConfig: getTextureConfig, createFloat32MatrixTexture: createFloat32MatrixTexture, createFloat16MatrixTexture: createFloat16MatrixTexture, createUnsignedBytesMatrixTexture: createUnsignedBytesMatrixTexture, createPackedMatrixTexture: createPackedMatrixTexture, createFloat16PackedMatrixTexture: createFloat16PackedMatrixTexture, bindVertexProgramAttributeStreams: bindVertexProgramAttributeStreams, uploadPixelDataToTexture: uploadPixelDataToTexture, uploadMatrixToTexture: uploadMatrixToTexture, uploadMatrixToPackedTexture: uploadMatrixToPackedTexture, maybeCreateBufferFromOutputTexture: maybeCreateBufferFromOutputTexture, downloadFloat32MatrixFromBuffer: downloadFloat32MatrixFromBuffer, downloadFloat32MatrixFromOutputTexture: downloadFloat32MatrixFromOutputTexture, downloadByteEncodedFloatMatrixFromOutputTexture: downloadByteEncodedFloatMatrixFromOutputTexture, downloadPackedMatrixFromBuffer: downloadPackedMatrixFromBuffer, downloadMatrixFromPackedOutputTexture: downloadMatrixFromPackedOutputTexture }); var GPGPUContext = (function () { function GPGPUContext(gl) { this.outputTexture = null; this.program = null; this.disposed = false; this.autoDebugValidate = false; this.vertexAttrsAreBound = false; this.itemsToPoll = []; if (gl != null) { this.gl = gl; } else { this.gl = getWebGLContext(ENV.get('WEBGL_VERSION')); } if (ENV.get('WEBGL_VERSION') === 1) { this.textureFloatExtension = getExtensionOrThrow(this.gl, 'OES_texture_float'); this.colorBufferFloatExtension = this.gl.getExtension('WEBGL_color_buffer_float'); if (!ENV.get('WEBGL_RENDER_FLOAT32_ENABLED')) { this.textureHalfFloatExtension = getExtensionOrThrow(this.gl, 'OES_texture_half_float'); this.colorBufferHalfFloatExtension = this.gl.getExtension('EXT_color_buffer_half_float'); } } else { this.colorBufferFloatExtension = getExtensionOrThrow(this.gl, 'EXT_color_buffer_float'); } this.vertexBuffer = createVertexBuffer(this.gl); this.indexBuffer = createIndexBuffer(this.gl); this.framebuffer = createFramebuffer(this.gl); this.textureConfig = getTextureConfig(this.gl, this.textureHalfFloatExtension); } GPGPUContext.prototype.dispose = function () { var _this = this; if (this.disposed) { return; } if (this.program != null) { console.warn('Disposing a GPGPUContext that still has a bound WebGLProgram.' + ' This is probably a resource leak, delete the program with ' + 'GPGPUContext.deleteProgram before disposing.'); } if (this.outputTexture != null) { console.warn('Disposing a GPGPUContext that still has a bound output matrix ' + 'texture. This is probably a resource leak, delete the output ' + 'matrix texture with GPGPUContext.deleteMatrixTexture before ' + 'disposing.'); } var gl = this.gl; callAndCheck(gl, function () { return gl.finish(); }); callAndCheck(gl, function () { return gl.bindFramebuffer(gl.FRAMEBUFFER, null); }); callAndCheck(gl, function () { return gl.deleteFramebuffer(_this.framebuffer); }); callAndCheck(gl, function () { return gl.bindBuffer(gl.ARRAY_BUFFER, null); }); callAndCheck(gl, function () { return gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null); }); callAndCheck(gl, function () { return gl.deleteBuffer(_this.indexBuffer); }); this.disposed = true; }; GPGPUContext.prototype.enableAutomaticDebugValidation = function (enabled) { this.autoDebugValidate = enabled; enableDebugWebGLErrorChecking(enabled); }; GPGPUContext.prototype.createFloat32MatrixTexture = function (rows, columns) { this.throwIfDisposed(); return createFloat32MatrixTexture(this.gl, rows, columns, this.textureConfig); }; GPGPUContext.prototype.createFloat16MatrixTexture = function (rows, columns) { this.throwIfDisposed(); return createFloat16MatrixTexture(this.gl, rows, columns, this.textureConfig); }; GPGPUContext.prototype.createUnsignedBytesMatrixTexture = function (rows, columns) { this.throwIfDisposed(); return createUnsignedBytesMatrixTexture(this.gl, rows, columns, this.textureConfig); }; GPGPUContext.prototype.uploadPixelDataToTexture = function (texture, pixels) { this.throwIfDisposed(); uploadPixelDataToTexture(this.gl, texture, pixels); }; GPGPUContext.prototype.createFloat16PackedMatrixTexture = function (rows, columns) { this.throwIfDisposed(); return createFloat16PackedMatrixTexture(this.gl, rows, columns, this.textureConfig); }; GPGPUContext.prototype.createPackedMatrixTexture = function (rows, columns) { this.throwIfDisposed(); return createPackedMatrixTexture(this.gl, rows, columns, this.textureConfig); }; GPGPUContext.prototype.deleteMatrixTexture = function (texture) { var _this = this; this.throwIfDisposed(); if (this.outputTexture === texture) { unbindColorTextureFromFramebuffer(this.gl, this.framebuffer); this.outputTexture = null; } callAndCheck(this.gl, function () { return _this.gl.deleteTexture(texture); }); }; GPGPUContext.prototype.uploadMatrixToTexture = function (texture, rows, columns, matrix) { this.throwIfDisposed(); var numChannels = getNumChannels(); return uploadMatrixToTexture(this.gl, texture, rows, columns, matrix, numChannels, this.textureConfig); }; GPGPUContext.prototype.uploadMatrixToPackedTexture = function (texture, batch, rows, columns, physicalRows, physicalCols, matrix) { this.throwIfDisposed(); return uploadMatrixToPackedTexture(this.gl, texture, batch, rows, columns, physicalRows, physicalCols, matrix, this.textureConfig); }; GPGPUContext.prototype.downloadFloat32MatrixFromOutputTexture = function (texture, rows, columns) { var _this = this; return this.downloadMatrixDriver(texture, function () { return downloadFloat32MatrixFromOutputTexture(_this.gl, rows, columns, _this.textureConfig); }); }; GPGPUContext.prototype.downloadByteEncodedFloatMatrixFromOutputTexture = function (texture, rows, columns) { var _this = this; return this.downloadMatrixDriver(texture, function () { return downloadByteEncodedFloatMatrixFromOutputTexture(_this.gl, rows, columns, _this.textureConfig); }); }; GPGPUContext.prototype.downloadPackedMatrixFromBuffer = function (buffer, batch, rows, columns, physicalRows, physicalCols) { return downloadPackedMatrixFromBuffer(this.gl, buffer, batch, rows, columns, physicalRows, physicalCols, this.textureConfig); }; GPGPUContext.prototype.downloadFloat32MatrixFromBuffer = function (buffer, rows, columns) { return downloadFloat32MatrixFromBuffer(this.gl, buffer, rows, columns, this.textureConfig); }; GPGPUContext.prototype.maybeCreateBufferFromTexture = function (texture, rows, columns) { this.bindTextureToFrameBuffer(texture); var result = maybeCreateBufferFromOutputTexture(this.gl, texture, rows, columns, this.textureConfig); this.unbindTextureToFrameBuffer(); return result; }; GPGPUContext.prototype.createAndWaitForFence = function () { var fenceContext = this.createFence(this.gl); return this.pollFence(fenceContext); }; GPGPUContext.prototype.createFence = function (gl) { var _this = this; var query; var isFencePassed; if (ENV.get('WEBGL_FENCE_API_ENABLED')) { var gl2_1 = gl; var sync_1 = gl2_1.fenceSync(gl2_1.SYNC_GPU_COMMANDS_COMPLETE, 0); gl.flush(); isFencePassed = function () { var status = gl2_1.clientWaitSync(sync_1, 0, 0); return status === gl2_1.ALREADY_SIGNALED || status === gl2_1.CONDITION_SATISFIED; }; query = sync_1; } else if (ENV.get('WEBGL_DISJOINT_QUERY_TIMER_EXTENSION_VERSION') > 0) { query = this.beginQuery(); this.endQuery(); isFencePassed = function () { return _this.isQueryAvailable(query, ENV.get('WEBGL_DISJOINT_QUERY_TIMER_EXTENSION_VERSION')); }; } else { isFencePassed = function () { return true; }; } return { query: query, isFencePassed: isFencePassed }; }; GPGPUContext.prototype.downloadMatrixFromPackedTexture = function (texture, batch, rows, columns, physicalRows, physicalCols) { var _this = this; return this.downloadMatrixDriver(texture, function () { return downloadMatrixFromPackedOutputTexture(_this.gl, batch, rows, columns, physicalRows, physicalCols, _this.textureConfig); }); }; GPGPUContext.prototype.createProgram = function (fragmentShaderSource) { this.throwIfDisposed(); var gl = this.gl; var fragmentShader = createFragmentShader(gl, fragmentShaderSource); var vertexShader = createVertexShader$1(gl); var program = createProgram(gl); callAndCheck(gl, function () { return gl.attachShader(program, vertexShader); }); callAndCheck(gl, function () { return gl.attachShader(program, fragmentShader); }); linkProgram(gl, program); if (this.autoDebugValidate) { validateProgram(gl, program); } if (!this.vertexAttrsAreBound) { this.setProgram(program); this.vertexAttrsAreBound = bindVertexProgramAttributeStreams(gl, this.program, this.vertexBuffer); } return program; }; GPGPUContext.prototype.deleteProgram = function (program) { var _this = this; this.throwIfDisposed(); if (program === this.program) { this.program = null; } if (program != null) { callAndCheck(this.gl, function () { return _this.gl.deleteProgram(program); }); } }; GPGPUContext.prototype.setProgram = function (program) { var _this = this; this.throwIfDisposed(); this.program = program; if ((this.program != null) && this.autoDebugValidate) { validateProgram(this.gl, this.program); } callAndCheck(this.gl, function () { return _this.gl.useProgram(program); }); }; GPGPUContext.prototype.getUniformLocation = function (program, uniformName, shouldThrow) { if (shouldThrow === void 0) { shouldThrow = true; } this.throwIfDisposed(); if (shouldThrow) { return getProgramUniformLocationOrThrow(this.gl, program, uniformName); } else { return getProgramUniformLocation(this.gl, program, uniformName); } }; GPGPUContext.prototype.getAttributeLocation = function (program, attribute) { var _this = this; this.throwIfDisposed(); return callAndCheck(this.gl, function () { return _this.gl.getAttribLocation(program, attribute); }); }; GPGPUContext.prototype.getUniformLocationNoThrow = function (program, uniformName) { this.throwIfDisposed(); return this.gl.getUniformLocation(program, uniformName); }; GPGPUContext.prototype.setInputMatrixTexture = function (inputMatrixTexture, uniformLocation, textureUnit) { this.throwIfDisposed(); this.throwIfNoProgram(); bindTextureToProgramUniformSampler(this.gl, this.program, inputMatrixTexture, uniformLocation, textureUnit); }; GPGPUContext.prototype.setOutputMatrixTexture = function (outputMatrixTexture, rows, columns) { this.setOutputMatrixTextureDriver(outputMatrixTexture, columns, rows); }; GPGPUContext.prototype.setOutputPackedMatrixTexture = function (outputPackedMatrixTexture, rows, columns) { this.throwIfDisposed(); var _a = getPackedMatrixTextureShapeWidthHeight(rows, columns), width = _a[0], height = _a[1]; this.setOutputMatrixTextureDriver(outputPackedMatrixTexture, width, height); }; GPGPUContext.prototype.setOutputMatrixWriteRegion = function (startRow, numRows, startColumn, numColumns) { this.setOutputMatrixWriteRegionDriver(startColumn, startRow, numColumns, numRows); }; GPGPUContext.prototype.setOutputPackedMatrixWriteRegion = function (startRow, numRows, startColumn, numColumns) { throw new Error('setOutputPackedMatrixWriteRegion not implemented.'); }; GPGPUContext.prototype.debugValidate = function () { if (this.program != null) { validateProgram(this.gl, this.program); } validateFramebuffer(this.gl); }; GPGPUContext.prototype.executeProgram = function () { this.throwIfDisposed(); this.throwIfNoProgram(); var gl = this.gl; if (this.autoDebugValidate) { this.debugValidate(); } callAndCheck(gl, function () { return gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0); }); }; GPGPUContext.prototype.blockUntilAllProgramsCompleted = function () { var _this = this; this.throwIfDisposed(); callAndCheck(this.gl, function () { return _this.gl.finish(); }); }; GPGPUContext.prototype.getQueryTimerExtension = function () { if (this.disjointQueryTimerExtension == null) { this.disjointQueryTimerExtension = getExtensionOrThrow(this.gl, ENV.get('WEBGL_DISJOINT_QUERY_TIMER_EXTENSION_VERSION') === 2 ? 'EXT_disjoint_timer_query_webgl2' : 'EXT_disjoint_timer_query'); } return this.disjointQueryTimerExtension; }; GPGPUContext.prototype.getQueryTimerExtensionWebGL2 = function () { return this.getQueryTimerExtension(); }; GPGPUContext.prototype.getQueryTimerExtensionWebGL1 = function () { return this.getQueryTimerExtension(); }; GPGPUContext.prototype.beginQuery = function () { if (ENV.get('WEBGL_DISJOINT_QUERY_TIMER_EXTENSION_VERSION') === 2) { var gl2 = this.gl; var ext_1 = this.getQueryTimerExtensionWebGL2(); var query_1 = gl2.createQuery(); gl2.beginQuery(ext_1.TIME_ELAPSED_EXT, query_1); return query_1; } var ext = this.getQueryTimerExtensionWebGL1(); var query = ext.createQueryEXT(); ext.beginQueryEXT(ext.TIME_ELAPSED_EXT, query); return query; }; GPGPUContext.prototype.endQuery = function () { if (ENV.get('WEBGL_DISJOINT_QUERY_TIMER_EXTENSION_VERSION') === 2) { var gl2 = this.gl; var ext_2 = this.getQueryTimerExtensionWebGL2(); gl2.endQuery(ext_2.TIME_ELAPSED_EXT); return; } var ext = this.getQueryTimerExtensionWebGL1(); ext.endQueryEXT(ext.TIME_ELAPSED_EXT); }; GPGPUContext.prototype.waitForQueryAndGetTime = function (query) { return __awaiter(this, void 0, void 0, function () { var _this = this; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4, repeatedTry(function () { return _this.disposed || _this.isQueryAvailable(query, ENV.get('WEBGL_DISJOINT_QUERY_TIMER_EXTENSION_VERSION')); })]; case 1: _a.sent(); return [2, this.getQueryTime(query, ENV.get('WEBGL_DISJOINT_QUERY_TIMER_EXTENSION_VERSION'))]; } }); }); }; GPGPUContext.prototype.getQueryTime = function (query, queryTimerVersion) { if (queryTimerVersion === 0) { return null; } if (queryTimerVersion === 2) { var gl2 = this.gl; var timeElapsedNanos = gl2.getQueryParameter(query, gl2.QUERY_RESULT); return timeElapsedNanos / 1000000; } else { var ext = this.getQueryTimerExtensionWebGL1(); var timeElapsedNanos = ext.getQueryObjectEXT(query, ext.QUERY_RESULT_EXT); return timeElapsedNanos / 1000000; } }; GPGPUContext.prototype.isQueryAvailable = function (query, queryTimerVersion) { if (queryTimerVersion === 0) { return true; } if (queryTimerVersion === 2) { var gl2 = this.gl; var ext = this.getQueryTimerExtensionWebGL2(); var available = gl2.getQueryParameter(query, gl2.QUERY_RESULT_AVAILABLE); if (this.disjoint == null) { this.disjoint = this.gl.getParameter(ext.GPU_DISJOINT_EXT); } return available && !this.disjoint; } else { var ext = this.getQueryTimerExtensionWebGL1(); var available = ext.getQueryObjectEXT(query, ext.QUERY_RESULT_AVAILABLE_EXT); if (this.disjoint == null) { this.disjoint = this.gl.getParameter(ext.GPU_DISJOINT_EXT); } return available && !this.disjoint; } }; GPGPUContext.prototype.pollFence = function (fenceContext) { var _this = this; return new Promise(function (resolve) { _this.addItemToPoll(function () { return fenceContext.isFencePassed(); }, function () { return resolve(); }); }); }; GPGPUContext.prototype.pollItems = function () { var index = binSearchLastTrue(this.itemsToPoll.map(function (x) { return x.isDoneFn; })); for (var i = 0; i <= index; ++i) { var resolveFn = this.itemsToPoll[i].resolveFn; resolveFn(); } this.itemsToPoll = this.itemsToPoll.slice(index + 1); }; GPGPUContext.prototype.addItemToPoll = function (isDoneFn, resolveFn) { var _this = this; this.itemsToPoll.push({ isDoneFn: isDoneFn, resolveFn: resolveFn }); if (this.itemsToPoll.length > 1) { return; } repeatedTry(function () { _this.pollItems(); return _this.itemsToPoll.length === 0; }); }; GPGPUContext.prototype.bindTextureToFrameBuffer = function (texture) { this.throwIfDisposed(); bindColorTextureToFramebuffer(this.gl, texture, this.framebuffer); if (this.autoDebugValidate) { validateFramebuffer(this.gl); } }; GPGPUContext.prototype.unbindTextureToFrameBuffer = function () { if (this.outputTexture != null) { bindColorTextureToFramebuffer(this.gl, this.outputTexture, this.framebuffer); if (this.autoDebugValidate) { validateFramebuffer(this.gl); } } else { unbindColorTextureFromFramebuffer(this.gl, this.framebuffer); } }; GPGPUContext.prototype.downloadMatrixDriver = function (texture, downloadAndDecode) { this.bindTextureToFrameBuffer(texture); var result = downloadAndDecode(); this.unbindTextureToFrameBuffer(); return result; }; GPGPUContext.prototype.setOutputMatrixTextureDriver = function (outputMatrixTextureMaybePacked, width, height) { this.throwIfDisposed(); var gl = this.gl; bindColorTextureToFramebuffer(gl, outputMatrixTextureMaybePacked, this.framebuffer); if (this.autoDebugValidate) { validateFramebuffer(gl); } this.outputTexture = outputMatrixTextureMaybePacked; callAndCheck(gl, function () { return gl.viewport(0, 0, width, height); }); callAndCheck(gl, function () { return gl.scissor(0, 0, width, height); }); }; GPGPUContext.prototype.setOutputMatrixWriteRegionDriver = function (x, y, width, height) { var _this = this; this.throwIfDisposed(); callAndCheck(this.gl, function () { return _this.gl.scissor(x, y, width, height); }); }; GPGPUContext.prototype.throwIfDisposed = function () { if (this.disposed) { throw new Error('Attempted to use disposed GPGPUContext.'); } }; GPGPUContext.prototype.throwIfNoProgram = function () { if (this.program == null) { throw new Error('No GPU program is currently set.'); } }; return GPGPUContext; }()); function binSearchLastTrue(arr) { var start = 0; var end = arr.length - 1; var best = -1; while (start <= end) { var mid = (start + end) >> 1; var isDone = arr[mid](); if (isDone) { best = mid; start = mid + 1; } else { end = mid - 1; } } return best; } function compileProgram(gpgpu, program, inputs, output) { var userCode = program.userCode; var inputInfos = inputs.map(function (input, i) { var shapeInfo = { logicalShape: input.shape, texShape: input.isUniform ? null : input.texData.texShape, isUniform: input.isUniform, isPacked: input.isUniform ? false : input.texData.isPacked, flatOffset: null }; if (input.texData != null && input.texData.slice != null && input.texData.slice.flatOffset > 0) { shapeInfo.flatOffset = input.texData.slice.flatOffset; } return { name: program.variableNames[i], shapeInfo: shapeInfo }; }); var inShapeInfos = inputInfos.map(function (x) { return x.shapeInfo; }); var outShapeInfo = { logicalShape: output.shape, texShape: output.texData.texShape, isUniform: false, isPacked: output.texData.isPacked, flatOffset: null }; var source = makeShader(inputInfos, outShapeInfo, userCode, program.usesPackedTextures); var webGLProgram = gpgpu.createProgram(source); var uniformLocations = {}; for (var i = 0; i < program.variableNames.length; i++) { var varName = program.variableNames[i]; var shouldThrow = false; uniformLocations[varName] = gpgpu.getUniformLocation(webGLProgram, varName, shouldThrow); uniformLocations["offset" + varName] = gpgpu.getUniformLocation(webGLProgram, "offset" + varName, shouldThrow); } return { program: program, source: source, webGLProgram: webGLProgram, uniformLocations: uniformLocations, gpgpu: gpgpu, inShapeInfos: inShapeInfos, outShapeInfo: outShapeInfo }; } function validateBinaryAndProgram(shapeInfos, inputs) { if (shapeInfos.length !== inputs.length) { throw Error("Binary was compiled with " + shapeInfos.length + " inputs, but " + ("was executed with " + inputs.length + " inputs")); } shapeInfos.forEach(function (s, i) { var shapeA = s.logicalShape; var input = inputs[i]; var shapeB = input.shape; if (!arraysEqual(shapeA, shapeB)) { throw Error("Binary was compiled with different shapes than " + ("the current args. Shapes " + shapeA + " and " + shapeB + " must match")); } if (s.isUniform && input.isUniform) { return; } var texShapeA = s.texShape; var texShapeB = input.isUniform ? null : input.texData.texShape; if (!arraysEqual(texShapeA, texShapeB)) { throw Error("Binary was compiled with different texture shapes than the" + (" current args. Shape " + texShapeA + " and " + texShapeB + " must match")); } }); } function runProgram(binary, inputs, output, customSetup) { validateBinaryAndProgram(binary.inShapeInfos, inputs); validateBinaryAndProgram([binary.outShapeInfo], [output]); var outTex = output.texData.texture; var outTexShape = output.texData.texShape; var gpgpu = binary.gpgpu; if (output.texData.isPacked) { gpgpu.setOutputPackedMatrixTexture(outTex, outTexShape[0], outTexShape[1]); } else { gpgpu.setOutputMatrixTexture(outTex, outTexShape[0], outTexShape[1]); } gpgpu.setProgram(binary.webGLProgram); inputs.forEach(function (input, i) { var varName = binary.program.variableNames[i]; var varLoc = binary.uniformLocations[varName]; var varOffsetLoc = binary.uniformLocations["offset" + varName]; if (varLoc == null) { return; } if (input.isUniform) { if (sizeFromShape(input.shape) === 1) { gpgpu.gl.uniform1f(varLoc, input.uniformValues[0]); } else { var vals = input.uniformValues; if (!(vals instanceof Float32Array)) { vals = new Float32Array(vals); } gpgpu.gl.uniform1fv(varLoc, vals); } return; } if (input.texData.slice != null && varOffsetLoc != null) { gpgpu.gl.uniform1i(varOffsetLoc, input.texData.slice.flatOffset); } gpgpu.setInputMatrixTexture(input.texData.texture, varLoc, i); }); if (customSetup != null) { customSetup(gpgpu, binary.webGLProgram); } gpgpu.executeProgram(); } function makeShaderKey(program, inputs, output) { var keyInputs = ''; inputs.concat(output).forEach(function (x) { var hasOffset = x.texData != null && x.texData.slice != null && x.texData.slice.flatOffset > 0; var texShape = x.isUniform ? 'uniform' : x.texData.texShape; keyInputs += x.shape + "_" + texShape + "_" + hasOffset; }); var keyUserCode = program.userCode; var key = program.constructor.name; key += '_' + keyInputs + '_' + keyUserCode; return key; } var Im2ColProgram = (function () { function Im2ColProgram(outputShape, inputShape, convInfo) { this.variableNames = ['A']; this.outputShape = outputShape; var filterWidth = convInfo.filterWidth, inChannels = convInfo.inChannels, strideWidth = convInfo.strideWidth, strideHeight = convInfo.strideHeight, padInfo = convInfo.padInfo, outWidth = convInfo.outWidth, dilationWidth = convInfo.dilationWidth, dilationHeight = convInfo.dilationHeight; var left = padInfo.left, top = padInfo.top; var itemsPerBlockRow = inChannels * filterWidth; var glsl = getGlslDifferences(); this.userCode = "\n void main() {\n ivec2 rc = getOutputCoords();\n\n vec4 result = vec4(0);\n\n for(int row=0; row<=1; row++) {\n for(int col=0; col<=1; col++) {\n int blockIndex = rc.y + col;\n int pos = rc.x + row;\n\n if(blockIndex >= " + outputShape[1] + " || pos >= " + outputShape[0] + ") continue;\n\n int offsetY = int(blockIndex / (" + outWidth + ")) * " + strideHeight + " - " + top + ";\n int d0 = offsetY + " + dilationHeight + " * (pos / " + itemsPerBlockRow + ");\n\n if(d0 >= " + inputShape[0] + " || d0 < 0) continue;\n\n int offsetX = int(mod(float(blockIndex), " + outWidth + ".) * " + strideWidth + ". - " + left + ".);\n int d1 = offsetX + " + dilationWidth + " * (int(mod(float(pos), " + itemsPerBlockRow + ".) / " + inChannels + ".));\n\n if(d1 >= " + inputShape[1] + " || d1 < 0) continue;\n\n result[row * 2 + col] = getA(d0, d1, int(mod(float(pos), " + inChannels + ".)));\n }\n }\n\n " + glsl.output + " = result;\n }\n "; } return Im2ColProgram; }()); var LRNProgram = (function () { function LRNProgram(xShape, radius, bias, alpha, beta) { this.variableNames = ['x']; this.outputShape = []; var rad = radius; var maxD = xShape[3] - 1; this.outputShape = xShape; var powOperator; var basis = "float(" + bias + ") + float(" + alpha + ") * sum"; if (beta === 0.5) { powOperator = "inversesqrt(" + basis + ")"; } else if (beta === 1.0) { powOperator = "1.0/(" + basis + ")"; } else { powOperator = "exp(log(" + basis + ") * float(-" + beta + "));"; } this.userCode = "\n void main() {\n ivec4 coords = getOutputCoords();\n int b = coords[0];\n int r = coords[1];\n int c = coords[2];\n int d = coords[3];\n float x = getX(b, r, c, d);\n float sum = 0.0;\n for (int j = -" + rad + "; j <= " + rad + "; j++) {\n int idx = d + j;\n if (idx >= 0 && idx <= " + maxD + ") {\n float z = getX(b, r, c, idx);\n sum += z * z;\n }\n }\n float val = x * " + powOperator + ";\n setOutput(val);\n }\n "; } return LRNProgram; }()); var LRNGradProgram = (function () { function LRNGradProgram(inputShape, depthRadius, bias, alpha, beta) { this.variableNames = ['inputImage', 'outputImage', 'dy']; this.outputShape = []; this.outputShape = inputShape; this.depth = inputShape[3]; this.depthRadius = depthRadius; this.bias = bias; this.alpha = alpha; this.beta = beta; this.userCode = "\n void main() {\n ivec4 coords = getOutputCoords();\n int b = coords[0];\n int r = coords[1];\n int c = coords[2];\n\n float result = 0.0;\n for (int d = 0; d < " + this.depth + "; ++d) {\n int depthBegin = int(max(0.0, float(d - " + depthRadius + ")));\n int depthEnd = int(min(float(" + this.depth + "),\n float(d + " + depthRadius + " + 1)));\n\n const int MIN_DEPTH_BEGIN = 0;\n const int MAX_DEPTH_END = " + this.depth + ";\n\n float norm = 0.0;\n for (int k = MIN_DEPTH_BEGIN; k < MAX_DEPTH_END; ++k) {\n if (k < depthBegin){\n continue;\n }\n else if (k >= depthBegin && k < depthEnd) {\n norm += getInputImage(b, r, c, k) * getInputImage(b, r, c, k);\n }\n else {\n break;\n }\n }\n\n norm = float(" + alpha + ") * norm + float(" + bias + ");\n\n for(int k = MIN_DEPTH_BEGIN; k < MAX_DEPTH_END; ++k){\n if (k < depthBegin){\n continue;\n }\n else if (k >= depthBegin && k < depthEnd){\n float dyi = -2.0 * float(" + alpha + ")\n * float(" + beta + ")\n * getInputImage(b ,r ,c, k) * getOutputImage(b, r, c, d)\n / norm;\n if (k == d) {\n dyi += pow(norm, -1.0 * " + beta + ");\n }\n if (k == coords[3]) {\n dyi *= getDy(b, r, c, d);\n result += dyi;\n }\n }\n else {\n break;\n }\n }\n }\n setOutput(result);\n }\n "; } return LRNGradProgram; }()); var MaxPool2DBackpropProgram = (function () { function MaxPool2DBackpropProgram(convInfo) { this.variableNames = ['dy', 'maxPos']; this.outputShape = convInfo.inShape; var strideHeight = convInfo.strideHeight; var strideWidth = convInfo.strideWidth; var dilationHeight = convInfo.dilationHeight; var effectiveFilterHeight = convInfo.effectiveFilterHeight; var effectiveFilterWidth = convInfo.effectiveFilterWidth; var padTop = effectiveFilterHeight - 1 - convInfo.padInfo.top; var padLeft = effectiveFilterWidth - 1 - convInfo.padInfo.left; var lastIndex = effectiveFilterHeight * effectiveFilterWidth - 1; this.userCode = "\n const ivec2 pads = ivec2(" + padTop + ", " + padLeft + ");\n\n void main() {\n ivec4 coords = getOutputCoords();\n int b = coords[0];\n int d = coords[3];\n\n ivec2 dyRCCorner = coords.yz - pads;\n int dyRCorner = dyRCCorner.x;\n int dyCCorner = dyRCCorner.y;\n\n // Convolve dy(?, ?, d) with pos mask(:, :, d) to get dx(xR, xC, d).\n // ? = to be determined. : = across all values in that axis.\n float dotProd = 0.0;\n for (int wR = 0; wR < " + effectiveFilterHeight + ";\n wR += " + dilationHeight + ") {\n float dyR = float(dyRCorner + wR) / " + strideHeight + ".0;\n\n if (dyR < 0.0 || dyR >= " + convInfo.outHeight + ".0 || fract(dyR) > 0.0) {\n continue;\n }\n int idyR = int(dyR);\n\n for (int wC = 0; wC < " + effectiveFilterWidth + "; wC++) {\n float dyC = float(dyCCorner + wC) / " + strideWidth + ".0;\n\n if (dyC < 0.0 || dyC >= " + convInfo.outWidth + ".0 ||\n fract(dyC) > 0.0) {\n continue;\n }\n int idyC = int(dyC);\n\n float dyValue = getDy(b, idyR, idyC, d);\n int maxPosValue = " + lastIndex + " - int(getMaxPos(b, idyR, idyC, d));\n\n // Get the current value, check it against the value from the\n // position matrix.\n int curPosValue = wR * " + effectiveFilterWidth + " + wC;\n float mask = float(maxPosValue == curPosValue ? 1.0 : 0.0);\n\n dotProd += dyValue * mask;\n }\n }\n setOutput(dotProd);\n }\n "; } return MaxPool2DBackpropProgram; }()); var MatMulProgram = (function () { function MatMulProgram(aShape, bShape, transposeA, transposeB, addBias, activation) { if (transposeA === void 0) { transposeA = false; } if (transposeB === void 0) { transposeB = false; } if (addBias === void 0) { addBias = false; } if (activation === void 0) { activation = null; } this.variableNames = ['matrixA', 'matrixB']; var batchSize = aShape[0]; var outerShapeA = transposeA ? aShape[2] : aShape[1]; var outerShapeB = transposeB ? bShape[1] : bShape[2]; var sharedDim = transposeA ? aShape[1] : aShape[2]; this.outputShape = [batchSize, outerShapeA, outerShapeB]; var aSnippetFromOffset = function (vec4Offset, indexVar) { return transposeA ? "batch, " + indexVar + " + " + vec4Offset + ", aRow" : "batch, aRow, " + indexVar + " + " + vec4Offset; }; var bSnippetFromOffset = function (vec4Offset, indexVar) { return transposeB ? "batch, bCol, " + indexVar + " + " + vec4Offset : "batch, " + indexVar + " + " + vec4Offset + ", bCol"; }; var sharedDimNearestVec4 = Math.floor(sharedDim / 4) * 4; var sharedDimVec4Remainder = sharedDim % 4; var activationSnippet = '', applyActivationSnippet = ''; if (activation) { activationSnippet = "float activation(float x) {\n " + activation + "\n }"; applyActivationSnippet = "result = activation(result);"; } var addBiasSnippet = addBias ? 'result += getBiasAtOutCoords();' : ''; if (addBias) { this.variableNames.push('bias'); } this.userCode = "\n " + activationSnippet + "\n\n float dotARowBCol(int batch, int aRow, int bCol) {\n float result = 0.0;\n for (int i = 0; i < " + sharedDimNearestVec4 + "; i += 4) {\n vec4 a = vec4(\n getMatrixA(" + aSnippetFromOffset(0, 'i') + "),\n getMatrixA(" + aSnippetFromOffset(1, 'i') + "),\n getMatrixA(" + aSnippetFromOffset(2, 'i') + "),\n getMatrixA(" + aSnippetFromOffset(3, 'i') + ")\n );\n vec4 b = vec4(\n getMatrixB(" + bSnippetFromOffset(0, 'i') + "),\n getMatrixB(" + bSnippetFromOffset(1, 'i') + "),\n getMatrixB(" + bSnippetFromOffset(2, 'i') + "),\n getMatrixB(" + bSnippetFromOffset(3, 'i') + ")\n );\n\n result += dot(a, b);\n }\n\n if (" + (sharedDimVec4Remainder === 1) + ") {\n result += getMatrixA(" + aSnippetFromOffset(0, sharedDimNearestVec4) + ") *\n getMatrixB(" + bSnippetFromOffset(0, sharedDimNearestVec4) + ");\n } else if (" + (sharedDimVec4Remainder === 2) + ") {\n vec2 a = vec2(\n getMatrixA(" + aSnippetFromOffset(0, sharedDimNearestVec4) + "),\n getMatrixA(" + aSnippetFromOffset(1, sharedDimNearestVec4) + ")\n );\n vec2 b = vec2(\n getMatrixB(" + bSnippetFromOffset(0, sharedDimNearestVec4) + "),\n getMatrixB(" + bSnippetFromOffset(1, sharedDimNearestVec4) + ")\n );\n result += dot(a, b);\n } else if (" + (sharedDimVec4Remainder === 3) + ") {\n vec3 a = vec3(\n getMatrixA(" + aSnippetFromOffset(0, sharedDimNearestVec4) + "),\n getMatrixA(" + aSnippetFromOffset(1, sharedDimNearestVec4) + "),\n getMatrixA(" + aSnippetFromOffset(2, sharedDimNearestVec4) + ")\n );\n vec3 b = vec3(\n getMatrixB(" + bSnippetFromOffset(0, sharedDimNearestVec4) + "),\n getMatrixB(" + bSnippetFromOffset(1, sharedDimNearestVec4) + "),\n getMatrixB(" + bSnippetFromOffset(2, sharedDimNearestVec4) + ")\n );\n result += dot(a, b);\n }\n\n return result;\n }\n\n void main() {\n ivec3 resBRC = getOutputCoords();\n float result = dotARowBCol(resBRC.x, resBRC.y, resBRC.z);\n\n " + addBiasSnippet + "\n\n " + applyActivationSnippet + "\n\n setOutput(result);\n }\n "; } return MatMulProgram; }()); var MatMulPackedProgram = (function () { function MatMulPackedProgram(aShape, bShape, outputShape, transposeA, transposeB, addBias, activation) { if (transposeA === void 0) { transposeA = false; } if (transposeB === void 0) { transposeB = false; } if (addBias === void 0) { addBias = false; } if (activation === void 0) { activation = null; } this.variableNames = ['matrixA', 'matrixB']; this.usesPackedTextures = true; this.outputShape = outputShape; var sharedDim = transposeA ? aShape[0] : aShape[1]; var sharedDimensionPacked = Math.ceil(sharedDim / 2); var aSample = transposeA ? 'i * 2, rc.x' : 'rc.x, i * 2'; var bSample = transposeB ? 'rc.y, i * 2' : 'i * 2, rc.y'; var aSwizzle = transposeA ? ['a.xxyy', 'a.zzww'] : ['a.xxzz', 'a.yyww']; var bSwizzle = transposeB ? ['b.xzxz', 'b.ywyw'] : ['b.xyxy', 'b.zwzw']; var activationSnippet = '', applyActivationSnippet = ''; if (activation) { activationSnippet = "vec4 activation(vec4 x) {\n " + activation + "\n }"; applyActivationSnippet = "result = activation(result);"; } var addBiasSnippet = addBias ? 'result += getBiasAtOutCoords();' : ''; if (addBias) { this.variableNames.push('bias'); } this.userCode = "\n " + activationSnippet + "\n\n const float sharedDimension = " + sharedDimensionPacked + ".0;\n\n vec4 dot2x2ARowBCol(ivec2 rc) {\n vec4 result = vec4(0);\n for (int i = 0; i < " + sharedDimensionPacked + "; i++) {\n vec4 a = getMatrixA(" + aSample + ");\n vec4 b = getMatrixB(" + bSample + ");\n\n result += (" + aSwizzle[0] + " * " + bSwizzle[0] + ") + (" + aSwizzle[1] + " * " + bSwizzle[1] + ");\n }\n return result;\n }\n\n void main() {\n ivec2 rc = getOutputCoords();\n vec4 result = dot2x2ARowBCol(rc);\n\n " + addBiasSnippet + "\n\n " + applyActivationSnippet + "\n\n setOutput(result);\n }\n "; } return MatMulPackedProgram; }()); var MultinomialProgram = (function () { function MultinomialProgram(batchSize, numOutcomes, numSamples) { this.variableNames = ['probs']; this.outputShape = [batchSize, numSamples]; this.userCode = "\n uniform float seed;\n\n void main() {\n ivec2 coords = getOutputCoords();\n int batch = coords[0];\n\n float r = random(seed);\n float cdf = 0.0;\n\n for (int i = 0; i < " + (numOutcomes - 1) + "; i++) {\n cdf += getProbs(batch, i);\n\n if (r < cdf) {\n setOutput(float(i));\n return;\n }\n }\n\n // If no other event happened, last event happened.\n setOutput(float(" + (numOutcomes - 1) + "));\n }\n "; } MultinomialProgram.prototype.getCustomSetupFunc = function (seed) { var _this = this; return function (gpgpu, webGLProgram) { if (_this.seedLoc == null) { _this.seedLoc = gpgpu.getUniformLocation(webGLProgram, 'seed'); } gpgpu.gl.uniform1f(_this.seedLoc, seed); }; }; return MultinomialProgram; }()); var OneHotProgram = (function () { function OneHotProgram(numIndices, depth, onValue, offValue) { this.variableNames = ['indices']; this.outputShape = [numIndices, depth]; this.userCode = "\n void main() {\n ivec2 coords = getOutputCoords();\n int index = round(getIndices(coords.x));\n setOutput(mix(float(" + offValue + "), float(" + onValue + "),\n float(index == coords.y)));\n }\n "; } return OneHotProgram; }()); function getVecChannels(name, rank) { return ['x', 'y', 'z', 'w', 'u', 'v'].slice(0, rank).map(function (d) { return name + "." + d; }); } function getChannels(name, rank) { if (rank === 1) { return [name]; } return getVecChannels(name, rank); } function getSourceCoords$1(rank, dims) { if (rank === 1) { return 'rc'; } var coords = ''; for (var i = 0; i < rank; i++) { coords += dims[i]; if (i < rank - 1) { coords += ','; } } return coords; } var PackProgram = (function () { function PackProgram(outputShape) { this.variableNames = ['A']; this.isPackShader = true; this.outputShape = outputShape; var rank = outputShape.length; if (rank === 0) { this.userCode = "\n void main() {\n setOutput(vec4(getA(), 0., 0., 0.));\n }\n "; } else { var channels = getChannels('rc', rank); var dtype = getCoordsDataType(rank); var outOfBoundsCondition = getOutOfBoundsCondition(rank, outputShape, channels); var setup = getSetup(rank, outputShape[outputShape.length - 1], outputShape[outputShape.length - 2], channels); var output = getOutput(outputShape, channels); this.userCode = "\n void main() {\n " + dtype + " rc = getOutputCoords();\n\n if(" + outOfBoundsCondition + ") {\n setOutput(vec4(0));\n } else {\n " + setup + "\n\n setOutput(vec4(" + output + "));\n }\n }\n "; } } return PackProgram; }()); function getSourceCoordsArr(rank, dims) { var coords = []; for (var row = 0; row <= 1; row++) { for (var col = 0; col <= 1; col++) { var coord = (row === 0 ? 'r' : 'rp1') + ", " + (col === 0 ? 'c' : 'cp1'); for (var d = 2; d < rank; d++) { coord = dims[dims.length - 1 - d] + "," + coord; } coords.push(coord); } } return coords; } function getOutOfBoundsCondition(rank, shape, dims) { if (rank === 1) { return "rc > " + shape[0]; } var cond = ''; for (var i = rank - 2; i < rank; i++) { cond += dims[i] + " >= " + shape[i]; if (i < rank - 1) { cond += '||'; } } return cond; } function getSetup(rank, cols, rows, dims) { if (rank === 1) { return ''; } var innerDims = dims.slice(-2); return "\n int r = " + innerDims[0] + ";\n int c = " + innerDims[1] + ";\n int rp1 = r + 1;\n int cp1 = c + 1;\n\n bool cEdge = cp1 >= " + cols + ";\n bool rEdge = rp1 >= " + rows + ";\n "; } function getOutput(shape, dims) { var rank = shape.length; var sourceCoords = getSourceCoordsArr(rank, dims); if (rank === 1) { return "getA(rc),\n rc + 1 >= " + shape[0] + " ? 0. : getA(rc + 1),\n 0, 0"; } return "getA(" + sourceCoords[0] + "),\n cEdge ? 0. : getA(" + sourceCoords[1] + "),\n rEdge ? 0. : getA(" + sourceCoords[2] + "),\n rEdge || cEdge ? 0. : getA(" + sourceCoords[3] + ")"; } var PadProgram = (function () { function PadProgram(xShape, paddings, constantValue) { this.variableNames = ['x']; this.outputShape = paddings.map(function (p, i) { return p[0] + xShape[i] + p[1]; }); var rank = xShape.length; var type = getCoordsDataType(rank); var start = paddings.map(function (p) { return p[0]; }).join(','); var end = paddings.map(function (p, i) { return p[0] + xShape[i]; }).join(','); var unpackedCoords = ['coords[0]', 'coords[1]', 'coords[2]', 'coords[3]'].slice(0, rank); if (rank === 1) { this.userCode = "\n int start = " + start + ";\n int end = " + end + ";\n\n void main() {\n int outC = getOutputCoords();\n if (outC < start || outC >= end) {\n setOutput(float(" + constantValue + "));\n } else {\n setOutput(getX(outC - start));\n }\n }\n "; return; } this.userCode = "\n " + type + " start = " + type + "(" + start + ");\n " + type + " end = " + type + "(" + end + ");\n\n void main() {\n " + type + " outC = getOutputCoords();\n if (any(lessThan(outC, start)) || any(greaterThanEqual(outC, end))) {\n setOutput(float(" + constantValue + "));\n } else {\n " + type + " coords = outC - start;\n setOutput(getX(" + unpackedCoords + "));\n }\n }\n "; } return PadProgram; }()); var PadPackedProgram = (function () { function PadPackedProgram(xShape, paddings, constantValue) { this.variableNames = ['x']; this.usesPackedTextures = true; this.outputShape = paddings.map(function (p, i) { return p[0] + xShape[i] + p[1]; }); var rank = xShape.length; var dtype = getCoordsDataType(rank); var start = paddings.map(function (p) { return p[0]; }).join(','); var end = paddings.map(function (p, i) { return p[0] + xShape[i]; }).join(','); var coords = getChannels('rc', rank); var source = getChannels('source', rank); var cLimit = coords[rank - 1] + " < " + this.outputShape[rank - 1]; var innerDims = rank === 1 ? 'source' : "vec2(" + source.slice(-2).join() + ")"; var componentSetup = [ dtype + " rc = outputLoc;", coords[rank - 1] + " += 1;\n if(" + cLimit + ") {\n ", rank === 1 ? '' : "}\n rc = outputLoc;\n " + coords[rank - 2] + " += 1;\n if(" + coords[rank - 2] + " < " + this.outputShape[rank - 2] + ") {", rank === 1 ? '' : " " + coords[rank - 1] + " += 1;\n if(" + cLimit + ") {" ]; var paddingArea = rank === 1 ? 'rc < start || rc >= end' : 'any(lessThan(rc, start)) || any(greaterThanEqual(rc, end))'; var mainLoop = ''; for (var i = 0, j = rank === 1 ? 2 : 4; i < j; i++) { mainLoop += "\n " + componentSetup[i] + "\n if (" + paddingArea + ") {\n result[" + i + "] = float(" + constantValue + ");\n } else {\n " + dtype + " source = rc - start;\n result[" + i + "] = getChannel(getX(" + source.join() + "), " + innerDims + ");\n }\n "; } mainLoop += (rank === 1 ? "} " : "}}"); this.userCode = "\n const " + dtype + " start = " + dtype + "(" + start + ");\n const " + dtype + " end = " + dtype + "(" + end + ");\n\n void main() {\n " + dtype + " outputLoc = getOutputCoords();\n vec4 result = vec4(0.);\n " + mainLoop + "\n setOutput(result);\n }\n "; } return PadPackedProgram; }()); var Pool2DProgram = (function () { function Pool2DProgram(convInfo, poolType, computePositions) { this.variableNames = ['x']; if (poolType === 'avg' && computePositions) { throw new Error('Cannot compute positions for average pool.'); } var filterWidth = convInfo.filterWidth; var strideHeight = convInfo.strideHeight; var strideWidth = convInfo.strideWidth; var dilationHeight = convInfo.dilationHeight; var dilationWidth = convInfo.dilationWidth; var effectiveFilterHeight = convInfo.effectiveFilterHeight; var effectiveFilterWidth = convInfo.effectiveFilterWidth; var padTop = convInfo.padInfo.top; var padLeft = convInfo.padInfo.left; this.outputShape = convInfo.outShape; var isAvgPool = poolType === 'avg'; var initializationValue = '0.0'; if (!isAvgPool) { initializationValue = '-1.0 / 1e-20'; } if (computePositions) { var compareOp_1 = '>='; this.userCode = "\n const ivec2 strides = ivec2(" + strideHeight + ", " + strideWidth + ");\n const ivec2 pads = ivec2(" + padTop + ", " + padLeft + ");\n\n void main() {\n ivec4 coords = getOutputCoords();\n int batch = coords[0];\n int d = coords[3];\n\n ivec2 xRCCorner = coords.yz * strides - pads;\n int xRCorner = xRCCorner.x;\n int xCCorner = xRCCorner.y;\n\n // max/min x(?, ?, d) to get y(yR, yC, d).\n // ? = to be determined\n float minMaxValue = 0.0;\n float minMaxValueFound = 0.0;\n int minMaxPosition = 0;\n float avgValue = 0.0;\n\n for (int wR = 0; wR < " + effectiveFilterHeight + ";\n wR += " + dilationHeight + ") {\n int xR = xRCorner + wR;\n\n if (xR < 0 || xR >= " + convInfo.inHeight + ") {\n continue;\n }\n\n for (int wC = 0; wC < " + effectiveFilterWidth + ";\n wC += " + dilationWidth + ") {\n int xC = xCCorner + wC;\n\n if (xC < 0 || xC >= " + convInfo.inWidth + ") {\n continue;\n }\n\n float value = getX(batch, xR, xC, d);\n\n // If a min / max value has already been found, use it. If not,\n // use the current value.\n float currMinMaxValue = mix(\n value, minMaxValue, minMaxValueFound);\n if (value " + compareOp_1 + " currMinMaxValue) {\n minMaxValue = value;\n minMaxValueFound = 1.0;\n minMaxPosition = wR * " + effectiveFilterWidth + " + wC;\n }\n }\n }\n setOutput(float(minMaxPosition));\n }\n "; return; } var compareOp = 'max'; var returnValue = poolType + "(" + poolType + "(" + poolType + "(" + 'minMaxValue[0], minMaxValue[1]), minMaxValue[2]), minMaxValue[3])'; if (poolType === 'avg') { returnValue = "avgValue / count"; } var filterWidthNearestVec4 = Math.floor(filterWidth / 4) * 4; var filterWidthVec4Remainder = filterWidth % 4; var updateSnippet = "\n if (" + isAvgPool + ") {\n avgValue += dot(values, ones);\n } else {\n minMaxValue = " + compareOp + "(values, minMaxValue);\n }\n "; this.userCode = "\n const ivec2 strides = ivec2(" + strideHeight + ", " + strideWidth + ");\n const ivec2 pads = ivec2(" + padTop + ", " + padLeft + ");\n const float initializationValue = " + initializationValue + ";\n const vec4 ones = vec4(1.0, 1.0, 1.0, 1.0);\n\n float count = 0.0;\n\n float getValue(int batch, int xR, int xC, int d) {\n if (xC < 0 || xC >= " + convInfo.inWidth + ") {\n return initializationValue;\n }\n count += 1.0;\n return getX(batch, xR, xC, d);\n }\n\n void main() {\n ivec4 coords = getOutputCoords();\n int batch = coords[0];\n int d = coords[3];\n\n ivec2 xRCCorner = coords.yz * strides - pads;\n int xRCorner = xRCCorner.x;\n int xCCorner = xRCCorner.y;\n\n // max/min x(?, ?, d) to get y(yR, yC, d).\n // ? = to be determined\n vec4 minMaxValue = vec4(" + initializationValue + ");\n float avgValue = 0.0;\n count = 0.0;\n\n for (int wR = 0; wR < " + effectiveFilterHeight + ";\n wR += " + dilationHeight + ") {\n int xR = xRCorner + wR;\n\n if (xR < 0 || xR >= " + convInfo.inHeight + ") {\n continue;\n }\n\n for (int wC = 0; wC < " + filterWidthNearestVec4 + "; wC += 4) {\n int xC = xCCorner + wC * " + dilationWidth + ";\n\n vec4 values = vec4(\n getValue(batch, xR, xC, d),\n getValue(batch, xR, xC + " + dilationWidth + ", d),\n getValue(batch, xR, xC + 2 * " + dilationWidth + ", d),\n getValue(batch, xR, xC + 3 * " + dilationWidth + ", d)\n );\n\n " + updateSnippet + "\n }\n\n int xC = xCCorner + " + filterWidthNearestVec4 + ";\n if (" + (filterWidthVec4Remainder === 1) + ") {\n vec4 values = vec4(\n getValue(batch, xR, xC, d),\n initializationValue,\n initializationValue,\n initializationValue\n );\n\n " + updateSnippet + "\n } else if (" + (filterWidthVec4Remainder === 2) + ") {\n vec4 values = vec4(\n getValue(batch, xR, xC, d),\n getValue(batch, xR, xC + " + dilationWidth + ", d),\n initializationValue,\n initializationValue\n );\n\n " + updateSnippet + "\n } else if (" + (filterWidthVec4Remainder === 3) + ") {\n vec4 values = vec4(\n getValue(batch, xR, xC, d),\n getValue(batch, xR, xC + " + dilationWidth + ", d),\n getValue(batch, xR, xC + 2 * " + dilationWidth + ", d),\n initializationValue\n );\n\n " + updateSnippet + "\n }\n }\n setOutput(" + returnValue + ");\n }\n "; } return Pool2DProgram; }()); var ReduceProgram = (function () { function ReduceProgram(reduceInfo, reduceType) { this.variableNames = ['x']; var windowSize = reduceInfo.windowSize; var batchSize = reduceInfo.batchSize; var inSize = reduceInfo.inSize; var outSize = Math.ceil(inSize / windowSize); this.outputShape = [batchSize, outSize]; var initializationValue = '0.0'; var compareOp = ""; if (reduceType === 'prod') { initializationValue = '1.0'; } else if (reduceType === 'min') { initializationValue = '1.0 / 1e-20'; compareOp = "min"; } else if (reduceType === 'max') { initializationValue = '-1.0 / 1e-20'; compareOp = "max"; } var returnValue = reduceType + "(" + reduceType + "(" + reduceType + "(" + 'minMaxValue[0], minMaxValue[1]), minMaxValue[2]), minMaxValue[3])'; if (reduceType === 'sum') { returnValue = "sumValue"; } else if (reduceType === 'prod') { returnValue = "prodValue"; } else if (reduceType === 'all') { returnValue = "allValue"; } else if (reduceType === 'any') { returnValue = "anyValue"; } var windowSizeNearestVec4 = Math.floor(windowSize / 4) * 4; var windowSizeVec4Remainder = windowSize % 4; var updateSnippet = "\n if (" + (reduceType === 'sum') + ") {\n sumValue += dot(values, ones);\n } else if (" + (reduceType === 'prod') + ") {\n vec2 tmp = vec2(values[0], values[1]) * vec2(values[2], values[3]);\n prodValue *= tmp[0] * tmp[1];\n } else {\n minMaxValue = " + compareOp + "(values, minMaxValue);\n }\n "; var vecType = "vec4"; if (reduceType === 'all') { initializationValue = '1.0'; updateSnippet = "\n bool reducedAllValue = all(values);\n float floatedReducedAllValue = float(reducedAllValue);\n allValue = float(allValue >= 1.0 && floatedReducedAllValue >= 1.0);\n "; vecType = "bvec4"; } else if (reduceType === 'any') { initializationValue = '0.0'; updateSnippet = "\n bool reducedAnyValue = any(values);\n float floatedReducedAnyValue = float(reducedAnyValue);\n anyValue = float(anyValue >= 1.0 || floatedReducedAnyValue >= 1.0);\n "; vecType = "bvec4"; } var checkOutOfBounds = ''; if (inSize % windowSize > 0) { checkOutOfBounds = "\n if (inIdx < 0 || inIdx >= " + inSize + ") {\n return initializationValue;\n }\n "; } this.userCode = "\n const float initializationValue = " + initializationValue + ";\n const vec4 ones = vec4(1.0, 1.0, 1.0, 1.0);\n\n float getValue(int batch, int inIdx) {\n " + checkOutOfBounds + "\n return getX(batch, inIdx);\n }\n\n void main() {\n ivec2 coords = getOutputCoords();\n int batch = coords[0];\n int outIdx = coords[1];\n int inOffset = outIdx * " + windowSize + ";\n\n vec4 minMaxValue = vec4(" + initializationValue + ");\n float prodValue = 1.0;\n float sumValue = 0.0;\n float allValue = 1.0;\n float anyValue = 0.0;\n\n for (int i = 0; i < " + windowSizeNearestVec4 + "; i += 4) {\n int inIdx = inOffset + i;\n " + vecType + " values = " + vecType + "(\n getValue(batch, inIdx),\n getValue(batch, inIdx + 1),\n getValue(batch, inIdx + 2),\n getValue(batch, inIdx + 3)\n );\n\n " + updateSnippet + "\n }\n\n int inIdx = inOffset + " + windowSizeNearestVec4 + ";\n if (" + (windowSizeVec4Remainder === 1) + ") {\n " + vecType + " values = " + vecType + "(\n getValue(batch, inIdx),\n initializationValue,\n initializationValue,\n initializationValue\n );\n\n " + updateSnippet + "\n } else if (" + (windowSizeVec4Remainder === 2) + ") {\n " + vecType + " values = " + vecType + "(\n getValue(batch, inIdx),\n getValue(batch, inIdx + 1),\n initializationValue,\n initializationValue\n );\n\n " + updateSnippet + "\n } else if (" + (windowSizeVec4Remainder === 3) + ") {\n " + vecType + " values = " + vecType + "(\n getValue(batch, inIdx),\n getValue(batch, inIdx + 1),\n getValue(batch, inIdx + 2),\n initializationValue\n );\n\n " + updateSnippet + "\n }\n setOutput(" + returnValue + ");\n }\n "; } return ReduceProgram; }()); var ReshapePackedProgram = (function () { function ReshapePackedProgram(outputShape, inputShape) { this.variableNames = ['A']; this.usesPackedTextures = true; this.outputShape = outputShape; var mainLoop = ""; for (var i = 0; i < 4; i++) { var thisRC = "thisRC = rc;"; if (i % 2 === 1) { thisRC += "thisRC.z += 1;"; } if (i > 1) { thisRC += "thisRC.y += 1;"; } mainLoop += "\n " + thisRC + "\n " + (i > 0 ? "if(thisRC.y < rows && thisRC.z < cols){" : '') + "\n int flatIndex = getFlatIndex(thisRC);\n\n ivec3 inputRC = inputCoordsFromReshapedOutCoords(flatIndex);\n vec2 inputRCInnerDims = vec2(float(inputRC.y),float(inputRC.z));\n\n result[" + i + "] =\n getChannel(getA(inputRC.x, inputRC.y, inputRC.z), inputRCInnerDims);\n " + (i > 0 ? '}' : '') + "\n "; } this.userCode = "\n " + getReshapedInputCoords(inputShape) + "\n " + getFlatIndex(outputShape) + "\n\n void main() {\n ivec3 rc = getOutputCoords();\n\n vec4 result = vec4(0.);\n\n ivec3 thisRC;\n int rows = " + outputShape[1] + ";\n int cols = " + outputShape[2] + ";\n\n " + mainLoop + "\n\n setOutput(result);\n }\n "; } return ReshapePackedProgram; }()); function getFlatIndex(shape) { var dotCoordsWithStrides = dotify(['coords.x', 'coords.y', 'coords.z'], computeStrides(shape).map(function (d) { return d.toString(); }).concat(['1.'])); return "\n int getFlatIndex(ivec3 coords) {\n return round(" + dotCoordsWithStrides + ");\n }\n "; } function getReshapedInputCoords(shape) { var coordsFromIndexSnippet = getLogicalCoordinatesFromFlatIndex(['r', 'c', 'd'], shape); return "\n ivec3 inputCoordsFromReshapedOutCoords(int index) {\n " + coordsFromIndexSnippet + "\n return ivec3(r, c, d);\n }\n "; } var ResizeBilinearBackpropProgram = (function () { function ResizeBilinearBackpropProgram(dy, x, alignCorners) { this.variableNames = ['dy']; this.outputShape = []; this.outputShape = x.shape; var _a = x.shape, xHeight = _a[1], xWidth = _a[2]; var _b = dy.shape, yHeight = _b[1], yWidth = _b[2]; var effectiveXSize = [ (alignCorners && yHeight > 1) ? xHeight - 1 : xHeight, (alignCorners && yWidth > 1) ? xWidth - 1 : xWidth ]; var effectiveYSize = [ (alignCorners && yHeight > 1) ? yHeight - 1 : yHeight, (alignCorners && yWidth > 1) ? yWidth - 1 : yWidth ]; var heightScale = effectiveXSize[0] / effectiveYSize[0]; var widthScale = effectiveXSize[1] / effectiveYSize[1]; var invHeightScale = 1 / heightScale; var invWidthScale = 1 / widthScale; var winHeight = (Math.ceil(invHeightScale) * 2) + 2; var winWidth = (Math.ceil(invWidthScale) * 2) + 2; this.userCode = "\n void main() {\n ivec4 coords = getOutputCoords();\n int b = coords[0];\n int d = coords[3];\n int r = coords[1];\n int c = coords[2];\n\n float accumulator = 0.0;\n\n const float heightScale = float(" + heightScale + ");\n const float widthScale = float(" + widthScale + ");\n\n const float invHeightScale = float(" + invHeightScale + ");\n const float invWidthScale = float(" + invWidthScale + ");\n\n const int winHeight = int(" + winHeight + ");\n const int winWidth = int(" + winWidth + ");\n\n // Compute bounds for where in dy we will look\n float startRLerp = floor(float(r) * invHeightScale);\n int startDyR = int(startRLerp - float(winHeight / 2));\n\n float startCLerp = floor(float(c) * invWidthScale);\n int startDyC = int(startCLerp - float(winWidth / 2));\n\n // Loop over dy\n for (int dyROffset = 0; dyROffset < winHeight; dyROffset++) {\n int dyR = dyROffset + startDyR;\n\n // Guard against the window exceeding the bounds of dy\n if (dyR < 0 || dyR >= " + yHeight + ") {\n continue;\n }\n\n for (int dyCOffset = 0; dyCOffset < winWidth; dyCOffset++) {\n int dyC = dyCOffset + startDyC;\n\n // Guard against the window exceeding the bounds of dy\n if (dyC < 0 || dyC >= " + yWidth + ") {\n continue;\n }\n\n float dxR = float(dyR) * heightScale;\n int topDxRIndex = int(floor(dxR));\n int bottomDxRIndex = int(min(ceil(dxR), " + (xHeight - 1) + ".0));\n float dxRLerp = dxR - float(topDxRIndex);\n float inverseDxRLerp = 1.0 - dxRLerp;\n\n float dxC = float(dyC) * widthScale;\n int leftDxCIndex = int(floor(dxC));\n int rightDxCIndex = int(min(ceil(dxC), " + (xWidth - 1) + ".0));\n float dxCLerp = dxC - float(leftDxCIndex);\n float inverseDxCLerp = 1.0 - dxCLerp;\n\n if (r == topDxRIndex && c == leftDxCIndex) {\n // topLeft\n accumulator +=\n getDy(b, dyR, dyC, d) * inverseDxRLerp * inverseDxCLerp;\n }\n\n if (r == topDxRIndex && c == rightDxCIndex) {\n // topRight\n accumulator += getDy(b, dyR, dyC, d) * inverseDxRLerp * dxCLerp;\n }\n\n if (r == bottomDxRIndex && c == leftDxCIndex) {\n // bottomLeft\n accumulator += getDy(b, dyR, dyC, d) * dxRLerp * inverseDxCLerp;\n }\n\n if (r == bottomDxRIndex && c == rightDxCIndex) {\n // bottomRight\n accumulator += getDy(b, dyR, dyC, d) * dxRLerp * dxCLerp;\n }\n }\n }\n // End loop over dy\n\n setOutput(accumulator);\n }\n "; } return ResizeBilinearBackpropProgram; }()); var ResizeBilinearProgram = (function () { function ResizeBilinearProgram(inputShape, newHeight, newWidth, alignCorners) { this.variableNames = ['A']; this.outputShape = []; var batch = inputShape[0], oldHeight = inputShape[1], oldWidth = inputShape[2], depth = inputShape[3]; this.outputShape = [batch, newHeight, newWidth, depth]; var effectiveInSize = [ (alignCorners && newHeight > 1) ? oldHeight - 1 : oldHeight, (alignCorners && newWidth > 1) ? oldWidth - 1 : oldWidth ]; var effectiveOutSize = [ (alignCorners && newHeight > 1) ? newHeight - 1 : newHeight, (alignCorners && newWidth > 1) ? newWidth - 1 : newWidth ]; this.userCode = "\n const vec2 effectiveInputOverOutputRatioRC = vec2(\n " + effectiveInSize[0] / effectiveOutSize[0] + ",\n " + effectiveInSize[1] / effectiveOutSize[1] + ");\n const vec2 inputShapeRC = vec2(" + oldHeight + ".0, " + oldWidth + ".0);\n\n void main() {\n ivec4 coords = getOutputCoords();\n int b = coords[0];\n int d = coords[3];\n ivec2 yRC = coords.yz;\n\n // Fractional source index.\n vec2 sourceFracIndexRC = vec2(yRC) * effectiveInputOverOutputRatioRC;\n\n // Compute the four integer indices.\n ivec2 sourceFloorRC = ivec2(sourceFracIndexRC);\n ivec2 sourceCeilRC = ivec2(\n min(inputShapeRC - 1.0, ceil(sourceFracIndexRC)));\n\n float topLeft = getA(b, sourceFloorRC.x, sourceFloorRC.y, d);\n float bottomLeft = getA(b, sourceCeilRC.x, sourceFloorRC.y, d);\n float topRight = getA(b, sourceFloorRC.x, sourceCeilRC.y, d);\n float bottomRight = getA(b, sourceCeilRC.x, sourceCeilRC.y, d);\n\n vec2 fracRC = sourceFracIndexRC - vec2(sourceFloorRC);\n\n float top = topLeft + (topRight - topLeft) * fracRC.y;\n float bottom = bottomLeft + (bottomRight - bottomLeft) * fracRC.y;\n float newValue = top + (bottom - top) * fracRC.x;\n\n setOutput(newValue);\n }\n "; } return ResizeBilinearProgram; }()); var ResizeNearestNeigborBackpropProgram = (function () { function ResizeNearestNeigborBackpropProgram(dy, x, alignCorners) { this.variableNames = ['dy']; this.outputShape = []; this.outputShape = x.shape; var _a = x.shape, xHeight = _a[1], xWidth = _a[2]; var _b = dy.shape, yHeight = _b[1], yWidth = _b[2]; var effectiveXSize = [ (alignCorners && yHeight > 1) ? xHeight - 1 : xHeight, (alignCorners && yWidth > 1) ? xWidth - 1 : xWidth ]; var effectiveYSize = [ (alignCorners && yHeight > 1) ? yHeight - 1 : yHeight, (alignCorners && yWidth > 1) ? yWidth - 1 : yWidth ]; var heightScale = effectiveXSize[0] / effectiveYSize[0]; var widthScale = effectiveXSize[1] / effectiveYSize[1]; var invHeightScale = 1 / heightScale; var invWidthScale = 1 / widthScale; var winHeight = (Math.ceil(invHeightScale) * 2) + 2; var winWidth = (Math.ceil(invWidthScale) * 2) + 2; this.userCode = "\n void main() {\n ivec4 coords = getOutputCoords();\n int b = coords[0];\n int d = coords[3];\n int r = coords[1];\n int c = coords[2];\n\n float accumulator = 0.0;\n\n const float heightScale = float(" + heightScale + ");\n const float widthScale = float(" + widthScale + ");\n\n const float invHeightScale = float(" + invHeightScale + ");\n const float invWidthScale = float(" + invWidthScale + ");\n\n const int winHeight = int(" + winHeight + ");\n const int winWidth = int(" + winWidth + ");\n\n // Compute bounds for where in dy we will look\n float startRLerp = floor(float(r) * invHeightScale);\n int startDyR = int(floor(startRLerp - float(winHeight / 2)));\n\n float startCLerp = floor(float(c) * invWidthScale);\n int startDyC = int(floor(startCLerp - float(winWidth / 2)));\n\n // Loop over dy\n for (int dyROffset = 0; dyROffset < winHeight; dyROffset++) {\n int dyR = dyROffset + startDyR;\n\n // Guard against the window exceeding the bounds of dy\n if (dyR < 0 || dyR >= " + yHeight + ") {\n continue;\n }\n\n for (int dyCOffset = 0; dyCOffset < winWidth; dyCOffset++) {\n int dyC = dyCOffset + startDyC;\n\n // Guard against the window exceeding the bounds of dy\n if (dyC < 0 || dyC >= " + yWidth + ") {\n continue;\n }\n\n float sourceFracRow =\n float(" + effectiveXSize[0] + ") *\n (float(dyR) / float(" + effectiveYSize[0] + "));\n\n float sourceFracCol =\n float(" + effectiveXSize[1] + ") *\n (float(dyC) / float(" + effectiveYSize[1] + "));\n\n int sourceNearestRow = int(min(\n float(int(" + xHeight + ") - 1),\n " + alignCorners + " ? float(round(sourceFracRow)) :\n float(floor(sourceFracRow))));\n\n int sourceNearestCol = int(min(\n float(int(" + xWidth + ") - 1),\n " + alignCorners + " ? float(round(sourceFracCol)) :\n float(floor(sourceFracCol))));\n\n if (r == sourceNearestRow && c == sourceNearestCol) {\n accumulator += getDy(b, dyR, dyC, d);\n }\n }\n }\n // End loop over dy\n\n setOutput(accumulator);\n }\n "; } return ResizeNearestNeigborBackpropProgram; }()); var ResizeNearestNeighborProgram = (function () { function ResizeNearestNeighborProgram(inputShape, newHeight, newWidth, alignCorners) { this.variableNames = ['A']; this.outputShape = []; var batch = inputShape[0], oldHeight = inputShape[1], oldWidth = inputShape[2], depth = inputShape[3]; this.outputShape = [batch, newHeight, newWidth, depth]; var effectiveInSize = [ (alignCorners && newHeight > 1) ? oldHeight - 1 : oldHeight, (alignCorners && newWidth > 1) ? oldWidth - 1 : oldWidth ]; var effectiveOutSize = [ (alignCorners && newHeight > 1) ? newHeight - 1 : newHeight, (alignCorners && newWidth > 1) ? newWidth - 1 : newWidth ]; var roundBase = alignCorners ? '0.5' : '0.0'; this.userCode = "\n const vec2 effectiveInputOverOutputRatioRC = vec2(\n " + effectiveInSize[0] / effectiveOutSize[0] + ",\n " + effectiveInSize[1] / effectiveOutSize[1] + ");\n const vec2 inputShapeRC = vec2(" + oldHeight + ".0, " + oldWidth + ".0);\n\n void main() {\n ivec4 coords = getOutputCoords();\n int b = coords[0];\n int d = coords[3];\n ivec2 yRC = coords.yz;\n\n // Fractional source index.\n vec2 sourceFracIndexRC = vec2(yRC) * effectiveInputOverOutputRatioRC;\n\n // Compute the coordinators of nearest neighbor point.\n ivec2 sourceNearestRC = ivec2(\n min(inputShapeRC - 1.0, floor(sourceFracIndexRC + " + roundBase + ")));\n\n float newValue = getA(b, sourceNearestRC.x, sourceNearestRC.y, d);\n\n setOutput(newValue);\n }\n "; } return ResizeNearestNeighborProgram; }()); var ReverseProgram = (function () { function ReverseProgram(xShape, axis) { this.variableNames = ['x']; var rank = xShape.length; if (rank > 4) { throw new Error("WebGL backend: Reverse of rank-" + rank + " tensor is not yet supported"); } this.outputShape = xShape; if (rank === 1) { this.userCode = "\n void main() {\n int coord = getOutputCoords();\n setOutput(getX(" + xShape[0] + " - coord - 1));\n }\n "; return; } var getInCoord = function (i) { if (axis.indexOf(i) !== -1 && xShape[i] !== 1) { return xShape[i] + " - coords[" + i + "] - 1"; } return "coords[" + i + "]"; }; var inCoords = xShape.map(function (_, i) { return getInCoord(i); }).join(','); var type = getCoordsDataType(rank); this.userCode = "\n void main() {\n " + type + " coords = getOutputCoords();\n setOutput(getX(" + inCoords + "));\n }\n "; } return ReverseProgram; }()); var ScatterProgram = (function () { function ScatterProgram(updateSize, sliceDim, indicesRank, updatesRank, strides, shape, summingDupeIndex) { if (summingDupeIndex === void 0) { summingDupeIndex = true; } this.variableNames = ['updates', 'indices', 'defaultValue']; this.outputShape = shape; var stridesType = getCoordsDataType(strides.length); var dtype = getCoordsDataType(shape.length); var indicesString = ''; if (indicesRank === 1) { indicesString = 'i'; } else if (indicesRank === 2) { indicesString = 'i, j'; } var indicesSnippet = "getIndices(" + indicesString + ")"; var updatesString = ''; if (updatesRank === 1) { updatesString = 'i'; } else if (updatesRank === 2) { updatesString = 'i, coords[1]'; } var updatesSnippet = "getUpdates(" + updatesString + ")"; var strideString = sliceDim > 1 ? 'strides[j]' : 'strides'; this.userCode = "\n " + stridesType + " strides = " + stridesType + "(" + strides + ");\n\n void main() {\n " + dtype + " coords = getOutputCoords();\n float sum = 0.0;\n bool found = false;\n for (int i = 0; i < " + updateSize + "; i++) {\n int flattenedIndex = 0;\n for (int j = 0; j < " + sliceDim + "; j++) {\n int index = round(" + indicesSnippet + ");\n flattenedIndex += index * " + strideString + ";\n }\n if (flattenedIndex == coords[0]) {\n sum += " + updatesSnippet + ";\n found = true;\n }\n }\n setOutput(mix(getDefaultValue(), sum, float(found)));\n }\n "; } return ScatterProgram; }()); var SegmentOpProgram = (function () { function SegmentOpProgram(segOpInfo, segOpType) { this.variableNames = ['x', 'segmentIds']; var windowSize = segOpInfo.windowSize; var batchSize = segOpInfo.batchSize; var inSize = segOpInfo.inSize; var numSegments = segOpInfo.numSegments; var outSize = numSegments * Math.ceil(inSize / windowSize); this.outputShape = [batchSize, outSize]; var initializationValue = '0.0'; var returnValue = "sumValue"; var windowSizeNearestVec4 = Math.floor(windowSize / 4) * 4; var windowSizeVec4Remainder = windowSize % 4; var updateSnippet = "\n sumValue += dot(values, segFilter);\n "; var checkValueOutOfBounds = ''; if (inSize % windowSize > 0) { checkValueOutOfBounds = "\n if (inIdx < 0 || inIdx >= " + inSize + ") {\n return initializationValue;\n }\n "; } var checkSegmentIdOutOfBounds = ''; if (inSize % windowSize > 0) { checkSegmentIdOutOfBounds = "\n if (inIdx < 0 || inIdx >= " + inSize + ") {\n return -1.0;\n }\n "; } this.userCode = "\n const float initializationValue = " + initializationValue + ";\n\n float getValue(int batch, int inIdx) {\n " + checkValueOutOfBounds + "\n return getX(batch, inIdx);\n }\n\n float getSegmentIdAtIndex(int inIdx) {\n " + checkSegmentIdOutOfBounds + "\n return getSegmentIds(inIdx);\n }\n\n void main() {\n ivec2 coords = getOutputCoords();\n int batch = coords[0];\n int outIdx = coords[1];\n int inOffset = int(floor(float(outIdx) / float(\n " + numSegments + ")) * float(" + windowSize + "));\n int currentSeg = int(mod(float(outIdx), float(" + numSegments + ")));\n\n float sumValue = 0.0;\n\n for (int i = 0; i < " + windowSizeNearestVec4 + "; i += 4) {\n int inIdx = inOffset + i;\n vec4 values = vec4(\n getValue(batch, inIdx),\n getValue(batch, inIdx + 1),\n getValue(batch, inIdx + 2),\n getValue(batch, inIdx + 3)\n );\n\n vec4 segFilter = vec4(\n int(getSegmentIdAtIndex(inIdx)) == currentSeg ? 1 : 0,\n int(getSegmentIdAtIndex(inIdx + 1)) == currentSeg ? 1 : 0,\n int(getSegmentIdAtIndex(inIdx + 2)) == currentSeg ? 1 : 0,\n int(getSegmentIdAtIndex(inIdx + 3)) == currentSeg ? 1 : 0\n );\n\n " + updateSnippet + "\n }\n\n int inIdx = inOffset + " + windowSizeNearestVec4 + ";\n if (" + (windowSizeVec4Remainder === 1) + ") {\n vec4 values = vec4(\n getValue(batch, inIdx),\n initializationValue,\n initializationValue,\n initializationValue\n );\n\n int inIdxSeg = int(getSegmentIdAtIndex(inIdx));\n\n vec4 segFilter = vec4(\n int(getSegmentIdAtIndex(inIdx)) == currentSeg ? 1 : 0,\n 0,\n 0,\n 0\n );\n\n " + updateSnippet + "\n } else if (" + (windowSizeVec4Remainder === 2) + ") {\n vec4 values = vec4(\n getValue(batch, inIdx),\n getValue(batch, inIdx + 1),\n initializationValue,\n initializationValue\n );\n\n vec4 segFilter = vec4(\n int(getSegmentIdAtIndex(inIdx)) == currentSeg ? 1 : 0,\n int(getSegmentIdAtIndex(inIdx + 1)) == currentSeg ? 1 : 0,\n 0,\n 0\n );\n\n " + updateSnippet + "\n } else if (" + (windowSizeVec4Remainder === 3) + ") {\n vec4 values = vec4(\n getValue(batch, inIdx),\n getValue(batch, inIdx + 1),\n getValue(batch, inIdx + 2),\n initializationValue\n );\n\n vec4 segFilter = vec4(\n int(getSegmentIdAtIndex(inIdx)) == currentSeg ? 1 : 0,\n int(getSegmentIdAtIndex(inIdx + 1)) == currentSeg ? 1 : 0,\n int(getSegmentIdAtIndex(inIdx + 2)) == currentSeg ? 1 : 0,\n 0\n );\n\n " + updateSnippet + "\n }\n setOutput(" + returnValue + ");\n }\n "; } return SegmentOpProgram; }()); var SelectProgram = (function () { function SelectProgram(cRank, shape, rank) { this.variableNames = ['c', 'a', 'b']; this.outputShape = shape; var cCoords; var abCoords; if (rank > 4) { throw Error("Where for rank " + rank + " is not yet supported"); } if (rank === 1) { abCoords = "resRC"; cCoords = "resRC"; } else { var currentCoords = ['resRC.x', 'resRC.y', 'resRC.z', 'resRC.w']; var cCoordVars = []; var abCoordVars = []; for (var i = 0; i < shape.length; i++) { abCoordVars.push("" + currentCoords[i]); if (i < cRank) { cCoordVars.push("" + currentCoords[i]); } } cCoords = cCoordVars.join(); abCoords = abCoordVars.join(); } var dtype = getCoordsDataType(rank); this.userCode = "\n void main() {\n " + dtype + " resRC = getOutputCoords();\n float cVal = getC(" + cCoords + ");\n if (cVal >= 1.0) {\n setOutput(getA(" + abCoords + "));\n } else {\n setOutput(getB(" + abCoords + "));\n }\n }\n "; } return SelectProgram; }()); var SliceProgram = (function () { function SliceProgram(destSize) { this.variableNames = ['source']; this.outputShape = destSize; this.rank = destSize.length; var dtype = getCoordsDataType(this.rank); var uniformPart = "uniform int start[" + this.rank + "];"; var sourceCoords = getCoords$1(this.rank); var body; var coordSum = destSize.map(function (_, i) { return "sourceLoc." + coords[i] + " = start[" + i + "] + coords." + coords[i] + ";"; }); body = "\n " + dtype + " sourceLoc;\n " + dtype + " coords = getOutputCoords();\n " + coordSum.join('\n') + "\n "; this.userCode = "\n " + uniformPart + "\n void main() {\n " + body + "\n setOutput(getSource(" + sourceCoords + "));\n }\n "; } SliceProgram.prototype.getCustomSetupFunc = function (start) { var _this = this; if (start.length !== this.rank) { throw Error("The rank (" + this.rank + ") of the program must match the " + ("length of start (" + start.length + ")")); } return function (gpgpu, webGLProgram) { if (_this.startLoc == null) { _this.startLoc = gpgpu.getUniformLocationNoThrow(webGLProgram, 'start'); if (_this.startLoc == null) { return; } } gpgpu.gl.uniform1iv(_this.startLoc, start); }; }; return SliceProgram; }()); var coords = ['x', 'y', 'z', 'w', 'u', 'v']; function getCoords$1(rank) { if (rank === 1) { return 'sourceLoc'; } else if (rank <= 6) { return coords.slice(0, rank).map(function (x) { return 'sourceLoc.' + x; }).join(','); } else { throw Error("Slicing for rank " + rank + " is not yet supported"); } } var SlicePackedProgram = (function () { function SlicePackedProgram(destSize) { this.variableNames = ['source']; this.usesPackedTextures = true; this.outputShape = destSize; this.rank = destSize.length; var dtype = getCoordsDataType(this.rank); var coords = getChannels('coords', this.rank); var sourceLoc = getChannels('sourceLoc', this.rank); var innerDims = this.rank === 1 ? 'sourceLoc' : "vec2(" + sourceLoc.slice(-2).join() + ")"; var getChannel = "getChannel(getSource(" + sourceLoc.join() + "), " + innerDims + ")"; var upperRow = "\n result.x = " + getChannel + ";\n if (++" + coords[this.rank - 1] + " < " + destSize[this.rank - 1] + ") {\n ++" + sourceLoc[this.rank - 1] + ";\n result.y = " + getChannel + ";\n --" + sourceLoc[this.rank - 1] + ";\n }\n "; var lowerRow = this.rank === 1 ? '' : "\n --" + coords[this.rank - 1] + ";\n if (++" + coords[this.rank - 2] + " < " + destSize[this.rank - 2] + ") {\n ++" + sourceLoc[this.rank - 2] + ";\n result.z = " + getChannel + ";\n if (++" + coords[this.rank - 1] + " < " + destSize[this.rank - 1] + ") {\n ++" + sourceLoc[this.rank - 1] + ";\n result.w = " + getChannel + ";\n }\n }\n "; var sourceLocSetup = this.rank <= 4 ? "sourceLoc = coords +\n " + dtype + "(" + destSize.map(function (_, i) { return "start[" + i + "]"; }).join() + ");" : destSize.map(function (_, i) { return sourceLoc[i] + " = " + coords[i] + " + start[" + i + "];"; }) .join('\n'); this.userCode = "\n uniform int start[" + this.rank + "];\n void main() {\n " + dtype + " coords = getOutputCoords();\n " + dtype + " sourceLoc;\n " + sourceLocSetup + " \n vec4 result = vec4(0.);\n " + upperRow + "\n " + lowerRow + "\n setOutput(result);\n }\n "; } SlicePackedProgram.prototype.getCustomSetupFunc = function (start) { var _this = this; if (start.length !== this.rank) { throw Error("The rank (" + this.rank + ") of the program must match the " + ("length of start (" + start.length + ")")); } return function (gpgpu, webGLProgram) { if (_this.startLoc == null) { _this.startLoc = gpgpu.getUniformLocationNoThrow(webGLProgram, 'start'); if (_this.startLoc == null) { return; } } gpgpu.gl.uniform1iv(_this.startLoc, start); }; }; return SlicePackedProgram; }()); var StridedSliceProgram = (function () { function StridedSliceProgram(begin, strides, size, shrinkAxis) { this.variableNames = ['x']; var shape = size.filter(function (v, index) { return shrinkAxis.indexOf(index) === -1; }); this.outputShape = shape; var rank = size.length; var inputDtype = getCoordsDataType(size.length); var dtype = getCoordsDataType(shape.length); var newCoords = ''; if (rank === 1) { newCoords = 'coords * strides + begin'; } else { var outputAxis_1 = 0; newCoords = size.map(function (_, i) { if (shrinkAxis.indexOf(i) === -1) { outputAxis_1++; return shape.length === 1 ? "coords * strides[" + i + "] + begin[" + i + "]" : "coords[" + (outputAxis_1 - 1) + "] * strides[" + i + "] + begin[" + i + "]"; } else { return "begin[" + i + "]"; } }) .join(','); } this.userCode = "\n " + inputDtype + " begin = " + inputDtype + "(" + begin + ");\n " + inputDtype + " strides = " + inputDtype + "(" + strides + ");\n\n void main() {\n " + dtype + " coords = getOutputCoords();\n setOutput(getX(" + newCoords + "));\n }\n "; } return StridedSliceProgram; }()); var TextureManager = (function () { function TextureManager(gpgpu) { this.gpgpu = gpgpu; this.numUsedTextures = 0; this.numFreeTextures = 0; this.freeTextures = {}; this.logEnabled = false; this.usedTextures = {}; } TextureManager.prototype.acquireTexture = function (shapeRC, usage, isPacked) { var physicalTexType = getPhysicalFromLogicalTextureType(usage, isPacked); var shapeKey = getKeyFromTextureShape(shapeRC, physicalTexType, isPacked); if (!(shapeKey in this.freeTextures)) { this.freeTextures[shapeKey] = []; } if (!(shapeKey in this.usedTextures)) { this.usedTextures[shapeKey] = []; } if (this.freeTextures[shapeKey].length > 0) { this.numFreeTextures--; this.numUsedTextures++; this.log(); var newTexture_1 = this.freeTextures[shapeKey].shift(); this.usedTextures[shapeKey].push(newTexture_1); return newTexture_1; } this.numUsedTextures++; this.log(); var newTexture; if (physicalTexType === PhysicalTextureType.PACKED_2X2_FLOAT32) { newTexture = this.gpgpu.createPackedMatrixTexture(shapeRC[0], shapeRC[1]); } else if (physicalTexType === PhysicalTextureType.PACKED_2X2_FLOAT16) { newTexture = this.gpgpu.createFloat16PackedMatrixTexture(shapeRC[0], shapeRC[1]); } else if (physicalTexType === PhysicalTextureType.UNPACKED_FLOAT32) { newTexture = this.gpgpu.createFloat32MatrixTexture(shapeRC[0], shapeRC[1]); } else if (physicalTexType === PhysicalTextureType.UNPACKED_FLOAT16) { newTexture = this.gpgpu.createFloat16MatrixTexture(shapeRC[0], shapeRC[1]); } else if (physicalTexType === PhysicalTextureType.PACKED_4X1_UNSIGNED_BYTE) { newTexture = this.gpgpu.createUnsignedBytesMatrixTexture(shapeRC[0], shapeRC[1]); } this.usedTextures[shapeKey].push(newTexture); return newTexture; }; TextureManager.prototype.releaseTexture = function (texture, shape, logicalTexType, isPacked) { if (this.freeTextures == null) { return; } var physicalTexType = getPhysicalFromLogicalTextureType(logicalTexType, isPacked); var shapeKey = getKeyFromTextureShape(shape, physicalTexType, isPacked); if (!(shapeKey in this.freeTextures)) { this.freeTextures[shapeKey] = []; } this.freeTextures[shapeKey].push(texture); this.numFreeTextures++; this.numUsedTextures--; var texList = this.usedTextures[shapeKey]; var texIndex = texList.indexOf(texture); if (texIndex < 0) { throw new Error('Cannot release a texture that was never provided by this ' + 'texture manager'); } texList.splice(texIndex, 1); this.log(); }; TextureManager.prototype.log = function () { if (!this.logEnabled) { return; } var total = this.numFreeTextures + this.numUsedTextures; console.log('Free/Used', this.numFreeTextures + " / " + this.numUsedTextures, "(" + total + ")"); }; TextureManager.prototype.getNumUsedTextures = function () { return this.numUsedTextures; }; TextureManager.prototype.getNumFreeTextures = function () { return this.numFreeTextures; }; TextureManager.prototype.dispose = function () { var _this = this; if (this.freeTextures == null) { return; } for (var texShape in this.freeTextures) { this.freeTextures[texShape].forEach(function (tex) { _this.gpgpu.deleteMatrixTexture(tex); }); } for (var texShape in this.usedTextures) { this.usedTextures[texShape].forEach(function (tex) { _this.gpgpu.deleteMatrixTexture(tex); }); } this.freeTextures = null; this.usedTextures = null; this.numUsedTextures = 0; this.numFreeTextures = 0; }; return TextureManager; }()); function getPhysicalFromLogicalTextureType(logicalTexType, isPacked) { if (logicalTexType === TextureUsage.UPLOAD) { return isPacked ? PhysicalTextureType.PACKED_2X2_FLOAT32 : PhysicalTextureType.UNPACKED_FLOAT32; } else if (logicalTexType === TextureUsage.RENDER || logicalTexType == null) { if (isPacked) { return ENV.get('WEBGL_RENDER_FLOAT32_ENABLED') ? PhysicalTextureType.PACKED_2X2_FLOAT32 : PhysicalTextureType.PACKED_2X2_FLOAT16; } return ENV.get('WEBGL_RENDER_FLOAT32_ENABLED') ? PhysicalTextureType.UNPACKED_FLOAT32 : PhysicalTextureType.UNPACKED_FLOAT16; } else if (logicalTexType === TextureUsage.DOWNLOAD || logicalTexType === TextureUsage.PIXELS) { return PhysicalTextureType.PACKED_4X1_UNSIGNED_BYTE; } throw new Error("Unknown logical texture type " + logicalTexType); } function getKeyFromTextureShape(shapeRowsCol, physicalTexType, isPacked) { return shapeRowsCol[0] + "_" + shapeRowsCol[1] + "_" + physicalTexType + "_" + isPacked; } var TileProgram = (function () { function TileProgram(aShape, reps) { this.variableNames = ['A']; var outputShape = new Array(aShape.length); for (var i = 0; i < outputShape.length; i++) { outputShape[i] = aShape[i] * reps[i]; } this.outputShape = outputShape; this.rank = outputShape.length; var dtype = getCoordsDataType(this.rank); var sourceCoords = getSourceCoords$2(aShape); this.userCode = "\n void main() {\n " + dtype + " resRC = getOutputCoords();\n setOutput(getA(" + sourceCoords + "));\n }\n "; } return TileProgram; }()); function getSourceCoords$2(aShape) { var rank = aShape.length; if (rank > 5) { throw Error("Tile for rank " + rank + " is not yet supported"); } if (rank === 1) { return "imod(resRC, " + aShape[0] + ")"; } var currentCoords = ['resRC.x', 'resRC.y', 'resRC.z', 'resRC.w', 'resRC.u']; var sourceCoords = []; for (var i = 0; i < aShape.length; i++) { sourceCoords.push("imod(" + currentCoords[i] + ", " + aShape[i] + ")"); } return sourceCoords.join(); } var TransposeProgram = (function () { function TransposeProgram(aShape, newDim) { this.variableNames = ['A']; var outputShape = new Array(aShape.length); for (var i = 0; i < outputShape.length; i++) { outputShape[i] = aShape[newDim[i]]; } this.outputShape = outputShape; this.rank = outputShape.length; var dtype = getCoordsDataType(this.rank); var switched = getSwitchedCoords(newDim); this.userCode = "\n void main() {\n " + dtype + " resRC = getOutputCoords();\n setOutput(getA(" + switched + "));\n }\n "; } return TransposeProgram; }()); function getSwitchedCoords(newDim) { var rank = newDim.length; if (rank > 6) { throw Error("Transpose for rank " + rank + " is not yet supported"); } var originalOrder = ['resRC.x', 'resRC.y', 'resRC.z', 'resRC.w', 'resRC.u', 'resRC.v']; var switchedCoords = new Array(rank); for (var i = 0; i < newDim.length; i++) { switchedCoords[newDim[i]] = originalOrder[i]; } return switchedCoords.join(); } var TransposePackedProgram = (function () { function TransposePackedProgram(aShape, newDim) { this.variableNames = ['A']; this.usesPackedTextures = true; var outputShape = new Array(aShape.length); for (var i = 0; i < outputShape.length; i++) { outputShape[i] = aShape[newDim[i]]; } this.outputShape = outputShape; this.rank = outputShape.length; if (this.rank > 6) { throw Error("Packed transpose for rank " + this.rank + " is not yet supported."); } var dtype = getCoordsDataType(this.rank); var outputOrder = getVecChannels('rc', this.rank); var switchedOrder = new Array(this.rank); for (var i = 0; i < newDim.length; i++) { switchedOrder[newDim[i]] = outputOrder[i]; } var innerDims = "vec2(" + switchedOrder.slice(-2).join() + ")"; var nextColumn = "++" + outputOrder[this.rank - 1] + " < " + outputShape[this.rank - 1]; var getc = "getChannel(getA(" + switchedOrder.join() + "), " + innerDims + ")"; this.userCode = "\n void main() {\n " + dtype + " rc = getOutputCoords();\n vec4 result = vec4(0.);\n result[0] = " + getc + ";\n if(" + nextColumn + ") {\n result[1] = " + getc + ";\n }\n --" + outputOrder[this.rank - 1] + ";\n if(++" + outputOrder[this.rank - 2] + " < " + outputShape[this.rank - 2] + ") {\n result[2] = " + getc + ";\n if(" + nextColumn + ") {\n result[3] = " + getc + ";\n }\n } \n setOutput(result);\n }\n "; } return TransposePackedProgram; }()); var ERF_P = 0.3275911; var ERF_A1 = 0.254829592; var ERF_A2 = -0.284496736; var ERF_A3 = 1.421413741; var ERF_A4 = -1.453152027; var ERF_A5 = 1.061405429; var SELU_SCALEALPHA = 1.7580993408473768599402175208123; var SELU_SCALE = 1.0507009873554804934193349852946; var UnaryOpProgram = (function () { function UnaryOpProgram(aShape, opSnippet) { this.variableNames = ['A']; this.outputShape = aShape; this.userCode = "\n uniform float NAN;\n float unaryOperation(float x) {\n " + opSnippet + "\n }\n\n void main() {\n float x = getAAtOutCoords();\n float y = unaryOperation(x);\n\n setOutput(y);\n }\n "; } UnaryOpProgram.prototype.getCustomSetupFunc = function () { var _this = this; return function (gpgpu, webGLProgram) { if (_this.startLoc == null) { _this.startLoc = gpgpu.getUniformLocationNoThrow(webGLProgram, 'NAN'); if (_this.startLoc == null) { return; } } gpgpu.gl.uniform1f(_this.startLoc, NaN); }; }; return UnaryOpProgram; }()); var CHECK_NAN_SNIPPET$1 = "if (isNaN(x)) return x;"; var LINEAR = "return x;"; var ABS = "return abs(x);"; var RELU = CHECK_NAN_SNIPPET$1 + "\n return (x < 0.0) ? 0.0 : x;\n"; var ELU = "return (x >= 0.0) ? x : (exp(x) - 1.0);"; var SELU = "\n // Stable and Attracting Fixed Point (0, 1) for Normalized Weights.\n // see: https://arxiv.org/abs/1706.02515\n float scaleAlpha = " + SELU_SCALEALPHA + ";\n float scale = " + SELU_SCALE + ";\n return (x >= 0.0) ? scale * x : scaleAlpha * (exp(x) - 1.0);\n"; function STEP(alpha) { if (alpha === void 0) { alpha = 0.0; } return CHECK_NAN_SNIPPET$1 + ("\n return x > 0.0 ? 1.0 : float(" + alpha + ");\n "); } var NEG = "return -x;"; var CEIL = "return ceil(x);"; var FLOOR = "return floor(x);"; var SIGN = "\n if (isNaN(x)) { return 0.0; }\n return sign(x);\n"; var ROUND = "\n // OpenGL ES does not support round function.\n // The algorithm is based on banker's rounding.\n float base = floor(x);\n if ((x - base) < 0.5) {\n return floor(x);\n } else if ((x - base) > 0.5) {\n return ceil(x);\n } else {\n if (mod(base, 2.0) == 0.0) {\n return base;\n } else {\n return base + 1.0;\n }\n }\n"; var EXP = "return exp(x);"; var EXPM1 = "return exp(x) - 1.0;"; var LOG = "if (x < 0.0) return NAN;\n return log(x);"; var LOG1P = "return log(1.0 + x);"; var SQRT = "return sqrt(x);"; var RSQRT = "return inversesqrt(x);"; var SIGMOID = "return 1.0 / (1.0 + exp(-1.0 * x));"; var SOFTPLUS = "\n float epsilon = 1.1920928955078125e-7;\n float threshold = log(epsilon) + 2.0;\n\n bool too_large = x > -threshold;\n bool too_small = x < threshold;\n\n float result;\n float exp_x = exp(x);\n\n if (too_large){\n result = x;\n }\n else if (too_small){\n result = exp_x;\n }\n else{\n result = log(exp_x + 1.0);\n }\n return result;\n"; var SIN = CHECK_NAN_SNIPPET$1 + "\n return sin(x);\n"; var COS = CHECK_NAN_SNIPPET$1 + "\n return cos(x);\n"; var TAN = "return tan(x);"; var ASIN = "return asin(x);"; var ACOS = "return acos(x);"; var ATAN = CHECK_NAN_SNIPPET$1 + "\n return atan(x);\n"; var SINH = "\n float e2x = exp(x);\n return (e2x - 1.0 / e2x) / 2.0;\n"; var COSH = "\n float e2x = exp(-x);\n return (e2x + 1.0 / e2x) / 2.0;\n"; var TANH = "\n float e2x = exp(-2.0 * abs(x));\n return sign(x) * (1.0 - e2x) / (1.0 + e2x);\n"; var ASINH = "return log(x + sqrt(x * x + 1.0));"; var ACOSH = CHECK_NAN_SNIPPET$1 + "\n if (x < 1.0) return NAN;\n return log(x + sqrt(x * x - 1.0));"; var ATANH = CHECK_NAN_SNIPPET$1 + "\n if ((x < -1.0) || (x > 1.0)) return NAN;\n return (log(1.0 + x) - log(1.0 - x)) / 2.0;"; var ERF = "\n // Error function is calculated approximately with elementary function.\n // See \"Handbook of Mathematical Functions with Formulas,\n // Graphs, and Mathematical Tables\", Abramowitz and Stegun.\n float p = " + ERF_P + ";\n float a1 = " + ERF_A1 + ";\n float a2 = " + ERF_A2 + ";\n float a3 = " + ERF_A3 + ";\n float a4 = " + ERF_A4 + ";\n float a5 = " + ERF_A5 + ";\n\n float t = 1.0 / (1.0 + p * x);\n return 1.0 - (((((a5*t + a4)*t) + a3)*t + a2)*t + a1)*t*exp(-x*x);\n"; var SQUARE = "return x * x;"; var RECIPROCAL = "return 1.0 / x;"; var LOGICAL_NOT = "return float(!(x >= 1.0));"; var TO_INT = "return float(int(x));"; var CLONE = 'return x;'; var LINEAR$1 = "return x;"; var LOG$1 = "\n vec4 result = log(x);\n vec4 isNaN = vec4(lessThan(x, vec4(0.0)));\n result.r = isNaN.r == 1.0 ? NAN : result.r;\n result.g = isNaN.g == 1.0 ? NAN : result.g;\n result.b = isNaN.b == 1.0 ? NAN : result.b;\n result.a = isNaN.a == 1.0 ? NAN : result.a;\n\n return result;\n"; var RELU$1 = "\n vec4 result = x * vec4(greaterThanEqual(x, vec4(0.0)));\n\n result.r = isNaN(x.r) ? x.r : result.r;\n result.g = isNaN(x.g) ? x.g : result.g;\n result.b = isNaN(x.b) ? x.b : result.b;\n result.a = isNaN(x.a) ? x.a : result.a;\n\n return result;\n"; var UnaryOpPackedProgram = (function () { function UnaryOpPackedProgram(aShape, opSnippet) { this.variableNames = ['A']; this.usesPackedTextures = true; this.outputShape = aShape; this.userCode = "\n uniform float NAN;\n vec4 unaryOperation(vec4 x) {\n " + opSnippet + "\n }\n\n void main() {\n vec4 x = getAAtOutCoords();\n vec4 y = unaryOperation(x);\n\n setOutput(y);\n }\n "; } UnaryOpPackedProgram.prototype.getCustomSetupFunc = function () { var _this = this; return function (gpgpu, webGLProgram) { if (_this.startLoc == null) { _this.startLoc = gpgpu.getUniformLocationNoThrow(webGLProgram, 'NAN'); if (_this.startLoc == null) { return; } } gpgpu.gl.uniform1f(_this.startLoc, NaN); }; }; return UnaryOpPackedProgram; }()); var UnpackProgram = (function () { function UnpackProgram(outputShape) { this.variableNames = ['A']; this.usesPackedTextures = true; this.outputShape = outputShape; var rank = outputShape.length; var channels = getChannels('rc', rank); var dtype = getCoordsDataType(rank); var sourceCoords = getSourceCoords$1(rank, channels); var innerDims = channels.slice(-2); var coords = rank <= 1 ? 'rc' : "vec2(" + innerDims.join(',') + ")"; this.userCode = "\n void main() {\n " + dtype + " rc = getOutputCoords();\n vec4 packedInput = getA(" + sourceCoords + ");\n\n setOutput(getChannel(packedInput, " + coords + "));\n }\n "; } return UnpackProgram; }()); function concat1d_(tensors) { return concat(tensors, 0); } function concat2d_(tensors, axis) { return concat(tensors, axis); } function concat3d_(tensors, axis) { return concat(tensors, axis); } function concat4d_(tensors, axis) { return concat(tensors, axis); } function concat_(tensors, axis) { if (axis === void 0) { axis = 0; } assert(tensors.length >= 1, 'Pass at least one tensor to concat'); var $tensors = convertToTensorArray(tensors, 'tensors', 'concat'); axis = parseAxisParam(axis, $tensors[0].shape)[0]; var outShape = computeOutShape($tensors.map(function (t) { return t.shape; }), axis); if (sizeFromShape(outShape) === 0) { return tensor([], outShape); } $tensors = $tensors.filter(function (t) { return t.size > 0; }); if ($tensors.length === 1) { return $tensors[0]; } var shapes = $tensors.map(function (t) { return t.shape; }); assertParamsConsistent(shapes, axis); var der = function (dy) { var sizeSplits = shapes.map(function (s) { return s[axis]; }); var derTensors = split$1(dy, sizeSplits, axis); return derTensors.map(function (t) { return function () { return t; }; }); }; var inputs = $tensors; return ENV.engine.runKernel(function (backend) { return backend.concat($tensors, axis); }, inputs, der); } function split_(x, numOrSizeSplits, axis) { if (axis === void 0) { axis = 0; } var $x = convertToTensor(x, 'x', 'split'); axis = parseAxisParam(axis, $x.shape)[0]; var splitSizes; if (typeof (numOrSizeSplits) === 'number') { assert($x.shape[axis] % numOrSizeSplits === 0, 'Number of splits must evenly divide the axis.'); splitSizes = new Array(numOrSizeSplits).fill($x.shape[axis] / numOrSizeSplits); } else { assert($x.shape[axis] === numOrSizeSplits.reduce(function (a, b) { return a + b; }), 'The sum of sizes must match the size of the axis dimension.'); splitSizes = numOrSizeSplits; } var der = function (dy) { return ({ $x: function () { return concat(dy, axis); } }); }; return ENV.engine.runKernel(function (backend) { return backend.split($x, splitSizes, axis); }, { $x: $x }, der); } var concat = op({ concat_: concat_ }); var concat1d = op({ concat1d_: concat1d_ }); var concat2d = op({ concat2d_: concat2d_ }); var concat3d = op({ concat3d_: concat3d_ }); var concat4d = op({ concat4d_: concat4d_ }); var split$1 = op({ split_: split_ }); var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; function createCommonjsModule(fn, module) { return module = { exports: {} }, fn(module, module.exports), module.exports; } var alea = createCommonjsModule(function (module) { // A port of an algorithm by Johannes Baagøe , 2010 // http://baagoe.com/en/RandomMusings/javascript/ // https://github.com/nquinlan/better-random-numbers-for-javascript-mirror // Original work is under MIT license - // Copyright (C) 2010 by Johannes Baagøe // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. (function(global, module, define) { function Alea(seed) { var me = this, mash = Mash(); me.next = function() { var t = 2091639 * me.s0 + me.c * 2.3283064365386963e-10; // 2^-32 me.s0 = me.s1; me.s1 = me.s2; return me.s2 = t - (me.c = t | 0); }; // Apply the seeding algorithm from Baagoe. me.c = 1; me.s0 = mash(' '); me.s1 = mash(' '); me.s2 = mash(' '); me.s0 -= mash(seed); if (me.s0 < 0) { me.s0 += 1; } me.s1 -= mash(seed); if (me.s1 < 0) { me.s1 += 1; } me.s2 -= mash(seed); if (me.s2 < 0) { me.s2 += 1; } mash = null; } function copy(f, t) { t.c = f.c; t.s0 = f.s0; t.s1 = f.s1; t.s2 = f.s2; return t; } function impl(seed, opts) { var xg = new Alea(seed), state = opts && opts.state, prng = xg.next; prng.int32 = function() { return (xg.next() * 0x100000000) | 0; }; prng.double = function() { return prng() + (prng() * 0x200000 | 0) * 1.1102230246251565e-16; // 2^-53 }; prng.quick = prng; if (state) { if (typeof(state) == 'object') copy(state, xg); prng.state = function() { return copy(xg, {}); }; } return prng; } function Mash() { var n = 0xefc8249d; var mash = function(data) { data = data.toString(); for (var i = 0; i < data.length; i++) { n += data.charCodeAt(i); var h = 0.02519603282416938 * n; n = h >>> 0; h -= n; h *= n; n = h >>> 0; h -= n; n += h * 0x100000000; // 2^32 } return (n >>> 0) * 2.3283064365386963e-10; // 2^-32 }; return mash; } if (module && module.exports) { module.exports = impl; } else if (define && define.amd) { define(function() { return impl; }); } else { this.alea = impl; } })( commonjsGlobal, module, // present in node.js (typeof undefined) == 'function' && undefined // present with an AMD loader ); }); var xor128 = createCommonjsModule(function (module) { // A Javascript implementaion of the "xor128" prng algorithm by // George Marsaglia. See http://www.jstatsoft.org/v08/i14/paper (function(global, module, define) { function XorGen(seed) { var me = this, strseed = ''; me.x = 0; me.y = 0; me.z = 0; me.w = 0; // Set up generator function. me.next = function() { var t = me.x ^ (me.x << 11); me.x = me.y; me.y = me.z; me.z = me.w; return me.w ^= (me.w >>> 19) ^ t ^ (t >>> 8); }; if (seed === (seed | 0)) { // Integer seed. me.x = seed; } else { // String seed. strseed += seed; } // Mix in string seed, then discard an initial batch of 64 values. for (var k = 0; k < strseed.length + 64; k++) { me.x ^= strseed.charCodeAt(k) | 0; me.next(); } } function copy(f, t) { t.x = f.x; t.y = f.y; t.z = f.z; t.w = f.w; return t; } function impl(seed, opts) { var xg = new XorGen(seed), state = opts && opts.state, prng = function() { return (xg.next() >>> 0) / 0x100000000; }; prng.double = function() { do { var top = xg.next() >>> 11, bot = (xg.next() >>> 0) / 0x100000000, result = (top + bot) / (1 << 21); } while (result === 0); return result; }; prng.int32 = xg.next; prng.quick = prng; if (state) { if (typeof(state) == 'object') copy(state, xg); prng.state = function() { return copy(xg, {}); }; } return prng; } if (module && module.exports) { module.exports = impl; } else if (define && define.amd) { define(function() { return impl; }); } else { this.xor128 = impl; } })( commonjsGlobal, module, // present in node.js (typeof undefined) == 'function' && undefined // present with an AMD loader ); }); var xorwow = createCommonjsModule(function (module) { // A Javascript implementaion of the "xorwow" prng algorithm by // George Marsaglia. See http://www.jstatsoft.org/v08/i14/paper (function(global, module, define) { function XorGen(seed) { var me = this, strseed = ''; // Set up generator function. me.next = function() { var t = (me.x ^ (me.x >>> 2)); me.x = me.y; me.y = me.z; me.z = me.w; me.w = me.v; return (me.d = (me.d + 362437 | 0)) + (me.v = (me.v ^ (me.v << 4)) ^ (t ^ (t << 1))) | 0; }; me.x = 0; me.y = 0; me.z = 0; me.w = 0; me.v = 0; if (seed === (seed | 0)) { // Integer seed. me.x = seed; } else { // String seed. strseed += seed; } // Mix in string seed, then discard an initial batch of 64 values. for (var k = 0; k < strseed.length + 64; k++) { me.x ^= strseed.charCodeAt(k) | 0; if (k == strseed.length) { me.d = me.x << 10 ^ me.x >>> 4; } me.next(); } } function copy(f, t) { t.x = f.x; t.y = f.y; t.z = f.z; t.w = f.w; t.v = f.v; t.d = f.d; return t; } function impl(seed, opts) { var xg = new XorGen(seed), state = opts && opts.state, prng = function() { return (xg.next() >>> 0) / 0x100000000; }; prng.double = function() { do { var top = xg.next() >>> 11, bot = (xg.next() >>> 0) / 0x100000000, result = (top + bot) / (1 << 21); } while (result === 0); return result; }; prng.int32 = xg.next; prng.quick = prng; if (state) { if (typeof(state) == 'object') copy(state, xg); prng.state = function() { return copy(xg, {}); }; } return prng; } if (module && module.exports) { module.exports = impl; } else if (define && define.amd) { define(function() { return impl; }); } else { this.xorwow = impl; } })( commonjsGlobal, module, // present in node.js (typeof undefined) == 'function' && undefined // present with an AMD loader ); }); var xorshift7 = createCommonjsModule(function (module) { // A Javascript implementaion of the "xorshift7" algorithm by // François Panneton and Pierre L'ecuyer: // "On the Xorgshift Random Number Generators" // http://saluc.engr.uconn.edu/refs/crypto/rng/panneton05onthexorshift.pdf (function(global, module, define) { function XorGen(seed) { var me = this; // Set up generator function. me.next = function() { // Update xor generator. var X = me.x, i = me.i, t, v; t = X[i]; t ^= (t >>> 7); v = t ^ (t << 24); t = X[(i + 1) & 7]; v ^= t ^ (t >>> 10); t = X[(i + 3) & 7]; v ^= t ^ (t >>> 3); t = X[(i + 4) & 7]; v ^= t ^ (t << 7); t = X[(i + 7) & 7]; t = t ^ (t << 13); v ^= t ^ (t << 9); X[i] = v; me.i = (i + 1) & 7; return v; }; function init(me, seed) { var j, w, X = []; if (seed === (seed | 0)) { // Seed state array using a 32-bit integer. w = X[0] = seed; } else { // Seed state using a string. seed = '' + seed; for (j = 0; j < seed.length; ++j) { X[j & 7] = (X[j & 7] << 15) ^ (seed.charCodeAt(j) + X[(j + 1) & 7] << 13); } } // Enforce an array length of 8, not all zeroes. while (X.length < 8) X.push(0); for (j = 0; j < 8 && X[j] === 0; ++j); if (j == 8) w = X[7] = -1; else w = X[j]; me.x = X; me.i = 0; // Discard an initial 256 values. for (j = 256; j > 0; --j) { me.next(); } } init(me, seed); } function copy(f, t) { t.x = f.x.slice(); t.i = f.i; return t; } function impl(seed, opts) { if (seed == null) seed = +(new Date); var xg = new XorGen(seed), state = opts && opts.state, prng = function() { return (xg.next() >>> 0) / 0x100000000; }; prng.double = function() { do { var top = xg.next() >>> 11, bot = (xg.next() >>> 0) / 0x100000000, result = (top + bot) / (1 << 21); } while (result === 0); return result; }; prng.int32 = xg.next; prng.quick = prng; if (state) { if (state.x) copy(state, xg); prng.state = function() { return copy(xg, {}); }; } return prng; } if (module && module.exports) { module.exports = impl; } else if (define && define.amd) { define(function() { return impl; }); } else { this.xorshift7 = impl; } })( commonjsGlobal, module, // present in node.js (typeof undefined) == 'function' && undefined // present with an AMD loader ); }); var xor4096 = createCommonjsModule(function (module) { // A Javascript implementaion of Richard Brent's Xorgens xor4096 algorithm. // // This fast non-cryptographic random number generator is designed for // use in Monte-Carlo algorithms. It combines a long-period xorshift // generator with a Weyl generator, and it passes all common batteries // of stasticial tests for randomness while consuming only a few nanoseconds // for each prng generated. For background on the generator, see Brent's // paper: "Some long-period random number generators using shifts and xors." // http://arxiv.org/pdf/1004.3115v1.pdf // // Usage: // // var xor4096 = require('xor4096'); // random = xor4096(1); // Seed with int32 or string. // assert.equal(random(), 0.1520436450538547); // (0, 1) range, 53 bits. // assert.equal(random.int32(), 1806534897); // signed int32, 32 bits. // // For nonzero numeric keys, this impelementation provides a sequence // identical to that by Brent's xorgens 3 implementaion in C. This // implementation also provides for initalizing the generator with // string seeds, or for saving and restoring the state of the generator. // // On Chrome, this prng benchmarks about 2.1 times slower than // Javascript's built-in Math.random(). (function(global, module, define) { function XorGen(seed) { var me = this; // Set up generator function. me.next = function() { var w = me.w, X = me.X, i = me.i, t, v; // Update Weyl generator. me.w = w = (w + 0x61c88647) | 0; // Update xor generator. v = X[(i + 34) & 127]; t = X[i = ((i + 1) & 127)]; v ^= v << 13; t ^= t << 17; v ^= v >>> 15; t ^= t >>> 12; // Update Xor generator array state. v = X[i] = v ^ t; me.i = i; // Result is the combination. return (v + (w ^ (w >>> 16))) | 0; }; function init(me, seed) { var t, v, i, j, w, X = [], limit = 128; if (seed === (seed | 0)) { // Numeric seeds initialize v, which is used to generates X. v = seed; seed = null; } else { // String seeds are mixed into v and X one character at a time. seed = seed + '\0'; v = 0; limit = Math.max(limit, seed.length); } // Initialize circular array and weyl value. for (i = 0, j = -32; j < limit; ++j) { // Put the unicode characters into the array, and shuffle them. if (seed) v ^= seed.charCodeAt((j + 32) % seed.length); // After 32 shuffles, take v as the starting w value. if (j === 0) w = v; v ^= v << 10; v ^= v >>> 15; v ^= v << 4; v ^= v >>> 13; if (j >= 0) { w = (w + 0x61c88647) | 0; // Weyl. t = (X[j & 127] ^= (v + w)); // Combine xor and weyl to init array. i = (0 == t) ? i + 1 : 0; // Count zeroes. } } // We have detected all zeroes; make the key nonzero. if (i >= 128) { X[(seed && seed.length || 0) & 127] = -1; } // Run the generator 512 times to further mix the state before using it. // Factoring this as a function slows the main generator, so it is just // unrolled here. The weyl generator is not advanced while warming up. i = 127; for (j = 4 * 128; j > 0; --j) { v = X[(i + 34) & 127]; t = X[i = ((i + 1) & 127)]; v ^= v << 13; t ^= t << 17; v ^= v >>> 15; t ^= t >>> 12; X[i] = v ^ t; } // Storing state as object members is faster than using closure variables. me.w = w; me.X = X; me.i = i; } init(me, seed); } function copy(f, t) { t.i = f.i; t.w = f.w; t.X = f.X.slice(); return t; } function impl(seed, opts) { if (seed == null) seed = +(new Date); var xg = new XorGen(seed), state = opts && opts.state, prng = function() { return (xg.next() >>> 0) / 0x100000000; }; prng.double = function() { do { var top = xg.next() >>> 11, bot = (xg.next() >>> 0) / 0x100000000, result = (top + bot) / (1 << 21); } while (result === 0); return result; }; prng.int32 = xg.next; prng.quick = prng; if (state) { if (state.X) copy(state, xg); prng.state = function() { return copy(xg, {}); }; } return prng; } if (module && module.exports) { module.exports = impl; } else if (define && define.amd) { define(function() { return impl; }); } else { this.xor4096 = impl; } })( commonjsGlobal, // window object or global module, // present in node.js (typeof undefined) == 'function' && undefined // present with an AMD loader ); }); var tychei = createCommonjsModule(function (module) { // A Javascript implementaion of the "Tyche-i" prng algorithm by // Samuel Neves and Filipe Araujo. // See https://eden.dei.uc.pt/~sneves/pubs/2011-snfa2.pdf (function(global, module, define) { function XorGen(seed) { var me = this, strseed = ''; // Set up generator function. me.next = function() { var b = me.b, c = me.c, d = me.d, a = me.a; b = (b << 25) ^ (b >>> 7) ^ c; c = (c - d) | 0; d = (d << 24) ^ (d >>> 8) ^ a; a = (a - b) | 0; me.b = b = (b << 20) ^ (b >>> 12) ^ c; me.c = c = (c - d) | 0; me.d = (d << 16) ^ (c >>> 16) ^ a; return me.a = (a - b) | 0; }; /* The following is non-inverted tyche, which has better internal * bit diffusion, but which is about 25% slower than tyche-i in JS. me.next = function() { var a = me.a, b = me.b, c = me.c, d = me.d; a = (me.a + me.b | 0) >>> 0; d = me.d ^ a; d = d << 16 ^ d >>> 16; c = me.c + d | 0; b = me.b ^ c; b = b << 12 ^ d >>> 20; me.a = a = a + b | 0; d = d ^ a; me.d = d = d << 8 ^ d >>> 24; me.c = c = c + d | 0; b = b ^ c; return me.b = (b << 7 ^ b >>> 25); } */ me.a = 0; me.b = 0; me.c = 2654435769 | 0; me.d = 1367130551; if (seed === Math.floor(seed)) { // Integer seed. me.a = (seed / 0x100000000) | 0; me.b = seed | 0; } else { // String seed. strseed += seed; } // Mix in string seed, then discard an initial batch of 64 values. for (var k = 0; k < strseed.length + 20; k++) { me.b ^= strseed.charCodeAt(k) | 0; me.next(); } } function copy(f, t) { t.a = f.a; t.b = f.b; t.c = f.c; t.d = f.d; return t; } function impl(seed, opts) { var xg = new XorGen(seed), state = opts && opts.state, prng = function() { return (xg.next() >>> 0) / 0x100000000; }; prng.double = function() { do { var top = xg.next() >>> 11, bot = (xg.next() >>> 0) / 0x100000000, result = (top + bot) / (1 << 21); } while (result === 0); return result; }; prng.int32 = xg.next; prng.quick = prng; if (state) { if (typeof(state) == 'object') copy(state, xg); prng.state = function() { return copy(xg, {}); }; } return prng; } if (module && module.exports) { module.exports = impl; } else if (define && define.amd) { define(function() { return impl; }); } else { this.tychei = impl; } })( commonjsGlobal, module, // present in node.js (typeof undefined) == 'function' && undefined // present with an AMD loader ); }); var seedrandom = createCommonjsModule(function (module) { /* Copyright 2014 David Bau. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ (function (pool, math) { // // The following constants are related to IEEE 754 limits. // var global = this, width = 256, // each RC4 output is 0 <= x < 256 chunks = 6, // at least six RC4 outputs for each double digits = 52, // there are 52 significant digits in a double rngname = 'random', // rngname: name for Math.random and Math.seedrandom startdenom = math.pow(width, chunks), significance = math.pow(2, digits), overflow = significance * 2, mask = width - 1, nodecrypto; // node.js crypto module, initialized at the bottom. // // seedrandom() // This is the seedrandom function described above. // function seedrandom(seed, options, callback) { var key = []; options = (options == true) ? { entropy: true } : (options || {}); // Flatten the seed string or build one from local entropy if needed. var shortseed = mixkey(flatten( options.entropy ? [seed, tostring(pool)] : (seed == null) ? autoseed() : seed, 3), key); // Use the seed to initialize an ARC4 generator. var arc4 = new ARC4(key); // This function returns a random double in [0, 1) that contains // randomness in every bit of the mantissa of the IEEE 754 value. var prng = function() { var n = arc4.g(chunks), // Start with a numerator n < 2 ^ 48 d = startdenom, // and denominator d = 2 ^ 48. x = 0; // and no 'extra last byte'. while (n < significance) { // Fill up all significant digits by n = (n + x) * width; // shifting numerator and d *= width; // denominator and generating a x = arc4.g(1); // new least-significant-byte. } while (n >= overflow) { // To avoid rounding up, before adding n /= 2; // last byte, shift everything d /= 2; // right using integer math until x >>>= 1; // we have exactly the desired bits. } return (n + x) / d; // Form the number within [0, 1). }; prng.int32 = function() { return arc4.g(4) | 0; }; prng.quick = function() { return arc4.g(4) / 0x100000000; }; prng.double = prng; // Mix the randomness into accumulated entropy. mixkey(tostring(arc4.S), pool); // Calling convention: what to return as a function of prng, seed, is_math. return (options.pass || callback || function(prng, seed, is_math_call, state) { if (state) { // Load the arc4 state from the given state if it has an S array. if (state.S) { copy(state, arc4); } // Only provide the .state method if requested via options.state. prng.state = function() { return copy(arc4, {}); }; } // If called as a method of Math (Math.seedrandom()), mutate // Math.random because that is how seedrandom.js has worked since v1.0. if (is_math_call) { math[rngname] = prng; return seed; } // Otherwise, it is a newer calling convention, so return the // prng directly. else return prng; })( prng, shortseed, 'global' in options ? options.global : (this == math), options.state); } math['seed' + rngname] = seedrandom; // // ARC4 // // An ARC4 implementation. The constructor takes a key in the form of // an array of at most (width) integers that should be 0 <= x < (width). // // The g(count) method returns a pseudorandom integer that concatenates // the next (count) outputs from ARC4. Its return value is a number x // that is in the range 0 <= x < (width ^ count). // function ARC4(key) { var t, keylen = key.length, me = this, i = 0, j = me.i = me.j = 0, s = me.S = []; // The empty key [] is treated as [0]. if (!keylen) { key = [keylen++]; } // Set up S using the standard key scheduling algorithm. while (i < width) { s[i] = i++; } for (i = 0; i < width; i++) { s[i] = s[j = mask & (j + key[i % keylen] + (t = s[i]))]; s[j] = t; } // The "g" method returns the next (count) outputs as one number. (me.g = function(count) { // Using instance members instead of closure state nearly doubles speed. var t, r = 0, i = me.i, j = me.j, s = me.S; while (count--) { t = s[i = mask & (i + 1)]; r = r * width + s[mask & ((s[i] = s[j = mask & (j + t)]) + (s[j] = t))]; } me.i = i; me.j = j; return r; // For robust unpredictability, the function call below automatically // discards an initial batch of values. This is called RC4-drop[256]. // See http://google.com/search?q=rsa+fluhrer+response&btnI })(width); } // // copy() // Copies internal state of ARC4 to or from a plain object. // function copy(f, t) { t.i = f.i; t.j = f.j; t.S = f.S.slice(); return t; } // // flatten() // Converts an object tree to nested arrays of strings. // function flatten(obj, depth) { var result = [], typ = (typeof obj), prop; if (depth && typ == 'object') { for (prop in obj) { try { result.push(flatten(obj[prop], depth - 1)); } catch (e) {} } } return (result.length ? result : typ == 'string' ? obj : obj + '\0'); } // // mixkey() // Mixes a string seed into a key that is an array of integers, and // returns a shortened string seed that is equivalent to the result key. // function mixkey(seed, key) { var stringseed = seed + '', smear, j = 0; while (j < stringseed.length) { key[mask & j] = mask & ((smear ^= key[mask & j] * 19) + stringseed.charCodeAt(j++)); } return tostring(key); } // // autoseed() // Returns an object for autoseeding, using window.crypto and Node crypto // module if available. // function autoseed() { try { var out; if (nodecrypto && (out = nodecrypto.randomBytes)) { // The use of 'out' to remember randomBytes makes tight minified code. out = out(width); } else { out = new Uint8Array(width); (global.crypto || global.msCrypto).getRandomValues(out); } return tostring(out); } catch (e) { var browser = global.navigator, plugins = browser && browser.plugins; return [+new Date, global, plugins, global.screen, tostring(pool)]; } } // // tostring() // Converts an array of charcodes to a string // function tostring(a) { return String.fromCharCode.apply(0, a); } // // When seedrandom.js is loaded, we immediately mix a few bits // from the built-in RNG into the entropy pool. Because we do // not want to interfere with deterministic PRNG state later, // seedrandom will not call math.random on its own again after // initialization. // mixkey(math.random(), pool); // // Nodejs and AMD support: export the implementation as a module using // either convention. // if (module.exports) { module.exports = seedrandom; // When in node.js, try using crypto package for autoseeding. try { nodecrypto = require('crypto'); } catch (ex) {} } else if ((typeof undefined) == 'function' && undefined.amd) { undefined(function() { return seedrandom; }); } // End anonymous scope, and pass initial values. })( [], // pool: entropy pool starts empty Math // math: package containing random, pow, and seedrandom ); }); // A library of seedable RNGs implemented in Javascript. // // Usage: // // var seedrandom = require('seedrandom'); // var random = seedrandom(1); // or any seed. // var x = random(); // 0 <= x < 1. Every bit is random. // var x = random.quick(); // 0 <= x < 1. 32 bits of randomness. // alea, a 53-bit multiply-with-carry generator by Johannes Baagøe. // Period: ~2^116 // Reported to pass all BigCrush tests. // xor128, a pure xor-shift generator by George Marsaglia. // Period: 2^128-1. // Reported to fail: MatrixRank and LinearComp. // xorwow, George Marsaglia's 160-bit xor-shift combined plus weyl. // Period: 2^192-2^32 // Reported to fail: CollisionOver, SimpPoker, and LinearComp. // xorshift7, by François Panneton and Pierre L'ecuyer, takes // a different approach: it adds robustness by allowing more shifts // than Marsaglia's original three. It is a 7-shift generator // with 256 bits, that passes BigCrush with no systmatic failures. // Period 2^256-1. // No systematic BigCrush failures reported. // xor4096, by Richard Brent, is a 4096-bit xor-shift with a // very long period that also adds a Weyl generator. It also passes // BigCrush with no systematic failures. Its long period may // be useful if you have many generators and need to avoid // collisions. // Period: 2^4128-2^32. // No systematic BigCrush failures reported. // Tyche-i, by Samuel Neves and Filipe Araujo, is a bit-shifting random // number generator derived from ChaCha, a modern stream cipher. // https://eden.dei.uc.pt/~sneves/pubs/2011-snfa2.pdf // Period: ~2^127 // No systematic BigCrush failures reported. // The original ARC4-based prng included in this library. // Period: ~2^1600 seedrandom.alea = alea; seedrandom.xor128 = xor128; seedrandom.xorwow = xorwow; seedrandom.xorshift7 = xorshift7; seedrandom.xor4096 = xor4096; seedrandom.tychei = tychei; var seedrandom$1 = seedrandom; var seedrandom_1 = seedrandom$1.alea; var MPRandGauss = (function () { function MPRandGauss(mean, stdDeviation, dtype, truncated, seed) { this.mean = mean; this.stdDev = stdDeviation; this.dtype = dtype; this.nextVal = NaN; this.truncated = truncated; if (this.truncated) { this.upper = this.mean + this.stdDev * 2; this.lower = this.mean - this.stdDev * 2; } var seedValue = seed ? seed : Math.random(); this.random = seedrandom_1(seedValue.toString()); } MPRandGauss.prototype.nextValue = function () { if (!isNaN(this.nextVal)) { var value = this.nextVal; this.nextVal = NaN; return value; } var resultX, resultY; var isValid = false; while (!isValid) { var v1 = void 0, v2 = void 0, s = void 0; do { v1 = 2 * this.random() - 1; v2 = 2 * this.random() - 1; s = v1 * v1 + v2 * v2; } while (s >= 1 || s === 0); var mul = Math.sqrt(-2.0 * Math.log(s) / s); resultX = this.mean + this.stdDev * v1 * mul; resultY = this.mean + this.stdDev * v2 * mul; if (!this.truncated || this.isValidTruncated(resultX)) { isValid = true; } } if (!this.truncated || this.isValidTruncated(resultY)) { this.nextVal = this.convertValue(resultY); } return this.convertValue(resultX); }; MPRandGauss.prototype.convertValue = function (value) { if (this.dtype == null || this.dtype === 'float32') { return value; } return Math.round(value); }; MPRandGauss.prototype.isValidTruncated = function (value) { return value <= this.upper && value >= this.lower; }; return MPRandGauss; }()); function clone_(x) { var $x = convertToTensor(x, 'x', 'clone', null); var der = function (dy) { return { $x: function () { return dy.toFloat(); } }; }; return ENV.engine.runKernel(function (backend) { return Tensor.make($x.shape, { dataId: $x.dataId }, $x.dtype); }, { $x: $x }, der); } function eye_(numRows, numColumns, batchShape, dtype) { if (dtype === void 0) { dtype = 'float32'; } if (numColumns == null) { numColumns = numRows; } var buff = buffer([numRows, numColumns], dtype); var n = numRows <= numColumns ? numRows : numColumns; for (var i = 0; i < n; ++i) { buff.set(1, i, i); } var out = buff.toTensor().as2D(numRows, numColumns); if (batchShape == null) { return out; } else { if (batchShape.length === 1) { return tile(expandDims(out, 0), [batchShape[0], 1, 1]); } else if (batchShape.length === 2) { return tile(expandDims(expandDims(out, 0), 0), [batchShape[0], batchShape[1], 1, 1]); } else if (batchShape.length === 3) { return tile(expandDims(expandDims(expandDims(out, 0), 0), 0), [batchShape[0], batchShape[1], batchShape[2], 1, 1]); } else { throw new Error("eye() currently supports only 1D and 2D " + ("batchShapes, but received " + batchShape.length + "D.")); } } } function randomNormal_(shape, mean, stdDev, dtype, seed) { if (mean === void 0) { mean = 0; } if (stdDev === void 0) { stdDev = 1; } if (dtype != null && dtype === 'bool') { throw new Error("Unsupported data type " + dtype); } var randGauss = new MPRandGauss(mean, stdDev, dtype, false, seed); var res = buffer(shape, dtype); for (var i = 0; i < res.values.length; i++) { res.values[i] = randGauss.nextValue(); } return res.toTensor(); } function truncatedNormal_(shape, mean, stdDev, dtype, seed) { if (mean === void 0) { mean = 0; } if (stdDev === void 0) { stdDev = 1; } if (dtype != null && dtype === 'bool') { throw new Error("Unsupported data type " + dtype); } var randGauss = new MPRandGauss(mean, stdDev, dtype, true, seed); var res = buffer(shape, dtype); for (var i = 0; i < res.values.length; i++) { res.values[i] = randGauss.nextValue(); } return res.toTensor(); } function randomUniform_(shape, minval, maxval, dtype) { if (minval === void 0) { minval = 0; } if (maxval === void 0) { maxval = 1; } if (dtype === void 0) { dtype = 'float32'; } var res = buffer(shape, dtype); for (var i = 0; i < res.values.length; i++) { res.values[i] = randUniform(minval, maxval); } return res.toTensor(); } function rand_(shape, randFunction, dtype) { var size = sizeFromShape(shape); var values = null; if (dtype == null || dtype === 'float32') { values = new Float32Array(size); } else if (dtype === 'int32') { values = new Int32Array(size); } else if (dtype === 'bool') { values = new Uint8Array(size); } else { throw new Error("Unknown data type " + dtype); } for (var i = 0; i < size; i++) { values[i] = randFunction(); } return Tensor.make(shape, { values: values }, dtype); } function multinomial_(logits, numSamples, seed, normalized) { if (normalized === void 0) { normalized = false; } var $logits = convertToTensor(logits, 'logits', 'multinomial'); var numOutcomes = $logits.size; var origRank = $logits.rank; if (numOutcomes < 2) { throw new Error("Error in multinomial: you need at least 2 outcomes, but got " + (numOutcomes + ".")); } if (origRank > 2) { throw new Error("Rank of probabilities must be 1 or 2, but is " + origRank); } seed = seed || Math.random(); var logits2D = origRank === 1 ? $logits.as2D(1, -1) : $logits; var res = ENV.engine.runKernel(function (backend) { return backend.multinomial(logits2D, normalized, numSamples, seed); }, { logits2D: logits2D }); return origRank === 1 ? res.as1D() : res; } function oneHot_(indices, depth, onValue, offValue) { if (onValue === void 0) { onValue = 1; } if (offValue === void 0) { offValue = 0; } if (depth < 2) { throw new Error("Error in oneHot: depth must be >=2, but it is " + depth); } var $indices = convertToTensor(indices, 'indices', 'oneHot', 'int32'); var outShape = $indices.shape.concat([depth]); $indices = $indices.flatten(); var grad = function (dy) { return { $indices: function () { return zeros($indices.shape, 'float32'); } }; }; var result = ENV.engine.runKernel(function (backend) { return backend.oneHot($indices, depth, onValue, offValue); }, { $indices: $indices }, grad); return result.reshape(outShape); } function fromPixels_(pixels, numChannels) { if (numChannels === void 0) { numChannels = 3; } if (numChannels > 4) { throw new Error('Cannot construct Tensor with more than 4 channels from pixels.'); } return ENV.engine.fromPixels(pixels, numChannels); } function toPixels(img, canvas) { return __awaiter(this, void 0, void 0, function () { var $img, _a, height, width, depth, minTensor, maxTensor, min, max, data, multiplier, bytes, i, r, g, b, a, j, ctx, imageData; return __generator(this, function (_b) { switch (_b.label) { case 0: $img = convertToTensor(img, 'img', 'toPixels'); if (!(img instanceof Tensor)) { $img = $img.toInt(); } if ($img.rank !== 2 && $img.rank !== 3) { throw new Error("toPixels only supports rank 2 or 3 tensors, got rank " + $img.rank + "."); } _a = $img.shape.slice(0, 2), height = _a[0], width = _a[1]; depth = $img.rank === 2 ? 1 : $img.shape[2]; if (depth > 4 || depth === 2) { throw new Error("toPixels only supports depth of size " + ("1, 3 or 4 but got " + depth)); } minTensor = $img.min(); maxTensor = $img.max(); return [4, minTensor.data()]; case 1: min = (_b.sent())[0]; return [4, maxTensor.data()]; case 2: max = (_b.sent())[0]; minTensor.dispose(); maxTensor.dispose(); if ($img.dtype === 'float32') { if (min < 0 || max > 1) { throw new Error("Tensor values for a float32 Tensor must be in the " + ("range [0 - 1] but got range [" + min + " - " + max + "].")); } } else if ($img.dtype === 'int32') { if (min < 0 || max > 255) { throw new Error("Tensor values for a int32 Tensor must be in the " + ("range [0 - 255] but got range [" + min + " - " + max + "].")); } } else { throw new Error("Unsupported type for toPixels: " + $img.dtype + "." + " Please use float32 or int32 tensors."); } return [4, $img.data()]; case 3: data = _b.sent(); multiplier = $img.dtype === 'float32' ? 255 : 1; bytes = new Uint8ClampedArray(width * height * 4); for (i = 0; i < height * width; ++i) { r = void 0, g = void 0, b = void 0, a = void 0; if (depth === 1) { r = data[i] * multiplier; g = data[i] * multiplier; b = data[i] * multiplier; a = 255; } else if (depth === 3) { r = data[i * 3] * multiplier; g = data[i * 3 + 1] * multiplier; b = data[i * 3 + 2] * multiplier; a = 255; } else if (depth === 4) { r = data[i * 4] * multiplier; g = data[i * 4 + 1] * multiplier; b = data[i * 4 + 2] * multiplier; a = data[i * 4 + 3] * multiplier; } j = i * 4; bytes[j + 0] = Math.round(r); bytes[j + 1] = Math.round(g); bytes[j + 2] = Math.round(b); bytes[j + 3] = Math.round(a); } if (canvas != null) { canvas.width = width; canvas.height = height; ctx = canvas.getContext('2d'); imageData = new ImageData(bytes, width, height); ctx.putImageData(imageData, 0, 0); } if ($img !== img) { $img.dispose(); } return [2, bytes]; } }); }); } function reshape_(x, shape) { var $x = convertToTensor(x, 'x', 'reshape', null); shape = inferFromImplicitShape(shape, $x.size); assert($x.size === sizeFromShape(shape), 'new shape and old shape must have the same number of elements.'); var grad = function (dy) { return { $x: function () { return dy.reshape($x.shape); } }; }; return ENV.engine.runKernel(function (backend) { return backend.reshape($x, shape); }, { $x: $x }, grad); } function squeeze_(x, axis) { var $x = convertToTensor(x, 'x', 'squeeze'); return reshape($x, squeezeShape($x.shape, axis).newShape); } function cast_(x, dtype) { var $x = convertToTensor(x, 'x', 'cast'); var grad = function (dy) { return { $x: function () { return dy.clone(); } }; }; return ENV.engine.runKernel(function (backend) { return backend.cast($x, dtype); }, { $x: $x }, grad); } function tile_(x, reps) { var $x = convertToTensor(x, 'x', 'tile'); assert($x.rank === reps.length, "Error in transpose: rank of input " + $x.rank + " " + ("must match length of reps " + reps + ".")); var grad = function (dy) { var derX = function () { var xGrad = zerosLike($x); if ($x.rank === 1) { for (var i = 0; i < reps[0]; ++i) { xGrad = xGrad.add(dy.slice([i * $x.shape[0]], [$x.shape[0]])); } } else if ($x.rank === 2) { for (var i = 0; i < reps[0]; ++i) { for (var j = 0; j < reps[1]; ++j) { xGrad = xGrad.add(dy.slice([i * $x.shape[0], j * $x.shape[1]], [$x.shape[0], $x.shape[1]])); } } } else if ($x.rank === 3) { for (var i = 0; i < reps[0]; ++i) { for (var j = 0; j < reps[1]; ++j) { for (var k = 0; k < reps[2]; ++k) { xGrad = xGrad.add(dy.slice([i * $x.shape[0], j * $x.shape[1], k * $x.shape[2]], [$x.shape[0], $x.shape[1], $x.shape[2]])); } } } } else if ($x.rank === 4) { for (var i = 0; i < reps[0]; ++i) { for (var j = 0; j < reps[1]; ++j) { for (var k = 0; k < reps[2]; ++k) { for (var l = 0; l < reps[3]; ++l) { xGrad = xGrad.add(dy.slice([ i * $x.shape[0], j * $x.shape[1], k * $x.shape[2], l * $x.shape[3] ], [$x.shape[0], $x.shape[1], $x.shape[2], $x.shape[3]])); } } } } } else { throw new Error("Gradient for tile operation is not implemented for rank-" + ($x.rank + " tensors yet.")); } return xGrad; }; return { $x: derX }; }; return ENV.engine.runKernel(function (backend) { return backend.tile($x, reps); }, { $x: $x }, grad); } function pad1d_(x, paddings, constantValue) { if (constantValue === void 0) { constantValue = 0; } assert(paddings.length === 2, 'Invalid number of paddings. Must be length of 2.'); return pad(x, [paddings], constantValue); } function pad2d_(x, paddings, constantValue) { if (constantValue === void 0) { constantValue = 0; } assert(paddings.length === 2 && paddings[0].length === 2 && paddings[1].length === 2, 'Invalid number of paddings. Must be length of 2 each.'); return pad(x, paddings, constantValue); } function pad3d_(x, paddings, constantValue) { if (constantValue === void 0) { constantValue = 0; } assert(paddings.length === 3 && paddings[0].length === 2 && paddings[1].length === 2 && paddings[2].length === 2, 'Invalid number of paddings. Must be length of 2 each.'); return pad(x, paddings, constantValue); } function pad4d_(x, paddings, constantValue) { if (constantValue === void 0) { constantValue = 0; } assert(paddings.length === 4 && paddings[0].length === 2 && paddings[1].length === 2 && paddings[2].length === 2 && paddings[3].length === 2, 'Invalid number of paddings. Must be length of 2 each.'); return pad(x, paddings, constantValue); } function pad_(x, paddings, constantValue) { if (constantValue === void 0) { constantValue = 0; } var $x = convertToTensor(x, 'x', 'pad'); if ($x.rank === 0) { throw new Error('pad(scalar) is not defined. Pass non-scalar to pad'); } var begin = paddings.map(function (p) { return p[0]; }); var grad = function (dy) { return { $x: function () { return dy.slice(begin, $x.shape); } }; }; return ENV.engine.runKernel(function (backend) { return backend.pad($x, paddings, constantValue); }, { $x: $x }, grad); } function stack_(tensors, axis) { if (axis === void 0) { axis = 0; } var $tensors = convertToTensorArray(tensors, 'tensors', 'stack'); assert($tensors.length >= 1, 'Pass at least one tensor to tf.stack'); if ($tensors.length === 1) { return $tensors[0].expandDims(axis); } var rank = $tensors[0].rank; var shape = $tensors[0].shape; var dtype = $tensors[0].dtype; assert(axis <= rank, 'Axis must be <= rank of the tensor'); $tensors.forEach(function (t) { assertShapesMatch(shape, t.shape, 'All tensors passed to stack must have matching shapes'); }); $tensors.forEach(function (t) { assert(dtype === t.dtype, 'All tensors passed to stack must have matching dtypes'); }); var expandedTensors = $tensors.map(function (t) { return t.expandDims(axis); }); return concat(expandedTensors, axis); } function batchToSpaceND_(x, blockShape, crops) { var $x = convertToTensor(x, 'x', 'batchToSpaceND'); var prod = blockShape.reduce(function (a, b) { return a * b; }); assert($x.rank >= 1 + blockShape.length, "input rank is " + $x.rank + " but should be > than blockShape.length " + blockShape.length); assert(crops.length === blockShape.length, "crops.length is " + crops.length + " but should be equal to blockShape.length " + blockShape.length); assert($x.shape[0] % prod === 0, "input tensor batch is " + $x.shape[0] + " but is not divisible by the product of " + ("the elements of blockShape " + blockShape.join(' * ') + " === " + prod)); var grad = function (dy) { return { $x: function () { return dy.spaceToBatchND(blockShape, crops); } }; }; return ENV.engine.runKernel(function (backend) { return backend.batchToSpaceND($x, blockShape, crops); }, { $x: $x }, grad); } function spaceToBatchND_(x, blockShape, paddings) { var $x = convertToTensor(x, 'x', 'spaceToBatchND'); assert($x.rank >= 1 + blockShape.length, "input rank " + $x.rank + " should be > than [blockShape] " + blockShape.length); assert(paddings.length === blockShape.length, "paddings.shape[0] " + paddings.length + " must be equal to [blockShape] " + blockShape.length); assert($x.shape.reduce(function (a, b, i) { if (i > 0 && i <= blockShape.length) { return a && ((b + paddings[i - 1][0] + paddings[i - 1][1]) % blockShape[i - 1] === 0); } return a; }, true), "input spatial dimensions " + $x.shape.slice(1) + " with paddings " + paddings.toString() + " must be divisible by blockShapes " + blockShape.toString()); var grad = function (dy) { return { $x: function () { return dy.batchToSpaceND(blockShape, paddings); } }; }; return ENV.engine.runKernel(function (backend) { return backend.spaceToBatchND($x, blockShape, paddings); }, { $x: $x }, grad); } function unstack_(x, axis) { if (axis === void 0) { axis = 0; } axis = axis || 0; var $x = convertToTensor(x, 'x', 'unstack'); assert(axis < $x.shape.length, "Axis " + axis + " is >= to tensor shape length " + $x.shape.length); var grad = function (dy) { return { $x: function () { return stack(dy, axis); } }; }; return ENV.engine.runKernel(function (backend) { return backend.unstack($x, axis); }, { $x: $x }, grad); } function cumsum_(x, axis, exclusive, reverse) { if (axis === void 0) { axis = 0; } if (exclusive === void 0) { exclusive = false; } if (reverse === void 0) { reverse = false; } var $x = convertToTensor(x, 'x', 'cumsum'); axis = axis | 0; var permutation = getAxesPermutation([axis], $x.rank); var permutedX = $x; if (permutation != null) { permutedX = $x.transpose(permutation); } var permutedAxis = getInnerMostAxes(1, $x.rank)[0]; var grad = function (dy) { return { permutedX: function () { return dy.cumsum(axis, exclusive, !reverse); } }; }; var value = ENV.engine.runKernel(function (backend) { return backend.cumsum(permutedX, permutedAxis, exclusive, reverse); }, { permutedX: permutedX }, grad); if (permutation != null) { value = value.transpose(permutation); } return value; } function expandDims_(x, axis) { if (axis === void 0) { axis = 0; } var $x = convertToTensor(x, 'x', 'expandDims'); assert(axis <= $x.rank, 'Axis must be <= rank of the tensor'); var newShape = $x.shape.slice(); if (axis < 0) { assert(-($x.rank + 1) <= axis, "Axis must be in the interval [" + -($x.rank + 1) + ", " + $x.rank + "]"); axis = $x.rank + axis + 1; } newShape.splice(axis, 0, 1); return reshape($x, newShape); } function depthToSpace_(x, blockSize, dataFormat) { if (dataFormat === void 0) { dataFormat = 'NHWC'; } var $x = convertToTensor(x, 'x', 'depthToSpace'); var inputHeight = (dataFormat === 'NHWC') ? $x.shape[1] : $x.shape[2]; var inputWidth = (dataFormat === 'NHWC') ? $x.shape[2] : $x.shape[3]; var inputDepth = (dataFormat === 'NHWC') ? $x.shape[3] : $x.shape[1]; assert(inputHeight * blockSize >= 0, "Negative dimension size caused by overflow when multiplying\n " + inputHeight + " and " + blockSize + " for depthToSpace with input shape\n " + $x.shape); assert(inputWidth * blockSize >= 0, "Negative dimension size caused by overflow when multiplying\n " + inputWidth + " and " + blockSize + " for depthToSpace with input shape\n " + $x.shape); assert((inputDepth % (blockSize * blockSize) === 0), "Dimension size must be evenly divisible by " + blockSize * blockSize + " but is " + inputDepth + " for depthToSpace with input shape " + $x.shape); return ENV.engine.runKernel(function (backend) { return backend.depthToSpace($x, blockSize, dataFormat); }, { $x: $x }); } function setdiff1dAsync_(x, y) { return __awaiter(this, void 0, void 0, function () { var $x, $y, xVals, yVals, ySet, outputSize, i, buffer, indices, i, p; return __generator(this, function (_a) { switch (_a.label) { case 0: $x = convertToTensor(x, 'x', 'setdiff1d'); $y = convertToTensor(y, 'y', 'setdiff1d'); assert($x.dtype === $y.dtype, "x and y should have the same dtype, but got x (" + $x.dtype + ") and y (" + $y.dtype + ")."); assert($x.rank === 1, "x should be 1D tensor, but got x (" + $x.shape + ")."); assert($y.rank === 1, "y should be 1D tensor, but got y (" + $y.shape + ")."); return [4, $x.data()]; case 1: xVals = _a.sent(); return [4, $y.data()]; case 2: yVals = _a.sent(); ySet = new Set(yVals); outputSize = 0; for (i = 0; i < xVals.length; i++) { if (!ySet.has(xVals[i])) { outputSize++; } } buffer = new TensorBuffer([outputSize], $x.dtype); indices = new TensorBuffer([outputSize], 'int32'); for (i = 0, p = 0; i < xVals.length; i++) { if (!ySet.has(xVals[i])) { buffer.values[p] = xVals[i]; indices.values[p] = i; p++; } } return [2, [buffer.toTensor(), indices.toTensor()]]; } }); }); } function buffer(shape, dtype, values) { if (dtype === void 0) { dtype = 'float32'; } dtype = dtype || 'float32'; return new TensorBuffer(shape, dtype, values); } function print(x, verbose) { if (verbose === void 0) { verbose = false; } console.log(x.toString(verbose)); } var batchToSpaceND = op({ batchToSpaceND_: batchToSpaceND_ }); var cast = op({ cast_: cast_ }); var clone = op({ clone_: clone_ }); var cumsum = op({ cumsum_: cumsum_ }); var depthToSpace = op({ depthToSpace_: depthToSpace_ }); var expandDims = op({ expandDims_: expandDims_ }); var eye = op({ eye_: eye_ }); var fromPixels = op({ fromPixels_: fromPixels_ }); var multinomial = op({ multinomial_: multinomial_ }); var oneHot = op({ oneHot_: oneHot_ }); var pad = op({ pad_: pad_ }); var pad1d = op({ pad1d_: pad1d_ }); var pad2d = op({ pad2d_: pad2d_ }); var pad3d = op({ pad3d_: pad3d_ }); var pad4d = op({ pad4d_: pad4d_ }); var rand = op({ rand_: rand_ }); var randomNormal = op({ randomNormal_: randomNormal_ }); var randomUniform = op({ randomUniform_: randomUniform_ }); var reshape = op({ reshape_: reshape_ }); var spaceToBatchND = op({ spaceToBatchND_: spaceToBatchND_ }); var squeeze = op({ squeeze_: squeeze_ }); var stack = op({ stack_: stack_ }); var tile = op({ tile_: tile_ }); var truncatedNormal = op({ truncatedNormal_: truncatedNormal_ }); var unstack = op({ unstack_: unstack_ }); var setdiff1dAsync = setdiff1dAsync_; function whereImpl(condShape, condVals) { var indices = []; for (var i = 0; i < condVals.length; i++) { if (condVals[i]) { indices.push(i); } } var inBuffer = buffer(condShape, 'int32'); var out = buffer([indices.length, condShape.length], 'int32'); for (var i = 0; i < indices.length; i++) { var loc = inBuffer.indexToLoc(indices[i]); var offset = i * condShape.length; out.values.set(loc, offset); } return out.toTensor(); } function mapActivationToShaderProgram(activation, packed) { if (packed === void 0) { packed = false; } if (activation === 'linear') { if (packed) { return LINEAR$1; } return LINEAR; } else if (activation === 'relu') { if (packed) { return RELU$1; } return RELU; } throw new Error("Activation " + activation + " has not been implemented for the WebGL backend."); } var CPU_HANDOFF_SIZE_THRESHOLD = 10; var MATMUL_SHARED_DIM_THRESHOLD = 1000; var MathBackendWebGL = (function () { function MathBackendWebGL(gpgpu, delayedStorage) { if (delayedStorage === void 0) { delayedStorage = true; } this.gpgpu = gpgpu; this.delayedStorage = delayedStorage; this.pendingRead = new WeakMap(); this.pendingDisposal = new WeakSet(); this.dataRefCount = new WeakMap(); this.lruDataGPU = []; this.numBytesInGPU = 0; this.uploadWaitMs = 0; this.downloadWaitMs = 0; this.binaryCache = {}; this.disposed = false; if (ENV.get('WEBGL_VERSION') < 1) { throw new Error('WebGL is not supported on this device'); } if (gpgpu == null) { var gl = getWebGLContext(ENV.get('WEBGL_VERSION')); this.gpgpu = new GPGPUContext(gl); this.canvas = gl.canvas; this.gpgpuCreatedLocally = true; } else { this.gpgpuCreatedLocally = false; this.canvas = gpgpu.gl.canvas; } this.textureManager = new TextureManager(this.gpgpu); } MathBackendWebGL.prototype.register = function (dataId, shape, dtype) { if (this.texData.has(dataId)) { throw new Error('Data buffer is already registered'); } this.texData.set(dataId, { shape: shape, dtype: dtype }); }; MathBackendWebGL.prototype.setDataMover = function (dataMover) { this.texData = new DataStorage(dataMover); }; MathBackendWebGL.prototype.fromPixels = function (pixels, numChannels) { if (pixels == null) { throw new Error('pixels passed to tf.fromPixels() can not be null'); } var texShape = [pixels.height, pixels.width]; var outShape = [pixels.height, pixels.width, numChannels]; if (!(pixels instanceof HTMLVideoElement) && !(pixels instanceof HTMLImageElement) && !(pixels instanceof HTMLCanvasElement) && !(pixels instanceof ImageData)) { throw new Error('pixels passed to tf.fromPixels() must be either an ' + "HTMLVideoElement, HTMLImageElement, HTMLCanvasElement or " + ("ImageData, but was " + pixels.constructor.name)); } if (pixels instanceof HTMLVideoElement) { if (this.fromPixels2DContext == null) { if (!ENV.get('IS_BROWSER')) { throw new Error('Can\'t read pixels from HTMLImageElement outside the browser.'); } if (document.readyState !== 'complete') { throw new Error('The DOM is not ready yet. Please call tf.fromPixels() ' + 'once the DOM is ready. One way to do that is to add an event ' + 'listener for `DOMContentLoaded` on the document object'); } this.fromPixels2DContext = document.createElement('canvas').getContext('2d'); } this.fromPixels2DContext.canvas.width = pixels.width; this.fromPixels2DContext.canvas.height = pixels.height; this.fromPixels2DContext.drawImage(pixels, 0, 0, pixels.width, pixels.height); pixels = this.fromPixels2DContext.canvas; } var tempPixelHandle = this.makeTensorHandle(texShape, 'int32'); this.texData.get(tempPixelHandle.dataId).usage = TextureUsage.PIXELS; this.gpgpu.uploadPixelDataToTexture(this.getTexture(tempPixelHandle.dataId), pixels); var program = new FromPixelsProgram(outShape); var res = this.compileAndRun(program, [tempPixelHandle]); this.disposeData(tempPixelHandle.dataId); return res; }; MathBackendWebGL.prototype.makeTensorHandle = function (shape, dtype) { var dataId = {}; this.register(dataId, shape, dtype); return { dataId: dataId, shape: shape, dtype: dtype }; }; MathBackendWebGL.prototype.write = function (dataId, values) { if (values == null) { throw new Error('MathBackendWebGL.write(): values can not be null'); } if (ENV.get('DEBUG')) { for (var i = 0; i < values.length; i++) { var num = values[i]; if (!canBeRepresented(num)) { throw Error("The value " + num + " cannot be represented on this device."); } } } var texData = this.texData.get(dataId); var texture = texData.texture, texShape = texData.texShape, usage = texData.usage, dtype = texData.dtype, isPacked = texData.isPacked; if (dtype === 'complex64') { throw new Error("Cannot write to a complex64 dtype. " + "Please use tf.complex(real, imag)."); } if (texture != null) { this.releaseTexture(dataId, texture, texShape, usage, isPacked); texData.texture = null; texData.texShape = null; } texData.usage = TextureUsage.UPLOAD; texData.values = values; if (!this.delayedStorage) { this.uploadToGPU(dataId); } }; MathBackendWebGL.prototype.readSync = function (dataId) { var texData = this.texData.get(dataId); var values = texData.values, dtype = texData.dtype, complexTensors = texData.complexTensors, slice = texData.slice, shape = texData.shape; if (slice != null) { var program = new UnaryOpProgram(shape, CLONE); var res = this.compileAndRun(program, [{ dataId: dataId, shape: shape, dtype: dtype }]); return this.readSync(res.dataId); } if (values != null) { return this.convertAndCacheOnCPU(dataId); } if (dtype === 'string') { return values; } var shouldTimeProgram = this.activeTimers != null; var start; if (shouldTimeProgram) { start = performance.now(); } var result; if (dtype === 'complex64') { var realValues = complexTensors.real.dataSync(); var imagValues = complexTensors.imag.dataSync(); result = mergeRealAndImagArrays(realValues, imagValues); } else { result = this.getValuesFromTexture(dataId); } if (shouldTimeProgram) { this.downloadWaitMs += performance.now() - start; } return this.convertAndCacheOnCPU(dataId, result); }; MathBackendWebGL.prototype.read = function (dataId) { return __awaiter(this, void 0, void 0, function () { var _a, _b, subscribers_1, texData, texture, values, texShape, isPacked, shape, slice, dtype, program, res, width, height, bufferOrTexture, vals, size, batch, rows, cols, dTypeVals, subscribers; return __generator(this, function (_c) { switch (_c.label) { case 0: if (this.pendingRead.has(dataId)) { subscribers_1 = this.pendingRead.get(dataId); return [2, new Promise(function (resolve) { return subscribers_1.push(resolve); })]; } texData = this.texData.get(dataId); texture = texData.texture, values = texData.values, texShape = texData.texShape, isPacked = texData.isPacked, shape = texData.shape, slice = texData.slice, dtype = texData.dtype; if (slice != null) { program = new UnaryOpProgram(shape, CLONE); res = this.compileAndRun(program, [{ dataId: dataId, shape: shape, dtype: dtype }]); return [2, this.read(res.dataId)]; } if (values != null) { return [2, this.convertAndCacheOnCPU(dataId)]; } this.pendingRead.set(dataId, []); if (!ENV.get('WEBGL_DOWNLOAD_FLOAT_ENABLED') && ENV.get('WEBGL_VERSION') === 2) { throw new Error("tensor.data() with WEBGL_DOWNLOAD_FLOAT_ENABLED=false and " + "WEBGL_VERSION=2 not yet supported."); } width = texShape[1]; height = texShape[0]; if (isPacked) { _a = getPackedMatrixTextureShapeWidthHeight(texShape[0], texShape[1]), width = _a[0], height = _a[1]; } bufferOrTexture = this.gpgpu.maybeCreateBufferFromTexture(texture, height, width); return [4, this.gpgpu.createAndWaitForFence()]; case 1: _c.sent(); if (bufferOrTexture instanceof WebGLTexture) { vals = this.getValuesFromTexture(dataId); } else { size = sizeFromShape(shape); if (isPacked) { batch = getBatchDim(shape); rows = 1, cols = 1; if (shape.length) { _b = getRowsCols(shape), rows = _b[0], cols = _b[1]; } vals = this.gpgpu .downloadPackedMatrixFromBuffer(bufferOrTexture, batch, rows, cols, texShape[0], texShape[1]) .subarray(0, size); } else { vals = this.gpgpu .downloadFloat32MatrixFromBuffer(bufferOrTexture, texShape[0], texShape[1]) .subarray(0, size); } } dTypeVals = this.convertAndCacheOnCPU(dataId, vals); subscribers = this.pendingRead.get(dataId); this.pendingRead.delete(dataId); subscribers.forEach(function (resolve) { return resolve(dTypeVals); }); if (this.pendingDisposal.has(dataId)) { this.pendingDisposal.delete(dataId); this.disposeData(dataId); } return [2, dTypeVals]; } }); }); }; MathBackendWebGL.prototype.getValuesFromTexture = function (dataId) { var _a; var _b = this.texData.get(dataId), shape = _b.shape, dtype = _b.dtype, texture = _b.texture, texShape = _b.texShape; var size = sizeFromShape(shape); if (ENV.get('WEBGL_DOWNLOAD_FLOAT_ENABLED')) { if (this.texData.get(dataId).isPacked) { var batch = getBatchDim(shape); var rows = 1, cols = 1; if (shape.length) { _a = getRowsCols(shape), rows = _a[0], cols = _a[1]; } return this.gpgpu .downloadMatrixFromPackedTexture(texture, batch, rows, cols, texShape[0], texShape[1]) .subarray(0, size); } else { return this.gpgpu .downloadFloat32MatrixFromOutputTexture(texture, texShape[0], texShape[1]) .subarray(0, size); } } var tmpTarget = this.makeTensorHandle(shape, 'float32'); tmpTarget.size = sizeFromShape(shape); this.texData.get(tmpTarget.dataId).usage = TextureUsage.DOWNLOAD; var program = new EncodeFloatProgram(shape); var pageToCpu = false; this.compileAndRun(program, [{ shape: shape, dtype: dtype, dataId: dataId }], tmpTarget, null, pageToCpu); var tmpData = this.texData.get(tmpTarget.dataId); var vals = this.gpgpu .downloadByteEncodedFloatMatrixFromOutputTexture(tmpData.texture, tmpData.texShape[0], tmpData.texShape[1]) .subarray(0, size); this.disposeData(tmpTarget.dataId); return vals; }; MathBackendWebGL.prototype.time = function (f) { return __awaiter(this, void 0, void 0, function () { var oldActiveTimers, newActiveTimers, outerMostTime, flattenedActiveTimerQueries, flattenedActiveTimerNames, kernelMs, res; return __generator(this, function (_a) { switch (_a.label) { case 0: oldActiveTimers = this.activeTimers; newActiveTimers = []; outerMostTime = false; if (this.programTimersStack == null) { this.programTimersStack = newActiveTimers; outerMostTime = true; } else { this.activeTimers.push(newActiveTimers); } this.activeTimers = newActiveTimers; f(); flattenedActiveTimerQueries = flatten(this.activeTimers.map(function (d) { return d.query; })) .filter(function (d) { return d != null; }); flattenedActiveTimerNames = flatten(this.activeTimers.map(function (d) { return d.name; })) .filter(function (d) { return d != null; }); this.activeTimers = oldActiveTimers; if (outerMostTime) { this.programTimersStack = null; } return [4, Promise.all(flattenedActiveTimerQueries)]; case 1: kernelMs = _a.sent(); res = { uploadWaitMs: this.uploadWaitMs, downloadWaitMs: this.downloadWaitMs, kernelMs: sum(kernelMs), getExtraProfileInfo: function () { return kernelMs.map(function (d, i) { return ({ name: flattenedActiveTimerNames[i], ms: d }); }) .map(function (d) { return d.name + ": " + d.ms; }) .join(', '); }, wallMs: null }; this.uploadWaitMs = 0; this.downloadWaitMs = 0; return [2, res]; } }); }); }; MathBackendWebGL.prototype.memory = function () { return { unreliable: false, numBytesInGPU: this.numBytesInGPU }; }; MathBackendWebGL.prototype.startTimer = function () { if (ENV.get('WEBGL_DISJOINT_QUERY_TIMER_EXTENSION_VERSION') > 0) { return this.gpgpu.beginQuery(); } return { startMs: performance.now(), endMs: null }; }; MathBackendWebGL.prototype.endTimer = function (query) { if (ENV.get('WEBGL_DISJOINT_QUERY_TIMER_EXTENSION_VERSION') > 0) { this.gpgpu.endQuery(); return query; } query.endMs = performance.now(); return query; }; MathBackendWebGL.prototype.getQueryTime = function (query) { return __awaiter(this, void 0, void 0, function () { var timerQuery; return __generator(this, function (_a) { if (ENV.get('WEBGL_DISJOINT_QUERY_TIMER_EXTENSION_VERSION') > 0) { return [2, this.gpgpu.waitForQueryAndGetTime(query)]; } timerQuery = query; return [2, timerQuery.endMs - timerQuery.startMs]; }); }); }; MathBackendWebGL.prototype.disposeData = function (dataId) { if (this.pendingDisposal.has(dataId)) { return; } if (this.pendingRead.has(dataId)) { this.pendingDisposal.add(dataId); return; } if (this.texData.has(dataId)) { var _a = this.texData.get(dataId), texture = _a.texture, texShape = _a.texShape, usage = _a.usage, complexTensors = _a.complexTensors, isPacked = _a.isPacked, slice = _a.slice; if (texture != null) { var key = slice && slice.origDataId || dataId; var refCount = this.dataRefCount.get(key); if (refCount > 1) { this.dataRefCount.set(key, refCount - 1); } else { this.dataRefCount.delete(key); this.releaseTexture(dataId, texture, texShape, usage, isPacked); this.texData.delete(dataId); } } if (complexTensors != null) { complexTensors.real.dispose(); complexTensors.imag.dispose(); } } }; MathBackendWebGL.prototype.getTexture = function (dataId) { this.uploadToGPU(dataId); return this.texData.get(dataId).texture; }; MathBackendWebGL.prototype.getCPUBackend = function () { if (!ENV.get('WEBGL_CPU_FORWARD')) { return null; } if (this.cpuBackend == null) { this.cpuBackend = ENV.findBackend('cpu'); } return this.cpuBackend; }; MathBackendWebGL.prototype.shouldExecuteOnCPU = function (inputs, sizeThreshold) { var _this = this; if (sizeThreshold === void 0) { sizeThreshold = CPU_HANDOFF_SIZE_THRESHOLD; } return this.getCPUBackend() != null && inputs.every(function (input) { return _this.texData.get(input.dataId).texture == null && input.size < sizeThreshold; }); }; MathBackendWebGL.prototype.getGPGPUContext = function () { return this.gpgpu; }; MathBackendWebGL.prototype.getCanvas = function () { return this.canvas; }; MathBackendWebGL.prototype.complex = function (real, imag) { var result = this.makeOutputArray(real.shape, 'complex64'); var resultData = this.texData.get(result.dataId); resultData.complexTensors = { real: ENV.engine.keep(real.clone()), imag: ENV.engine.keep(imag.clone()) }; return result; }; MathBackendWebGL.prototype.real = function (input) { var resultData = this.texData.get(input.dataId); return resultData.complexTensors.real.clone(); }; MathBackendWebGL.prototype.imag = function (input) { var resultData = this.texData.get(input.dataId); return resultData.complexTensors.imag.clone(); }; MathBackendWebGL.prototype.slice = function (x, begin, size) { if (this.shouldExecuteOnCPU([x])) { return this.cpuBackend.slice(x, begin, size); } var isPacked = this.texData.get(x.dataId).isPacked; var isContinous = isSliceContinous(x.shape, begin, size); if (isPacked || !isContinous) { var program = ENV.get('WEBGL_PACK_ARRAY_OPERATIONS') ? new SlicePackedProgram(size) : new SliceProgram(size); var customSetup = program.getCustomSetupFunc(begin); return this.compileAndRun(program, [x], null, customSetup); } this.uploadToGPU(x.dataId); return this.shallowSlice(x, begin, size); }; MathBackendWebGL.prototype.shallowSlice = function (x, begin, size) { var xTexData = this.texData.get(x.dataId); var t = Tensor.make(size, {}, xTexData.dtype); var newTexData = this.texData.get(t.dataId); Object.assign(newTexData, xTexData); newTexData.shape = size; var flatOffset = computeFlatOffset(begin, x.strides); if (xTexData.slice) { flatOffset += xTexData.slice.flatOffset; } newTexData.slice = { flatOffset: flatOffset, origDataId: xTexData.slice && xTexData.slice.origDataId || x.dataId }; var refCount = this.dataRefCount.get(newTexData.slice.origDataId) || 1; this.dataRefCount.set(newTexData.slice.origDataId, refCount + 1); return t; }; MathBackendWebGL.prototype.stridedSlice = function (x, begin, end, strides, beginMask, endMask, ellipsisMask, newAxisMask, shrinkAxisMask) { if (this.shouldExecuteOnCPU([x])) { return this.cpuBackend.stridedSlice(x, begin, end, strides, beginMask, endMask, ellipsisMask, newAxisMask, shrinkAxisMask); } var _a = getStridedSlicedInfo(x.shape, begin, end, strides, beginMask, endMask, ellipsisMask, newAxisMask, shrinkAxisMask), beginIndex = _a[0], size = _a[1], shrinkAxis = _a[2]; var shape = size.filter(function (v, index) { return shrinkAxis.indexOf(index) === -1; }); if (shape.some(function (axis) { return axis === 0; })) { return tensor([], shape); } var program = new StridedSliceProgram(beginIndex, strides, size, shrinkAxis); return this.compileAndRun(program, [x]); }; MathBackendWebGL.prototype.reverse = function (x, axis) { var program = new ReverseProgram(x.shape, axis); return this.compileAndRun(program, [x]); }; MathBackendWebGL.prototype.concat = function (tensors, axis) { if (this.shouldExecuteOnCPU(tensors)) { return this.cpuBackend.concat(tensors, axis); } if (tensors.length === 1) { return tensors[0]; } if (tensors.length > ENV.get('WEBGL_MAX_TEXTURES_IN_SHADER')) { var midIndex = Math.floor(tensors.length / 2); var leftSide = this.concat(tensors.slice(0, midIndex), axis); var rightSide = this.concat(tensors.slice(midIndex), axis); return this.concat([leftSide, rightSide], axis); } var outShape = computeOutShape(tensors.map(function (t) { return t.shape; }), axis); var tensors2D = tensors.map(function (t) { return t.as2D(-1, sizeFromShape(t.shape.slice(axis))); }); var program = new ConcatProgram(tensors2D.map(function (t) { return t.shape; })); var res = this.compileAndRun(program, tensors2D); return res.reshape(outShape); }; MathBackendWebGL.prototype.neg = function (x) { var program = new UnaryOpProgram(x.shape, NEG); return this.compileAndRun(program, [x]); }; MathBackendWebGL.prototype.batchMatMul = function (a, b, transposeA, transposeB) { var outerShapeA = transposeA ? a.shape[2] : a.shape[1]; var outerShapeB = transposeB ? b.shape[1] : b.shape[2]; var sharedDim = transposeA ? a.shape[1] : a.shape[2]; var _a = a.shape, batch = _a[0]; if ((outerShapeA === 1 || outerShapeB === 1) && sharedDim > MATMUL_SHARED_DIM_THRESHOLD) { if (transposeA) { a = a.transpose([0, 2, 1]); } if (transposeB) { b = b.transpose([0, 2, 1]); } var a3D = outerShapeB === 1 ? a : a.as3D(batch, sharedDim, 1); var axis = outerShapeB === 1 ? 2 : 1; var b3D = outerShapeB === 1 ? b.as3D(batch, 1, sharedDim) : b; return this.multiply(a3D, b3D).sum(axis, true); } var dtype = upcastType(a.dtype, b.dtype); if (batch === 1) { var aSqueezed = a.as2D(a.shape[1], a.shape[2]); var bSqueezed = b.as2D(b.shape[1], b.shape[2]); var program = new MatMulPackedProgram(aSqueezed.shape, bSqueezed.shape, [outerShapeA, outerShapeB], transposeA, transposeB); var output = this.makePackedTensor(program.outputShape, dtype); var result = this.compileAndRun(program, [aSqueezed, bSqueezed], output); return result.reshape([1, result.shape[0], result.shape[1]]); } else { var program = new MatMulProgram(a.shape, b.shape, transposeA, transposeB); var output = this.makeOutputArray(program.outputShape, dtype); return this.compileAndRun(program, [a, b], output); } }; MathBackendWebGL.prototype.fusedBatchMatMul = function (a, b, transposeA, transposeB, bias, activation) { var outerShapeA = transposeA ? a.shape[2] : a.shape[1]; var outerShapeB = transposeB ? b.shape[1] : b.shape[2]; var _a = a.shape, batch = _a[0]; var dtype = upcastType(a.dtype, b.dtype); if (batch === 1) { var aSqueezed = a.as2D(a.shape[1], a.shape[2]); var bSqueezed = b.as2D(b.shape[1], b.shape[2]); var program = new MatMulPackedProgram(aSqueezed.shape, bSqueezed.shape, [outerShapeA, outerShapeB], transposeA, transposeB, !!bias, activation ? mapActivationToShaderProgram(activation, true) : null); var output = this.makePackedTensor(program.outputShape, dtype); var inputs = [aSqueezed, bSqueezed]; if (bias) { inputs.push(bias); } var result = this.compileAndRun(program, inputs, output); return result.reshape([1, result.shape[0], result.shape[1]]); } else { var program = new MatMulProgram(a.shape, b.shape, transposeA, transposeB, !!bias, activation ? mapActivationToShaderProgram(activation) : null); var inputs = [a, b]; if (bias) { inputs.push(bias); } var output = this.makeOutputArray(program.outputShape, dtype); return this.compileAndRun(program, inputs, output); } }; MathBackendWebGL.prototype.multiply = function (a, b) { if (a.dtype === 'complex64') { var aData = this.texData.get(a.dataId); var bData = this.texData.get(b.dataId); var realProgram = new BinaryOpComplexProgram(COMPLEX_MULTIPLY.REAL, a.shape, b.shape); var imagProgram = new BinaryOpComplexProgram(COMPLEX_MULTIPLY.IMAG, a.shape, b.shape); var inputs = [ this.makeComplexComponentTensorHandle(a, aData.complexTensors.real), this.makeComplexComponentTensorHandle(a, aData.complexTensors.imag), this.makeComplexComponentTensorHandle(b, bData.complexTensors.real), this.makeComplexComponentTensorHandle(b, bData.complexTensors.imag) ]; var real = this.compileAndRun(realProgram, inputs); var imag = this.compileAndRun(imagProgram, inputs); var complex = this.complex(real, imag); real.dispose(); imag.dispose(); return complex; } if (this.shouldExecuteOnCPU([a, b])) { return this.cpuBackend.multiply(a, b); } if (this.usePackedBinaryOp(a, b)) { return this.packedBinaryOp(a, b, MUL, a.dtype); } var program = new BinaryOpProgram(MUL, a.shape, b.shape); var output = this.makeOutputArray(program.outputShape, a.dtype); return this.compileAndRun(program, [a, b], output); }; MathBackendWebGL.prototype.batchNormalization = function (x, mean, variance, varianceEpsilon, scale, offset) { var inputs = [x, mean, variance]; var offsetShape = null; if (offset != null) { offsetShape = offset.shape; inputs.push(offset); } var scaleShape = null; if (scale != null) { scaleShape = scale.shape; inputs.push(scale); } if (ENV.get('WEBGL_PACK_BATCHNORMALIZATION')) { var batchNormPackedProgram = new BatchNormPackedProgram(x.shape, mean.shape, variance.shape, offsetShape, scaleShape, varianceEpsilon); return this.compileAndRun(batchNormPackedProgram, inputs); } var batchNormProgram = new BatchNormProgram(x.shape, mean.shape, variance.shape, offsetShape, scaleShape, varianceEpsilon); return this.compileAndRun(batchNormProgram, inputs); }; MathBackendWebGL.prototype.localResponseNormalization4D = function (x, radius, bias, alpha, beta) { var program = new LRNProgram(x.shape, radius, bias, alpha, beta); return this.compileAndRun(program, [x]); }; MathBackendWebGL.prototype.LRNGrad = function (dy, inputImage, outputImage, depthRadius, bias, alpha, beta) { var program = new LRNGradProgram(inputImage.shape, depthRadius, bias, alpha, beta); return this.compileAndRun(program, [inputImage, outputImage, dy]); }; MathBackendWebGL.prototype.tile = function (x, reps) { var program = new TileProgram(x.shape, reps); return this.compileAndRun(program, [x]); }; MathBackendWebGL.prototype.pad = function (x, paddings, constantValue) { var program = ENV.get('WEBGL_PACK_ARRAY_OPERATIONS') ? new PadPackedProgram(x.shape, paddings, constantValue) : new PadProgram(x.shape, paddings, constantValue); return this.compileAndRun(program, [x]); }; MathBackendWebGL.prototype.transpose = function (x, perm) { var program = ENV.get('WEBGL_PACK_ARRAY_OPERATIONS') ? new TransposePackedProgram(x.shape, perm) : new TransposeProgram(x.shape, perm); return this.compileAndRun(program, [x]); }; MathBackendWebGL.prototype.gather = function (x, indices, axis) { var program = new GatherProgram(x.shape, indices.size, axis); return this.compileAndRun(program, [x, indices]); }; MathBackendWebGL.prototype.batchToSpaceND = function (x, blockShape, crops) { assert(x.rank <= 4, 'batchToSpaceND for rank > 4 with a WebGL backend not implemented yet'); var prod = blockShape.reduce(function (a, b) { return a * b; }); var reshaped = getReshaped(x.shape, blockShape, prod); var permuted = getPermuted(reshaped.length, blockShape.length); var reshapedPermuted = getReshapedPermuted(x.shape, blockShape, prod); var sliceBeginCoords = getSliceBeginCoords(crops, blockShape.length); var sliceSize = getSliceSize(reshapedPermuted, crops, blockShape.length); return x.reshape(reshaped) .transpose(permuted) .reshape(reshapedPermuted) .slice(sliceBeginCoords, sliceSize); }; MathBackendWebGL.prototype.spaceToBatchND = function (x, blockShape, paddings) { assert(x.rank <= 4, 'spaceToBatchND for rank > 4 with a WebGL backend not implemented yet'); var prod = blockShape.reduce(function (a, b) { return a * b; }); var completePaddings = [[0, 0]]; completePaddings.push.apply(completePaddings, paddings); for (var i = 1 + blockShape.length; i < x.shape.length; ++i) { completePaddings.push([0, 0]); } var paddedX = x.pad(completePaddings); var reshapedPaddedShape = getReshaped(paddedX.shape, blockShape, prod, false); var permutedReshapedPaddedPermutation = getPermuted(reshapedPaddedShape.length, blockShape.length, false); var flattenShape = getReshapedPermuted(paddedX.shape, blockShape, prod, false); return paddedX.reshape(reshapedPaddedShape) .transpose(permutedReshapedPaddedPermutation) .reshape(flattenShape); }; MathBackendWebGL.prototype.reduce = function (x, reduceType, dtype) { var batchSize = x.shape[0]; var inSize = x.shape[1]; var windowSize = computeOptimalWindowSize(inSize); var reduceInfo = { windowSize: windowSize, inSize: inSize, batchSize: batchSize }; var program = new ReduceProgram(reduceInfo, reduceType); var _a = program.outputShape, rows = _a[0], cols = _a[1]; var output = this.makeOutputArray([rows, cols], dtype); this.compileAndRun(program, [x], output); if (output.shape[1] === 1) { return output; } return this.reduce(output, reduceType, dtype); }; MathBackendWebGL.prototype.argReduce = function (x, reduceType, bestIndicesA) { if (bestIndicesA === void 0) { bestIndicesA = null; } var batchSize = x.shape[0]; var inSize = x.shape[1]; if (bestIndicesA != null) { batchSize = bestIndicesA.shape[0]; inSize = bestIndicesA.shape[1]; } var windowSize = computeOptimalWindowSize(inSize); var reduceInfo = { windowSize: windowSize, inSize: inSize, batchSize: batchSize }; var program = new ArgMinMaxProgram(reduceInfo, reduceType, bestIndicesA == null); var _a = program.outputShape, rows = _a[0], cols = _a[1]; var output = this.makeOutputArray([rows, cols], 'int32'); var inputs = [x]; if (bestIndicesA != null) { inputs.push(bestIndicesA); } this.compileAndRun(program, inputs, output); if (output.shape[1] === 1) { return output; } return this.argReduce(x, reduceType, output); }; MathBackendWebGL.prototype.sum = function (x, axes) { assertAxesAreInnerMostDims('sum', axes, x.rank); var _a = computeOutAndReduceShapes(x.shape, axes), outShape = _a[0], reduceShape = _a[1]; var inSize = sizeFromShape(reduceShape); var a2D = x.as2D(-1, inSize); var outputDType = sumOutType(x.dtype); return this.reduce(a2D, 'sum', outputDType).reshape(outShape); }; MathBackendWebGL.prototype.prod = function (x, axes) { var _a = computeOutAndReduceShapes(x.shape, axes), outShape = _a[0], reduceShape = _a[1]; var inSize = sizeFromShape(reduceShape); var a2D = x.as2D(-1, inSize); var outputDType = sumOutType(x.dtype); return this.reduce(a2D, 'prod', outputDType).reshape(outShape); }; MathBackendWebGL.prototype.unsortedSegmentSum = function (x, segmentIds, numSegments) { var axis = 0; var permutation = getAxesPermutation([axis], x.rank); var permutedX = x; if (permutation != null) { permutedX = x.transpose(permutation); axis = getInnerMostAxes(1, x.rank)[0]; } var outShape = computeOutShape$1(permutedX.shape, axis, numSegments); var inSize = sizeFromShape([permutedX.shape[axis]]); var a2D = permutedX.as2D(-1, inSize); var outputDType = sumOutType(x.dtype); var result = this.segOpCompute(a2D, 'unsortedSegmentSum', segmentIds, outputDType, numSegments) .reshape(outShape); if (permutation != null) { result = result.transpose(getUndoAxesPermutation(permutation)); } return result; }; MathBackendWebGL.prototype.segOpCompute = function (x, segOpType, segmentIds, dtype, numSegments) { var batchSize = x.shape[0]; var inSize = x.shape[1]; var windowSize = segOpComputeOptimalWindowSize(inSize, numSegments); var segOpInfo = { windowSize: windowSize, inSize: inSize, batchSize: batchSize, numSegments: numSegments }; var program = new SegmentOpProgram(segOpInfo, segOpType); var _a = program.outputShape, rows = _a[0], cols = _a[1]; var output = this.makeOutputArray([rows, cols], dtype); this.compileAndRun(program, [x, segmentIds], output); if (output.shape[1] === numSegments) { return output; } segmentIds = range(0, numSegments).tile([inSize / windowSize]); return this.segOpCompute(output, segOpType, segmentIds, dtype, numSegments); }; MathBackendWebGL.prototype.argMin = function (x, axis) { var axes = [axis]; assertAxesAreInnerMostDims('argMin', axes, x.rank); var _a = computeOutAndReduceShapes(x.shape, axes), outShape = _a[0], reduceShape = _a[1]; var inSize = sizeFromShape(reduceShape); var a2D = x.as2D(-1, inSize); return this.argReduce(a2D, 'min').reshape(outShape); }; MathBackendWebGL.prototype.argMax = function (x, axis) { var axes = [axis]; assertAxesAreInnerMostDims('argMax', axes, x.rank); var _a = computeOutAndReduceShapes(x.shape, axes), outShape = _a[0], reduceShape = _a[1]; var inSize = sizeFromShape(reduceShape); var a2D = x.as2D(-1, inSize); return this.argReduce(a2D, 'max').reshape(outShape); }; MathBackendWebGL.prototype.cumsum = function (x, axis, exclusive, reverse) { if (axis !== x.rank - 1) { throw new Error("WebGL cumsum shader expects an inner-most axis=" + (x.rank - 1) + " " + ("but got axis=" + axis)); } var program = new CumSumProgram(x.shape, exclusive, reverse); return this.compileAndRun(program, [x]); }; MathBackendWebGL.prototype.equal = function (a, b) { var program = new BinaryOpProgram(EQUAL, a.shape, b.shape); var output = this.makeOutputArray(program.outputShape, 'bool'); return this.compileAndRun(program, [a, b], output); }; MathBackendWebGL.prototype.notEqual = function (a, b) { var program = new BinaryOpProgram(NOT_EQUAL, a.shape, b.shape); var output = this.makeOutputArray(program.outputShape, 'bool'); return this.compileAndRun(program, [a, b], output); }; MathBackendWebGL.prototype.less = function (a, b) { if (this.shouldExecuteOnCPU([a, b])) { return this.cpuBackend.less(a, b); } var program = new BinaryOpProgram(LESS, a.shape, b.shape); var output = this.makeOutputArray(program.outputShape, 'bool'); return this.compileAndRun(program, [a, b], output); }; MathBackendWebGL.prototype.lessEqual = function (a, b) { var program = new BinaryOpProgram(LESS_EQUAL, a.shape, b.shape); var output = this.makeOutputArray(program.outputShape, 'bool'); return this.compileAndRun(program, [a, b], output); }; MathBackendWebGL.prototype.greater = function (a, b) { if (this.shouldExecuteOnCPU([a, b])) { return this.cpuBackend.greater(a, b); } var program = new BinaryOpProgram(GREATER, a.shape, b.shape); var output = this.makeOutputArray(program.outputShape, 'bool'); return this.compileAndRun(program, [a, b], output); }; MathBackendWebGL.prototype.greaterEqual = function (a, b) { var program = new BinaryOpProgram(GREATER_EQUAL, a.shape, b.shape); var output = this.makeOutputArray(program.outputShape, 'bool'); return this.compileAndRun(program, [a, b], output); }; MathBackendWebGL.prototype.logicalNot = function (x) { var program = new UnaryOpProgram(x.shape, LOGICAL_NOT); return this.compileAndRun(program, [x]); }; MathBackendWebGL.prototype.logicalAnd = function (a, b) { var program = new BinaryOpProgram(LOGICAL_AND, a.shape, b.shape); var output = this.makeOutputArray(program.outputShape, 'bool'); return this.compileAndRun(program, [a, b], output); }; MathBackendWebGL.prototype.logicalOr = function (a, b) { var program = new BinaryOpProgram(LOGICAL_OR, a.shape, b.shape); var output = this.makeOutputArray(program.outputShape, 'bool'); return this.compileAndRun(program, [a, b], output); }; MathBackendWebGL.prototype.select = function (condition, a, b) { var program = new SelectProgram(condition.rank, a.shape, a.rank); var output = this.makeOutputArray(program.outputShape, upcastType(a.dtype, b.dtype)); return this.compileAndRun(program, [condition, a, b], output); }; MathBackendWebGL.prototype.where = function (condition) { warn('tf.where() in webgl locks the UI thread. ' + 'Call tf.whereAsync() instead'); var condVals = condition.dataSync(); return whereImpl(condition.shape, condVals); }; MathBackendWebGL.prototype.topk = function (x, k, sorted) { var xVals = x.dataSync(); return topkImpl(xVals, x.shape, x.dtype, k, sorted); }; MathBackendWebGL.prototype.min = function (x, axes) { assertAxesAreInnerMostDims('min', axes, x.rank); var _a = computeOutAndReduceShapes(x.shape, axes), outShape = _a[0], reduceShape = _a[1]; var inSize = sizeFromShape(reduceShape); var a2D = x.as2D(-1, inSize); return this.reduce(a2D, 'min', a2D.dtype).reshape(outShape); }; MathBackendWebGL.prototype.minimum = function (a, b) { if (this.shouldExecuteOnCPU([a, b])) { return this.cpuBackend.minimum(a, b); } var program = new BinaryOpProgram(MIN, a.shape, b.shape); return this.compileAndRun(program, [a, b]); }; MathBackendWebGL.prototype.mod = function (a, b) { var program = new BinaryOpProgram(MOD, a.shape, b.shape); var customSetup = program.getCustomSetupFunc(); return this.compileAndRun(program, [a, b], null, customSetup); }; MathBackendWebGL.prototype.max = function (x, axes) { assertAxesAreInnerMostDims('max', axes, x.rank); var _a = computeOutAndReduceShapes(x.shape, axes), outShape = _a[0], reduceShape = _a[1]; var inSize = sizeFromShape(reduceShape); var a2D = x.as2D(-1, inSize); return this.reduce(a2D, 'max', a2D.dtype).reshape(outShape); }; MathBackendWebGL.prototype.maximum = function (a, b) { if (this.shouldExecuteOnCPU([a, b])) { return this.cpuBackend.maximum(a, b); } var program = new BinaryOpProgram(MAX, a.shape, b.shape); return this.compileAndRun(program, [a, b]); }; MathBackendWebGL.prototype.all = function (x, axes) { assertAxesAreInnerMostDims('all', axes, x.rank); var _a = computeOutAndReduceShapes(x.shape, axes), outShape = _a[0], reduceShape = _a[1]; var inSize = sizeFromShape(reduceShape); var a2D = x.as2D(-1, inSize); return this.reduce(a2D, 'all', a2D.dtype).reshape(outShape); }; MathBackendWebGL.prototype.any = function (x, axes) { assertAxesAreInnerMostDims('any', axes, x.rank); var _a = computeOutAndReduceShapes(x.shape, axes), outShape = _a[0], reduceShape = _a[1]; var inSize = sizeFromShape(reduceShape); var a2D = x.as2D(-1, inSize); return this.reduce(a2D, 'any', a2D.dtype).reshape(outShape); }; MathBackendWebGL.prototype.squaredDifference = function (a, b) { var program = new BinaryOpProgram(SQUARED_DIFFERENCE, a.shape, b.shape); return this.compileAndRun(program, [a, b]); }; MathBackendWebGL.prototype.realDivide = function (a, b) { var op = DIV; var outputDtype = 'float32'; if (this.usePackedBinaryOp(a, b)) { return this.packedBinaryOp(a, b, PACKED_DIV, outputDtype); } var program = new BinaryOpProgram(op, a.shape, b.shape); var output = this.makeOutputArray(program.outputShape, outputDtype); return this.compileAndRun(program, [a, b], output); }; MathBackendWebGL.prototype.floorDiv = function (a, b) { var op = INT_DIV; var outputDtype = 'int32'; if (this.usePackedBinaryOp(a, b)) { return this.packedBinaryOp(a, b, PACKED_INT_DIV, outputDtype); } var program = new BinaryOpProgram(op, a.shape, b.shape); var output = this.makeOutputArray(program.outputShape, outputDtype); return this.compileAndRun(program, [a, b], output); }; MathBackendWebGL.prototype.add = function (a, b) { if (a.dtype === 'complex64' && b.dtype === 'complex64') { return this.complexSeparableBinaryOp(a, b, ADD); } var dtype = upcastType(a.dtype, b.dtype); if (this.usePackedBinaryOp(a, b)) { return this.packedBinaryOp(a, b, ADD, dtype); } var program = new BinaryOpProgram(ADD, a.shape, b.shape); var output = this.makeOutputArray(program.outputShape, dtype); return this.compileAndRun(program, [a, b], output); }; MathBackendWebGL.prototype.usePackedBinaryOp = function (a, b) { if (!ENV.get('WEBGL_PACK_BINARY_OPERATIONS')) { return false; } var outputShape = assertAndGetBroadcastShape(a.shape, b.shape); if (outputShape.length > 4) { return false; } if (getBroadcastDims(a.shape, outputShape).length || getBroadcastDims(b.shape, outputShape).length) { return false; } return true; }; MathBackendWebGL.prototype.packedBinaryOp = function (a, b, op, dtype) { var program = new BinaryOpPackedProgram(op, a.shape, b.shape); var output = this.makePackedTensor(program.outputShape, dtype); return this.compileAndRun(program, [a, b], output); }; MathBackendWebGL.prototype.complexSeparableBinaryOp = function (a, b, op) { var _this = this; var aData = this.texData.get(a.dataId); var bData = this.texData.get(b.dataId); var _a = [ [aData.complexTensors.real, bData.complexTensors.real], [aData.complexTensors.imag, bData.complexTensors.imag] ].map(function (complexParts) { var aPart = complexParts[0], bPart = complexParts[1]; var program = new BinaryOpProgram(op, a.shape, b.shape); var output = _this.makeOutputArray(program.outputShape, upcastType(aPart.dtype, bPart.dtype)); var aHandle = _this.makeComplexComponentTensorHandle(a, aPart); var bHandle = _this.makeComplexComponentTensorHandle(b, bPart); return _this.compileAndRun(program, [aHandle, bHandle], output); }), real = _a[0], imag = _a[1]; var complex = this.complex(real, imag); real.dispose(); imag.dispose(); return complex; }; MathBackendWebGL.prototype.makeComplexComponentTensorHandle = function (complexTensor, complexPart) { return { dataId: complexPart.dataId, dtype: complexPart.dtype, shape: complexTensor.shape }; }; MathBackendWebGL.prototype.addN = function (tensors) { var res = tensors[0]; for (var i = 1; i < tensors.length; i++) { res = this.add(res, tensors[i]); } return res; }; MathBackendWebGL.prototype.subtract = function (a, b) { if (a.dtype === 'complex64' && b.dtype === 'complex64') { return this.complexSeparableBinaryOp(a, b, SUB); } if (this.shouldExecuteOnCPU([a, b])) { return this.cpuBackend.subtract(a, b); } var dtype = upcastType(a.dtype, b.dtype); if (this.usePackedBinaryOp(a, b)) { return this.packedBinaryOp(a, b, SUB, a.dtype); } var program = new BinaryOpProgram(SUB, a.shape, b.shape); var output = this.makeOutputArray(program.outputShape, dtype); return this.compileAndRun(program, [a, b], output); }; MathBackendWebGL.prototype.pow = function (a, b) { var usePackedOp = this.usePackedBinaryOp(a, b); var program = usePackedOp ? new BinaryOpPackedProgram(PACKED_POW, a.shape, b.shape) : new BinaryOpProgram(POW, a.shape, b.shape); var dtype = upcastType(a.dtype, b.dtype); var output = usePackedOp ? this.makePackedTensor(program.outputShape, dtype) : this.makeOutputArray(program.outputShape, dtype); var customSetup = program.getCustomSetupFunc(); return this.compileAndRun(program, [a, b], output, customSetup); }; MathBackendWebGL.prototype.ceil = function (x) { var program = new UnaryOpProgram(x.shape, CEIL); return this.compileAndRun(program, [x]); }; MathBackendWebGL.prototype.floor = function (x) { var program = new UnaryOpProgram(x.shape, FLOOR); return this.compileAndRun(program, [x]); }; MathBackendWebGL.prototype.sign = function (x) { var program = new UnaryOpProgram(x.shape, SIGN); return this.compileAndRun(program, [x]); }; MathBackendWebGL.prototype.round = function (x) { var program = new UnaryOpProgram(x.shape, ROUND); return this.compileAndRun(program, [x]); }; MathBackendWebGL.prototype.exp = function (x) { var program; if (ENV.get('WEBGL_PACK')) { program = new UnaryOpPackedProgram(x.shape, EXP); } else { program = new UnaryOpProgram(x.shape, EXP); } return this.compileAndRun(program, [x]); }; MathBackendWebGL.prototype.expm1 = function (x) { var program = new UnaryOpProgram(x.shape, EXPM1); return this.compileAndRun(program, [x]); }; MathBackendWebGL.prototype.log = function (x) { var program; if (ENV.get('WEBGL_PACK')) { program = new UnaryOpPackedProgram(x.shape, LOG$1); } else { program = new UnaryOpProgram(x.shape, LOG); } var customSetup = program.getCustomSetupFunc(); return this.compileAndRun(program, [x], null, customSetup); }; MathBackendWebGL.prototype.log1p = function (x) { var program = new UnaryOpProgram(x.shape, LOG1P); return this.compileAndRun(program, [x]); }; MathBackendWebGL.prototype.sqrt = function (x) { var program = new UnaryOpProgram(x.shape, SQRT); return this.compileAndRun(program, [x]); }; MathBackendWebGL.prototype.rsqrt = function (x) { var program = new UnaryOpProgram(x.shape, RSQRT); return this.compileAndRun(program, [x]); }; MathBackendWebGL.prototype.square = function (x) { var program = new UnaryOpProgram(x.shape, SQUARE); return this.compileAndRun(program, [x]); }; MathBackendWebGL.prototype.reciprocal = function (x) { var program = new UnaryOpProgram(x.shape, RECIPROCAL); return this.compileAndRun(program, [x]); }; MathBackendWebGL.prototype.relu = function (x) { var program; if (ENV.get('WEBGL_PACK')) { program = new UnaryOpPackedProgram(x.shape, RELU$1); } else { program = new UnaryOpProgram(x.shape, RELU); } return this.compileAndRun(program, [x]); }; MathBackendWebGL.prototype.prelu = function (x, alpha) { var program = new BinaryOpProgram(PRELU, x.shape, alpha.shape); return this.compileAndRun(program, [x, alpha]); }; MathBackendWebGL.prototype.elu = function (x) { var program = new UnaryOpProgram(x.shape, ELU); return this.compileAndRun(program, [x]); }; MathBackendWebGL.prototype.eluDer = function (dy, y) { var program = new BinaryOpProgram(ELU_DER, dy.shape, y.shape); return this.compileAndRun(program, [dy, y]); }; MathBackendWebGL.prototype.selu = function (x) { var program = new UnaryOpProgram(x.shape, SELU); return this.compileAndRun(program, [x]); }; MathBackendWebGL.prototype.int = function (x) { var program = new UnaryOpProgram(x.shape, TO_INT); var output = this.makeOutputArray(program.outputShape, 'int32'); return this.compileAndRun(program, [x], output); }; MathBackendWebGL.prototype.clip = function (x, min, max) { var program; if (ENV.get('WEBGL_PACK_CLIP')) { program = new ClipPackedProgram(x.shape); } else { program = new ClipProgram(x.shape); } var customSetup = program.getCustomSetupFunc(min, max); return this.compileAndRun(program, [x], null, customSetup); }; MathBackendWebGL.prototype.abs = function (x) { var program = new UnaryOpProgram(x.shape, ABS); return this.compileAndRun(program, [x]); }; MathBackendWebGL.prototype.complexAbs = function (x) { var xData = this.texData.get(x.dataId); var program = new ComplexAbsProgram(x.shape); var inputs = [ this.makeComplexComponentTensorHandle(x, xData.complexTensors.real), this.makeComplexComponentTensorHandle(x, xData.complexTensors.imag), ]; return this.compileAndRun(program, inputs); }; MathBackendWebGL.prototype.sigmoid = function (x) { var program = new UnaryOpProgram(x.shape, SIGMOID); return this.compileAndRun(program, [x]); }; MathBackendWebGL.prototype.softplus = function (x) { var program = new UnaryOpProgram(x.shape, SOFTPLUS); return this.compileAndRun(program, [x]); }; MathBackendWebGL.prototype.sin = function (x) { var program = new UnaryOpProgram(x.shape, SIN); return this.compileAndRun(program, [x]); }; MathBackendWebGL.prototype.cos = function (x) { var program = new UnaryOpProgram(x.shape, COS); return this.compileAndRun(program, [x]); }; MathBackendWebGL.prototype.tan = function (x) { var program = new UnaryOpProgram(x.shape, TAN); return this.compileAndRun(program, [x]); }; MathBackendWebGL.prototype.asin = function (x) { var program = new UnaryOpProgram(x.shape, ASIN); return this.compileAndRun(program, [x]); }; MathBackendWebGL.prototype.acos = function (x) { var program = new UnaryOpProgram(x.shape, ACOS); return this.compileAndRun(program, [x]); }; MathBackendWebGL.prototype.atan = function (x) { var program = new UnaryOpProgram(x.shape, ATAN); return this.compileAndRun(program, [x]); }; MathBackendWebGL.prototype.atan2 = function (a, b) { var program = new BinaryOpProgram(ATAN2, a.shape, b.shape); return this.compileAndRun(program, [a, b]); }; MathBackendWebGL.prototype.sinh = function (x) { var program = new UnaryOpProgram(x.shape, SINH); return this.compileAndRun(program, [x]); }; MathBackendWebGL.prototype.cosh = function (x) { var program = new UnaryOpProgram(x.shape, COSH); return this.compileAndRun(program, [x]); }; MathBackendWebGL.prototype.tanh = function (x) { var program = new UnaryOpProgram(x.shape, TANH); return this.compileAndRun(program, [x]); }; MathBackendWebGL.prototype.asinh = function (x) { var program = new UnaryOpProgram(x.shape, ASINH); return this.compileAndRun(program, [x]); }; MathBackendWebGL.prototype.acosh = function (x) { var program = new UnaryOpProgram(x.shape, ACOSH); var customSetup = program.getCustomSetupFunc(); return this.compileAndRun(program, [x], null, customSetup); }; MathBackendWebGL.prototype.atanh = function (x) { var program = new UnaryOpProgram(x.shape, ATANH); var customSetup = program.getCustomSetupFunc(); return this.compileAndRun(program, [x], null, customSetup); }; MathBackendWebGL.prototype.erf = function (x) { var program = new UnaryOpProgram(x.shape, ERF); return this.compileAndRun(program, [x]); }; MathBackendWebGL.prototype.step = function (x, alpha) { var program = new UnaryOpProgram(x.shape, STEP(alpha)); return this.compileAndRun(program, [x]); }; MathBackendWebGL.prototype.conv2dByMatMul = function (x, filter, convInfo) { if (!ENV.get('WEBGL_LAZILY_UNPACK') || !ENV.get('WEBGL_PACK_BINARY_OPERATIONS')) { var $x_1 = this.reshape(x, [1, x.shape[0] * x.shape[1] * x.shape[2], convInfo.inChannels]); var $filter_1 = this.reshape(filter, [1, convInfo.inChannels, convInfo.outChannels]); return this.batchMatMul($x_1, $filter_1, false, false).reshape(convInfo.outShape); } var xshape = x.shape; var xTexData = this.texData.get(x.dataId); var packedXRowPad = xshape[2] % 2 === 1 && xTexData.isPacked ? 1 : 0; var $x = Tensor.make([1, xshape[0] * xshape[1] * (xshape[2] + packedXRowPad), convInfo.inChannels], { dataId: x.dataId }, x.dtype); if (packedXRowPad) { xTexData.shape[xTexData.shape.length - 2]++; assert(isReshapeFree(xTexData.shape, $x.shape), "packed reshape from " + xTexData.shape + " to " + $x.shape + " isn't free"); } var $filter = this.reshape(filter, [1, convInfo.inChannels, convInfo.outChannels]); var xf = this.batchMatMul($x, $filter, false, false); var xfTexData = this.texData.get(xf.dataId); assert(xfTexData.isPacked, 'expected packed result of batchMatMul'); if (packedXRowPad) { xTexData.shape[xTexData.shape.length - 2]--; xfTexData.shape = convInfo.outShape; return Tensor.make(convInfo.outShape, { dataId: xf.dataId }, xf.dtype); } return this.reshape(xf, convInfo.outShape); }; MathBackendWebGL.prototype.conv2dWithIm2Row = function (x, filter, convInfo) { var filterWidth = convInfo.filterWidth, filterHeight = convInfo.filterHeight, inChannels = convInfo.inChannels, outWidth = convInfo.outWidth, outHeight = convInfo.outHeight; var sharedDim = filterWidth * filterHeight * inChannels; var numCols = outHeight * outWidth; var x2ColShape = [sharedDim, numCols]; var xSqueezed = x.squeeze([0]); var w2Row = filter.reshape([sharedDim, -1]); var im2ColProgram = new Im2ColProgram(x2ColShape, xSqueezed.shape, convInfo); var im2Col = this.compileAndRun(im2ColProgram, [xSqueezed]); var matmulProgram = new MatMulPackedProgram(im2Col.shape, w2Row.shape, [numCols, convInfo.outChannels], true, false); var product = this.compileAndRun(matmulProgram, [im2Col, w2Row]); return product.reshape([1, outHeight, outWidth, convInfo.outChannels]); }; MathBackendWebGL.prototype.conv2d = function (x, filter, convInfo) { if (convInfo.filterHeight === 1 && convInfo.filterWidth === 1 && convInfo.dilationHeight === 1 && convInfo.dilationWidth === 1 && convInfo.strideHeight === 1 && convInfo.strideWidth === 1 && (convInfo.padInfo.type === 'SAME' || convInfo.padInfo.type === 'VALID')) { return this.conv2dByMatMul(x, filter, convInfo); } if (ENV.get('WEBGL_CONV_IM2COL') && x.shape[0] === 1) { return this.conv2dWithIm2Row(x, filter, convInfo); } var program = new Conv2DProgram(convInfo); return this.compileAndRun(program, [x, filter]); }; MathBackendWebGL.prototype.conv2dDerInput = function (dy, filter, convInfo) { var program = new Conv2DDerInputProgram(convInfo); return this.compileAndRun(program, [dy, filter]); }; MathBackendWebGL.prototype.conv2dDerFilter = function (x, dy, convInfo) { var program = new Conv2DDerFilterProgram(convInfo); return this.compileAndRun(program, [x, dy]); }; MathBackendWebGL.prototype.depthwiseConv2D = function (x, filter, convInfo) { var program; if (ENV.get('WEBGL_PACK_DEPTHWISECONV') && convInfo.strideWidth <= 2 && convInfo.outChannels / convInfo.inChannels === 1) { program = new DepthwiseConvPacked2DProgram(convInfo); return this.compileAndRun(program, [x, filter], this.makePackedTensor(convInfo.outShape, x.dtype)); } program = new DepthwiseConv2DProgram(convInfo); return this.compileAndRun(program, [x, filter]); }; MathBackendWebGL.prototype.depthwiseConv2DDerInput = function (dy, filter, convInfo) { var program = new DepthwiseConv2DDerInputProgram(convInfo); return this.compileAndRun(program, [dy, filter]); }; MathBackendWebGL.prototype.depthwiseConv2DDerFilter = function (x, dy, convInfo) { var program = new DepthwiseConv2DDerFilterProgram(convInfo); return this.compileAndRun(program, [x, dy]); }; MathBackendWebGL.prototype.conv3d = function (x, filter, convInfo) { var program = new Conv3DProgram(convInfo); return this.compileAndRun(program, [x, filter]); }; MathBackendWebGL.prototype.conv3dDerInput = function (dy, filter, convInfo) { var program = new Conv3DDerInputProgram(convInfo); return this.compileAndRun(program, [dy, filter]); }; MathBackendWebGL.prototype.conv3dDerFilter = function (x, dy, convInfo) { var program = new Conv3DDerFilterProgram(convInfo); return this.compileAndRun(program, [x, dy]); }; MathBackendWebGL.prototype.maxPool = function (x, convInfo) { var program = new Pool2DProgram(convInfo, 'max', false); var output = this.makeOutputArray(program.outputShape, x.dtype); return this.compileAndRun(program, [x], output); }; MathBackendWebGL.prototype.avgPool = function (x, convInfo) { var program = new Pool2DProgram(convInfo, 'avg', false); var output = this.makeOutputArray(program.outputShape, 'float32'); return this.compileAndRun(program, [x], output); }; MathBackendWebGL.prototype.maxPoolBackprop = function (dy, x, y, convInfo) { var getPositions = true; var maxPoolPositionsProgram = new Pool2DProgram(convInfo, 'max', getPositions); var maxPoolPositions = this.compileAndRun(maxPoolPositionsProgram, [x]); var maxPoolBackPropProgram = new MaxPool2DBackpropProgram(convInfo); var output = this.makeOutputArray(maxPoolBackPropProgram.outputShape, x.dtype); var result = this.compileAndRun(maxPoolBackPropProgram, [dy, maxPoolPositions], output); maxPoolPositions.dispose(); return result; }; MathBackendWebGL.prototype.avgPoolBackprop = function (dy, x, convInfo) { var avgPoolBackpropProgram = new AvgPool2DBackpropProgram(convInfo); var output = this.makeOutputArray(avgPoolBackpropProgram.outputShape, x.dtype); return this.compileAndRun(avgPoolBackpropProgram, [dy], output); }; MathBackendWebGL.prototype.cast = function (x, dtype) { return castTensor(x, dtype, this); }; MathBackendWebGL.prototype.unstack = function (x, axis) { var num = x.shape[axis]; var outShape = new Array(x.rank - 1); var outIndex = 0; for (var i = 0; i < x.rank; i++) { if (i !== axis) { outShape[outIndex++] = x.shape[i]; } } var begin = new Array(x.rank).fill(0); var size = x.shape.slice(); size[axis] = 1; var res = new Array(num); for (var i = 0; i < res.length; i++) { begin[axis] = i; res[i] = this.slice(x, begin, size).reshape(outShape); } return res; }; MathBackendWebGL.prototype.reshape = function (x, shape) { var texData = this.texData.get(x.dataId); if (texData.isPacked && !isReshapeFree(x.shape, shape) && !(texData.texture !== null && isReshapeFree(texData.shape, shape))) { return this.packedReshape(x, shape); } return reshapeTensor(x, shape); }; MathBackendWebGL.prototype.resizeBilinear = function (x, newHeight, newWidth, alignCorners) { var program = new ResizeBilinearProgram(x.shape, newHeight, newWidth, alignCorners); return this.compileAndRun(program, [x]); }; MathBackendWebGL.prototype.resizeBilinearBackprop = function (dy, x, alignCorners) { var program = new ResizeBilinearBackpropProgram(dy, x, alignCorners); return this.compileAndRun(program, [dy]); }; MathBackendWebGL.prototype.resizeNearestNeighbor = function (x, newHeight, newWidth, alignCorners) { var program = new ResizeNearestNeighborProgram(x.shape, newHeight, newWidth, alignCorners); return this.compileAndRun(program, [x]); }; MathBackendWebGL.prototype.resizeNearestNeighborBackprop = function (dy, x, alignCorners) { var program = new ResizeNearestNeigborBackpropProgram(dy, x, alignCorners); return this.compileAndRun(program, [dy]); }; MathBackendWebGL.prototype.multinomial = function (logits, normalized, numSamples, seed) { var probs = normalized ? logits : softmax(logits); var batchSize = probs.shape[0]; var numOutcomes = probs.shape[1]; var program = new MultinomialProgram(batchSize, numOutcomes, numSamples); var output = this.makeOutputArray(program.outputShape, 'int32'); var customSetup = program.getCustomSetupFunc(seed); return this.compileAndRun(program, [probs], output, customSetup); }; MathBackendWebGL.prototype.oneHot = function (indices, depth, onValue, offValue) { var program = new OneHotProgram(indices.size, depth, onValue, offValue); return this.compileAndRun(program, [indices]); }; MathBackendWebGL.prototype.nonMaxSuppression = function (boxes, scores, maxOutputSize, iouThreshold, scoreThreshold) { warn('tf.nonMaxSuppression() in webgl locks the UI thread. ' + 'Call tf.nonMaxSuppressionAsync() instead'); var boxesVals = boxes.dataSync(); var scoresVals = scores.dataSync(); return nonMaxSuppressionImpl(boxesVals, scoresVals, maxOutputSize, iouThreshold, scoreThreshold); }; MathBackendWebGL.prototype.cropAndResize = function (image, boxes, boxIndex, cropSize, method, extrapolationValue) { var program = new CropAndResizeProgram(image.shape, boxes.shape, cropSize, method, extrapolationValue); return this.compileAndRun(program, [image, boxes, boxIndex]); }; MathBackendWebGL.prototype.depthToSpace = function (x, blockSize, dataFormat) { assert(blockSize > 1, "blockSize should be > 1 for depthToSpace, but was: " + blockSize); var batchSize = x.shape[0]; var inputHeight = (dataFormat === 'NHWC') ? x.shape[1] : x.shape[2]; var inputWidth = (dataFormat === 'NHWC') ? x.shape[2] : x.shape[3]; var inputDepth = (dataFormat === 'NHWC') ? x.shape[3] : x.shape[1]; var outputHeight = inputHeight * blockSize; var outputWidth = inputWidth * blockSize; var outputDepth = inputDepth / (blockSize * blockSize); var outputShape = (dataFormat === 'NHWC') ? [batchSize, outputHeight, outputWidth, outputDepth] : [batchSize, outputDepth, outputHeight, outputWidth]; var program = new DepthToSpaceProgram(outputShape, blockSize, dataFormat); return this.compileAndRun(program, [x]); }; MathBackendWebGL.prototype.split = function (x, sizeSplits, axis) { return split(x, sizeSplits, axis); }; MathBackendWebGL.prototype.scatterND = function (indices, updates, shape) { var _a = calculateShapes(updates, indices, shape), sliceRank = _a.sliceRank, numUpdates = _a.numUpdates, sliceSize = _a.sliceSize, strides = _a.strides, outputSize = _a.outputSize; var flattenShape = [outputSize / sliceSize, sliceSize]; var flattenIndices = indices.reshape([numUpdates, sliceRank]); var flattenX = updates.reshape([numUpdates, sliceSize]); if (outputSize === 0) { return reshapeTensor(tensor([]), shape); } var defaultValue = scalar(0); var program = new ScatterProgram(numUpdates, sliceRank, flattenIndices.rank, flattenX.rank, strides, flattenShape); return this.compileAndRun(program, [flattenX, flattenIndices, defaultValue]) .reshape(shape); }; MathBackendWebGL.prototype.sparseToDense = function (sparseIndices, sparseValues, outputShape, defaultValue) { var _a = calculateShapes(sparseValues, sparseIndices, outputShape), sliceRank = _a.sliceRank, numUpdates = _a.numUpdates, strides = _a.strides, outputSize = _a.outputSize; var sumDupeIndices = false; var program = new ScatterProgram(numUpdates, sliceRank, sparseIndices.rank, sparseValues.rank, strides, [outputSize, 1], sumDupeIndices); return this.compileAndRun(program, [sparseValues, sparseIndices, defaultValue]) .reshape(outputShape); }; MathBackendWebGL.prototype.fft = function (x) { var inverse = false; return this.fftImpl(x, inverse); }; MathBackendWebGL.prototype.ifft = function (x) { var inverse = true; return this.fftImpl(x, inverse); }; MathBackendWebGL.prototype.fftImpl = function (x, inverse) { var xData = this.texData.get(x.dataId); var realProgram = new FFTProgram(COMPLEX_FFT.REAL, x.shape, inverse); var imagProgram = new FFTProgram(COMPLEX_FFT.IMAG, x.shape, inverse); var inputs = [ this.makeComplexComponentTensorHandle(x, xData.complexTensors.real), this.makeComplexComponentTensorHandle(x, xData.complexTensors.imag), ]; var real = this.compileAndRun(realProgram, inputs); var imag = this.compileAndRun(imagProgram, inputs); var complex = this.complex(real, imag).as2D(x.shape[0], x.shape[1]); real.dispose(); imag.dispose(); return complex; }; MathBackendWebGL.prototype.gatherND = function (x, indices) { var indicesShape = indices.shape; var sliceRank = indicesShape[indicesShape.length - 1]; var _a = prepareAndValidate(x, indices), resultShape = _a[0], numSlices = _a[1], sliceSize = _a[2], strides = _a[3]; var flattenIndices = indices.reshape([numSlices, sliceRank]); var flattenX = x.reshape([x.size / sliceSize, sliceSize]); var program = new GatherNDProgram(sliceRank, strides, [numSlices, sliceSize]); return this.compileAndRun(program, [flattenX, flattenIndices]) .reshape(resultShape); }; MathBackendWebGL.prototype.makeOutputArray = function (shape, dtype) { return Tensor.make(shape, {}, dtype); }; MathBackendWebGL.prototype.makePackedTensor = function (shape, dtype) { var packedTensor = Tensor.make(shape, {}, dtype); this.texData.get(packedTensor.dataId).isPacked = true; return packedTensor; }; MathBackendWebGL.prototype.unpackTensor = function (input) { var program = new UnpackProgram(input.shape); return this.compileAndRun(program, [input], Tensor.make(program.outputShape, {}, input.dtype)); }; MathBackendWebGL.prototype.packTensor = function (input) { var program = new PackProgram(input.shape); return this.compileAndRun(program, [input], this.makePackedTensor(input.shape, input.dtype)); }; MathBackendWebGL.prototype.packedReshape = function (input, afterShape) { var inputAs3D = input.reshape([ getBatchDim(input.shape) ].concat(getRowsCols(input.shape))); var afterShapeAs3D = [ getBatchDim(afterShape) ].concat(getRowsCols(afterShape)); var program = new ReshapePackedProgram(afterShapeAs3D, inputAs3D.shape); return this.compileAndRun(program, [inputAs3D]) .reshape(afterShape); }; MathBackendWebGL.prototype.compileAndRun = function (program, inputs, output, customSetup, pageToCpu) { var _this = this; if (pageToCpu === void 0) { pageToCpu = true; } if (output == null) { if (program.usesPackedTextures) { output = this.makePackedTensor(program.outputShape, inputs[0].dtype); } else { output = this.makeOutputArray(program.outputShape, inputs[0].dtype); } } if (output.size === 0) { this.texData.get(output.dataId).values = getTypedArrayFromDType(output.dtype, 0); return output; } var inputsData = inputs.map(function (input) { if (input.dtype === 'complex64') { throw new Error("GPGPUProgram does not support complex64 input. For complex64 " + "dtypes, please separate the program into real and imaginary " + "parts."); } var texData = _this.texData.get(input.dataId); if (texData.texture == null) { if (!program.usesPackedTextures && sizeFromShape(input.shape) <= ENV.get('WEBGL_SIZE_UPLOAD_UNIFORM')) { return { shape: input.shape, texData: null, isUniform: true, uniformValues: _this.readSync(input.dataId) }; } if (program.usesPackedTextures) { texData.isPacked = true; texData.shape = input.shape; } } else if (!!texData.isPacked !== !!program.usesPackedTextures) { input = texData.isPacked ? _this.unpackTensor(input) : _this.packTensor(input); texData = _this.texData.get(input.dataId); } else if (texData.isPacked && !isReshapeFree(texData.shape, input.shape)) { _this.delayedStorage = false; var inputValues = input.dataSync(); _this.delayedStorage = true; input = Tensor.make(input.shape, { values: inputValues }, input.dtype); texData = _this.texData.get(input.dataId); texData.isPacked = true; } _this.uploadToGPU(input.dataId); return { shape: input.shape, texData: texData, isUniform: false }; }); this.uploadToGPU(output.dataId); var outputData = { shape: output.shape, texData: this.texData.get(output.dataId), isUniform: false }; var key = makeShaderKey(program, inputsData, outputData); var binary = this.getAndSaveBinary(key, function () { return compileProgram(_this.gpgpu, program, inputsData, outputData); }); var shouldTimeProgram = this.activeTimers != null; var query; if (shouldTimeProgram) { query = this.startTimer(); } runProgram(binary, inputsData, outputData, customSetup); var numBytesBeforePaging = ENV.get('WEBGL_NUM_MB_BEFORE_PAGING') * 1024; if (pageToCpu && this.numBytesInGPU > numBytesBeforePaging) { var numBytesToPage = this.numBytesInGPU - numBytesBeforePaging; while (numBytesToPage > 0 && this.lruDataGPU.length > 0) { var dataId = this.lruDataGPU.shift(); var _a = this.texData.get(dataId), shape = _a.shape, dtype = _a.dtype; numBytesToPage -= this.computeBytes(shape, dtype); this.read(dataId); } } if (shouldTimeProgram) { query = this.endTimer(query); this.activeTimers.push({ name: program.constructor.name, query: this.getQueryTime(query) }); } if (!ENV.get('WEBGL_LAZILY_UNPACK') && this.texData.get(output.dataId).isPacked && !program.isPackShader) { return this.unpackTensor(output); } return output; }; MathBackendWebGL.prototype.getAndSaveBinary = function (key, getBinary) { if (!(key in this.binaryCache)) { this.binaryCache[key] = getBinary(); } return this.binaryCache[key]; }; MathBackendWebGL.prototype.getTextureManager = function () { return this.textureManager; }; MathBackendWebGL.prototype.dispose = function () { if (this.disposed) { return; } for (var key in this.binaryCache) { this.gpgpu.deleteProgram(this.binaryCache[key].webGLProgram); } this.textureManager.dispose(); this.canvas.remove(); if (this.fromPixels2DContext != null) { this.fromPixels2DContext.canvas.remove(); } if (this.gpgpuCreatedLocally) { this.gpgpu.dispose(); } this.disposed = true; }; MathBackendWebGL.prototype.floatPrecision = function () { var _this = this; return tidy(function () { var debugFlag = ENV.get('DEBUG'); ENV.set('DEBUG', false); var underflowCheckVluae = _this.abs(scalar(1e-8)).get(); ENV.set('DEBUG', debugFlag); if (underflowCheckVluae > 0) { return 32; } return 16; }); }; MathBackendWebGL.prototype.uploadToGPU = function (dataId) { var _a; var texData = this.texData.get(dataId); var shape = texData.shape, values = texData.values, texture = texData.texture, usage = texData.usage, isPacked = texData.isPacked; if (texture != null) { if (ENV.get('WEBGL_NUM_MB_BEFORE_PAGING') < Number.POSITIVE_INFINITY) { var index = this.lruDataGPU.indexOf(dataId); if (index >= 0) { this.lruDataGPU.splice(this.lruDataGPU.indexOf(dataId), 1); this.lruDataGPU.push(dataId); } } return; } var shouldTimeProgram = this.activeTimers != null; var start; if (shouldTimeProgram) { start = performance.now(); } var texShape = getTextureShapeFromLogicalShape(shape, isPacked); texData.texShape = texShape; var newTexture = this.acquireTexture(dataId, texShape, usage, isPacked); texData.texture = newTexture; if (values != null) { if (isPacked) { var batch = getBatchDim(shape); var rows = 1, cols = 1; if (shape.length) { _a = getRowsCols(shape), rows = _a[0], cols = _a[1]; } this.gpgpu.uploadMatrixToPackedTexture(newTexture, batch, rows, cols, texShape[0], texShape[1], typedArrayToFloat32(values)); } else { this.gpgpu.uploadMatrixToTexture(newTexture, texShape[0], texShape[1], typedArrayToFloat32(values)); } texData.values = null; if (shouldTimeProgram) { this.uploadWaitMs += performance.now() - start; } } }; MathBackendWebGL.prototype.convertAndCacheOnCPU = function (dataId, float32Values) { var dontKeepCopyOnGPU = this.delayedStorage; var texData = this.texData.get(dataId); var texture = texData.texture, texShape = texData.texShape, dtype = texData.dtype, usage = texData.usage, isPacked = texData.isPacked; if (dontKeepCopyOnGPU && texture != null) { this.releaseTexture(dataId, texture, texShape, usage, isPacked); texData.texture = null; texData.texShape = null; texData.isPacked = false; } texData.usage = TextureUsage.UPLOAD; if (float32Values != null) { texData.values = float32ToTypedArray(float32Values, dtype); } return texData.values; }; MathBackendWebGL.prototype.releaseTexture = function (dataId, texture, texShape, texType, isPacked) { var _a = this.texData.get(dataId), shape = _a.shape, dtype = _a.dtype; if (ENV.get('WEBGL_NUM_MB_BEFORE_PAGING') < Number.POSITIVE_INFINITY) { var idx = this.lruDataGPU.indexOf(dataId); if (idx >= 0) { this.lruDataGPU.splice(idx, 1); } } this.numBytesInGPU -= this.computeBytes(shape, dtype); this.textureManager.releaseTexture(texture, texShape, texType, isPacked); }; MathBackendWebGL.prototype.acquireTexture = function (dataId, texShape, texType, isPacked) { var _a = this.texData.get(dataId), shape = _a.shape, dtype = _a.dtype; if (ENV.get('WEBGL_NUM_MB_BEFORE_PAGING') < Number.POSITIVE_INFINITY) { this.lruDataGPU.push(dataId); } this.numBytesInGPU += this.computeBytes(shape, dtype); return this.textureManager.acquireTexture(texShape, texType, isPacked); }; MathBackendWebGL.prototype.computeBytes = function (shape, dtype) { return sizeFromShape(shape) * bytesPerElement(dtype); }; return MathBackendWebGL; }()); if (ENV.get('IS_BROWSER')) { ENV.registerBackend('webgl', function () { return new MathBackendWebGL(); }, 2, setTensorTracker); } function float32ToTypedArray(a, dtype) { if (dtype === 'float32' || dtype === 'complex64') { return a; } else if (dtype === 'int32' || dtype === 'bool') { var result = (dtype === 'int32') ? new Int32Array(a.length) : new Uint8Array(a.length); for (var i = 0; i < result.length; ++i) { result[i] = Math.round(a[i]); } return result; } else { throw new Error("Unknown dtype " + dtype); } } function typedArrayToFloat32(a) { return (a instanceof Float32Array) ? a : new Float32Array(a); } function neg_(x) { var $x = convertToTensor(x, 'x', 'neg'); var grad = function (dy) { return { $x: function () { return dy.neg(); } }; }; return ENV.engine.runKernel(function (backend) { return backend.neg($x); }, { $x: $x }, grad); } function ceil_(x) { var $x = convertToTensor(x, 'x', 'ceil'); var grad = function (dy) { return { $x: function () { return zerosLike(dy); } }; }; return ENV.engine.runKernel(function (backend) { return backend.ceil($x); }, { $x: $x }, grad); } function floor_(x) { var $x = convertToTensor(x, 'x', 'floor'); var grad = function (dy) { return { $x: function () { return zerosLike(dy); } }; }; return ENV.engine.runKernel(function (backend) { return backend.floor($x); }, { $x: $x }, grad); } function sign_(x) { var $x = convertToTensor(x, 'x', 'sign'); var grad = function (dy) { return { $x: function () { return zerosLike(dy); } }; }; return ENV.engine.runKernel(function (backend) { return backend.sign($x); }, { $x: $x }, grad); } function round_(x) { var $x = convertToTensor(x, 'x', 'round'); var grad = function (dy) { return { $x: function () { return zerosLike(dy); } }; }; return ENV.engine.runKernel(function (backend) { return backend.round($x); }, { $x: $x }, grad); } function exp_(x) { var $x = convertToTensor(x, 'x', 'exp'); var bck = function (dy, saved) { var y = saved[0]; return { $x: function () { return dy.mulStrict(y); } }; }; return ENV.engine.runKernel(function (backend, save) { return save(backend.exp($x)); }, { $x: $x }, bck); } function expm1_(x) { var $x = convertToTensor(x, 'x', 'expm1'); var grad = function (dy) { return { $x: function () { return dy.mulStrict($x.exp()); } }; }; return ENV.engine.runKernel(function (backend) { return backend.expm1($x); }, { $x: $x }, grad); } function log_(x) { var $x = convertToTensor(x, 'x', 'log'); var grad = function (dy) { return { $x: function () { return dy.divStrict($x.toFloat()); } }; }; return ENV.engine.runKernel(function (backend) { return backend.log($x); }, { $x: $x }, grad); } function log1p_(x) { var $x = convertToTensor(x, 'x', 'log1p'); var grad = function (dy) { return { $x: function () { return dy.div($x.add(1)); } }; }; return ENV.engine.runKernel(function (backend) { return backend.log1p($x); }, { $x: $x }, grad); } function sqrt_(x) { var $x = convertToTensor(x, 'x', 'sqrt'); var grad = function (dy) { return { $x: function () { return dy.div($x.toFloat().sqrt().mul(2)); } }; }; return ENV.engine.runKernel(function (backend) { return backend.sqrt($x); }, { $x: $x }, grad); } function rsqrt_(x) { var $x = convertToTensor(x, 'x', 'rsqrt'); var grad = function (dy) { return { $x: function () { return dy.div($x.pow(1.5).mul(2)).neg(); } }; }; return ENV.engine.runKernel(function (backend) { return backend.rsqrt($x); }, { $x: $x }, grad); } function square_(x) { var $x = convertToTensor(x, 'x', 'square'); var grad = function (dy) { return { $x: function () { return dy.mul($x.toFloat().mul(2)); } }; }; return ENV.engine.runKernel(function (backend) { return backend.square($x); }, { $x: $x }, grad); } function reciprocal_(x) { var $x = convertToTensor(x, 'x', 'reciprocal'); var grad = function (dy) { return { $x: function () { return dy.divStrict($x.square().neg()); } }; }; return ENV.engine.runKernel(function (backend) { return backend.reciprocal($x); }, { $x: $x }, grad); } function abs_(x) { var $x = convertToTensor(x, 'x', 'abs'); if ($x.dtype === 'complex64') { return ENV.engine.runKernel(function (backend) { return backend.complexAbs($x); }, { $x: $x }); } var grad = function (dy) { return { $x: function () { return dy.mulStrict($x.toFloat().step(-1)); } }; }; return ENV.engine.runKernel(function (backend) { return backend.abs($x); }, { $x: $x }, grad); } function clipByValue_(x, clipValueMin, clipValueMax) { var $x = convertToTensor(x, 'x', 'clipByValue'); assert((clipValueMin <= clipValueMax), "Error in clip: min (" + clipValueMin + ") must be " + ("less than or equal to max (" + clipValueMax + ").")); var grad = function (dy) { return { $x: function () { return dy.where($x.greaterEqual(clipValueMin) .logicalAnd($x.lessEqual(clipValueMax)), zerosLike(dy)); }, }; }; return ENV.engine.runKernel(function (backend) { return backend.clip($x, clipValueMin, clipValueMax); }, { $x: $x }, grad); } function sigmoid_(x) { var $x = convertToTensor(x, 'x', 'sigmoid'); var grad = function (dy, saved) { var y = saved[0]; return { $x: function () { return dy.mul(y.mul(scalar(1).sub(y))); } }; }; return ENV.engine.runKernel(function (backend, save) { return save(backend.sigmoid($x)); }, { $x: $x }, grad); } function logSigmoid_(x) { var $x = convertToTensor(x, 'x', 'logSigmoid'); var grad = function (dy) { return { $x: function () { return dy.mulStrict($x.neg().sigmoid()); } }; }; return ENV.engine.runKernel(function (backend) { return backend.softplus($x.neg()).neg(); }, { $x: $x }, grad); } function softplus_(x) { var $x = convertToTensor(x, 'x', 'softplus'); var grad = function (dy) { return { $x: function () { return dy.mulStrict($x.sigmoid()); } }; }; return ENV.engine.runKernel(function (backend) { return backend.softplus($x); }, { $x: $x }, grad); } function sin_(x) { var $x = convertToTensor(x, 'x', 'sin'); var grad = function (dy) { return { $x: function () { return $x.toFloat().cos().mulStrict(dy); } }; }; return ENV.engine.runKernel(function (backend) { return backend.sin($x); }, { $x: $x }, grad); } function cos_(x) { var $x = convertToTensor(x, 'x', 'cos'); var grad = function (dy) { return { $x: function () { return $x.toFloat().sin().neg().mulStrict(dy); } }; }; return ENV.engine.runKernel(function (backend) { return backend.cos($x); }, { $x: $x }, grad); } function tan_(x) { var $x = convertToTensor(x, 'x', 'tan'); var grad = function (dy) { return { $x: function () { return dy.divStrict($x.cos().square()); } }; }; return ENV.engine.runKernel(function (backend) { return backend.tan($x); }, { $x: $x }, grad); } function asin_(x) { var $x = convertToTensor(x, 'x', 'asin'); var grad = function (dy) { return { $x: function () { return dy.divStrict(scalar(1).sub($x.toFloat().square()).sqrt()); } }; }; return ENV.engine.runKernel(function (backend) { return backend.asin($x); }, { $x: $x }, grad); } function acos_(x) { var $x = convertToTensor(x, 'x', 'acos'); var grad = function (dy) { return { $x: function () { return dy.divStrict(scalar(1).sub($x.toFloat().square()).sqrt()).neg(); } }; }; return ENV.engine.runKernel(function (backend) { return backend.acos($x); }, { $x: $x }, grad); } function atan_(x) { var $x = convertToTensor(x, 'x', 'atan'); var grad = function (dy) { return { $x: function () { return dy.div($x.toFloat().square().add(1)); } }; }; return ENV.engine.runKernel(function (backend) { return backend.atan($x); }, { $x: $x }, grad); } function sinh_(x) { var $x = convertToTensor(x, 'x', 'sinh'); var grad = function (dy) { return { $x: function () { return $x.toFloat().cosh().mulStrict(dy); } }; }; return ENV.engine.runKernel(function (backend) { return backend.sinh($x); }, { $x: $x }, grad); } function cosh_(x) { var $x = convertToTensor(x, 'x', 'cosh'); var grad = function (dy) { return { $x: function () { return $x.toFloat().sinh().mulStrict(dy); } }; }; return ENV.engine.runKernel(function (backend) { return backend.cosh($x); }, { $x: $x }, grad); } function tanh_(x) { var $x = convertToTensor(x, 'x', 'tanh'); var grad = function (dy, saved) { var y = saved[0]; return { $x: function () { return scalar(1).sub(y.square()).mulStrict(dy); } }; }; return ENV.engine.runKernel(function (backend, save) { return save(backend.tanh($x)); }, { $x: $x }, grad); } function asinh_(x) { var $x = convertToTensor(x, 'x', 'asinh'); var grad = function (dy) { return { $x: function () { return dy.divStrict(scalar(1).add($x.toFloat().square()).sqrt()); } }; }; return ENV.engine.runKernel(function (backend) { return backend.asinh($x); }, { $x: $x }, grad); } function acosh_(x) { var $x = convertToTensor(x, 'x', 'acosh'); var grad = function (dy) { return { $x: function () { return dy.divStrict($x.toFloat().square().sub(1).sqrt()); } }; }; return ENV.engine.runKernel(function (backend) { return backend.acosh($x); }, { $x: $x }, grad); } function atanh_(x) { var $x = convertToTensor(x, 'x', 'atanh'); var grad = function (dy) { return { $x: function () { return dy.div(scalar(1).sub($x.toFloat().square())); } }; }; return ENV.engine.runKernel(function (backend) { return backend.atanh($x); }, { $x: $x }, grad); } function erf_(x) { var $x = convertToTensor(x, 'x', 'erf'); assert($x.dtype === 'int32' || $x.dtype === 'float32', 'Input dtype must be `int32` or `float32`.'); if ($x.dtype === 'int32') { $x = $x.toFloat(); } var grad = function (dy) { return { $x: function () { return dy.mul($x.square().neg().exp().mul(2 / Math.sqrt(Math.PI))); } }; }; return ENV.engine.runKernel(function (backend) { return backend.erf($x); }, { $x: $x }, grad); } function step_(x, alpha) { if (alpha === void 0) { alpha = 0.0; } var $x = convertToTensor(x, 'x', 'step'); var grad = function (dy) { return { $x: function () { return zerosLike(dy); } }; }; return ENV.engine.runKernel(function (backend) { return backend.step($x, alpha); }, { $x: $x }, grad); } var abs = op({ abs_: abs_ }); var acos = op({ acos_: acos_ }); var acosh = op({ acosh_: acosh_ }); var asin = op({ asin_: asin_ }); var asinh = op({ asinh_: asinh_ }); var atan = op({ atan_: atan_ }); var atanh = op({ atanh_: atanh_ }); var ceil = op({ ceil_: ceil_ }); var clipByValue = op({ clipByValue_: clipByValue_ }); var cos = op({ cos_: cos_ }); var cosh = op({ cosh_: cosh_ }); var erf = op({ erf_: erf_ }); var exp = op({ exp_: exp_ }); var expm1 = op({ expm1_: expm1_ }); var floor = op({ floor_: floor_ }); var log$1 = op({ log_: log_ }); var log1p = op({ log1p_: log1p_ }); var logSigmoid = op({ logSigmoid_: logSigmoid_ }); var neg = op({ neg_: neg_ }); var reciprocal = op({ reciprocal_: reciprocal_ }); var round = op({ round_: round_ }); var rsqrt = op({ rsqrt_: rsqrt_ }); var sigmoid = op({ sigmoid_: sigmoid_ }); var sign = op({ sign_: sign_ }); var sin = op({ sin_: sin_ }); var sinh = op({ sinh_: sinh_ }); var softplus = op({ softplus_: softplus_ }); var sqrt = op({ sqrt_: sqrt_ }); var square = op({ square_: square_ }); var step = op({ step_: step_ }); var tan = op({ tan_: tan_ }); var tanh$1 = op({ tanh_: tanh_ }); function batchNormalization2d_(x, mean, variance, varianceEpsilon, scale, offset) { if (varianceEpsilon === void 0) { varianceEpsilon = .001; } var $x = convertToTensor(x, 'x', 'batchNormalization'); var $mean = convertToTensor(mean, 'mean', 'batchNormalization'); var $variance = convertToTensor(variance, 'variance', 'batchNormalization'); var $scale; if (scale != null) { $scale = convertToTensor(scale, 'scale', 'batchNormalization'); } var $offset; if (offset != null) { $offset = convertToTensor(offset, 'offset', 'batchNormalization'); } assert($x.rank === 2, "Error in batchNormalization3D: x must be rank 3 but got rank " + ($x.rank + ".")); assert($mean.rank === 2 || $mean.rank === 1, "Error in batchNormalization2D: mean must be rank 2 or rank 1 but " + ("got rank " + $mean.rank + ".")); assert($variance.rank === 2 || $variance.rank === 1, "Error in batchNormalization2D: variance must be rank 2 or rank 1 " + ("but got rank " + $variance.rank + ".")); if ($scale != null) { assert($scale.rank === 2 || $scale.rank === 1, "Error in batchNormalization2D: scale must be rank 2 or rank 1 " + ("but got rank " + $scale.rank + ".")); } if ($offset != null) { assert($offset.rank === 2 || $offset.rank === 1, "Error in batchNormalization2D: offset must be rank 2 or rank 1 " + ("but got rank " + $offset.rank + ".")); } return batchNormalization($x, $mean, $variance, varianceEpsilon, $scale, $offset); } function batchNormalization3d_(x, mean, variance, varianceEpsilon, scale, offset) { if (varianceEpsilon === void 0) { varianceEpsilon = .001; } var $x = convertToTensor(x, 'x', 'batchNormalization'); var $mean = convertToTensor(mean, 'mean', 'batchNormalization'); var $variance = convertToTensor(variance, 'variance', 'batchNormalization'); var $scale; if (scale != null) { $scale = convertToTensor(scale, 'scale', 'batchNormalization'); } var $offset; if (offset != null) { $offset = convertToTensor(offset, 'offset', 'batchNormalization'); } assert($x.rank === 3, "Error in batchNormalization3D: x must be rank 3 but got rank " + ($x.rank + ".")); assert($mean.rank === 3 || $mean.rank === 1, "Error in batchNormalization3D: mean must be rank 3 or rank 1 but " + ("got rank " + $mean.rank + ".")); assert($variance.rank === 3 || $variance.rank === 1, "Error in batchNormalization3D: variance must be rank 3 or rank 1 " + ("but got rank " + $variance.rank + ".")); if ($scale != null) { assert($scale.rank === 3 || $scale.rank === 1, "Error in batchNormalization3D: scale must be rank 3 or rank 1 " + ("but got rank " + $scale.rank + ".")); } if ($offset != null) { assert($offset.rank === 3 || $offset.rank === 1, "Error in batchNormalization3D: offset must be rank 3 or rank 1 " + ("but got rank " + $offset.rank + ".")); } return batchNormalization($x, $mean, $variance, varianceEpsilon, $scale, $offset); } function batchNormalization4d_(x, mean, variance, varianceEpsilon, scale, offset) { if (varianceEpsilon === void 0) { varianceEpsilon = .001; } var $x = convertToTensor(x, 'x', 'batchNormalization'); var $mean = convertToTensor(mean, 'mean', 'batchNormalization'); var $variance = convertToTensor(variance, 'variance', 'batchNormalization'); var $scale; if (scale != null) { $scale = convertToTensor(scale, 'scale', 'batchNormalization'); } var $offset; if (offset != null) { $offset = convertToTensor(offset, 'offset', 'batchNormalization'); } assert($x.rank === 4, "Error in batchNormalization4D: x must be rank 4 but got rank " + ($x.rank + ".")); assert($mean.rank === 4 || $mean.rank === 1, "Error in batchNormalization4D: mean must be rank 4 or rank 1 but " + ("got rank " + $mean.rank + ".")); assert($variance.rank === 4 || $variance.rank === 1, "Error in batchNormalization4D: variance must be rank 4 or rank 1 " + ("but got rank " + $variance.rank + ".")); if ($scale != null) { assert($scale.rank === 4 || $scale.rank === 1, "Error in batchNormalization4D: scale must be rank 4 or rank 1 " + ("but got rank " + $scale.rank + ".")); } if ($offset != null) { assert($offset.rank === 4 || $offset.rank === 1, "Error in batchNormalization4D: offset must be rank 4 or rank 1 " + ("but got rank " + $offset.rank + ".")); } return batchNormalization($x, $mean, $variance, varianceEpsilon, $scale, $offset); } function batchNormalization_(x, mean, variance, varianceEpsilon, scale, offset) { if (varianceEpsilon === void 0) { varianceEpsilon = .001; } var $x = convertToTensor(x, 'x', 'batchNormalization'); var $mean = convertToTensor(mean, 'mean', 'batchNormalization'); var $variance = convertToTensor(variance, 'variance', 'batchNormalization'); var $scale; if (scale != null) { $scale = convertToTensor(scale, 'scale', 'batchNormalization'); } var $offset; if (offset != null) { $offset = convertToTensor(offset, 'offset', 'batchNormalization'); } assert($mean.rank === $variance.rank, 'Batch normalization gradient requires mean and variance to have ' + 'equal ranks.'); assert($offset == null || $mean.rank === $offset.rank, 'Batch normalization gradient requires mean and offset to have ' + 'equal ranks.'); assert($scale == null || $mean.rank === $scale.rank, 'Batch normalization gradient requires mean and scale to have ' + 'equal ranks.'); var x4D; if ($x.rank === 0 || $x.rank === 1) { x4D = $x.as4D(1, 1, 1, $x.size); } else if ($x.rank === 2) { x4D = $x.as4D(1, 1, $x.shape[0], $x.shape[1]); } else if ($x.rank === 3) { x4D = $x.as4D(1, $x.shape[0], $x.shape[1], $x.shape[2]); } else { x4D = $x; } var der = function (dy) { var scaleValue = $scale == null ? scalar(1) : $scale; var reductionAxes = getReductionAxes($mean.shape, x4D.shape); var tileShape = []; if ($mean.rank === 1) { for (var i = 0; i < x4D.shape.length - 1; ++i) { tileShape.push(x4D.shape[i]); } tileShape.push(1); } var xMinusMean = $x.sub($mean); var dyTimesScaleValue = dy.mul(scaleValue); var oneOverSqrtVariance = rsqrt($variance.add(scalar(varianceEpsilon))); var minusHalfRCube = oneOverSqrtVariance.mul(oneOverSqrtVariance) .mul(oneOverSqrtVariance) .mul(scalar(-0.5)); var derX = function () { if ($mean.rank === 1) { return dy .mul(tile(oneOverSqrtVariance.as4D(1, 1, 1, $mean.shape[0]), tileShape)) .mul(scaleValue) .reshape($x.shape); } else { return dy.mul(oneOverSqrtVariance).mul(scaleValue).reshape($x.shape); } }; var derMean = function () { var meanDer = oneOverSqrtVariance.mul(scalar(-1)).mul(dyTimesScaleValue); if ($mean.rank === 1) { meanDer = meanDer.sum(reductionAxes); } return meanDer.reshape($mean.shape); }; var derVariance = function () { var varianceDer = minusHalfRCube.mul(xMinusMean).mul(dyTimesScaleValue); if ($mean.rank === 1) { varianceDer = varianceDer.sum(reductionAxes); } return varianceDer.reshape($mean.shape); }; var derScale = function () { var xMinusMean2TimesRsqrt = xMinusMean.mul(oneOverSqrtVariance); var scaleDer = dy.mul(xMinusMean2TimesRsqrt); if ($mean.rank === 1) { scaleDer = scaleDer.sum(reductionAxes); } return scaleDer.reshape($mean.shape); }; var derOffset = function () { var offsetDer = dy; if ($mean.rank === 1) { offsetDer = offsetDer.sum(reductionAxes); } return offsetDer.reshape($mean.shape); }; return { $x: derX, $mean: derMean, $variance: derVariance, $scale: derScale, $offset: derOffset }; }; var res = ENV.engine.runKernel(function (backend) { return backend.batchNormalization(x4D, batchnormReshape4D($mean), batchnormReshape4D($variance), varianceEpsilon, batchnormReshape4D($scale), batchnormReshape4D($offset)); }, { $x: $x, $mean: $mean, $variance: $variance, $scale: $scale, $offset: $offset }, der); return res.reshape($x.shape); } function batchnormReshape4D(x) { if (x == null) { return null; } if (x.rank === 0) { return x.as1D(); } else if (x.rank === 1) { return x; } else if (x.rank === 2) { return x.as4D(1, 1, x.shape[0], x.shape[1]); } else if (x.rank === 3) { return x.as4D(1, x.shape[0], x.shape[1], x.shape[2]); } return x; } var batchNormalization2d = op({ batchNormalization2d_: batchNormalization2d_ }); var batchNormalization3d = op({ batchNormalization3d_: batchNormalization3d_ }); var batchNormalization4d = op({ batchNormalization4d_: batchNormalization4d_ }); var batchNormalization = op({ batchNormalization_: batchNormalization_ }); function computePool2DInfo(inShape, filterSize, strides, dilations, pad, roundingMode, dataFormat) { if (dataFormat === void 0) { dataFormat = 'channelsLast'; } var _a = parseTupleParam(filterSize), filterHeight = _a[0], filterWidth = _a[1]; var filterShape; if (dataFormat === 'channelsLast') { filterShape = [filterHeight, filterWidth, inShape[3], inShape[3]]; } else if (dataFormat === 'channelsFirst') { filterShape = [filterHeight, filterWidth, inShape[1], inShape[1]]; } else { throw new Error("Unknown dataFormat " + dataFormat); } return computeConv2DInfo(inShape, filterShape, strides, dilations, pad, roundingMode, false, dataFormat); } function computeConv2DInfo(inShape, filterShape, strides, dilations, pad, roundingMode, depthwise, dataFormat) { if (depthwise === void 0) { depthwise = false; } if (dataFormat === void 0) { dataFormat = 'channelsLast'; } var _a = [-1, -1, -1, -1], batchSize = _a[0], inHeight = _a[1], inWidth = _a[2], inChannels = _a[3]; if (dataFormat === 'channelsLast') { batchSize = inShape[0], inHeight = inShape[1], inWidth = inShape[2], inChannels = inShape[3]; } else if (dataFormat === 'channelsFirst') { batchSize = inShape[0], inChannels = inShape[1], inHeight = inShape[2], inWidth = inShape[3]; } else { throw new Error("Unknown dataFormat " + dataFormat); } var filterHeight = filterShape[0], filterWidth = filterShape[1], filterChannels = filterShape[3]; var _b = parseTupleParam(strides), strideHeight = _b[0], strideWidth = _b[1]; var _c = parseTupleParam(dilations), dilationHeight = _c[0], dilationWidth = _c[1]; var effectiveFilterHeight = getEffectiveFilterSize(filterHeight, dilationHeight); var effectiveFilterWidth = getEffectiveFilterSize(filterWidth, dilationWidth); var _d = getPadAndOutInfo(pad, inHeight, inWidth, strideHeight, strideWidth, effectiveFilterHeight, effectiveFilterWidth, roundingMode), padInfo = _d.padInfo, outHeight = _d.outHeight, outWidth = _d.outWidth; var outChannels = depthwise ? filterChannels * inChannels : filterChannels; var outShape; if (dataFormat === 'channelsFirst') { outShape = [batchSize, outChannels, outHeight, outWidth]; } else if (dataFormat === 'channelsLast') { outShape = [batchSize, outHeight, outWidth, outChannels]; } return { batchSize: batchSize, dataFormat: dataFormat, inHeight: inHeight, inWidth: inWidth, inChannels: inChannels, outHeight: outHeight, outWidth: outWidth, outChannels: outChannels, padInfo: padInfo, strideHeight: strideHeight, strideWidth: strideWidth, filterHeight: filterHeight, filterWidth: filterWidth, effectiveFilterHeight: effectiveFilterHeight, effectiveFilterWidth: effectiveFilterWidth, dilationHeight: dilationHeight, dilationWidth: dilationWidth, inShape: inShape, outShape: outShape, filterShape: filterShape }; } function computeConv3DInfo(inShape, filterShape, strides, dilations, pad, depthwise, dataFormat) { if (depthwise === void 0) { depthwise = false; } if (dataFormat === void 0) { dataFormat = 'channelsLast'; } var _a = [-1, -1, -1, -1, -1], batchSize = _a[0], inDepth = _a[1], inHeight = _a[2], inWidth = _a[3], inChannels = _a[4]; if (dataFormat === 'channelsLast') { batchSize = inShape[0], inDepth = inShape[1], inHeight = inShape[2], inWidth = inShape[3], inChannels = inShape[4]; } else if (dataFormat === 'channelsFirst') { batchSize = inShape[0], inChannels = inShape[1], inDepth = inShape[2], inHeight = inShape[3], inWidth = inShape[4]; } else { throw new Error("Unknown dataFormat " + dataFormat); } var filterDepth = filterShape[0], filterHeight = filterShape[1], filterWidth = filterShape[2], filterChannels = filterShape[4]; var _b = parse3TupleParam(strides), strideDepth = _b[0], strideHeight = _b[1], strideWidth = _b[2]; var _c = parse3TupleParam(dilations), dilationDepth = _c[0], dilationHeight = _c[1], dilationWidth = _c[2]; var effectiveFilterDepth = getEffectiveFilterSize(filterDepth, dilationDepth); var effectiveFilterHeight = getEffectiveFilterSize(filterHeight, dilationHeight); var effectiveFilterWidth = getEffectiveFilterSize(filterWidth, dilationWidth); var _d = get3DPadAndOutInfo(pad, inDepth, inHeight, inWidth, strideDepth, strideHeight, strideWidth, effectiveFilterDepth, effectiveFilterHeight, effectiveFilterWidth), padInfo = _d.padInfo, outDepth = _d.outDepth, outHeight = _d.outHeight, outWidth = _d.outWidth; var outChannels = depthwise ? filterChannels * inChannels : filterChannels; var outShape; if (dataFormat === 'channelsFirst') { outShape = [batchSize, outChannels, outDepth, outHeight, outWidth]; } else if (dataFormat === 'channelsLast') { outShape = [batchSize, outDepth, outHeight, outWidth, outChannels]; } return { batchSize: batchSize, dataFormat: dataFormat, inDepth: inDepth, inHeight: inHeight, inWidth: inWidth, inChannels: inChannels, outDepth: outDepth, outHeight: outHeight, outWidth: outWidth, outChannels: outChannels, padInfo: padInfo, strideDepth: strideDepth, strideHeight: strideHeight, strideWidth: strideWidth, filterDepth: filterDepth, filterHeight: filterHeight, filterWidth: filterWidth, dilationDepth: dilationDepth, dilationHeight: dilationHeight, dilationWidth: dilationWidth, inShape: inShape, outShape: outShape, filterShape: filterShape }; } function computeOutputShape3D(inShape, fieldSize, outDepth, stride, zeroPad, roundingMode) { if (zeroPad == null) { zeroPad = computeDefaultPad(inShape, fieldSize, stride); } var inputRows = inShape[0]; var inputCols = inShape[1]; var outputRows = conditionalRound((inputRows - fieldSize + 2 * zeroPad) / stride + 1, roundingMode); assert(isInt(outputRows), "The output # of rows (" + outputRows + ") must be an integer. Change the " + "stride and/or zero pad parameters"); var outputCols = conditionalRound((inputCols - fieldSize + 2 * zeroPad) / stride + 1, roundingMode); assert(isInt(outputCols), "The output # of columns (" + outputCols + ") must be an integer. Change " + "the stride and/or zero pad parameters"); return [outputRows, outputCols, outDepth]; } function computeDefaultPad(inputShape, fieldSize, stride, dilation) { if (dilation === void 0) { dilation = 1; } var effectiveFieldSize = getEffectiveFilterSize(fieldSize, dilation); return Math.floor((inputShape[0] * (stride - 1) - stride + effectiveFieldSize) / 2); } function parseTupleParam(param) { return typeof param === 'number' ? [param, param] : param; } function parse3TupleParam(param) { return typeof param === 'number' ? [param, param, param] : param; } function getEffectiveFilterSize(filterSize, dilation) { if (dilation <= 1) { return filterSize; } return filterSize + (filterSize - 1) * (dilation - 1); } function getPadAndOutInfo(pad, inHeight, inWidth, strideHeight, strideWidth, filterHeight, filterWidth, roundingMode) { var padInfo; var outHeight; var outWidth; if (typeof pad === 'number') { var padType = (pad === 0) ? 'VALID' : 'NUMBER'; padInfo = { top: pad, bottom: pad, left: pad, right: pad, type: padType }; var outShape = computeOutputShape3D([inHeight, inWidth, 1], filterHeight, 1, strideHeight, pad, roundingMode); outHeight = outShape[0]; outWidth = outShape[1]; } else if (pad === 'same') { outHeight = Math.ceil(inHeight / strideHeight); outWidth = Math.ceil(inWidth / strideWidth); var padAlongHeight = (outHeight - 1) * strideHeight + filterHeight - inHeight; var padAlongWidth = (outWidth - 1) * strideWidth + filterWidth - inWidth; var top_1 = Math.floor(padAlongHeight / 2); var bottom = padAlongHeight - top_1; var left = Math.floor(padAlongWidth / 2); var right = padAlongWidth - left; padInfo = { top: top_1, bottom: bottom, left: left, right: right, type: 'SAME' }; } else if (pad === 'valid') { padInfo = { top: 0, bottom: 0, left: 0, right: 0, type: 'VALID' }; outHeight = Math.ceil((inHeight - filterHeight + 1) / strideHeight); outWidth = Math.ceil((inWidth - filterWidth + 1) / strideWidth); } else { throw Error("Unknown padding parameter: " + pad); } return { padInfo: padInfo, outHeight: outHeight, outWidth: outWidth }; } function get3DPadAndOutInfo(pad, inDepth, inHeight, inWidth, strideDepth, strideHeight, strideWidth, filterDepth, filterHeight, filterWidth) { var padInfo; var outDepth; var outHeight; var outWidth; if (pad === 'same') { outDepth = Math.ceil(inDepth / strideDepth); outHeight = Math.ceil(inHeight / strideHeight); outWidth = Math.ceil(inWidth / strideWidth); var padAlongDepth = (outDepth - 1) * strideDepth + filterDepth - inDepth; var padAlongHeight = (outHeight - 1) * strideHeight + filterHeight - inHeight; var padAlongWidth = (outWidth - 1) * strideWidth + filterWidth - inWidth; var front = Math.floor(padAlongDepth / 2); var back = padAlongDepth - front; var top_2 = Math.floor(padAlongHeight / 2); var bottom = padAlongHeight - top_2; var left = Math.floor(padAlongWidth / 2); var right = padAlongWidth - left; padInfo = { top: top_2, bottom: bottom, left: left, right: right, front: front, back: back, type: 'SAME' }; } else if (pad === 'valid') { padInfo = { top: 0, bottom: 0, left: 0, right: 0, front: 0, back: 0, type: 'VALID' }; outDepth = Math.ceil((inDepth - filterDepth + 1) / strideDepth); outHeight = Math.ceil((inHeight - filterHeight + 1) / strideHeight); outWidth = Math.ceil((inWidth - filterWidth + 1) / strideWidth); } else { throw Error("Unknown padding parameter: " + pad); } return { padInfo: padInfo, outDepth: outDepth, outHeight: outHeight, outWidth: outWidth }; } function conditionalRound(value, roundingMode) { if (!roundingMode) { return value; } switch (roundingMode) { case 'round': return Math.round(value); case 'ceil': return Math.ceil(value); case 'floor': return Math.floor(value); default: throw new Error("Unknown roundingMode " + roundingMode); } } function tupleValuesAreOne(param) { var _a = parseTupleParam(param), dimA = _a[0], dimB = _a[1]; return dimA === 1 && dimB === 1; } function eitherStridesOrDilationsAreOne(strides, dilations) { return tupleValuesAreOne(strides) || tupleValuesAreOne(dilations); } function conv1d_(x, filter, stride, pad, dataFormat, dilation, dimRoundingMode) { if (dataFormat === void 0) { dataFormat = 'NWC'; } if (dilation === void 0) { dilation = 1; } var $x = convertToTensor(x, 'x', 'conv1d'); var $filter = convertToTensor(filter, 'filter', 'conv1d'); var x3D = $x; var reshapedTo3D = false; if ($x.rank === 2) { reshapedTo3D = true; x3D = $x.as3D(1, $x.shape[0], $x.shape[1]); } assert(x3D.rank === 3, "Error in conv1d: input must be rank 3, but got rank " + x3D.rank + "."); assert($filter.rank === 3, "Error in conv1d: filter must be rank 3, but got rank " + ($filter.rank + ".")); if (dimRoundingMode != null) { assert(isInt(pad), "Error in conv1d: pad must be an integer when using, " + ("dimRoundingMode " + dimRoundingMode + " but got pad " + pad + ".")); } assert(x3D.shape[2] === $filter.shape[1], "Error in conv1d: depth of input (" + x3D.shape[2] + ") must match " + ("input depth for filter " + $filter.shape[1] + ".")); assert(eitherStridesOrDilationsAreOne(stride, dilation), 'Error in conv1D: Either stride or dilation must be 1. ' + ("Got stride " + stride + " and dilation '" + dilation + "'")); assert(dataFormat === 'NWC', "Error in conv1d: got dataFormat of " + dataFormat + " but only NWC is currently supported."); var filter4D = $filter.as4D(1, $filter.shape[0], $filter.shape[1], $filter.shape[2]); var input4D = x3D.as4D(x3D.shape[0], 1, x3D.shape[1], x3D.shape[2]); var strides = [1, stride]; var dilations = [1, dilation]; var conv2dDataFormat = 'NHWC'; var res = conv2d(input4D, filter4D, strides, pad, conv2dDataFormat, dilations, dimRoundingMode); if (reshapedTo3D) { return res.as2D(res.shape[2], res.shape[3]); } return res.as3D(res.shape[0], res.shape[2], res.shape[3]); } function conv2d_(x, filter, strides, pad, dataFormat, dilations, dimRoundingMode) { if (dataFormat === void 0) { dataFormat = 'NHWC'; } if (dilations === void 0) { dilations = [1, 1]; } var $x = convertToTensor(x, 'x', 'conv2d'); var $filter = convertToTensor(filter, 'filter', 'conv2d'); var x4D = $x; var reshapedTo4D = false; if ($x.rank === 3) { reshapedTo4D = true; x4D = $x.as4D(1, $x.shape[0], $x.shape[1], $x.shape[2]); } assert(x4D.rank === 4, "Error in conv2d: input must be rank 4, but got rank " + x4D.rank + "."); assert($filter.rank === 4, "Error in conv2d: filter must be rank 4, but got rank " + ($filter.rank + ".")); if (dimRoundingMode != null) { assert(isInt(pad), "Error in conv2d: pad must be an integer when using, " + ("dimRoundingMode " + dimRoundingMode + " but got pad " + pad + ".")); } assert(x4D.shape[3] === $filter.shape[2], "Error in conv2d: depth of input (" + x4D.shape[3] + ") must match " + ("input depth for filter " + $filter.shape[2] + ".")); assert(eitherStridesOrDilationsAreOne(strides, dilations), 'Error in conv2D: Either strides or dilations must be 1. ' + ("Got strides " + strides + " and dilations '" + dilations + "'")); assert(dataFormat === 'NHWC', "Error in conv2d: got dataFormat of " + dataFormat + " but only NHWC is currently supported."); var convInfo = computeConv2DInfo(x4D.shape, $filter.shape, strides, dilations, pad, dimRoundingMode); var grad = function (dy) { assert(tupleValuesAreOne(dilations), 'Error in gradient of conv2D: dilation rates greater than 1 are not' + ("yet supported in gradients. Got dilations '" + dilations + "'")); return { x: function () { return conv2dDerInput_(x4D.shape, dy, $filter, strides, pad); }, $filter: function () { return conv2dDerFilter_(x4D, dy, $filter.shape, strides, pad); } }; }; var res = ENV.engine.runKernel(function (backend) { return backend.conv2d(x4D, $filter, convInfo); }, { x: x4D, $filter: $filter }, grad); if (reshapedTo4D) { return res.as3D(res.shape[1], res.shape[2], res.shape[3]); } return res; } function conv2dDerInput_(xShape, dy, filter, strides, pad, dimRoundingMode) { assert(xShape.length === dy.rank, "Length of inShape " + ("(" + xShape.length + ") and rank of dy (" + dy.rank + ") must match")); var xShape4D = xShape; var dy4D = dy; var reshapedTo4D = false; if (dy.rank === 3) { reshapedTo4D = true; dy4D = dy.as4D(1, dy.shape[0], dy.shape[1], dy.shape[2]); xShape4D = [1, xShape[0], xShape[1], xShape[2]]; } var inDepth = xShape4D[3]; var outDepth = dy4D.shape[3]; assert(xShape4D.length === 4, "Error in conv2dDerInput: inShape must be length 4, but got length " + (xShape4D.length + ".")); assert(dy4D.rank === 4, "Error in conv2dDerInput: dy must be rank 4, but got " + ("rank " + dy4D.rank)); assert(filter.rank === 4, "Error in conv2dDerInput: filter must be rank 4, but got " + ("rank " + filter.rank)); assert(inDepth === filter.shape[2], "Error in conv2dDerInput: depth of input (" + inDepth + ") must " + ("match input depth for filter " + filter.shape[2] + ".")); assert(outDepth === filter.shape[3], "Error in conv2dDerInput: depth of output (" + outDepth + ") must " + ("match output depth for filter " + filter.shape[3] + ".")); if (dimRoundingMode != null) { assert(isInt(pad), "Error in conv2dDerInput: pad must be an integer when using, " + ("dimRoundingMode " + dimRoundingMode + " but got pad " + pad + ".")); } var dilations = 1; var grad = function (ddx) { var dataFormat = 'NHWC'; return { dy4D: function () { return conv2d(ddx, filter, strides, pad, dataFormat, dilations, dimRoundingMode); }, filter: function () { return conv2dDerFilter(ddx, dy4D, filter.shape, strides, pad, dimRoundingMode); } }; }; var convInfo = computeConv2DInfo(xShape4D, filter.shape, strides, dilations, pad, dimRoundingMode); var res = ENV.engine.runKernel(function (backend) { return backend.conv2dDerInput(dy4D, filter, convInfo); }, { dy4D: dy4D, filter: filter }, grad); if (reshapedTo4D) { return res.as3D(res.shape[1], res.shape[2], res.shape[3]); } return res; } function conv2dDerFilter_(x, dy, filterShape, strides, pad, dimRoundingMode) { var x4D = x; if (x.rank === 3) { x4D = x.as4D(1, x.shape[0], x.shape[1], x.shape[2]); } var dy4D = dy; if (dy4D.rank === 3) { dy4D = dy.as4D(1, dy.shape[0], dy.shape[1], dy.shape[2]); } assert(x4D.rank === 4, "Error in conv2dDerFilter: input must be rank 4, but got shape " + (x4D.shape + ".")); assert(dy4D.rank === 4, "Error in conv2dDerFilter: dy must be rank 4, but got shape " + (dy4D.shape + ".")); assert(filterShape.length === 4, "Error in conv2dDerFilter: filterShape must be length 4, but got " + (filterShape + ".")); assert(x4D.shape[3] === filterShape[2], "Error in conv2dDerFilter: depth of input " + x4D.shape[3] + ") must " + ("match input depth in filter (" + filterShape[2] + ".")); assert(dy4D.shape[3] === filterShape[3], "Error in conv2dDerFilter: depth of dy (" + dy4D.shape[3] + ") must " + ("match output depth for filter (" + filterShape[3] + ").")); if (dimRoundingMode != null) { assert(isInt(pad), "Error in conv2dDerFilter: pad must be an integer when using, " + ("dimRoundingMode " + dimRoundingMode + " but got pad " + pad + ".")); } var dilations = 1; var convInfo = computeConv2DInfo(x4D.shape, filterShape, strides, dilations, pad, dimRoundingMode); return ENV.engine.runKernel(function (backend) { return backend.conv2dDerFilter(x4D, dy4D, convInfo); }, { x4D: x4D, dy4D: dy4D }); } function conv2dTranspose_(x, filter, outputShape, strides, pad, dimRoundingMode) { var $x = convertToTensor(x, 'x', 'conv2dTranspose'); var $filter = convertToTensor(filter, 'filter', 'conv2dTranspose'); return conv2dDerInput_(outputShape, $x, $filter, strides, pad, dimRoundingMode); } function depthwiseConv2d_(x, filter, strides, pad, dataFormat, dilations, dimRoundingMode) { if (dataFormat === void 0) { dataFormat = 'NHWC'; } if (dilations === void 0) { dilations = [1, 1]; } var $x = convertToTensor(x, 'x', 'depthwiseConv2d'); var $filter = convertToTensor(filter, 'filter', 'depthwiseConv2d'); var x4D = $x; var reshapedTo4D = false; if ($x.rank === 3) { reshapedTo4D = true; x4D = $x.as4D(1, $x.shape[0], $x.shape[1], $x.shape[2]); } assert(x4D.rank === 4, "Error in depthwiseConv2d: input must be rank 4, but got " + ("rank " + x4D.rank + ".")); assert($filter.rank === 4, "Error in depthwiseConv2d: filter must be rank 4, but got rank " + ($filter.rank + ".")); assert(x4D.shape[3] === $filter.shape[2], "Error in depthwiseConv2d: number of input channels " + ("(" + x4D.shape[3] + ") must match the inChannels dimension in ") + ("filter " + $filter.shape[2] + ".")); if (dilations == null) { dilations = [1, 1]; } assert(eitherStridesOrDilationsAreOne(strides, dilations), 'Error in depthwiseConv2d: Either strides or dilations must be 1. ' + ("Got strides " + strides + " and dilations '" + dilations + "'")); if (dimRoundingMode != null) { assert(isInt(pad), "Error in depthwiseConv2d: pad must be an integer when using, " + ("dimRoundingMode " + dimRoundingMode + " but got pad " + pad + ".")); } var convInfo = computeConv2DInfo(x4D.shape, $filter.shape, strides, dilations, pad, dimRoundingMode, true); var grad = function (dy) { assert(tupleValuesAreOne(dilations), 'Error in gradient of depthwiseConv2d: dilation rates greater than ' + ("1 are not yet supported. Got dilations '" + dilations + "'")); return { x: function () { return depthwiseConv2dDerInput(x4D.shape, dy, $filter, convInfo); }, $filter: function () { return depthwiseConv2dDerFilter(x4D, dy, $filter.shape, convInfo); }, }; }; var res = ENV.engine.runKernel(function (backend) { return backend.depthwiseConv2D(x4D, $filter, convInfo); }, { x: x4D, $filter: $filter }, grad); if (reshapedTo4D) { return res.as3D(res.shape[1], res.shape[2], res.shape[3]); } return res; } function separableConv2d_(x, depthwiseFilter, pointwiseFilter, strides, pad, dilation, dataFormat) { if (dilation === void 0) { dilation = [1, 1]; } if (dataFormat === void 0) { dataFormat = 'NHWC'; } var $x = convertToTensor(x, 'x', 'separableConv2d'); var $depthwiseFilter = convertToTensor(depthwiseFilter, 'depthwiseFilter', 'separableConv2d'); var $pointwiseFilter = convertToTensor(pointwiseFilter, 'pointwiseFilter', 'separableConv2d'); var x4D = $x; var reshapedTo4D = false; if ($x.rank === 3) { reshapedTo4D = true; x4D = $x.as4D(1, $x.shape[0], $x.shape[1], $x.shape[2]); } if (dataFormat === 'NCHW') { throw new Error('separableConv2d currently does not support dataFormat NCHW; only ' + 'NHWC is supported'); } assert(x4D.rank === 4, "Error in separableConv2d: input must be rank 4, but got " + ("rank " + x4D.rank + ".")); assert($depthwiseFilter.rank === 4, "Error in separableConv2d: depthwise filter must be rank 4, but got " + ("rank " + $depthwiseFilter.rank + ".")); assert($pointwiseFilter.rank === 4, "Error in separableConv2d: pointwise filter must be rank 4, but got " + ("rank " + $depthwiseFilter.rank + ".")); assert($pointwiseFilter.shape[0] === 1, "Error in separableConv2d: the first dimension of pointwise filter " + (" must be 1, but got " + $pointwiseFilter.shape[0] + ".")); assert($pointwiseFilter.shape[1] === 1, "Error in separableConv2d: the second dimension of pointwise filter " + (" must be 1, but got " + $pointwiseFilter.shape[1] + ".")); var inChannels = $depthwiseFilter.shape[2]; var channelMultiplier = $depthwiseFilter.shape[3]; assert($pointwiseFilter.shape[2] === inChannels * channelMultiplier, "Error in separableConv2d: the third dimension of pointwise filter " + ("must be " + inChannels * channelMultiplier + ", ") + ("but got " + $pointwiseFilter.shape[2] + ".")); var depthwise = depthwiseConv2d(x4D, $depthwiseFilter, strides, pad, dataFormat, dilation); var pointwiseStride = 1; var res = conv2d(depthwise, $pointwiseFilter, pointwiseStride, 'valid', dataFormat); if (reshapedTo4D) { return res.as3D(res.shape[1], res.shape[2], res.shape[3]); } return res; } function parseTupleParam$1(param) { if (typeof param === 'number') { return [param, param, param]; } if (param.length === 2) { return [param[0], param[1], 1]; } return param; } function tupleValuesAreOne$1(param) { var _a = parseTupleParam$1(param), dimA = _a[0], dimB = _a[1], dimC = _a[2]; return dimA === 1 && dimB === 1 && dimC === 1; } function eitherStridesOrDilationsAreOne$1(strides, dilations) { return tupleValuesAreOne$1(strides) || tupleValuesAreOne$1(dilations); } function depthwiseConv2dDerInput(xShape, dy, filter, convInfo) { var dy4D = dy; var reshapedTo4D = false; if (dy.rank === 3) { reshapedTo4D = true; dy4D = dy.as4D(1, dy.shape[0], dy.shape[1], dy.shape[2]); } var res = ENV.engine.runKernel(function (backend) { return backend.depthwiseConv2DDerInput(dy4D, filter, convInfo); }, { dy4D: dy4D }); if (reshapedTo4D) { return res.as3D(res.shape[1], res.shape[2], res.shape[3]); } return res; } function depthwiseConv2dDerFilter(x, dy, filterShape, convInfo) { var x4D = x; if (x.rank === 3) { x4D = x.as4D(1, x.shape[0], x.shape[1], x.shape[2]); } var dy4D = dy; if (dy4D.rank === 3) { dy4D = dy.as4D(1, dy.shape[0], dy.shape[1], dy.shape[2]); } return ENV.engine.runKernel(function (backend) { return backend.depthwiseConv2DDerFilter(x4D, dy4D, convInfo); }, { x4D: x4D, dy4D: dy4D }); } function conv3d_(x, filter, strides, pad, dataFormat, dilations) { if (dataFormat === void 0) { dataFormat = 'NHWC'; } if (dilations === void 0) { dilations = [1, 1, 1]; } var $x = convertToTensor(x, 'x', 'conv3d'); var $filter = convertToTensor(filter, 'filter', 'conv3d'); var x5D = $x; var reshapedTo5D = false; if ($x.rank === 4) { reshapedTo5D = true; x5D = $x.as5D(1, $x.shape[0], $x.shape[1], $x.shape[2], $x.shape[3]); } assert(x5D.rank === 5, "Error in conv3d: input must be rank 5, but got rank " + x5D.rank + "."); assert($filter.rank === 5, "Error in conv3d: filter must be rank 5, but got rank " + ($filter.rank + ".")); assert(x5D.shape[4] === $filter.shape[3], "Error in conv3d: depth of input (" + x5D.shape[4] + ") must match " + ("input depth for filter " + $filter.shape[3] + ".")); assert(eitherStridesOrDilationsAreOne$1(strides, dilations), 'Error in conv3D: Either strides or dilations must be 1. ' + ("Got strides " + strides + " and dilations '" + dilations + "'")); assert(dataFormat === 'NHWC', "Error in conv3d: got dataFormat of " + dataFormat + " but only NHWC is currently supported."); var convInfo = computeConv3DInfo(x5D.shape, $filter.shape, strides, dilations, pad); var grad = function (dy) { assert(tupleValuesAreOne$1(dilations), 'Error in gradient of conv3D: dilation rates greater than 1 are not' + ("yet supported in gradients. Got dilations '" + dilations + "'")); return { x: function () { return conv3dDerInput_(x5D.shape, dy, $filter, strides, pad); }, $filter: function () { return conv3dDerFilter_(x5D, dy, $filter.shape, strides, pad); } }; }; var res = ENV.engine.runKernel(function (backend) { return backend.conv3d(x5D, $filter, convInfo); }, { x: x5D, $filter: $filter }, grad); if (reshapedTo5D) { return res.as4D(res.shape[1], res.shape[2], res.shape[3], res.shape[4]); } return res; } function conv3dDerInput_(xShape, dy, filter, strides, pad) { assert(xShape.length === dy.rank, "Length of inShape " + ("(" + xShape.length + ") and rank of dy (" + dy.rank + ") must match")); var xShape5D = xShape; var dy5D = dy; var reshapedTo5D = false; if (dy.rank === 4) { reshapedTo5D = true; dy5D = dy.as5D(1, dy.shape[0], dy.shape[1], dy.shape[2], dy.shape[3]); xShape5D = [1, xShape[0], xShape[1], xShape[2], xShape[3]]; } var inDepth = xShape5D[4]; var outDepth = dy5D.shape[4]; assert(xShape5D.length === 5, "Error in conv3dDerInput: inShape must be length 5, but got length " + (xShape5D.length + ".")); assert(dy5D.rank === 5, "Error in conv3dDerInput: dy must be rank 5, but got " + ("rank " + dy5D.rank)); assert(filter.rank === 5, "Error in conv3dDerInput: filter must be rank 5, but got " + ("rank " + filter.rank)); assert(inDepth === filter.shape[3], "Error in conv3dDerInput: depth of input (" + inDepth + ") must " + ("match input depth for filter " + filter.shape[3] + ".")); assert(outDepth === filter.shape[4], "Error in conv3dDerInput: depth of output (" + outDepth + ") must " + ("match output depth for filter " + filter.shape[4] + ".")); var dilations = 1; var convInfo = computeConv3DInfo(xShape5D, filter.shape, strides, dilations, pad); var res = ENV.engine.runKernel(function (backend) { return backend.conv3dDerInput(dy5D, filter, convInfo); }, { dy5D: dy5D }); if (reshapedTo5D) { return res.as4D(res.shape[1], res.shape[2], res.shape[3], res.shape[4]); } return res; } function conv3dDerFilter_(x, dy, filterShape, strides, pad) { var x5D = x; if (x.rank === 4) { x5D = x.as5D(1, x.shape[0], x.shape[1], x.shape[2], x.shape[3]); } var dy5D = dy; if (dy5D.rank === 4) { dy5D = dy.as5D(1, dy.shape[0], dy.shape[1], dy.shape[2], dy.shape[3]); } assert(x5D.rank === 5, "Error in conv3dDerFilter: input must be rank 5, but got shape " + (x5D.shape + ".")); assert(dy5D.rank === 5, "Error in conv3dDerFilter: dy must be rank 5, but got shape " + (dy5D.shape + ".")); assert(filterShape.length === 5, "Error in conv3dDerFilter: filterShape must be length 5, but got " + (filterShape + ".")); assert(x5D.shape[4] === filterShape[3], "Error in conv3dDerFilter: depth of input " + x5D.shape[4] + ") must " + ("match input depth in filter (" + filterShape[3] + ".")); assert(dy5D.shape[4] === filterShape[4], "Error in conv3dDerFilter: depth of dy (" + dy5D.shape[4] + ") must " + ("match output depth for filter (" + filterShape[4] + ").")); var dilations = 1; var convInfo = computeConv3DInfo(x5D.shape, filterShape, strides, dilations, pad); return ENV.engine.runKernel(function (backend) { return backend.conv3dDerFilter(x5D, dy5D, convInfo); }, { x5D: x5D, dy5D: dy5D }); } var conv1d = op({ conv1d_: conv1d_ }); var conv2d = op({ conv2d_: conv2d_ }); var conv3d = op({ conv3d_: conv3d_ }); var conv2dDerFilter = op({ conv2dDerFilter_: conv2dDerFilter_ }); var depthwiseConv2d = op({ depthwiseConv2d_: depthwiseConv2d_ }); var separableConv2d = op({ separableConv2d_: separableConv2d_ }); var conv2dTranspose = op({ conv2dTranspose_: conv2dTranspose_ }); function matMul_(a, b, transposeA, transposeB) { if (transposeA === void 0) { transposeA = false; } if (transposeB === void 0) { transposeB = false; } var _a; var $a = convertToTensor(a, 'a', 'matMul'); var $b = convertToTensor(b, 'b', 'matMul'); _a = makeTypesMatch($a, $b), $a = _a[0], $b = _a[1]; var innerShapeA = transposeA ? $a.shape[$a.rank - 2] : $a.shape[$a.rank - 1]; var innerShapeB = transposeB ? $b.shape[$b.rank - 1] : $b.shape[$b.rank - 2]; var outerShapeA = transposeA ? $a.shape[$a.rank - 1] : $a.shape[$a.rank - 2]; var outerShapeB = transposeB ? $b.shape[$b.rank - 2] : $b.shape[$b.rank - 1]; var outerDimsA = $a.shape.slice(0, -2); var outerDimsB = $b.shape.slice(0, -2); var batchDimA = sizeFromShape(outerDimsA); var batchDimB = sizeFromShape(outerDimsB); assert($a.rank >= 2 && $b.rank >= 2 && $a.rank === $b.rank, "Error in matMul: inputs must have the same rank of at least 2, " + ("got ranks " + $a.rank + " and " + $b.rank + ".")); assert(arraysEqual(outerDimsA, outerDimsB), "Error in matMul: outer dimensions (" + outerDimsA + ") and (" + (outerDimsB + ") of Tensors with shapes " + $a.shape + " and ") + ($b.shape + " must match.")); assert(innerShapeA === innerShapeB, "Error in matMul: inner shapes (" + innerShapeA + ") and (" + (innerShapeB + ") of Tensors with shapes " + $a.shape + " and ") + ($b.shape + " and transposeA=" + transposeA) + (" and transposeB=" + transposeB + " must match.")); var outShape = $a.shape.slice(0, -2).concat([outerShapeA, outerShapeB]); var a3D = transposeA ? $a.as3D(batchDimA, innerShapeA, outerShapeA) : $a.as3D(batchDimA, outerShapeA, innerShapeA); var b3D = transposeB ? $b.as3D(batchDimB, outerShapeB, innerShapeB) : $b.as3D(batchDimB, innerShapeB, outerShapeB); var grad = function (dy) { if (!transposeA && !transposeB) { return { $a: function () { return dy.matMul(b3D, false, true); }, $b: function () { return a3D.matMul(dy, true, false); } }; } else if (!transposeA && transposeB) { return { $a: function () { return dy.matMul(b3D, false, false); }, $b: function () { return dy.matMul(a3D, true, false); } }; } else if (transposeA && !transposeB) { return { $a: function () { return b3D.matMul(dy, false, true); }, $b: function () { return a3D.matMul(dy, false, false); } }; } else { return { $a: function () { return b3D.matMul(dy, true, true); }, $b: function () { return dy.matMul(a3D, true, true); } }; } }; var res = ENV.engine.runKernel(function (backend) { return backend.batchMatMul(a3D, b3D, transposeA, transposeB); }, { $a: a3D, $b: b3D }, grad); return res.reshape(outShape); } function outerProduct_(v1, v2) { var $v1 = convertToTensor(v1, 'v1', 'outerProduct'); var $v2 = convertToTensor(v2, 'v2', 'outerProduct'); assert($v1.rank === 1 && $v2.rank === 1, "Error in outerProduct: inputs must be rank 1, but got ranks " + ($v1.rank + " and " + $v2.rank + ".")); return $v1.as2D(-1, 1).matMul($v2.as2D(1, -1)); } function dot_(t1, t2) { var $t1 = convertToTensor(t1, 't1', 'dot'); var $t2 = convertToTensor(t2, 't2', 'dot'); assert(($t1.rank === 1 || $t1.rank === 2) && ($t2.rank === 1 || $t2.rank === 2), "Error in dot: inputs must all be rank 1 or 2, but got ranks " + ($t1.rank + " and " + $t2.rank + ".")); var t1Inner = ($t1.rank === 1 ? $t1.size : $t1.shape[1]); var t2Inner = ($t2.rank === 1 ? $t2.size : $t2.shape[0]); assert(t1Inner === t2Inner, "Error in dot: inner dimensions of inputs must match, but got " + (t1Inner + " and " + t2Inner + ".")); if ($t1.rank === 1 && $t2.rank === 1) { return $t1.as2D(1, -1).matMul($t2.as2D(-1, 1)).asScalar(); } else if ($t1.rank === 1 && $t2.rank === 2) { return $t1.as2D(1, -1).matMul($t2.as2D($t2.shape[0], $t2.shape[1])).as1D(); } else if ($t1.rank === 2 && $t2.rank === 1) { return $t1.matMul($t2.as2D(-1, 1)).as1D(); } else { return $t1.matMul($t2.as2D($t2.shape[0], $t2.shape[1])); } } var matMul = op({ matMul_: matMul_ }); var dot = op({ dot_: dot_ }); var outerProduct = op({ outerProduct_: outerProduct_ }); function reverse1d_(x) { var $x = convertToTensor(x, 'x', 'reverse'); assert($x.rank === 1, "Error in reverse1D: x must be rank 1 but got\n rank " + $x.rank + "."); return reverse($x, 0); } function reverse2d_(x, axis) { var $x = convertToTensor(x, 'x', 'reverse'); assert($x.rank === 2, "Error in reverse2D: x must be rank 2 but got\n rank " + $x.rank + "."); return reverse($x, axis); } function reverse3d_(x, axis) { var $x = convertToTensor(x, 'x', 'reverse'); assert($x.rank === 3, "Error in reverse3D: x must be rank 3 but got\n rank " + $x.rank + "."); return reverse($x, axis); } function reverse4d_(x, axis) { var $x = convertToTensor(x, 'x', 'reverse'); assert($x.rank === 4, "Error in reverse4D: x must be rank 4 but got\n rank " + $x.rank + "."); return reverse($x, axis); } function reverse_(x, axis) { var $x = convertToTensor(x, 'x', 'reverse'); if ($x.rank === 0) { return $x.clone(); } var axes = parseAxisParam(axis, $x.shape); var grad = function (dy) { return { $x: function () { return dy.reverse(axes); } }; }; var res = ENV.engine.runKernel(function (backend) { return backend.reverse($x, axes); }, { $x: $x }, grad); return res.reshapeAs($x); } var reverse = op({ reverse_: reverse_ }); var reverse1d = op({ reverse1d_: reverse1d_ }); var reverse2d = op({ reverse2d_: reverse2d_ }); var reverse3d = op({ reverse3d_: reverse3d_ }); var reverse4d = op({ reverse4d_: reverse4d_ }); function maxPoolImpl_(x, filterSize, strides, dilations, pad$$1, dimRoundingMode) { var $x = convertToTensor(x, 'x', 'maxPool'); var x4D = $x; var reshapedTo4D = false; if ($x.rank === 3) { reshapedTo4D = true; x4D = $x.as4D(1, $x.shape[0], $x.shape[1], $x.shape[2]); } if (dilations == null) { dilations = [1, 1]; } assert(x4D.rank === 4, "Error in maxPool: input must be rank 4 but got rank " + x4D.rank + "."); assert(eitherStridesOrDilationsAreOne(strides, dilations), 'Error in maxPool: Either strides or dilations must be 1. ' + ("Got strides " + strides + " and dilations '" + dilations + "'")); if (dimRoundingMode != null) { assert(isInt(pad$$1), "Error in maxPool: pad must be an integer when using, " + ("dimRoundingMode " + dimRoundingMode + " but got pad " + pad$$1 + ".")); } var convInfo = computePool2DInfo(x4D.shape, filterSize, strides, dilations, pad$$1, dimRoundingMode); var grad = function (dy, saved) { var y4D = saved[0]; return { x: function () { return maxPoolBackprop(dy, x4D, y4D, filterSize, strides, dilations, pad$$1); } }; }; var res = ENV.engine.runKernel(function (backend, save) { return save(backend.maxPool(x4D, convInfo)); }, { x: x4D }, grad); if (reshapedTo4D) { return res.as3D(res.shape[1], res.shape[2], res.shape[3]); } return res; } function maxPool_(x, filterSize, strides, pad$$1, dimRoundingMode) { return maxPoolImpl_(x, filterSize, strides, 1, pad$$1, dimRoundingMode); } function avgPoolImpl_(x, filterSize, strides, dilations, pad$$1, dimRoundingMode) { var $x = convertToTensor(x, 'x', 'avgPool', 'float32'); if (dilations == null) { dilations = [1, 1]; } assert(eitherStridesOrDilationsAreOne(strides, dilations), 'Error in avgPool: Either strides or dilations must be 1. ' + ("Got strides " + strides + " and dilations '" + dilations + "'")); var x4D = $x; var reshapedTo4D = false; if ($x.rank === 3) { reshapedTo4D = true; x4D = $x.as4D(1, $x.shape[0], $x.shape[1], $x.shape[2]); } assert(x4D.rank === 4, "Error in avgPool: x must be rank 4 but got rank " + x4D.rank + "."); if (dimRoundingMode != null) { assert(isInt(pad$$1), "Error in avgPool: pad must be an integer when using, " + ("dimRoundingMode " + dimRoundingMode + " but got pad " + pad$$1 + ".")); } var convInfo = computePool2DInfo(x4D.shape, filterSize, strides, dilations, pad$$1, dimRoundingMode); var grad = function (dy) { return { x: function () { return avgPoolBackprop(dy, x4D, filterSize, strides, dilations, pad$$1); } }; }; var res = ENV.engine.runKernel(function (backend) { return backend.avgPool(x4D, convInfo); }, { x: x4D }, grad); res = res.cast($x.dtype); if (reshapedTo4D) { return res.as3D(res.shape[1], res.shape[2], res.shape[3]); } return res; } function avgPool_(x, filterSize, strides, pad$$1, dimRoundingMode) { return avgPoolImpl_(x, filterSize, strides, 1, pad$$1, dimRoundingMode); } function pool_(input, windowShape, poolingType, pad$$1, dilations, strides) { if (dilations == null) { dilations = [1, 1]; } if (strides == null) { strides = 1; } if (pad$$1 === 0) { pad$$1 = 'valid'; } var $x = convertToTensor(input, 'x', 'maxPool'); var x4D = $x; var reshapedTo4D = false; if ($x.rank === 3) { reshapedTo4D = true; x4D = $x.as4D(1, $x.shape[0], $x.shape[1], $x.shape[2]); } assert(eitherStridesOrDilationsAreOne(strides, dilations), 'Error in pool: Either strides or dilations must be 1. ' + ("Got strides " + strides + " and dilations '" + dilations + "'")); var convInfo = computePool2DInfo(x4D.shape, windowShape, strides, dilations, pad$$1); var dilation = [convInfo.dilationHeight, convInfo.dilationWidth]; var basePadding; if (pad$$1 === 'same') { basePadding = withSpaceToBatchBasePaddings([convInfo.filterHeight, convInfo.filterWidth], dilation); } else { basePadding = [[0, 0], [0, 0]]; } var isDilationOne = dilation[0] === 1 && dilation[1] === 1; var _a = requiredSpaceToBatchPaddings([convInfo.inHeight, convInfo.inWidth], dilation, basePadding), adjustedPadding = _a[0], adjustedCrops = _a[1]; var convertedPad = isDilationOne ? pad$$1 : 'valid'; var convertedX = isDilationOne ? x4D : spaceToBatchND(x4D, dilation, adjustedPadding); var forwardOp = poolingType === 'avg' ? function () { return avgPoolImpl_(convertedX, windowShape, strides, 1, convertedPad); } : function () { return maxPoolImpl_(convertedX, windowShape, strides, 1, convertedPad); }; var y = forwardOp(); var res = isDilationOne ? y : batchToSpaceND(y, dilation, adjustedCrops); if (reshapedTo4D) { return res.as3D(res.shape[1], res.shape[2], res.shape[3]); } return res; } function maxPoolBackprop(dy, input, output, filterSize, strides, dilations, pad$$1, dimRoundingMode) { var $dy = convertToTensor(dy, 'dy', 'maxPoolBackprop'); var $input = convertToTensor(input, 'input', 'maxPoolBackprop'); var $output = convertToTensor(output, 'output', 'maxPoolBackprop'); assert($input.rank === $dy.rank, "Rank of input (" + $input.rank + ") does not match rank of dy (" + $dy.rank + ")"); if (dilations == null) { dilations = [1, 1]; } assert(eitherStridesOrDilationsAreOne(strides, dilations), 'Error in maxPoolBackProp: Either strides or dilations must be 1. ' + ("Got strides " + strides + " and dilations '" + dilations + "'")); assert($dy.rank === 4, "Error in maxPoolBackprop: dy must be rank 4 but got rank " + ($dy.rank + ".")); assert($input.rank === 4, "Error in maxPoolBackprop: input must be rank 4 but got rank " + ($input.rank + ".")); if (dimRoundingMode != null) { assert(isInt(pad$$1), "Error in maxPoolBackprop: pad must be an integer when using, " + ("dimRoundingMode " + dimRoundingMode + " but got pad " + pad$$1 + ".")); } var convInfo = computePool2DInfo($input.shape, filterSize, strides, dilations, pad$$1, dimRoundingMode); var res = ENV.engine.runKernel(function (backend) { return backend.maxPoolBackprop($dy, $input, $output, convInfo); }, { $dy: $dy, $input: $input }); return res; } function avgPoolBackprop(dy, input, filterSize, strides, dilations, pad$$1) { var $dy = convertToTensor(dy, 'dy', 'avgPoolBackprop'); var $input = convertToTensor(input, 'input', 'avgPoolBackprop'); assert($input.rank === $dy.rank, "Rank of input (" + $input.rank + ") does not match rank of dy (" + $dy.rank + ")"); if (dilations == null) { dilations = [1, 1]; } assert(eitherStridesOrDilationsAreOne(strides, dilations), 'Error in avgPoolBackprop: Either strides or dilations must be 1. ' + ("Got strides " + strides + " and dilations '" + dilations + "'")); var input4D = $input; var dy4D = $dy; var reshapedTo4D = false; if ($input.rank === 3) { reshapedTo4D = true; input4D = $input.as4D(1, $input.shape[0], $input.shape[1], $input.shape[2]); dy4D = $dy.as4D(1, $dy.shape[0], $dy.shape[1], $dy.shape[2]); } assert(dy4D.rank === 4, "Error in avgPoolBackprop: dy must be rank 4 but got rank " + (dy4D.rank + ".")); assert(input4D.rank === 4, "Error in avgPoolBackprop: input must be rank 4 but got rank " + (input4D.rank + ".")); var convInfo = computePool2DInfo(input4D.shape, filterSize, strides, dilations, pad$$1); var res = ENV.engine.runKernel(function (backend) { return backend.avgPoolBackprop(dy4D, input4D, convInfo); }, { dy4D: dy4D, input4D: input4D }); if (reshapedTo4D) { return res.as3D(res.shape[1], res.shape[2], res.shape[3]); } return res; } function requiredSpaceToBatchPaddings(inputShape, blockShape, basePadding) { var padStart = basePadding.map(function (b) { return b[0]; }); var origPadEnd = basePadding.map(function (b) { return b[1]; }); var fullInputShape = inputShape.concat(padStart, origPadEnd); var padEndExtra = blockShape.map(function (b, i) { return (b - fullInputShape[i] % b) % b; }); var padEnd = origPadEnd.map(function (s, i) { return s + padEndExtra[i]; }); var paddings = blockShape.map(function (_, i) { return [padStart[i], padEnd[i]]; }); var crops = blockShape.map(function (_, i) { return [0, padEndExtra[i]]; }); return [paddings, crops]; } function withSpaceToBatchBasePaddings(filterShape, dilation) { var dilatedFilterShape = filterShape.map(function (s, i) { return s + (s - 1) * (dilation[i] - 1); }); var padExtraShape = dilatedFilterShape.map(function (s) { return s - 1; }); var padExtraStart = padExtraShape.map(function (s) { return Math.floor(s / 2); }); var padExtraEnd = padExtraShape.map(function (s, i) { return s - padExtraStart[i]; }); return padExtraShape.map(function (_, i) { return [padExtraStart[i], padExtraEnd[i]]; }); } var maxPool = op({ maxPool_: maxPool_ }); var avgPool = op({ avgPool_: avgPool_ }); var pool = op({ pool_: pool_ }); function slice1d_(x, begin, size) { var $x = convertToTensor(x, 'x', 'slice1d'); assert($x.rank === 1, "slice1d expects a rank-1 tensor, but got a rank-" + $x.rank + " tensor"); return slice($x, [begin], [size]); } function slice2d_(x, begin, size) { var $x = convertToTensor(x, 'x', 'slice2d'); assert($x.rank === 2, "slice2d expects a rank-2 tensor, but got a rank-" + $x.rank + " tensor"); return slice($x, begin, size); } function slice3d_(x, begin, size) { var $x = convertToTensor(x, 'x', 'slice3d'); assert($x.rank === 3, "slice3d expects a rank-3 tensor, but got a rank-" + $x.rank + " tensor"); return slice($x, begin, size); } function slice4d_(x, begin, size) { var $x = convertToTensor(x, 'x', 'slice4d'); assert($x.rank === 4, "slice4d expects a rank-4 tensor, but got a rank-" + $x.rank + " tensor"); return slice($x, begin, size); } function slice_(x, begin, size) { var $x = convertToTensor(x, 'x', 'slice'); if ($x.rank === 0) { throw new Error('Slicing scalar is not possible'); } var begin_; if (typeof begin === 'number') { begin_ = [begin].concat(new Array($x.rank - 1).fill(0)); } else if (begin.length < $x.rank) { begin_ = begin.concat(new Array($x.rank - begin.length).fill(0)); } else { begin_ = begin.slice(); } var size_; if (size == null) { size_ = new Array($x.rank).fill(-1); } else if (typeof size === 'number') { size_ = [size].concat(new Array($x.rank - 1).fill(-1)); } else if (size.length < $x.rank) { size_ = size.concat(new Array($x.rank - size.length).fill(-1)); } else { size_ = size; } size_ = size_.map(function (d, i) { if (d >= 0) { return d; } else { assert(d === -1, 'Bad value in size'); return $x.shape[i] - begin_[i]; } }); assertParamsValid($x, begin_, size_); var inputShape = $x.shape; var grad = function (dy) { var paddings = []; for (var i = 0; i < dy.rank; i++) { paddings.push([begin_[i], inputShape[i] - begin_[i] - size_[i]]); } return { $x: function () { return dy.pad(paddings); } }; }; return ENV.engine.runKernel(function (backend) { return backend.slice($x, begin_, size_); }, { $x: $x }, grad); } var slice = op({ slice_: slice_ }); var slice1d = op({ slice1d_: slice1d_ }); var slice2d = op({ slice2d_: slice2d_ }); var slice3d = op({ slice3d_: slice3d_ }); var slice4d = op({ slice4d_: slice4d_ }); function logSumExp_(x, axis, keepDims) { if (axis === void 0) { axis = null; } if (keepDims === void 0) { keepDims = false; } var $x = convertToTensor(x, 'x', 'logSumExp'); var axes = parseAxisParam(axis, $x.shape); var xMax = $x.max(axes, true); var a = $x.sub(xMax); var b = a.exp(); var c = b.sum(axes); var d = c.log(); var res = xMax.reshape(d.shape).add(d); if (keepDims) { var newShape = expandShapeToKeepDim(res.shape, axes); return res.reshape(newShape); } return res; } function sum_(x, axis, keepDims) { if (axis === void 0) { axis = null; } if (keepDims === void 0) { keepDims = false; } var $x = convertToTensor(x, 'x', 'sum'); if ($x.dtype === 'bool') { $x = $x.toInt(); } var axes = parseAxisParam(axis, $x.shape); var customOp = customGrad(function (x) { var permutation = getAxesPermutation(axes, x.rank); var reductionAxes = axes; var permutedX = x; if (permutation != null) { permutedX = x.transpose(permutation); reductionAxes = getInnerMostAxes(reductionAxes.length, x.rank); } var value = ENV.engine.runKernel(function (backend) { return backend.sum(permutedX, reductionAxes); }, { permutedX: permutedX }); if (keepDims) { var newShape = expandShapeToKeepDim(value.shape, axes); value = value.reshape(newShape); } var gradFunc = function (dy) { var expandedDyShape = x.shape.slice(); axes.forEach(function (axis) { expandedDyShape[axis] = 1; }); var expandedDy = dy.reshape(expandedDyShape); var derX = expandedDy.mul(ones$1(x.shape, 'float32')); return derX; }; return { value: value, gradFunc: gradFunc }; }); return customOp($x); } function prod_(x, axis, keepDims) { if (axis === void 0) { axis = null; } if (keepDims === void 0) { keepDims = false; } var $x = convertToTensor(x, 'x', 'prod'); if ($x.dtype === 'bool') { $x = $x.toInt(); } var axes = parseAxisParam(axis, $x.shape); var permutation = getAxesPermutation(axes, $x.rank); var reductionAxes = axes; var permutedX = $x; if (permutation != null) { permutedX = $x.transpose(permutation); reductionAxes = getInnerMostAxes(reductionAxes.length, $x.rank); } var value = ENV.engine.runKernel(function (backend) { return backend.prod(permutedX, reductionAxes); }, { permutedX: permutedX }); if (keepDims) { var newShape = expandShapeToKeepDim(value.shape, axes); value = value.reshape(newShape); } return value; } function mean_(x, axis, keepDims) { if (axis === void 0) { axis = null; } if (keepDims === void 0) { keepDims = false; } var $x = convertToTensor(x, 'x', 'mean'); var axes = parseAxisParam(axis, $x.shape); var shapes = computeOutAndReduceShapes($x.shape, axes); var reduceShape = shapes[1]; var reduceSize = sizeFromShape(reduceShape); var customOp = customGrad(function (x) { var reduceSizeScalar = scalar(reduceSize); var xReduce = reduceSizeScalar.dtype === x.dtype ? x : x.cast(reduceSizeScalar.dtype); var res = xReduce.div(reduceSizeScalar); var value = res.sum(axis, keepDims); var gradFunc = function (dy) { var expandedDyShape = x.shape.slice(); axes.forEach(function (axis) { expandedDyShape[axis] = 1; }); var expandedDy = dy.reshape(expandedDyShape); var derX = expandedDy.mul(ones$1(x.shape, 'float32')).div(reduceSizeScalar); return derX; }; return { value: value, gradFunc: gradFunc }; }); return customOp($x); } function gradForMinAndMax(dy, saved, xOrig, origAxes, permutedAxes) { var y = saved[0]; if (y.rank < xOrig.rank) { y = y.reshape(expandShapeToKeepDim(y.shape, origAxes)); } if (dy.rank < xOrig.rank) { dy = dy.reshape(expandShapeToKeepDim(dy.shape, origAxes)); } return { $x: function () { var dx = dy.mul(xOrig.equal(y).cast(dy.dtype)); return permutedAxes == null ? dx : dx.transpose(permutedAxes); } }; } function min_(x, axis, keepDims) { if (axis === void 0) { axis = null; } if (keepDims === void 0) { keepDims = false; } var $x = convertToTensor(x, 'x', 'min'); var xOrig = $x; var origAxes = parseAxisParam(axis, $x.shape); var axes = origAxes; var permutedAxes = getAxesPermutation(axes, $x.rank); if (permutedAxes != null) { $x = $x.transpose(permutedAxes); axes = getInnerMostAxes(axes.length, $x.rank); } var grad$$1 = function (dy, saved) { return gradForMinAndMax(dy, saved, xOrig, origAxes, permutedAxes); }; var res = ENV.engine.runKernel(function (backend, save) { return save(backend.min($x, axes)); }, { $x: $x }, grad$$1); if (keepDims) { var newShape = expandShapeToKeepDim(res.shape, origAxes); res = res.reshape(newShape); } return res; } function max_(x, axis, keepDims) { if (axis === void 0) { axis = null; } if (keepDims === void 0) { keepDims = false; } var $x = convertToTensor(x, 'x', 'max'); var xOrig = $x; var origAxes = parseAxisParam(axis, $x.shape); var axes = origAxes; var permutedAxes = getAxesPermutation(axes, $x.rank); if (permutedAxes != null) { $x = $x.transpose(permutedAxes); axes = getInnerMostAxes(axes.length, $x.rank); } var grad$$1 = function (dy, saved) { return gradForMinAndMax(dy, saved, xOrig, origAxes, permutedAxes); }; var res = ENV.engine.runKernel(function (backend, save) { return save(backend.max($x, axes)); }, { $x: $x }, grad$$1); if (keepDims) { var newShape = expandShapeToKeepDim(res.shape, origAxes); res = res.reshape(newShape); } return res; } function argMin_(x, axis) { if (axis === void 0) { axis = 0; } var $x = convertToTensor(x, 'x', 'argMin'); if (axis == null) { axis = 0; } var axes = parseAxisParam(axis, $x.shape); var permutedAxes = getAxesPermutation(axes, $x.rank); if (permutedAxes != null) { $x = $x.transpose(permutedAxes); axes = getInnerMostAxes(axes.length, $x.rank); } var grad$$1 = function (dy) { return { $x: function () { return zerosLike($x); } }; }; return ENV.engine.runKernel(function (backend) { return backend.argMin($x, axes[0]); }, { $x: $x }, grad$$1); } function argMax_(x, axis) { if (axis === void 0) { axis = 0; } var $x = convertToTensor(x, 'x', 'argMax'); if (axis == null) { axis = 0; } var axes = parseAxisParam(axis, $x.shape); var permutedAxes = getAxesPermutation(axes, $x.rank); if (permutedAxes != null) { $x = $x.transpose(permutedAxes); axes = getInnerMostAxes(axes.length, $x.rank); } var grad$$1 = function (dy) { return { $x: function () { return zerosLike($x); } }; }; return ENV.engine.runKernel(function (backend) { return backend.argMax($x, axes[0]); }, { $x: $x }, grad$$1); } function all_(x, axis, keepDims) { if (axis === void 0) { axis = null; } if (keepDims === void 0) { keepDims = false; } var $x = convertToTensor(x, 'x', 'all', 'bool'); var origAxes = parseAxisParam(axis, $x.shape); var axes = origAxes; var permutedAxes = getAxesPermutation(axes, $x.rank); if (permutedAxes != null) { $x = $x.transpose(permutedAxes); axes = getInnerMostAxes(axes.length, $x.rank); } var res = ENV.engine.runKernel(function (backend) { return backend.all($x, axes); }, { $x: $x }); if (keepDims) { var newShape = expandShapeToKeepDim(res.shape, origAxes); return res.reshape(newShape); } return res; } function any_(x, axis, keepDims) { if (axis === void 0) { axis = null; } if (keepDims === void 0) { keepDims = false; } var $x = convertToTensor(x, 'x', 'any', 'bool'); var origAxes = parseAxisParam(axis, $x.shape); var axes = origAxes; var permutedAxes = getAxesPermutation(axes, $x.rank); if (permutedAxes != null) { $x = $x.transpose(permutedAxes); axes = getInnerMostAxes(axes.length, $x.rank); } var res = ENV.engine.runKernel(function (backend) { return backend.any($x, axes); }, { $x: $x }); if (keepDims) { var newShape = expandShapeToKeepDim(res.shape, origAxes); return res.reshape(newShape); } return res; } function moments_(x, axis, keepDims) { if (axis === void 0) { axis = null; } if (keepDims === void 0) { keepDims = false; } x = convertToTensor(x, 'x', 'moments'); var axes = parseAxisParam(axis, x.shape); var mean = x.mean(axes, keepDims); var keepDimsShape = mean.shape; if (!keepDims) { keepDimsShape = expandShapeToKeepDim(mean.shape, axes); } var devSquared = x.toFloat().sub(mean.reshape(keepDimsShape)).square(); var variance = devSquared.mean(axes, keepDims); return { mean: mean, variance: variance }; } var all = op({ all_: all_ }); var any = op({ any_: any_ }); var argMax = op({ argMax_: argMax_ }); var argMin = op({ argMin_: argMin_ }); var logSumExp = op({ logSumExp_: logSumExp_ }); var max = op({ max_: max_ }); var mean = op({ mean_: mean_ }); var min = op({ min_: min_ }); var moments = op({ moments_: moments_ }); var sum$1 = op({ sum_: sum_ }); var prod = op({ prod_: prod_ }); function notEqual_(a, b) { var _a; var $a = convertToTensor(a, 'a', 'notEqual'); var $b = convertToTensor(b, 'b', 'notEqual'); _a = makeTypesMatch($a, $b), $a = _a[0], $b = _a[1]; assertAndGetBroadcastShape($a.shape, $b.shape); return ENV.engine.runKernel(function (backend) { return backend.notEqual($a, $b); }, { $a: $a, $b: $b }); } function notEqualStrict_(a, b) { var $a = convertToTensor(a, 'a', 'notEqualStrict'); var $b = convertToTensor(b, 'b', 'notEqualStrict'); assertShapesMatch($a.shape, $b.shape, 'Error in notEqualStrict: '); return $a.notEqual($b); } function less_(a, b) { var _a; var $a = convertToTensor(a, 'a', 'less'); var $b = convertToTensor(b, 'b', 'less'); _a = makeTypesMatch($a, $b), $a = _a[0], $b = _a[1]; assertAndGetBroadcastShape($a.shape, $b.shape); return ENV.engine.runKernel(function (backend) { return backend.less($a, $b); }, { $a: $a, $b: $b }); } function lessStrict_(a, b) { var $a = convertToTensor(a, 'a', 'lessStrict'); var $b = convertToTensor(b, 'b', 'lessStrict'); assertShapesMatch($a.shape, $b.shape, 'Error in lessStrict: '); return $a.less($b); } function equal_(a, b) { var _a; var $a = convertToTensor(a, 'a', 'equal'); var $b = convertToTensor(b, 'b', 'equal'); _a = makeTypesMatch($a, $b), $a = _a[0], $b = _a[1]; assertAndGetBroadcastShape($a.shape, $b.shape); return ENV.engine.runKernel(function (backend) { return backend.equal($a, $b); }, { $a: $a, $b: $b }); } function equalStrict_(a, b) { var $a = convertToTensor(a, 'a', 'equalStrict'); var $b = convertToTensor(b, 'b', 'equalStrict'); assertShapesMatch($a.shape, $b.shape, 'Error in equalStrict: '); return $a.equal($b); } function lessEqual_(a, b) { var _a; var $a = convertToTensor(a, 'a', 'lessEqual'); var $b = convertToTensor(b, 'b', 'lessEqual'); _a = makeTypesMatch($a, $b), $a = _a[0], $b = _a[1]; assertAndGetBroadcastShape($a.shape, $b.shape); return ENV.engine.runKernel(function (backend) { return backend.lessEqual($a, $b); }, { $a: $a, $b: $b }); } function lessEqualStrict_(a, b) { var $a = convertToTensor(a, 'a', 'lessEqualStrict'); var $b = convertToTensor(b, 'b', 'lessEqualStrict'); assertShapesMatch($a.shape, $b.shape, 'Error in lessEqualStrict: '); return $a.lessEqual($b); } function greater_(a, b) { var _a; var $a = convertToTensor(a, 'a', 'greater'); var $b = convertToTensor(b, 'b', 'greater'); _a = makeTypesMatch($a, $b), $a = _a[0], $b = _a[1]; assertAndGetBroadcastShape($a.shape, $b.shape); return ENV.engine.runKernel(function (backend) { return backend.greater($a, $b); }, { $a: $a, $b: $b }); } function greaterStrict_(a, b) { var $a = convertToTensor(a, 'a', 'greaterStrict'); var $b = convertToTensor(b, 'b', 'greaterStrict'); assertShapesMatch($a.shape, $b.shape, 'Error in greaterStrict: '); return $a.greater($b); } function greaterEqual_(a, b) { var _a; var $a = convertToTensor(a, 'a', 'greaterEqual'); var $b = convertToTensor(b, 'b', 'greaterEqual'); _a = makeTypesMatch($a, $b), $a = _a[0], $b = _a[1]; assertAndGetBroadcastShape($a.shape, $b.shape); var grad = function (dy) { return { $a: function () { return zerosLike($a); }, $b: function () { return zerosLike($b); } }; }; return ENV.engine.runKernel(function (backend) { return backend.greaterEqual($a, $b); }, { $a: $a, $b: $b }, grad); } function greaterEqualStrict_(a, b) { var $a = convertToTensor(a, 'a', 'greaterEqualStrict'); var $b = convertToTensor(b, 'b', 'greaterEqualStrict'); assertShapesMatch($a.shape, $b.shape, 'Error in greaterEqualStrict: '); return $a.greaterEqual($b); } var equal = op({ equal_: equal_ }); var equalStrict = op({ equalStrict_: equalStrict_ }); var greater = op({ greater_: greater_ }); var greaterEqual = op({ greaterEqual_: greaterEqual_ }); var greaterEqualStrict = op({ greaterEqualStrict_: greaterEqualStrict_ }); var greaterStrict = op({ greaterStrict_: greaterStrict_ }); var less = op({ less_: less_ }); var lessEqual = op({ lessEqual_: lessEqual_ }); var lessEqualStrict = op({ lessEqualStrict_: lessEqualStrict_ }); var lessStrict = op({ lessStrict_: lessStrict_ }); var notEqual = op({ notEqual_: notEqual_ }); var notEqualStrict = op({ notEqualStrict_: notEqualStrict_ }); function add_(a, b) { var _a; var $a = convertToTensor(a, 'a', 'add'); var $b = convertToTensor(b, 'b', 'add'); _a = makeTypesMatch($a, $b), $a = _a[0], $b = _a[1]; var outShape = assertAndGetBroadcastShape($a.shape, $b.shape); var der = function (dy) { var derA = function () { var res = dy; var reduceAxes = getReductionAxes($a.shape, outShape); if (reduceAxes.length > 0) { res = res.sum(reduceAxes); } return res.reshape($a.shape); }; var derB = function () { var res = dy; var reduceAxes = getReductionAxes($b.shape, outShape); if (reduceAxes.length > 0) { res = res.sum(reduceAxes); } return res.reshape($b.shape); }; return { $a: derA, $b: derB }; }; return ENV.engine.runKernel(function (backend) { return backend.add($a, $b); }, { $a: $a, $b: $b }, der); } function addN_(tensors) { assert(Array.isArray(tensors), function () { return 'The argument passed to tf.addN() must be a list of tensors'; }); assert(tensors.length >= 1, function () { return "Must pass at least one tensor to tf.addN(), but got " + ("" + tensors.length); }); var $tensors = tensors.map(function (t, i) { return convertToTensor(t, "tensors" + i, 'addN'); }); var firstTensor = $tensors[0]; $tensors.forEach(function (t) { if (t.dtype !== firstTensor.dtype) { throw new Error('All tensors passed to tf.addN() must have the same dtype'); } }); $tensors.forEach(function (t) { if (!arraysEqual(t.shape, firstTensor.shape)) { throw new Error('All tensors passed to tf.addN() must have the same shape'); } }); var der = function (dy) { var ders = {}; $tensors.forEach(function (t, i) { ders[i] = function () { return dy.clone(); }; }); return ders; }; var inputs = $tensors; return ENV.engine.runKernel(function (backend) { return backend.addN($tensors); }, inputs, der); } function addStrict_(a, b) { var $a = convertToTensor(a, 'a', 'addStrict'); var $b = convertToTensor(b, 'b', 'addStrict'); assertShapesMatch($a.shape, $b.shape, 'Error in addStrict: '); return $a.add($b); } function sub_(a, b) { var _a; var $a = convertToTensor(a, 'a', 'sub'); var $b = convertToTensor(b, 'b', 'sub'); _a = makeTypesMatch($a, $b), $a = _a[0], $b = _a[1]; var outShape = assertAndGetBroadcastShape($a.shape, $b.shape); var der = function (dy) { var derA = function () { var res = dy; var reduceAxes = getReductionAxes($a.shape, outShape); if (reduceAxes.length > 0) { res = res.sum(reduceAxes); } return res.reshape($a.shape); }; var derB = function () { var res = dy; var reduceAxes = getReductionAxes($b.shape, outShape); if (reduceAxes.length > 0) { res = res.sum(reduceAxes); } return res.neg().reshape($b.shape); }; return { $a: derA, $b: derB }; }; return ENV.engine.runKernel(function (backend) { return backend.subtract($a, $b); }, { $a: $a, $b: $b }, der); } function subStrict_(a, b) { var $a = convertToTensor(a, 'a', 'subStrict'); var $b = convertToTensor(b, 'b', 'subStrict'); assertShapesMatch($a.shape, $b.shape, 'Error in subStrict: '); return $a.sub($b); } function pow_(base, exp$$1) { var $base = convertToTensor(base, 'base', 'pow'); var $exp = convertToTensor(exp$$1, 'exp', 'pow'); var outShape = assertAndGetBroadcastShape($base.shape, $exp.shape); base = $base.cast(upcastType($base.dtype, $exp.dtype)); exp$$1 = $exp.cast(upcastType($base.dtype, $exp.dtype)); var grad = function (dy, saved) { var y = saved[0]; var derBase = function () { var expFloat = $exp.toFloat(); var res = dy.mul(expFloat.mul($base.pow(expFloat.sub(scalar(1))))); var reduceAxes = getReductionAxes($base.shape, outShape); if (reduceAxes.length > 0) { res = res.sum(reduceAxes); } return res.reshape($base.shape); }; var derExp = function () { var condition = $base.greater(0); var logBase = $base.log().where(condition, zerosLike($base)); var res = dy.mul(y.mul(logBase)); var reduceAxes = getReductionAxes($exp.shape, outShape); if (reduceAxes.length > 0) { res = res.sum(reduceAxes); } return res.reshape($exp.shape); }; return { $base: derBase, $exp: derExp }; }; return ENV.engine.runKernel(function (backend, save) { return save(backend.pow($base, $exp)); }, { $base: $base, $exp: $exp }, grad); } function powStrict_(base, exp$$1) { assertShapesMatch(base.shape, exp$$1.shape, 'Error in powStrict: '); return base.pow(exp$$1); } function mul_(a, b) { var _a; var $a = convertToTensor(a, 'a', 'mul'); var $b = convertToTensor(b, 'b', 'mul'); _a = makeTypesMatch($a, $b), $a = _a[0], $b = _a[1]; var outShape = assertAndGetBroadcastShape($a.shape, $b.shape); var der = function (dy) { var derA = function () { var res = dy.mul($b.toFloat()); var reduceAxes = getReductionAxes($a.shape, outShape); if (reduceAxes.length > 0) { return res.sum(reduceAxes).reshape($a.shape); } return res; }; var derB = function () { var res = dy.mul($a.toFloat()); var reduceAxes = getReductionAxes($b.shape, outShape); if (reduceAxes.length > 0) { return res.sum(reduceAxes).reshape($b.shape); } return res; }; return { $a: derA, $b: derB }; }; return ENV.engine.runKernel(function (backend) { return backend.multiply($a, $b); }, { $a: $a, $b: $b }, der); } function mulStrict_(a, b) { var $a = convertToTensor(a, 'a', 'mul'); var $b = convertToTensor(b, 'b', 'mul'); assertShapesMatch($a.shape, $b.shape, 'Error in multiplyStrict: '); return $a.mul($b); } function div_(a, b) { var _a; var $a = convertToTensor(a, 'a', 'div'); var $b = convertToTensor(b, 'b', 'div'); _a = makeTypesMatch($a, $b), $a = _a[0], $b = _a[1]; var forwardFunc; if ($a.dtype === 'int32' && $b.dtype === 'int32') { return floorDiv($a, $b); } else { forwardFunc = function (backend) { return backend.realDivide($a, $b); }; } var outShape = assertAndGetBroadcastShape($a.shape, $b.shape); var der = function (dy) { var derA = function () { var res = dy.div($b.toFloat()); var reduceAxes = getReductionAxes($a.shape, outShape); if (reduceAxes.length > 0) { return res.sum(reduceAxes).reshape($a.shape); } return res; }; var derB = function () { var res = dy.mul($a.toFloat()); var reduceAxes = getReductionAxes($b.shape, outShape); if (reduceAxes.length > 0) { res = res.sum(reduceAxes).reshape($b.shape); } var tmp = $b.square(); return res.div(tmp.toFloat()).neg(); }; return { $a: derA, $b: derB }; }; return ENV.engine.runKernel(forwardFunc, { $a: $a, $b: $b }, der); } function floorDiv_(a, b) { var _a; var $a = convertToTensor(a, 'a', 'floorDiv'); var $b = convertToTensor(b, 'b', 'floorDiv'); _a = makeTypesMatch($a, $b), $a = _a[0], $b = _a[1]; var forwardFunc = function (backend) { return backend.floorDiv($a, $b); }; var outShape = assertAndGetBroadcastShape($a.shape, $b.shape); var der = function (dy) { var derA = function () { var res = dy.div($b.toFloat()); var reduceAxes = getReductionAxes($a.shape, outShape); if (reduceAxes.length > 0) { return res.sum(reduceAxes).reshape($a.shape); } return res; }; var derB = function () { var res = dy.mul($a.toFloat()); var reduceAxes = getReductionAxes($b.shape, outShape); if (reduceAxes.length > 0) { res = res.sum(reduceAxes).reshape($b.shape); } var tmp = $b.square(); return res.div(tmp.toFloat()).neg(); }; return { $a: derA, $b: derB }; }; return ENV.engine.runKernel(forwardFunc, { $a: $a, $b: $b }, der); } function divStrict_(a, b) { var $a = convertToTensor(a, 'a', 'div'); var $b = convertToTensor(b, 'b', 'div'); assertShapesMatch($a.shape, $b.shape, 'Error in divideStrict: '); return $a.div($b); } function mod_(a, b) { var _a; var $a = convertToTensor(a, 'a', 'mod'); var $b = convertToTensor(b, 'b', 'mod'); _a = makeTypesMatch($a, $b), $a = _a[0], $b = _a[1]; var outShape = assertAndGetBroadcastShape($a.shape, $b.shape); var der = function (dy) { var derA = function () { var reduceAxes = getReductionAxes($a.shape, outShape); if (reduceAxes.length > 0) { return dy.sum(reduceAxes).reshape($a.shape); } return dy; }; var derB = function () { var res = dy.mul($a.div($b).floor().neg()); var reduceAxes = getReductionAxes($b.shape, outShape); if (reduceAxes.length > 0) { return res.sum(reduceAxes).reshape($b.shape); } return res; }; return { $a: derA, $b: derB }; }; return ENV.engine.runKernel(function (backend) { return backend.mod($a, $b); }, { $a: $a, $b: $b }, der); } function modStrict_(a, b) { var $a = convertToTensor(a, 'a', 'modStrict'); var $b = convertToTensor(b, 'b', 'modStrict'); assertShapesMatch($a.shape, $b.shape, 'Error in modStrict: '); return $a.mod($b); } function minimum_(a, b) { var _a; var $a = convertToTensor(a, 'a', 'minimum'); var $b = convertToTensor(b, 'b', 'minimum'); _a = makeTypesMatch($a, $b), $a = _a[0], $b = _a[1]; if ($a.dtype === 'bool') { $a = $a.toInt(); $b = $b.toInt(); } assertAndGetBroadcastShape($a.shape, $b.shape); var der = function (dy) { var derA = function () { return dy.mul($a.lessEqual($b).toFloat()); }; var derB = function () { return dy.mul($a.greater($b).toFloat()); }; return { $a: derA, $b: derB }; }; return ENV.engine.runKernel(function (backend) { return backend.minimum($a, $b); }, { $a: $a, $b: $b }, der); } function minimumStrict_(a, b) { var $a = convertToTensor(a, 'a', 'minimumStrict'); var $b = convertToTensor(b, 'b', 'minimumStrict'); assertShapesMatch($a.shape, $b.shape, 'Error in minimumStrict: '); return $a.minimum($b); } function maximum_(a, b) { var _a; var $a = convertToTensor(a, 'a', 'maximum'); var $b = convertToTensor(b, 'b', 'maximum'); _a = makeTypesMatch($a, $b), $a = _a[0], $b = _a[1]; if ($a.dtype === 'bool') { $a = $a.toInt(); $b = $b.toInt(); } assertAndGetBroadcastShape($a.shape, $b.shape); var der = function (dy) { var derA = function () { return dy.mul($a.greaterEqual($b).toFloat()); }; var derB = function () { return dy.mul($a.less($b).toFloat()); }; return { $a: derA, $b: derB }; }; return ENV.engine.runKernel(function (backend) { return backend.maximum($a, $b); }, { $a: $a, $b: $b }, der); } function maximumStrict_(a, b) { var $a = convertToTensor(a, 'a', 'maximumStrict'); var $b = convertToTensor(b, 'b', 'maximumStrict'); assertShapesMatch($a.shape, $b.shape, 'Error in maximumStrict: '); return $a.maximum($b); } function squaredDifference_(a, b) { var _a; var $a = convertToTensor(a, 'a', 'squaredDifference'); var $b = convertToTensor(b, 'b', 'squaredDifference'); _a = makeTypesMatch($a, $b), $a = _a[0], $b = _a[1]; assertAndGetBroadcastShape($a.shape, $b.shape); var der = function (dy) { var two = scalar(2); var derA = function () { return dy.mul($a.sub($b).mul(two)); }; var derB = function () { return dy.mul($b.sub($a).mul(two)); }; return { $a: derA, $b: derB }; }; return ENV.engine.runKernel(function (backend) { return backend.squaredDifference($a, $b); }, { $a: $a, $b: $b }, der); } function squaredDifferenceStrict_(a, b) { var $a = convertToTensor(a, 'a', 'squaredDifferenceStrict'); var $b = convertToTensor(b, 'b', 'squaredDifferenceStrict'); assertShapesMatch($a.shape, $b.shape, 'Error in squaredDifferenceStrict: '); return $a.squaredDifference($b); } function atan2_(a, b) { var _a; var $a = convertToTensor(a, 'a', 'atan2'); var $b = convertToTensor(b, 'b', 'atan2'); _a = makeTypesMatch($a, $b), $a = _a[0], $b = _a[1]; var outShape = assertAndGetBroadcastShape($a.shape, $b.shape); var der = function (dy) { var derA = function () { var d = add($a.square(), $b.square()); var res = dy.mul($b.div(d)); var reduceAxes = getReductionAxes($a.shape, outShape); if (reduceAxes.length > 0) { res = res.sum(reduceAxes); } return res.reshape($a.shape); }; var derB = function () { var d = add($a.square(), $b.square()); var res = neg(dy.mul($a.div(d))); var reduceAxes = getReductionAxes($b.shape, outShape); if (reduceAxes.length > 0) { res = res.sum(reduceAxes); } return res.reshape($b.shape); }; return { $a: derA, $b: derB }; }; return ENV.engine.runKernel(function (backend) { return backend.atan2($a, $b); }, { $a: $a, $b: $b }, der); } var add = op({ add_: add_ }); var addN = op({ addN_: addN_ }); var addStrict = op({ addStrict_: addStrict_ }); var atan2 = op({ atan2_: atan2_ }); var div = op({ div_: div_ }); var divStrict = op({ divStrict_: divStrict_ }); var floorDiv = op({ floorDiv_: floorDiv_ }); var maximum = op({ maximum_: maximum_ }); var maximumStrict = op({ maximumStrict_: maximumStrict_ }); var minimum = op({ minimum_: minimum_ }); var minimumStrict = op({ minimumStrict_: minimumStrict_ }); var mod = op({ mod_: mod_ }); var modStrict = op({ modStrict_: modStrict_ }); var mul = op({ mul_: mul_ }); var mulStrict = op({ mulStrict_: mulStrict_ }); var pow = op({ pow_: pow_ }); var powStrict = op({ powStrict_: powStrict_ }); var squaredDifference = op({ squaredDifference_: squaredDifference_ }); var squaredDifferenceStrict = op({ squaredDifferenceStrict_: squaredDifferenceStrict_ }); var sub = op({ sub_: sub_ }); var subStrict = op({ subStrict_: subStrict_ }); function logicalNot_(x) { var $x = convertToTensor(x, 'x', 'logicalNot', 'bool'); return ENV.engine.runKernel(function (backend) { return backend.logicalNot($x); }, { $x: $x }); } function logicalAnd_(a, b) { var $a = convertToTensor(a, 'a', 'logicalAnd', 'bool'); var $b = convertToTensor(b, 'b', 'logicalAnd', 'bool'); assertAndGetBroadcastShape($a.shape, $b.shape); return ENV.engine.runKernel(function (backend) { return backend.logicalAnd($a, $b); }, { $a: $a, $b: $b }); } function logicalOr_(a, b) { var $a = convertToTensor(a, 'a', 'logicalOr', 'bool'); var $b = convertToTensor(b, 'b', 'logicalOr', 'bool'); assertAndGetBroadcastShape($a.shape, $b.shape); return ENV.engine.runKernel(function (backend) { return backend.logicalOr($a, $b); }, { $a: $a, $b: $b }); } function logicalXor_(a, b) { var $a = convertToTensor(a, 'a', 'logicalXor', 'bool'); var $b = convertToTensor(b, 'b', 'logicalXor', 'bool'); assertAndGetBroadcastShape($a.shape, $b.shape); return logicalOr(a, b).logicalAnd(logicalAnd(a, b).logicalNot()); } function where_(condition, a, b) { var $a = convertToTensor(a, 'a', 'where'); var $b = convertToTensor(b, 'b', 'where'); var $condition = convertToTensor(condition, 'condition', 'where', 'bool'); assertShapesMatch($a.shape, $b.shape, 'Error in where: '); if ($condition.rank === 1) { assert($condition.shape[0] === $a.shape[0], 'The first dimension of `a` must match the size of `condition`.'); } else { assertShapesMatch($condition.shape, $b.shape, 'Error in where: '); } var grad = function (dy) { return ({ $condition: function () { return zerosLike($condition).toFloat(); }, $a: function () { return dy.mul($condition.cast(dy.dtype)); }, $b: function () { return dy.mul($condition.logicalNot().cast(dy.dtype)); } }); }; return ENV.engine.runKernel(function (backend) { return backend.select($condition, $a, $b); }, { $condition: $condition, $a: $a, $b: $b }, grad); } function whereAsync_(condition) { return __awaiter(this, void 0, void 0, function () { var $condition, vals, res; return __generator(this, function (_a) { switch (_a.label) { case 0: $condition = convertToTensor(condition, 'condition', 'whereAsync', 'bool'); return [4, $condition.data()]; case 1: vals = _a.sent(); res = whereImpl($condition.shape, vals); if (condition !== $condition) { $condition.dispose(); } return [2, res]; } }); }); } var logicalAnd = op({ logicalAnd_: logicalAnd_ }); var logicalNot = op({ logicalNot_: logicalNot_ }); var logicalOr = op({ logicalOr_: logicalOr_ }); var logicalXor = op({ logicalXor_: logicalXor_ }); var where = op({ where_: where_ }); var whereAsync = whereAsync_; function relu_(x) { var $x = convertToTensor(x, 'x', 'relu'); if ($x.dtype === 'bool') { return $x.toInt(); } var grad = function (dy) { var stepRes = $x.step(); return { $x: function () { return dy.mulStrict(stepRes.toFloat()); } }; }; return ENV.engine.runKernel(function (backend) { return backend.relu($x); }, { $x: $x }, grad); } function elu_(x) { var $x = convertToTensor(x, 'x', 'elu'); var grad = function (dy, saved) { var y = saved[0]; return { $x: function () { return ENV.engine.runKernel(function (backend) { return backend.eluDer(dy, y); }, { dy: dy, y: y }); } }; }; return ENV.engine.runKernel(function (backend, save) { return save(backend.elu($x)); }, { $x: $x }, grad); } function selu_(x) { var $x = convertToTensor(x, 'x', 'selu'); var grad = function (dy) { return { $x: function () { var mask = $x.greater(scalar(0)); var scaleAlpha = scalar(SELU_SCALEALPHA); var scale = scalar(SELU_SCALE); var greaterThanZeroDer = dy.mul(scale); var lessEqualZeroDer = dy.mul(scaleAlpha).mul($x.toFloat().exp()); return where(mask, greaterThanZeroDer, lessEqualZeroDer); } }; }; return ENV.engine.runKernel(function (backend) { return backend.selu($x); }, { $x: $x }, grad); } function leakyRelu_(x, alpha) { if (alpha === void 0) { alpha = 0.2; } var $x = convertToTensor(x, 'x', 'leakyRelu'); return maximum(scalar(alpha).mul($x), $x); } function prelu_(x, alpha) { var $x = convertToTensor(x, 'x', 'prelu'); var $alpha = convertToTensor(alpha, 'alpha', 'prelu'); var grad = function (dy) { var mask = $x.greater(0); return { $x: function () { return where(mask, dy, dy.mul($alpha)); }, $alpha: function () { var res = where(mask, zerosLike(dy), dy.mul($x)); var reduceAxes = getReductionAxes($alpha.shape, dy.shape); if (reduceAxes.length > 0) { res = res.sum(reduceAxes); } return res.reshape($alpha.shape); } }; }; return ENV.engine.runKernel(function (backend) { return backend.prelu($x, $alpha); }, { $x: $x, $alpha: $alpha }, grad); } var elu = op({ elu_: elu_ }); var leakyRelu = op({ leakyRelu_: leakyRelu_ }); var prelu = op({ prelu_: prelu_ }); var relu = op({ relu_: relu_ }); var selu = op({ selu_: selu_ }); function transpose_(x, perm) { var $x = convertToTensor(x, 'x', 'transpose'); if (perm == null) { perm = $x.shape.map(function (s, i) { return i; }).reverse(); } assert($x.rank === perm.length, "Error in transpose: rank of input " + $x.rank + " " + ("must match length of perm " + perm + ".")); perm.forEach(function (axis) { assert(axis >= 0 && axis < $x.rank, "All entries in 'perm' must be between 0 and " + ($x.rank - 1) + (" but got " + perm)); }); if ($x.rank <= 1) { return $x.clone(); } var der = function (dy) { var undoPerm = getUndoAxesPermutation(perm); return { $x: function () { return dy.transpose(undoPerm); } }; }; return ENV.engine.runKernel(function (backend) { return backend.transpose($x, perm); }, { $x: $x }, der); } var transpose = op({ transpose_: transpose_ }); function localResponseNormalization_(x, depthRadius, bias, alpha, beta) { if (depthRadius === void 0) { depthRadius = 5; } if (bias === void 0) { bias = 1; } if (alpha === void 0) { alpha = 1; } if (beta === void 0) { beta = 0.5; } var $x = convertToTensor(x, 'x', 'localResponseNormalization'); assert($x.rank === 4 || $x.rank === 3, "Error in localResponseNormalization: x must be rank 3 or 4 but got\n rank " + $x.rank + "."); assert(isInt(depthRadius), "Error in localResponseNormalization: depthRadius must be an integer\n but got depthRadius " + depthRadius + "."); var x4D = $x; var reshapedTo4D = false; if ($x.rank === 3) { reshapedTo4D = true; x4D = $x.as4D(1, $x.shape[0], $x.shape[1], $x.shape[2]); } var backward = function (dy, saved) { var outputImage = saved[0]; return { x4D: function () { return ENV.engine.runKernel(function (backend) { return backend.LRNGrad(dy, x4D, outputImage, depthRadius, bias, alpha, beta); }, {}); } }; }; var res = ENV.engine.runKernel(function (backend, save) { return save(backend.localResponseNormalization4D(x4D, depthRadius, bias, alpha, beta)); }, { x4D: x4D }, backward); if (reshapedTo4D) { return res.as3D(res.shape[1], res.shape[2], res.shape[3]); } else { return res; } } var localResponseNormalization = op({ localResponseNormalization_: localResponseNormalization_ }); function norm_(x, ord, axis, keepDims) { if (ord === void 0) { ord = 'euclidean'; } if (axis === void 0) { axis = null; } if (keepDims === void 0) { keepDims = false; } x = convertToTensor(x, 'x', 'norm'); var norm = normImpl(x, ord, axis); var keepDimsShape = norm.shape; if (keepDims) { var axes = parseAxisParam(axis, x.shape); keepDimsShape = expandShapeToKeepDim(norm.shape, axes); } return norm.reshape(keepDimsShape); } function normImpl(x, p, axis) { if (axis === void 0) { axis = null; } if (x.rank === 0) { return x.abs(); } if (x.rank !== 1 && axis === null) { return normImpl(x.reshape([-1]), p, axis); } if (x.rank === 1 || typeof axis === 'number' || Array.isArray(axis) && axis.length === 1) { if (p === 1) { return x.abs().sum(axis); } if (p === Infinity) { return x.abs().max(axis); } if (p === -Infinity) { return x.abs().min(axis); } if (p === 'euclidean' || p === 2) { return x.abs().pow(scalar(2, 'int32')).sum(axis).sqrt(); } throw new Error("Error in norm: invalid ord value: " + p); } if (Array.isArray(axis) && axis.length === 2) { if (p === 1) { return x.abs().sum(axis[0]).max(axis[1] - 1); } if (p === Infinity) { return x.abs().sum(axis[1]).max(axis[0]); } if (p === -Infinity) { return x.abs().sum(axis[1]).min(axis[0]); } if (p === 'fro' || p === 'euclidean') { return x.square().sum(axis).sqrt(); } throw new Error("Error in norm: invalid ord value: " + p); } throw new Error("Error in norm: invalid axis: " + axis); } var norm = op({ norm_: norm_ }); function unsortedSegmentSum_(x, segmentIds, numSegments) { var $x = convertToTensor(x, 'x', 'unsortedSegmentSum'); var $segmentIds = convertToTensor(segmentIds, 'segmentIds', 'unsortedSegmentSum', 'int32'); assert(isInt(numSegments), 'numSegments must be of dtype int'); var gradFunc = function (dy) { var derX = function () { return gatherDropNegatives(dy, $segmentIds); }; return { $x: derX }; }; return ENV.engine.runKernel(function (backend) { return backend.unsortedSegmentSum($x, $segmentIds, numSegments); }, { $x: $x }, gradFunc); } function gather_(x, indices, axis) { if (axis === void 0) { axis = 0; } var $x = convertToTensor(x, 'x', 'gather'); var $indices = convertToTensor(indices, 'indices', 'gather', 'int32'); axis = parseAxisParam(axis, $x.shape)[0]; var shapeInfo = collectGatherOpShapeInfo($x, $indices, axis); var grad = function (dy) { var derX = function () { var paramsShape = $x.shape; var indicesSize = $indices.size; var outerShape = paramsShape.slice(0, axis); var outerDims = outerShape.length; var innerShape = paramsShape.slice(axis, paramsShape.length).slice(1); var innerDims = innerShape.length; var outerAxesIndices = arrayRange(0, outerDims); var innerAxesIndices = arrayRange(outerDims + 1, outerDims + 1 + innerDims); var valuesShape = arrayConcat([outerShape, [indicesSize], innerShape]); var values = dy.reshape(valuesShape); var reshapedIndices = $indices.reshape([indicesSize]); var transposeDims = arrayConcat([[outerDims], outerAxesIndices, innerAxesIndices]); var valuesTranspose = values.transpose(transposeDims); var paramsGrad = unsortedSegmentSum(valuesTranspose, reshapedIndices, $x.shape[axis]); var invertTransposeDims = getUndoAxesPermutation(transposeDims); paramsGrad = paramsGrad.transpose(invertTransposeDims); return paramsGrad; }; return { $x: derX }; }; return (ENV.engine.runKernel(function (backend) { return backend.gather($x, $indices.flatten(), axis); }, { $x: $x }, grad)) .reshape(shapeInfo.outputShape); } function arrayRange(start, stop) { var result = []; for (var i = start; i < stop; ++i) { result.push(i); } return result; } function arrayConcat(arrays) { var result = []; for (var i = 0; i < arrays.length; ++i) { for (var j = 0; j < arrays[i].length; ++j) { result.push(arrays[i][j]); } } return result; } function gatherDropNegatives(x, indices) { var zeroClippedIndices = maximum(indices, zerosLike(indices)); var gathered = gather(x, zeroClippedIndices); var isPositive = greaterEqual(indices, scalar(0, 'int32')); var numIters = gathered.rank - isPositive.rank; for (var i = 0; i < numIters; ++i) { isPositive = expandDims(isPositive, i + 1); } isPositive = logicalAnd(isPositive, ones$1(gathered.shape, 'bool')); var zeroSlice = zerosLike(gathered); return where(isPositive, gathered, zeroSlice); } var gather = op({ gather_: gather_ }); var unsortedSegmentSum = op({ unsortedSegmentSum_: unsortedSegmentSum_ }); function multiRNNCell_(lstmCells, data, c, h) { var $data = convertToTensor(data, 'data', 'multiRNNCell'); var $c = convertToTensorArray(c, 'c', 'multiRNNCell'); var $h = convertToTensorArray(h, 'h', 'multiRNNCell'); var input = $data; var newStates = []; for (var i = 0; i < lstmCells.length; i++) { var output = lstmCells[i](input, $c[i], $h[i]); newStates.push(output[0]); newStates.push(output[1]); input = output[1]; } var newC = []; var newH = []; for (var i = 0; i < newStates.length; i += 2) { newC.push(newStates[i]); newH.push(newStates[i + 1]); } return [newC, newH]; } function basicLSTMCell_(forgetBias, lstmKernel, lstmBias, data, c, h) { var $forgetBias = convertToTensor(forgetBias, 'forgetBias', 'basicLSTMCell'); var $lstmKernel = convertToTensor(lstmKernel, 'lstmKernel', 'basicLSTMCell'); var $lstmBias = convertToTensor(lstmBias, 'lstmBias', 'basicLSTMCell'); var $data = convertToTensor(data, 'data', 'basicLSTMCell'); var $c = convertToTensor(c, 'c', 'basicLSTMCell'); var $h = convertToTensor(h, 'h', 'basicLSTMCell'); var combined = $data.concat($h, 1); var weighted = combined.matMul($lstmKernel); var res = weighted.add($lstmBias); var batchSize = res.shape[0]; var sliceCols = res.shape[1] / 4; var sliceSize = [batchSize, sliceCols]; var i = res.slice([0, 0], sliceSize); var j = res.slice([0, sliceCols], sliceSize); var f = res.slice([0, sliceCols * 2], sliceSize); var o = res.slice([0, sliceCols * 3], sliceSize); var newC = i.sigmoid().mulStrict(j.tanh()).addStrict($c.mulStrict($forgetBias.add(f).sigmoid())); var newH = newC.tanh().mulStrict(o.sigmoid()); return [newC, newH]; } var basicLSTMCell = op({ basicLSTMCell_: basicLSTMCell_ }); var multiRNNCell = op({ multiRNNCell_: multiRNNCell_ }); function movingAverage_(v, x, decay, step, zeroDebias) { if (zeroDebias === void 0) { zeroDebias = true; } var $v = convertToTensor(v, 'v', 'movingAverage'); var $x = convertToTensor(x, 'x', 'movingAverage'); var $decay = convertToTensor(decay, 'decay', 'movingAverage'); assertTypesMatch($v, $x); assert(arraysEqual($v.shape, $x.shape), 'Shape mismatch in v and x'); var one = scalar(1); var oneMinusDecay = one.sub($decay); var update = $x.sub($v).mul(oneMinusDecay); if (zeroDebias) { assert(step != null, 'When using zeroDebias: true, step is required.'); var $step = convertToTensor(step, 'step', 'movingAverage'); update = update.div(one.sub(pow($decay, $step))); } return $v.add(update); } var movingAverage = op({ movingAverage_: movingAverage_ }); function stridedSlice_(x, begin, end, strides, beginMask, endMask, ellipsisMask, newAxisMask, shrinkAxisMask) { if (beginMask === void 0) { beginMask = 0; } if (endMask === void 0) { endMask = 0; } if (ellipsisMask === void 0) { ellipsisMask = 0; } if (newAxisMask === void 0) { newAxisMask = 0; } if (shrinkAxisMask === void 0) { shrinkAxisMask = 0; } if (ellipsisMask !== 0) { throw new Error('ellipsis mask is not yet supported'); } if (newAxisMask !== 0) { throw new Error('new axis mask is not yet supported'); } var $x = convertToTensor(x, 'x', 'stridedSlice'); var nonStrided = strides.every(function (v) { return v === 1; }); if (nonStrided) { var _a = getStridedSlicedInfo($x.shape, begin, end, strides, beginMask, endMask, ellipsisMask, newAxisMask, shrinkAxisMask), beginIndex = _a[0], size = _a[1], shrinkAxis_1 = _a[2]; var outShape = size.filter(function (_, index) { return shrinkAxis_1.indexOf(index) === -1; }); return slice($x, beginIndex, size).reshape(outShape); } return ENV.engine.runKernel(function (backend) { return backend.stridedSlice($x, begin, end, strides, beginMask, endMask, ellipsisMask, newAxisMask, shrinkAxisMask); }, { $x: $x }); } var stridedSlice = op({ stridedSlice_: stridedSlice_ }); function topk_(x, k, sorted) { if (k === void 0) { k = 1; } if (sorted === void 0) { sorted = true; } var $x = convertToTensor(x, 'x', 'topk'); if ($x.rank === 0) { throw new Error('topk() expects the input to be of rank 1 or higher'); } var lastDim = $x.shape[$x.shape.length - 1]; if (k > lastDim) { throw new Error("'k' passed to topk() must be <= the last dimension (" + lastDim + ") " + ("but got " + k)); } var _a = ENV.engine.runKernel(function (b) { return b.topk($x, k, sorted); }, { $x: $x }), values = _a[0], indices = _a[1]; return { values: values, indices: indices }; } var topk = op({ topk_: topk_ }); function scatterND_(indices, updates, shape) { var $indices = convertToTensor(indices, 'indices', 'scatterND', 'int32'); var $updates = convertToTensor(updates, 'updates', 'scatterND'); validateInput($updates, $indices, shape); return ENV.engine.runKernel(function (backend) { return backend.scatterND($indices, $updates, shape); }, { $indices: $indices, $updates: $updates }); } var scatterND = op({ scatterND_: scatterND_ }); function fft_(input) { assert(input.dtype === 'complex64', "The dtype for tf.spectral.fft() must be complex64 " + ("but got " + input.dtype + ".")); var innerDimensionSize = input.shape[input.shape.length - 1]; var batch = input.size / innerDimensionSize; var input2D = input.as2D(batch, innerDimensionSize); var ret = ENV.engine.runKernel(function (backend) { return backend.fft(input2D); }, { input: input }); return ret.reshape(input.shape); } function ifft_(input) { assert(input.dtype === 'complex64', "The dtype for tf.spectral.ifft() must be complex64 " + ("but got " + input.dtype + ".")); var innerDimensionSize = input.shape[input.shape.length - 1]; var batch = input.size / innerDimensionSize; var input2D = input.as2D(batch, innerDimensionSize); var ret = ENV.engine.runKernel(function (backend) { return backend.ifft(input2D); }, { input: input }); return ret.reshape(input.shape); } function rfft_(input) { assert(input.dtype === 'float32', "The dtype for rfft() must be real value but\n got " + input.dtype); var innerDimensionSize = input.shape[input.shape.length - 1]; var batch = input.size / innerDimensionSize; var zeros$$1 = input.zerosLike(); var complexInput = complex(input, zeros$$1).as2D(batch, innerDimensionSize); var ret = fft(complexInput); var half = Math.floor(innerDimensionSize / 2) + 1; var realValues = real(ret); var imagValues = imag(ret); var realComplexConjugate = realValues.split([half, innerDimensionSize - half], realValues.shape.length - 1); var imagComplexConjugate = imagValues.split([half, innerDimensionSize - half], imagValues.shape.length - 1); var outputShape = input.shape.slice(); outputShape[input.shape.length - 1] = half; return complex(realComplexConjugate[0], imagComplexConjugate[0]) .reshape(outputShape); } function irfft_(input) { var innerDimensionSize = input.shape[input.shape.length - 1]; var batch = input.size / innerDimensionSize; if (innerDimensionSize <= 2) { var complexInput = input.as2D(batch, innerDimensionSize); var ret = ifft(complexInput); return real(ret); } else { var outputShape = [batch, 2 * (innerDimensionSize - 1)]; var realInput = real(input).as2D(batch, innerDimensionSize); var imagInput = imag(input).as2D(batch, innerDimensionSize); var realConjugate = realInput.slice([0, 1], [batch, innerDimensionSize - 2]).reverse(1); var imagConjugate = imagInput.slice([0, 1], [batch, innerDimensionSize - 2]) .reverse(1) .mul(scalar(-1)); var r = realInput.concat(realConjugate, 1); var i = imagInput.concat(imagConjugate, 1); var complexInput = complex(r, i).as2D(outputShape[0], outputShape[1]); var ret = ifft(complexInput); return real(ret); } } var fft = op({ fft_: fft_ }); var ifft = op({ ifft_: ifft_ }); var rfft = op({ rfft_: rfft_ }); var irfft = op({ irfft_: irfft_ }); var spectral_ops = /*#__PURE__*/Object.freeze({ fft: fft, ifft: ifft, rfft: rfft, irfft: irfft }); function validateInput$1(sparseIndices, sparseValues, outputShape, defaultValues) { if (sparseIndices.dtype !== 'int32') { throw new Error('tf.sparseToDense() expects the indices to be int32 type,' + (" but the dtype was " + sparseIndices.dtype + ".")); } if (sparseIndices.rank > 2) { throw new Error('sparseIndices should be a scalar, vector, or matrix,' + (" but got shape " + sparseIndices.shape + ".")); } var numElems = sparseIndices.rank > 0 ? sparseIndices.shape[0] : 1; var numDims = sparseIndices.rank > 1 ? sparseIndices.shape[1] : 1; if (outputShape.length !== numDims) { throw new Error('outputShape has incorrect number of elements:,' + (" " + outputShape.length + ", should be: " + numDims + ".")); } var numValues = sparseValues.size; if (!(sparseValues.rank === 0 || sparseValues.rank === 1 && numValues === numElems)) { throw new Error('sparseValues has incorrect shape ' + (sparseValues.shape + ", should be [] or [" + numElems + "]")); } if (sparseValues.dtype !== defaultValues.dtype) { throw new Error('sparseValues.dtype must match defaultValues.dtype'); } } function sparseToDense_(sparseIndices, sparseValues, outputShape, defaultValue) { var $sparseIndices = convertToTensor(sparseIndices, 'sparseIndices', 'sparseToDense', 'int32'); var $sparseValues = convertToTensor(sparseValues, 'sparseValues', 'sparseToDense'); var $defaultValue = convertToTensor(defaultValue, 'defaultValue', 'sparseToDense', $sparseValues.dtype); validateInput$1($sparseIndices, $sparseValues, outputShape, $defaultValue); return ENV.engine.runKernel(function (backend) { return backend.sparseToDense($sparseIndices, $sparseValues, outputShape, $defaultValue); }, { $sparseIndices: $sparseIndices, $sparseValues: $sparseValues, $defaultValue: $defaultValue }); } var sparseToDense = op({ sparseToDense_: sparseToDense_ }); function gatherND_(x, indices) { var $indices = convertToTensor(indices, 'indices', 'gatherND', 'int32'); var $x = convertToTensor(x, 'x', 'gatherND'); return ENV.engine.runKernel(function (backend) { return backend.gatherND($x, $indices); }, { $x: $x, $indices: $indices }); } var gatherND = op({ gatherND_: gatherND_ }); (function (Reduction) { Reduction[Reduction["NONE"] = 0] = "NONE"; Reduction[Reduction["MEAN"] = 1] = "MEAN"; Reduction[Reduction["SUM"] = 2] = "SUM"; Reduction[Reduction["SUM_BY_NONZERO_WEIGHTS"] = 3] = "SUM_BY_NONZERO_WEIGHTS"; })(exports.Reduction || (exports.Reduction = {})); function computeWeightedLoss_(losses, weights, reduction) { if (reduction === void 0) { reduction = exports.Reduction.SUM_BY_NONZERO_WEIGHTS; } var $losses = convertToTensor(losses, 'losses', 'computeWeightedLoss'); var $weights = null; if (weights != null) { $weights = convertToTensor(weights, 'weights', 'computeWeightedLoss'); } var weightedLoss = ($weights == null) ? $losses : $losses.mul($weights); if (reduction === exports.Reduction.NONE) { return weightedLoss; } if (reduction === exports.Reduction.SUM) { return weightedLoss.sum(); } if (reduction === exports.Reduction.MEAN) { if ($weights == null) { return weightedLoss.mean(); } else { var broadcastFactor = sizeFromShape($losses.shape) / sizeFromShape($weights.shape); var result = weightedLoss.sum().div($weights.sum()); return broadcastFactor > 1 ? result.div(scalar(broadcastFactor)) : result; } } if (reduction === exports.Reduction.SUM_BY_NONZERO_WEIGHTS) { if ($weights == null) { return weightedLoss.sum().div(scalar($losses.size)); } else { var broadcastedWeights = $weights.mul(ones$1($losses.shape)); var numNonZeros = broadcastedWeights.notEqual(scalar(0)).sum().toFloat(); return weightedLoss.sum().div(numNonZeros); } } throw Error("Unknown reduction: " + reduction); } function absoluteDifference_(labels, predictions, weights, reduction) { if (reduction === void 0) { reduction = exports.Reduction.SUM_BY_NONZERO_WEIGHTS; } var $labels = convertToTensor(labels, 'labels', 'absoluteDifference'); var $predictions = convertToTensor(predictions, 'predictions', 'absoluteDifference'); var $weights = null; if (weights != null) { $weights = convertToTensor(weights, 'weights', 'absoluteDifference'); } assertShapesMatch($labels.shape, $predictions.shape, 'Error in absoluteDifference: '); var losses = $labels.sub($predictions).abs(); return computeWeightedLoss(losses, $weights, reduction); } function meanSquaredError_(labels, predictions, weights, reduction) { if (reduction === void 0) { reduction = exports.Reduction.SUM_BY_NONZERO_WEIGHTS; } var $labels = convertToTensor(labels, 'labels', 'meanSquaredError'); var $predictions = convertToTensor(predictions, 'predictions', 'meanSquaredError'); var $weights = null; if (weights != null) { $weights = convertToTensor(weights, 'weights', 'meanSquaredError'); } assertShapesMatch($labels.shape, $predictions.shape, 'Error in meanSquaredError: '); var losses = $labels.squaredDifference($predictions); return computeWeightedLoss(losses, $weights, reduction); } function cosineDistance_(labels, predictions, axis, weights, reduction) { if (reduction === void 0) { reduction = exports.Reduction.SUM_BY_NONZERO_WEIGHTS; } var $labels = convertToTensor(labels, 'labels', 'cosineDistance'); var $predictions = convertToTensor(predictions, 'predictions', 'cosineDistance'); var $weights = null; if (weights != null) { $weights = convertToTensor(weights, 'weights', 'cosineDistance'); } assertShapesMatch($labels.shape, $predictions.shape, 'Error in cosineDistance: '); var one = scalar(1); var losses = one.sub($labels.mul($predictions).sum(axis, true)); return computeWeightedLoss(losses, $weights, reduction); } function hingeLoss_(labels, predictions, weights, reduction) { if (reduction === void 0) { reduction = exports.Reduction.SUM_BY_NONZERO_WEIGHTS; } var $labels = convertToTensor(labels, 'labels', 'hingeLoss'); var $predictions = convertToTensor(predictions, 'predictions', 'hingeLoss'); var $weights = null; if (weights != null) { $weights = convertToTensor(weights, 'weights', 'hingeLoss'); } assertShapesMatch($labels.shape, $predictions.shape, 'Error in hingeLoss: '); var one = scalar(1); $labels = scalar(2).mul($labels).sub(one); var losses = one.sub($labels.mul($predictions)).relu(); return computeWeightedLoss(losses, $weights, reduction); } function logLoss_(labels, predictions, weights, epsilon, reduction) { if (epsilon === void 0) { epsilon = 1e-7; } if (reduction === void 0) { reduction = exports.Reduction.SUM_BY_NONZERO_WEIGHTS; } var $labels = convertToTensor(labels, 'labels', 'logLoss'); var $predictions = convertToTensor(predictions, 'predictions', 'logLoss'); var $weights = null; if (weights != null) { $weights = convertToTensor(weights, 'weights', 'logLoss'); } assertShapesMatch($labels.shape, $predictions.shape, 'Error in logLoss: '); var one = scalar(1); var epsilonScalar = scalar(epsilon); var losses = $labels.mul($predictions.add(epsilonScalar).log()) .neg() .sub(one.sub($labels).mul(one.sub($predictions).add(epsilonScalar).log())); return computeWeightedLoss(losses, $weights, reduction); } function sigmoidCrossEntropyWithLogits_(labels, logits) { var $labels = convertToTensor(labels, 'labels', 'sigmoidCrossEntropyWithLogits'); var $logits = convertToTensor(logits, 'logits', 'sigmoidCrossEntropyWithLogits'); assertShapesMatch($labels.shape, $logits.shape, 'Error in sigmoidCrossEntropyWithLogits: '); var maxOutput = $logits.relu(); var outputXTarget = $logits.mul($labels); var sigmoidOutput = $logits.abs().neg().exp().log1p(); return maxOutput.sub(outputXTarget).add(sigmoidOutput); } function sigmoidCrossEntropy_(multiClassLabels, logits, weights, labelSmoothing, reduction) { if (labelSmoothing === void 0) { labelSmoothing = 0; } if (reduction === void 0) { reduction = exports.Reduction.SUM_BY_NONZERO_WEIGHTS; } var $multiClassLabels = convertToTensor(multiClassLabels, 'multiClassLabels', 'sigmoidCrossEntropy'); var $logits = convertToTensor(logits, 'logits', 'sigmoidCrossEntropy'); var $weights = null; if (weights != null) { $weights = convertToTensor(weights, 'weights', 'sigmoidCrossEntropy'); } assertShapesMatch($multiClassLabels.shape, $logits.shape, 'Error in sigmoidCrossEntropy: '); if (labelSmoothing > 0) { var labelSmoothingScalar = scalar(labelSmoothing); var one = scalar(1); var half = scalar(0.5); $multiClassLabels = $multiClassLabels.mul(one.sub(labelSmoothingScalar)) .add(half.mul(labelSmoothingScalar)); } var losses = sigmoidCrossEntropyWithLogits_($multiClassLabels, $logits); return computeWeightedLoss(losses, $weights, reduction); } function huberLoss_(labels, predictions, weights, delta, reduction) { if (delta === void 0) { delta = 1.0; } if (reduction === void 0) { reduction = exports.Reduction.SUM_BY_NONZERO_WEIGHTS; } var $labels = convertToTensor(labels, 'labels', 'huberLoss'); var $predictions = convertToTensor(predictions, 'predictions', 'huberLoss'); var $weights = null; if (weights != null) { $weights = convertToTensor(weights, 'weights', 'huberLoss'); } assertShapesMatch($labels.shape, $predictions.shape, 'Error in huberLoss: '); var deltaScalar = scalar(delta); var error = $predictions.sub($labels).abs(); var quadratic = minimum(error, deltaScalar); var linear = error.sub(quadratic); var losses = scalar(0.5).mul(quadratic.square()).add(deltaScalar.mul(linear)); return computeWeightedLoss(losses, $weights, reduction); } function softmaxCrossEntropyWithLogits_(labels, logits, dim) { if (dim === void 0) { dim = -1; } if (dim === -1) { dim = logits.rank - 1; } if (dim !== logits.rank - 1) { throw Error("Softmax cross entropy along a non-last dimension is not yet " + ("supported. Labels / logits was rank " + logits.rank + " ") + ("and dim was " + dim)); } var customOp = customGrad(function (labels, logits) { var keepDims = true; var lse = logits.logSumExp([dim], keepDims); var logResult = logits.toFloat().sub(lse); var costVector = logResult.mul(labels).neg(); var value = costVector.sum([dim]); var gradFunc = function (dy) { var dyShape = expandShapeToKeepDim(dy.shape, [dim]); return [ dy.reshape(dyShape).mul(labels.toFloat().sub(logResult.exp())), dy.reshape(dyShape).mul(logResult.exp().sub(labels.toFloat())), ]; }; return { value: value, gradFunc: gradFunc }; }); return customOp(labels, logits); } function softmaxCrossEntropy_(onehotLabels, logits, weights, labelSmoothing, reduction) { if (labelSmoothing === void 0) { labelSmoothing = 0; } if (reduction === void 0) { reduction = exports.Reduction.SUM_BY_NONZERO_WEIGHTS; } var $onehotLabels = convertToTensor(onehotLabels, 'onehotLabels', 'softmaxCrossEntropy'); var $logits = convertToTensor(logits, 'logits', 'softmaxCrossEntropy'); var $weights = null; if (weights != null) { $weights = convertToTensor(weights, 'weights', 'softmaxCrossEntropy'); } assertShapesMatch($onehotLabels.shape, $logits.shape, 'Error in softmaxCrossEntropy: '); if (labelSmoothing > 0) { var labelSmoothingScalar = scalar(labelSmoothing); var one = scalar(1); var numClasses = scalar($onehotLabels.shape[1]); $onehotLabels = $onehotLabels.mul(one.sub(labelSmoothingScalar)) .add(labelSmoothingScalar.div(numClasses)); } var losses = softmaxCrossEntropyWithLogits_($onehotLabels, $logits); return computeWeightedLoss(losses, $weights, reduction); } var absoluteDifference = op({ absoluteDifference_: absoluteDifference_ }); var computeWeightedLoss = op({ computeWeightedLoss_: computeWeightedLoss_ }); var cosineDistance = op({ cosineDistance_: cosineDistance_ }); var hingeLoss = op({ hingeLoss_: hingeLoss_ }); var huberLoss = op({ huberLoss_: huberLoss_ }); var logLoss = op({ logLoss_: logLoss_ }); var meanSquaredError = op({ meanSquaredError_: meanSquaredError_ }); var sigmoidCrossEntropy = op({ sigmoidCrossEntropy_: sigmoidCrossEntropy_ }); var softmaxCrossEntropy = op({ softmaxCrossEntropy_: softmaxCrossEntropy_ }); var loss_ops = /*#__PURE__*/Object.freeze({ get Reduction () { return exports.Reduction; }, absoluteDifference: absoluteDifference, computeWeightedLoss: computeWeightedLoss, cosineDistance: cosineDistance, hingeLoss: hingeLoss, huberLoss: huberLoss, logLoss: logLoss, meanSquaredError: meanSquaredError, sigmoidCrossEntropy: sigmoidCrossEntropy, softmaxCrossEntropy: softmaxCrossEntropy }); function gramSchmidt_(xs) { var inputIsTensor2D; if (Array.isArray(xs)) { inputIsTensor2D = false; assert(xs != null && xs.length > 0, 'Gram-Schmidt process: input must not be null, undefined, or empty'); var dim = xs[0].shape[0]; for (var i = 1; i < xs.length; ++i) { assert(xs[i].shape[0] === dim, 'Gram-Schmidt: Non-unique lengths found in the input vectors: ' + ("(" + xs[i].shape[0] + " vs. " + dim + ")")); } } else { inputIsTensor2D = true; xs = split$1(xs, xs.shape[0], 0).map(function (x) { return squeeze(x, [0]); }); } assert(xs.length <= xs[0].shape[0], "Gram-Schmidt: Number of vectors (" + xs.length + ") exceeds " + ("number of dimensions (" + xs[0].shape[0] + ").")); var ys = []; var xs1d = xs; var _loop_1 = function (i) { ys.push(ENV.engine.tidy(function () { var x = xs1d[i]; if (i > 0) { for (var j = 0; j < i; ++j) { var proj = sum$1(ys[j].mulStrict(x)).mul(ys[j]); x = x.sub(proj); } } return x.div(norm(x, 'euclidean')); })); }; for (var i = 0; i < xs.length; ++i) { _loop_1(i); } if (inputIsTensor2D) { return stack(ys, 0); } else { return ys; } } function qr_(x, fullMatrices) { if (fullMatrices === void 0) { fullMatrices = false; } if (x.rank < 2) { throw new Error("qr() requires input tensor to have a rank >= 2, but got rank " + x.rank); } else if (x.rank === 2) { return qr2d(x, fullMatrices); } else { var outerDimsProd = x.shape.slice(0, x.shape.length - 2) .reduce(function (value, prev) { return value * prev; }); var x2ds = unstack(x.reshape([ outerDimsProd, x.shape[x.shape.length - 2], x.shape[x.shape.length - 1] ]), 0); var q2ds_1 = []; var r2ds_1 = []; x2ds.forEach(function (x2d) { var _a = qr2d(x2d, fullMatrices), q2d = _a[0], r2d = _a[1]; q2ds_1.push(q2d); r2ds_1.push(r2d); }); var q = stack(q2ds_1, 0).reshape(x.shape); var r = stack(r2ds_1, 0).reshape(x.shape); return [q, r]; } } function qr2d(x, fullMatrices) { if (fullMatrices === void 0) { fullMatrices = false; } return ENV.engine.tidy(function () { if (x.shape.length !== 2) { throw new Error("qr2d() requires a 2D Tensor, but got a " + x.shape.length + "D Tensor."); } var m = x.shape[0]; var n = x.shape[1]; var q = eye(m); var r = x.clone(); var one2D = tensor2d([[1]], [1, 1]); var w = one2D.clone(); var iters = m >= n ? n : m; var _loop_2 = function (j) { var _a; var rTemp = r; var wTemp = w; var qTemp = q; _a = ENV.engine.tidy(function () { var rjEnd1 = r.slice([j, j], [m - j, 1]); var normX = rjEnd1.norm(); var rjj = r.slice([j, j], [1, 1]); var s = rjj.sign().neg(); var u1 = rjj.sub(s.mul(normX)); var wPre = rjEnd1.div(u1); if (wPre.shape[0] === 1) { w = one2D.clone(); } else { w = one2D.concat(wPre.slice([1, 0], [wPre.shape[0] - 1, wPre.shape[1]]), 0); } var tau = s.matMul(u1).div(normX).neg(); var rjEndAll = r.slice([j, 0], [m - j, n]); var tauTimesW = tau.mul(w); if (j === 0) { r = rjEndAll.sub(tauTimesW.matMul(w.transpose().matMul(rjEndAll))); } else { r = r.slice([0, 0], [j, n]) .concat(rjEndAll.sub(tauTimesW.matMul(w.transpose().matMul(rjEndAll))), 0); } var qAllJEnd = q.slice([0, j], [m, q.shape[1] - j]); if (j === 0) { q = qAllJEnd.sub(qAllJEnd.matMul(w).matMul(tauTimesW.transpose())); } else { q = q.slice([0, 0], [m, j]) .concat(qAllJEnd.sub(qAllJEnd.matMul(w).matMul(tauTimesW.transpose())), 1); } return [w, r, q]; }), w = _a[0], r = _a[1], q = _a[2]; dispose([rTemp, wTemp, qTemp]); }; for (var j = 0; j < iters; ++j) { _loop_2(j); } if (!fullMatrices && m > n) { q = q.slice([0, 0], [m, n]); r = r.slice([0, 0], [n, n]); } return [q, r]; }); } var gramSchmidt = op({ gramSchmidt_: gramSchmidt_ }); var qr = op({ qr_: qr_ }); var linalg_ops = /*#__PURE__*/Object.freeze({ gramSchmidt: gramSchmidt, qr: qr }); function resizeBilinear_(images, size, alignCorners) { if (alignCorners === void 0) { alignCorners = false; } var $images = convertToTensor(images, 'images', 'resizeBilinear'); assert($images.rank === 3 || $images.rank === 4, "Error in resizeBilinear: x must be rank 3 or 4, but got " + ("rank " + $images.rank + ".")); assert(size.length === 2, "Error in resizeBilinear: new shape must 2D, but got shape " + (size + ".")); var batchImages = $images; var reshapedTo4D = false; if ($images.rank === 3) { reshapedTo4D = true; batchImages = $images.as4D(1, $images.shape[0], $images.shape[1], $images.shape[2]); } var newHeight = size[0], newWidth = size[1]; var forward = function (backend, save) { return backend.resizeBilinear(batchImages, newHeight, newWidth, alignCorners); }; var backward = function (dy, saved) { return { batchImages: function () { return ENV.engine.runKernel(function (backend) { return backend.resizeBilinearBackprop(dy, batchImages, alignCorners); }, {}); } }; }; var res = ENV.engine.runKernel(forward, { batchImages: batchImages }, backward); if (reshapedTo4D) { return res.as3D(res.shape[1], res.shape[2], res.shape[3]); } return res; } function resizeNearestNeighbor_(images, size, alignCorners) { if (alignCorners === void 0) { alignCorners = false; } var $images = convertToTensor(images, 'images', 'resizeNearestNeighbor'); assert($images.rank === 3 || $images.rank === 4, "Error in resizeNearestNeighbor: x must be rank 3 or 4, but got " + ("rank " + $images.rank + ".")); assert(size.length === 2, "Error in resizeNearestNeighbor: new shape must 2D, but got shape " + (size + ".")); assert($images.dtype === 'float32' || $images.dtype === 'int32', '`images` must have `int32` or `float32` as dtype'); var batchImages = $images; var reshapedTo4D = false; if ($images.rank === 3) { reshapedTo4D = true; batchImages = $images.as4D(1, $images.shape[0], $images.shape[1], $images.shape[2]); } var newHeight = size[0], newWidth = size[1]; var forward = function (backend, save) { return backend.resizeNearestNeighbor(batchImages, newHeight, newWidth, alignCorners); }; var backward = function (dy, saved) { return { batchImages: function () { return ENV.engine.runKernel(function (backend) { return backend.resizeNearestNeighborBackprop(dy, batchImages, alignCorners); }, {}); } }; }; var res = ENV.engine.runKernel(forward, { batchImages: batchImages }, backward); if (reshapedTo4D) { return res.as3D(res.shape[1], res.shape[2], res.shape[3]); } return res; } function nonMaxSuppression_(boxes, scores, maxOutputSize, iouThreshold, scoreThreshold) { if (iouThreshold === void 0) { iouThreshold = 0.5; } if (scoreThreshold === void 0) { scoreThreshold = Number.NEGATIVE_INFINITY; } var $boxes = convertToTensor(boxes, 'boxes', 'nonMaxSuppression'); var $scores = convertToTensor(scores, 'scores', 'nonMaxSuppression'); var inputs = nonMaxSuppSanityCheck($boxes, $scores, maxOutputSize, iouThreshold, scoreThreshold); maxOutputSize = inputs.maxOutputSize; iouThreshold = inputs.iouThreshold; scoreThreshold = inputs.scoreThreshold; return ENV.engine.runKernel(function (b) { return b.nonMaxSuppression($boxes, $scores, maxOutputSize, iouThreshold, scoreThreshold); }, { $boxes: $boxes }); } function nonMaxSuppressionAsync_(boxes, scores, maxOutputSize, iouThreshold, scoreThreshold) { if (iouThreshold === void 0) { iouThreshold = 0.5; } if (scoreThreshold === void 0) { scoreThreshold = Number.NEGATIVE_INFINITY; } return __awaiter(this, void 0, void 0, function () { var $boxes, $scores, inputs, boxesVals, scoresVals, res; return __generator(this, function (_a) { switch (_a.label) { case 0: $boxes = convertToTensor(boxes, 'boxes', 'nonMaxSuppressionAsync'); $scores = convertToTensor(scores, 'scores', 'nonMaxSuppressionAsync'); inputs = nonMaxSuppSanityCheck($boxes, $scores, maxOutputSize, iouThreshold, scoreThreshold); maxOutputSize = inputs.maxOutputSize; iouThreshold = inputs.iouThreshold; scoreThreshold = inputs.scoreThreshold; return [4, $boxes.data()]; case 1: boxesVals = _a.sent(); return [4, $scores.data()]; case 2: scoresVals = _a.sent(); res = nonMaxSuppressionImpl(boxesVals, scoresVals, maxOutputSize, iouThreshold, scoreThreshold); if ($boxes !== boxes) { $boxes.dispose(); } if ($scores !== scores) { $scores.dispose(); } return [2, res]; } }); }); } function nonMaxSuppSanityCheck(boxes, scores, maxOutputSize, iouThreshold, scoreThreshold) { if (iouThreshold == null) { iouThreshold = 0.5; } if (scoreThreshold == null) { scoreThreshold = Number.NEGATIVE_INFINITY; } var numBoxes = boxes.shape[0]; maxOutputSize = Math.min(maxOutputSize, numBoxes); assert(0 <= iouThreshold && iouThreshold <= 1, "iouThreshold must be in [0, 1], but was '" + iouThreshold + "'"); assert(boxes.rank === 2, "boxes must be a 2D tensor, but was of rank '" + boxes.rank + "'"); assert(boxes.shape[1] === 4, "boxes must have 4 columns, but 2nd dimension was " + boxes.shape[1]); assert(scores.rank === 1, 'scores must be a 1D tensor'); assert(scores.shape[0] === numBoxes, "scores has incompatible shape with boxes. Expected " + numBoxes + ", " + ("but was " + scores.shape[0])); return { maxOutputSize: maxOutputSize, iouThreshold: iouThreshold, scoreThreshold: scoreThreshold }; } function cropAndResize_(image, boxes, boxInd, cropSize, method, extrapolationValue) { var $image = convertToTensor(image, 'image', 'cropAndResize', 'float32'); var $boxes = convertToTensor(boxes, 'boxes', 'cropAndResize', 'float32'); var $boxInd = convertToTensor(boxInd, 'boxInd', 'cropAndResize', 'int32'); method = method || 'bilinear'; extrapolationValue = extrapolationValue || 0; var numBoxes = $boxes.shape[0]; assert($image.rank === 4, 'Error in cropAndResize: image must be rank 4,' + ("but got rank " + $image.rank + ".")); assert($boxes.rank === 2 && $boxes.shape[1] === 4, "Error in cropAndResize: boxes must be have size [" + numBoxes + ",4] " + ("but had shape " + $boxes.shape + ".")); assert($boxInd.rank === 1 && $boxInd.shape[0] === numBoxes, "Error in cropAndResize: boxInd must be have size [" + numBoxes + "] " + ("but had shape " + $boxes.shape + ".")); assert(cropSize.length === 2, "Error in cropAndResize: cropSize must be of length 2, but got length " + (cropSize.length + ".")); assert(cropSize[0] >= 1 && cropSize[1] >= 1, "cropSize must be atleast [1,1], but was " + cropSize); assert(method === 'bilinear' || method === 'nearest', "method must be bilinear or nearest, but was " + method); var forward = function (backend, save) { return backend.cropAndResize($image, $boxes, $boxInd, cropSize, method, extrapolationValue); }; var res = ENV.engine.runKernel(forward, { $image: $image, $boxes: $boxes }); return res; } var resizeBilinear = op({ resizeBilinear_: resizeBilinear_ }); var resizeNearestNeighbor = op({ resizeNearestNeighbor_: resizeNearestNeighbor_ }); var nonMaxSuppression = op({ nonMaxSuppression_: nonMaxSuppression_ }); var nonMaxSuppressionAsync = nonMaxSuppressionAsync_; var cropAndResize = op({ cropAndResize_: cropAndResize_ }); var image_ops = /*#__PURE__*/Object.freeze({ resizeBilinear: resizeBilinear, resizeNearestNeighbor: resizeNearestNeighbor, nonMaxSuppression: nonMaxSuppression, nonMaxSuppressionAsync: nonMaxSuppressionAsync, cropAndResize: cropAndResize }); function matMul_$1(a, b, transposeA, transposeB, bias, activation) { if (transposeA === void 0) { transposeA = false; } if (transposeB === void 0) { transposeB = false; } if (activation === void 0) { activation = 'linear'; } var _a; var $a = convertToTensor(a, 'a', 'fused matMul'); var $b = convertToTensor(b, 'b', 'fused matMul'); _a = makeTypesMatch($a, $b), $a = _a[0], $b = _a[1]; var innerShapeA = transposeA ? $a.shape[$a.rank - 2] : $a.shape[$a.rank - 1]; var innerShapeB = transposeB ? $b.shape[$b.rank - 1] : $b.shape[$b.rank - 2]; var outerShapeA = transposeA ? $a.shape[$a.rank - 1] : $a.shape[$a.rank - 2]; var outerShapeB = transposeB ? $b.shape[$b.rank - 2] : $b.shape[$b.rank - 1]; var outerDimsA = $a.shape.slice(0, -2); var outerDimsB = $b.shape.slice(0, -2); var batchDimA = sizeFromShape(outerDimsA); var batchDimB = sizeFromShape(outerDimsB); assert($a.rank >= 2 && $b.rank >= 2 && $a.rank === $b.rank, "Error in fused matMul: inputs must have the same rank of at least 2, " + ("got ranks " + $a.rank + " and " + $b.rank + ".")); assert(arraysEqual(outerDimsA, outerDimsB), "Error in fused matMul: outer dimensions (" + outerDimsA + ") and (" + (outerDimsB + ") of Tensors with shapes " + $a.shape + " and ") + ($b.shape + " must match.")); assert(innerShapeA === innerShapeB, "Error in fused matMul: inner shapes (" + innerShapeA + ") and (" + (innerShapeB + ") of Tensors with shapes " + $a.shape + " and ") + ($b.shape + " and transposeA=" + transposeA) + (" and transposeB=" + transposeB + " must match.")); var outShape = $a.shape.slice(0, -2).concat([outerShapeA, outerShapeB]); var a3D = transposeA ? $a.as3D(batchDimA, innerShapeA, outerShapeA) : $a.as3D(batchDimA, outerShapeA, innerShapeA); var b3D = transposeB ? $b.as3D(batchDimB, outerShapeB, innerShapeB) : $b.as3D(batchDimB, innerShapeB, outerShapeB); var $bias; if (bias != null) { $bias = convertToTensor(bias, 'bias', 'fused matMul'); $bias = makeTypesMatch($bias, $a)[0]; assert(getBroadcastDims(outShape, $bias.shape).length === 0, "Error in fused matMul: broadcasting is not supported for bias add."); } var grad = function (dy, saved) { var y = saved[0]; var dyActivation; if (activation == null || activation === 'linear') { dyActivation = dy; } else if (activation === 'relu') { dyActivation = dy.mul(y.step()); } else { throw new Error("Gradient for activation " + activation + " has not been " + "implemented yet."); } var biasGradient = {}; if (bias != null) { biasGradient = { $bias: function () { var res = dyActivation; var reduceAxes = getReductionAxes($bias.shape, outShape); if (reduceAxes.length > 0) { res = res.sum(reduceAxes); } return res.reshape($bias.shape); } }; } if (!transposeA && !transposeB) { return Object.assign({ $a: function () { return dyActivation.matMul(b3D, false, true); }, $b: function () { return a3D.matMul(dyActivation, true, false); } }, biasGradient); } else if (!transposeA && transposeB) { return Object.assign({ $a: function () { return dyActivation.matMul(b3D, false, false); }, $b: function () { return dyActivation.matMul(a3D, true, false); } }, biasGradient); } else if (transposeA && !transposeB) { return Object.assign({ $a: function () { return b3D.matMul(dyActivation, false, true); }, $b: function () { return a3D.matMul(dyActivation, false, false); } }, biasGradient); } else { return Object.assign({ $a: function () { return b3D.matMul(dyActivation, true, true); }, $b: function () { return dyActivation.matMul(a3D, true, true); } }, biasGradient); } }; var inputs = { $a: a3D, $b: b3D }; if (bias != null) { inputs.$bias = $bias; } var res = ENV.engine.runKernel(function (backend, save) { return save(backend.fusedBatchMatMul(a3D, b3D, transposeA, transposeB, $bias, activation)); }, inputs, grad); return res.reshape(outShape); } var matMul$1 = op({ matMul_: matMul_$1 }); var fused_ops = /*#__PURE__*/Object.freeze({ matMul: matMul$1 }); var ops = /*#__PURE__*/Object.freeze({ image: image_ops, linalg: linalg_ops, losses: loss_ops, spectral: spectral_ops, fused: fused_ops, op: op, batchNormalization2d: batchNormalization2d, batchNormalization3d: batchNormalization3d, batchNormalization4d: batchNormalization4d, batchNormalization: batchNormalization, complex: complex, real: real, imag: imag, concat: concat, concat1d: concat1d, concat2d: concat2d, concat3d: concat3d, concat4d: concat4d, split: split$1, conv1d: conv1d, conv2d: conv2d, conv3d: conv3d, conv2dDerFilter: conv2dDerFilter, depthwiseConv2d: depthwiseConv2d, separableConv2d: separableConv2d, conv2dTranspose: conv2dTranspose, matMul: matMul, dot: dot, outerProduct: outerProduct, reverse: reverse, reverse1d: reverse1d, reverse2d: reverse2d, reverse3d: reverse3d, reverse4d: reverse4d, maxPool: maxPool, avgPool: avgPool, pool: pool, slice: slice, slice1d: slice1d, slice2d: slice2d, slice3d: slice3d, slice4d: slice4d, abs: abs, acos: acos, acosh: acosh, asin: asin, asinh: asinh, atan: atan, atanh: atanh, ceil: ceil, clipByValue: clipByValue, cos: cos, cosh: cosh, erf: erf, exp: exp, expm1: expm1, floor: floor, log: log$1, log1p: log1p, logSigmoid: logSigmoid, neg: neg, reciprocal: reciprocal, round: round, rsqrt: rsqrt, sigmoid: sigmoid, sign: sign, sin: sin, sinh: sinh, softplus: softplus, sqrt: sqrt, square: square, step: step, tan: tan, tanh: tanh$1, all: all, any: any, argMax: argMax, argMin: argMin, logSumExp: logSumExp, max: max, mean: mean, min: min, moments: moments, sum: sum$1, prod: prod, equal: equal, equalStrict: equalStrict, greater: greater, greaterEqual: greaterEqual, greaterEqualStrict: greaterEqualStrict, greaterStrict: greaterStrict, less: less, lessEqual: lessEqual, lessEqualStrict: lessEqualStrict, lessStrict: lessStrict, notEqual: notEqual, notEqualStrict: notEqualStrict, add: add, addN: addN, addStrict: addStrict, atan2: atan2, div: div, divStrict: divStrict, floorDiv: floorDiv, maximum: maximum, maximumStrict: maximumStrict, minimum: minimum, minimumStrict: minimumStrict, mod: mod, modStrict: modStrict, mul: mul, mulStrict: mulStrict, pow: pow, powStrict: powStrict, squaredDifference: squaredDifference, squaredDifferenceStrict: squaredDifferenceStrict, sub: sub, subStrict: subStrict, elu: elu, leakyRelu: leakyRelu, prelu: prelu, relu: relu, selu: selu, logicalAnd: logicalAnd, logicalNot: logicalNot, logicalOr: logicalOr, logicalXor: logicalXor, where: where, whereAsync: whereAsync, buffer: buffer, toPixels: toPixels, print: print, batchToSpaceND: batchToSpaceND, cast: cast, clone: clone, cumsum: cumsum, depthToSpace: depthToSpace, expandDims: expandDims, eye: eye, fromPixels: fromPixels, multinomial: multinomial, oneHot: oneHot, pad: pad, pad1d: pad1d, pad2d: pad2d, pad3d: pad3d, pad4d: pad4d, rand: rand, randomNormal: randomNormal, randomUniform: randomUniform, reshape: reshape, spaceToBatchND: spaceToBatchND, squeeze: squeeze, stack: stack, tile: tile, truncatedNormal: truncatedNormal, unstack: unstack, setdiff1dAsync: setdiff1dAsync, fill: fill, linspace: linspace, ones: ones$1, range: range, scalar: scalar, tensor: tensor, tensor1d: tensor1d, tensor2d: tensor2d, tensor3d: tensor3d, tensor4d: tensor4d, tensor5d: tensor5d, tensor6d: tensor6d, zeros: zeros, onesLike: onesLike, zerosLike: zerosLike, transpose: transpose, softmax: softmax, logSoftmax: logSoftmax, localResponseNormalization: localResponseNormalization, norm: norm, gather: gather, unsortedSegmentSum: unsortedSegmentSum, basicLSTMCell: basicLSTMCell, multiRNNCell: multiRNNCell, movingAverage: movingAverage, stridedSlice: stridedSlice, topk: topk, scatterND: scatterND, fft: fft, ifft: ifft, rfft: rfft, irfft: irfft, sparseToDense: sparseToDense, gatherND: gatherND }); function mapActivation(backend, activation, x) { if (activation === 'linear') { return backend.linear(x); } else if (activation === 'relu') { return backend.relu(x); } throw new Error("Activation " + activation + " has not been implemented for the CPU backend."); } var MathBackendCPU = (function () { function MathBackendCPU() { this.blockSize = 48; this.firstUse = true; if (ENV.get('IS_BROWSER')) { this.fromPixels2DContext = document.createElement('canvas').getContext('2d'); } } MathBackendCPU.prototype.setDataMover = function (dataMover) { this.data = new DataStorage(dataMover); }; MathBackendCPU.prototype.register = function (dataId, shape, dtype) { if (this.firstUse) { this.firstUse = false; if (ENV.get('IS_NODE')) { warn('\n============================\n' + 'Hi there 👋. Looks like you are running TensorFlow.js in ' + 'Node.js. To speed things up dramatically, install our node ' + 'backend, which binds to TensorFlow C++, by running ' + 'npm i @tensorflow/tfjs-node, ' + 'or npm i @tensorflow/tfjs-node-gpu if you have CUDA. ' + 'Then call require(\'@tensorflow/tfjs-node\'); (-gpu ' + 'suffix for CUDA) at the start of your program. ' + 'Visit https://github.com/tensorflow/tfjs-node for more details.' + '\n============================\n'); } } if (this.data.has(dataId)) { throw new Error("Data buffer is already registered"); } this.data.set(dataId, { dtype: dtype }); }; MathBackendCPU.prototype.write = function (dataId, values) { if (values == null) { throw new Error('MathBackendCPU.write(): values can not be null'); } this.data.get(dataId).values = values; }; MathBackendCPU.prototype.fromPixels = function (pixels, numChannels) { if (pixels == null) { throw new Error('pixels passed to tf.fromPixels() can not be null'); } var vals; if (ENV.get('IS_NODE') && pixels.getContext == null) { throw new Error('When running in node, pixels must be an HTMLCanvasElement ' + 'like the one returned by the `canvas` npm package'); } if (pixels.getContext != null) { vals = pixels .getContext('2d') .getImageData(0, 0, pixels.width, pixels.height) .data; } else if (pixels instanceof ImageData) { vals = pixels.data; } else if (pixels instanceof HTMLImageElement || pixels instanceof HTMLVideoElement) { if (this.fromPixels2DContext == null) { throw new Error('Can\'t read pixels from HTMLImageElement outside ' + 'the browser.'); } this.fromPixels2DContext.canvas.width = pixels.width; this.fromPixels2DContext.canvas.height = pixels.height; this.fromPixels2DContext.drawImage(pixels, 0, 0, pixels.width, pixels.height); vals = this.fromPixels2DContext .getImageData(0, 0, pixels.width, pixels.height) .data; } else { throw new Error('pixels passed to tf.fromPixels() must be either an ' + "HTMLVideoElement, HTMLImageElement, HTMLCanvasElement or " + ("ImageData, but was " + pixels.constructor.name)); } var values; if (numChannels === 4) { values = new Int32Array(vals); } else { var numPixels = pixels.width * pixels.height; values = new Int32Array(numPixels * numChannels); for (var i = 0; i < numPixels; i++) { for (var channel = 0; channel < numChannels; ++channel) { values[i * numChannels + channel] = vals[i * 4 + channel]; } } } var outShape = [pixels.height, pixels.width, numChannels]; return tensor3d(values, outShape, 'int32'); }; MathBackendCPU.prototype.read = function (dataId) { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { return [2, this.readSync(dataId)]; }); }); }; MathBackendCPU.prototype.readSync = function (dataId) { var _a = this.data.get(dataId), dtype = _a.dtype, complexTensors = _a.complexTensors; if (dtype === 'complex64') { var realValues = complexTensors.real.dataSync(); var imagValues = complexTensors.imag.dataSync(); return mergeRealAndImagArrays(realValues, imagValues); } return this.data.get(dataId).values; }; MathBackendCPU.prototype.disposeData = function (dataId) { if (this.data.has(dataId)) { var complexTensors = this.data.get(dataId).complexTensors; if (complexTensors != null) { complexTensors.real.dispose(); complexTensors.imag.dispose(); } this.data.delete(dataId); } }; MathBackendCPU.prototype.time = function (f) { return __awaiter(this, void 0, void 0, function () { var start, kernelMs; return __generator(this, function (_a) { start = now(); f(); kernelMs = now() - start; return [2, { kernelMs: kernelMs }]; }); }); }; MathBackendCPU.prototype.memory = function () { return { unreliable: true, reasons: ['The reported memory is an upper bound. Due to automatic garbage ' + 'collection, the true allocated memory may be less.'] }; }; MathBackendCPU.prototype.complex = function (real$$1, imag$$1) { var result = Tensor.make(real$$1.shape, {}, 'complex64'); var resultData = this.data.get(result.dataId); resultData.complexTensors = { real: ENV.engine.keep(real$$1.clone()), imag: ENV.engine.keep(imag$$1.clone()) }; return result; }; MathBackendCPU.prototype.real = function (input) { var resultData = this.data.get(input.dataId); return resultData.complexTensors.real.clone(); }; MathBackendCPU.prototype.imag = function (input) { var resultData = this.data.get(input.dataId); return resultData.complexTensors.imag.clone(); }; MathBackendCPU.prototype.assertNotComplex = function (tensor$$1, opName) { if (!Array.isArray(tensor$$1)) { tensor$$1 = [tensor$$1]; } tensor$$1.forEach(function (t) { if (t != null) { assert(t.dtype !== 'complex64', opName + " does not support complex64 tensors."); } }); }; MathBackendCPU.prototype.slice = function (x, begin, size) { this.assertNotComplex(x, 'slice'); var isContinous = isSliceContinous(x.shape, begin, size); if (isContinous) { var flatOffset = computeFlatOffset(begin, x.strides); var length_1 = sizeFromShape(size); var vals = x.dataSync(); return tensor(vals.subarray(flatOffset, flatOffset + length_1), size, x.dtype); } var buffer$$1 = buffer(size, x.dtype); for (var i = 0; i < buffer$$1.size; ++i) { var loc = buffer$$1.indexToLoc(i); var xLoc = loc.map(function (idx, j) { return idx + begin[j]; }); buffer$$1.values[i] = x.get.apply(x, xLoc); } return buffer$$1.toTensor(); }; MathBackendCPU.prototype.stridedSlice = function (x, begin, end, strides, beginMask, endMask, ellipsisMask, newAxisMask, shrinkAxisMask) { this.assertNotComplex(x, 'stridedSlice'); var _a = getStridedSlicedInfo(x.shape, begin, end, strides, beginMask, endMask, ellipsisMask, newAxisMask, shrinkAxisMask), beginIndex = _a[0], size = _a[1], shrinkAxis = _a[2]; var shape = size.filter(function (v, index) { return shrinkAxis.indexOf(index) === -1; }); if (shape.some(function (axis) { return axis === 0; })) { return tensor([], shape); } var buffer$$1 = buffer(size, x.dtype); for (var i = 0; i < buffer$$1.size; i++) { var loc = buffer$$1.indexToLoc(i); var newLoc = new Array(loc.length); for (var j = 0; j < newLoc.length; j++) { newLoc[j] = loc[j] * strides[j] + beginIndex[j]; } buffer$$1.set.apply(buffer$$1, [x.get.apply(x, newLoc)].concat(loc)); } return buffer$$1.toTensor().reshape(shape); }; MathBackendCPU.prototype.unstack = function (x, axis) { var num = x.shape[axis]; var outShape = new Array(x.rank - 1); var outIndex = 0; for (var i = 0; i < x.rank; i++) { if (i !== axis) { outShape[outIndex++] = x.shape[i]; } } var begin = new Array(x.rank).fill(0); var size = x.shape.slice(); size[axis] = 1; var res = new Array(num); for (var i = 0; i < res.length; i++) { begin[axis] = i; res[i] = this.slice(x, begin, size).reshape(outShape); } return res; }; MathBackendCPU.prototype.reverse = function (x, axis) { this.assertNotComplex(x, 'reverse'); var buffer$$1 = buffer(x.shape, x.dtype); var xBuffer = x.buffer(); var _loop_1 = function (i) { var outLoc = buffer$$1.indexToLoc(i); var inLoc = outLoc.slice(); axis.forEach(function (ax) { return inLoc[ax] = x.shape[ax] - 1 - inLoc[ax]; }); buffer$$1.set.apply(buffer$$1, [xBuffer.get.apply(xBuffer, inLoc)].concat(outLoc)); }; for (var i = 0; i < buffer$$1.size; i++) { _loop_1(i); } return buffer$$1.toTensor(); }; MathBackendCPU.prototype.concat = function (tensors, axis) { this.assertNotComplex(tensors, 'concat'); var tensors2D = tensors.map(function (t) { var innerSize = sizeFromShape(t.shape.slice(axis)); return t.as2D(-1, innerSize); }); var outShape = computeOutShape(tensors2D.map(function (t) { return t.shape; }), 1); var values = buffer(outShape, tensors[0].dtype) .values; if (tensors2D[0].shape[0] === 1) { var offset_1 = 0; tensors2D.forEach(function (t) { values.set(t.dataSync(), offset_1); offset_1 += t.size; }); } else { var colOffset_1 = 0; tensors2D.forEach(function (t) { var tVals = t.dataSync(); var tIdx = 0; for (var row = 0; row < t.shape[0]; ++row) { var resIdx = row * outShape[1] + colOffset_1; for (var col = 0; col < t.shape[1]; ++col) { values[resIdx + col] = tVals[tIdx++]; } } colOffset_1 += t.shape[1]; }); } var finalOutShape = computeOutShape(tensors.map(function (t) { return t.shape; }), axis); return tensor(values, finalOutShape, tensors[0].dtype); }; MathBackendCPU.prototype.neg = function (x) { this.assertNotComplex(x, 'neg'); return this.multiply(scalar(-1), x); }; MathBackendCPU.prototype.add = function (a, b) { if (a.dtype === 'complex64' || b.dtype === 'complex64') { return this.broadcastedBinaryComplexOp(a.cast('complex64'), b.cast('complex64'), function (aReal, aImag, bReal, bImag) { return { real: aReal + bReal, imag: aImag + bImag }; }); } return this.broadcastedBinaryOp(a, b, upcastType(a.dtype, b.dtype), function (aValue, bValue) { return aValue + bValue; }); }; MathBackendCPU.prototype.addN = function (tensors) { this.assertNotComplex(tensors, 'addN'); var vals = tensors.map(function (t) { return t.dataSync(); }); var result = buffer(tensors[0].shape, tensors[0].dtype); var resultVals = result.values; for (var i = 0; i < tensors.length; i++) { var currVals = vals[i]; for (var j = 0; j < resultVals.length; j++) { resultVals[j] += currVals[j]; } } return result.toTensor(); }; MathBackendCPU.prototype.subtract = function (a, b) { if (a.dtype === 'complex64' || b.dtype === 'complex64') { return this.broadcastedBinaryComplexOp(a.cast('complex64'), b.cast('complex64'), function (aReal, aImag, bReal, bImag) { return { real: aReal - bReal, imag: aImag - bImag }; }); } return this.broadcastedBinaryOp(a, b, upcastType(a.dtype, b.dtype), function (aValue, bValue) { return aValue - bValue; }); }; MathBackendCPU.prototype.pow = function (a, b) { this.assertNotComplex([a, b], 'pow'); return this.broadcastedBinaryOp(a, b, a.dtype, function (aValue, bValue) { return Math.pow(aValue, bValue); }); }; MathBackendCPU.prototype.batchMatMul = function (a, b, transposeA, transposeB) { this.assertNotComplex([a, b], 'matMul'); var sharedDim = transposeA ? a.shape[1] : a.shape[2]; var leftDim = transposeA ? a.shape[2] : a.shape[1]; var rightDim = transposeB ? b.shape[1] : b.shape[2]; var batchDim = a.shape[0]; var aValues = a.dataSync(); var bValues = b.dataSync(); var _a = transposeA ? [a.strides[0], 1, a.strides[1]] : [a.strides[0], a.strides[1], 1], aBatch = _a[0], aOuterStep = _a[1], aInnerStep = _a[2]; var _b = transposeB ? [1, b.strides[1], b.strides[0]] : [b.strides[1], 1, b.strides[0]], bInnerStep = _b[0], bOuterStep = _b[1], bBatch = _b[2]; var size = leftDim * rightDim; var result = buffer([batchDim, leftDim, rightDim], a.dtype); var resVals = result.values; var blockSize = this.blockSize; for (var b_1 = 0; b_1 < batchDim; b_1++) { for (var i0 = 0; i0 < leftDim; i0 += blockSize) { for (var j0 = 0; j0 < rightDim; j0 += blockSize) { for (var k0 = 0; k0 < sharedDim; k0 += blockSize) { var iBlock = Math.min(i0 + blockSize, leftDim); var jBlock = Math.min(j0 + blockSize, rightDim); var kBlock = Math.min(k0 + blockSize, sharedDim); for (var i = i0; i < iBlock; i++) { for (var j = j0; j < jBlock; j++) { var sum$$1 = 0.0; for (var k = k0; k < kBlock; k++) { sum$$1 += aValues[b_1 * aBatch + i * aOuterStep + k * aInnerStep] * bValues[k * bInnerStep + j * bOuterStep + b_1 * bBatch]; } resVals[b_1 * size + (i * rightDim + j)] += sum$$1; } } } } } } return result.toTensor(); }; MathBackendCPU.prototype.fusedBatchMatMul = function (a, b, transposeA, transposeB, bias, activation) { var result = this.batchMatMul(a, b, transposeA, transposeB); if (bias) { result = this.add(result, bias); } if (activation) { result = mapActivation(this, activation, result); } return result; }; MathBackendCPU.prototype.multiply = function (a, b) { if (a.dtype === 'complex64' || b.dtype === 'complex64') { return this.broadcastedBinaryComplexOp(a.cast('complex64'), b.cast('complex64'), function (aReal, aImag, bReal, bImag) { return { real: aReal * bReal - aImag * bImag, imag: aReal * bImag + aImag * bReal }; }); } return this.broadcastedBinaryOp(a, b, upcastType(a.dtype, b.dtype), function (aValue, bValue) { return aValue * bValue; }); }; MathBackendCPU.prototype.realDivide = function (a, b) { this.assertNotComplex([a, b], 'realDivide'); var op$$1 = function (a, b) { return a / b; }; var outputDtype = 'float32'; return this.broadcastedBinaryOp(a, b, outputDtype, op$$1); }; MathBackendCPU.prototype.floorDiv = function (a, b) { this.assertNotComplex([a, b], 'floorDiv'); var op$$1 = function (a, b) { return Math.floor(a / b); }; var outputDtype = 'int32'; return this.broadcastedBinaryOp(a, b, outputDtype, op$$1); }; MathBackendCPU.prototype.sum = function (x, axes) { this.assertNotComplex(x, 'sum'); assertAxesAreInnerMostDims('sum', axes, x.rank); var _a = computeOutAndReduceShapes(x.shape, axes), outShape = _a[0], reduceShape = _a[1]; var resultDtype = upcastType(x.dtype, 'int32'); var result = zeros(outShape, resultDtype); var reduceSize = sizeFromShape(reduceShape); var vals = result.dataSync(); var aVals = x.dataSync(); for (var i = 0; i < vals.length; ++i) { var offset = i * reduceSize; var sum$$1 = 0; for (var j = 0; j < reduceSize; ++j) { sum$$1 += aVals[offset + j]; } vals[i] = sum$$1; } return result; }; MathBackendCPU.prototype.prod = function (x, axes) { this.assertNotComplex(x, 'sum'); var _a = computeOutAndReduceShapes(x.shape, axes), outShape = _a[0], reduceShape = _a[1]; var resultDtype = upcastType(x.dtype, 'int32'); var result = zeros(outShape, resultDtype); var reduceSize = sizeFromShape(reduceShape); var vals = result.dataSync(); var aVals = x.dataSync(); for (var i = 0; i < vals.length; ++i) { var offset = i * reduceSize; var prod$$1 = 1; for (var j = 0; j < reduceSize; ++j) { prod$$1 *= aVals[offset + j]; } vals[i] = prod$$1; } return result; }; MathBackendCPU.prototype.unsortedSegmentSum = function (x, segmentIds, numSegments) { this.assertNotComplex(x, 'unsortedSegmentSum'); var res = []; var numIters = x.rank - segmentIds.rank; for (var i = 0; i < numIters; ++i) { segmentIds = segmentIds.expandDims(i + 1); } for (var i = 0; i < numSegments; ++i) { var segmentId = scalar(i, 'int32'); var mask = equal(segmentId, segmentIds).asType('float32'); var sum$$1 = mask.mul(x).sum(0); res.push(sum$$1); } return stack(res); }; MathBackendCPU.prototype.argMin = function (x, axis) { this.assertNotComplex(x, 'argMin'); var axes = [axis]; assertAxesAreInnerMostDims('argMin', axes, x.rank); var _a = computeOutAndReduceShapes(x.shape, axes), outShape = _a[0], reduceShape = _a[1]; var result = zeros(outShape, 'int32'); var reduceSize = sizeFromShape(reduceShape); var vals = result.dataSync(); var aVals = x.dataSync(); for (var i = 0; i < vals.length; ++i) { var offset = i * reduceSize; var min$$1 = aVals[offset]; var minIndex = 0; for (var j = 0; j < reduceSize; ++j) { var value = aVals[offset + j]; if (value < min$$1) { min$$1 = value; minIndex = j; } } vals[i] = minIndex; } return result; }; MathBackendCPU.prototype.argMax = function (x, axis) { this.assertNotComplex(x, 'argMax'); var axes = [axis]; assertAxesAreInnerMostDims('argMax', axes, x.rank); var _a = computeOutAndReduceShapes(x.shape, axes), outShape = _a[0], reduceShape = _a[1]; var result = zeros(outShape, 'int32'); var reduceSize = sizeFromShape(reduceShape); var vals = result.dataSync(); var aVals = x.dataSync(); for (var i = 0; i < vals.length; ++i) { var offset = i * reduceSize; var max$$1 = aVals[offset]; var maxIndex = 0; for (var j = 0; j < reduceSize; ++j) { var value = aVals[offset + j]; if (value > max$$1) { max$$1 = value; maxIndex = j; } } vals[i] = maxIndex; } return result; }; MathBackendCPU.prototype.cumsum = function (x, axis, exclusive, reverse$$1) { this.assertNotComplex(x, 'cumsum'); if (axis !== x.rank - 1) { throw new Error("backend.cumsum in CPU expects an inner-most axis=" + (x.rank - 1) + " " + ("but got axis=" + axis)); } var resultDtype = upcastType(x.dtype, 'int32'); var result = zeros(x.shape, resultDtype); var vals = result.dataSync(); var aVals = x.dataSync(); var finalDim = x.shape[x.rank - 1]; var indexAdjuster = reverse$$1 ? function (i, j) { return i + finalDim - j - 1; } : function (i, j) { return i + j; }; for (var i = 0; i < aVals.length; i += finalDim) { for (var j = 0; j < finalDim; j++) { var idx = indexAdjuster(i, j); if (j === 0) { vals[idx] = exclusive ? 0 : aVals[idx]; } else { var prevIdx = indexAdjuster(i, j - 1); vals[idx] = exclusive ? aVals[prevIdx] + vals[prevIdx] : aVals[idx] + vals[prevIdx]; } } } return result; }; MathBackendCPU.prototype.equal = function (a, b) { this.assertNotComplex([a, b], 'equal'); return this.broadcastedBinaryOp(a, b, 'bool', function (aVal, bVal) { return (aVal === bVal) ? 1 : 0; }); }; MathBackendCPU.prototype.notEqual = function (a, b) { this.assertNotComplex([a, b], 'notEqual'); return this.broadcastedBinaryOp(a, b, 'bool', function (aVal, bVal) { return (aVal !== bVal) ? 1 : 0; }); }; MathBackendCPU.prototype.less = function (a, b) { this.assertNotComplex([a, b], 'less'); return this.broadcastedBinaryOp(a, b, 'bool', function (aVal, bVal) { return (aVal < bVal) ? 1 : 0; }); }; MathBackendCPU.prototype.lessEqual = function (a, b) { this.assertNotComplex([a, b], 'lessEqual'); return this.broadcastedBinaryOp(a, b, 'bool', function (aVal, bVal) { return (aVal <= bVal) ? 1 : 0; }); }; MathBackendCPU.prototype.greater = function (a, b) { this.assertNotComplex([a, b], 'greater'); return this.broadcastedBinaryOp(a, b, 'bool', function (aVal, bVal) { return (aVal > bVal) ? 1 : 0; }); }; MathBackendCPU.prototype.greaterEqual = function (a, b) { this.assertNotComplex([a, b], 'greaterEqual'); return this.broadcastedBinaryOp(a, b, 'bool', function (aVal, bVal) { return (aVal >= bVal) ? 1 : 0; }); }; MathBackendCPU.prototype.logicalNot = function (x) { this.assertNotComplex(x, 'logicalNot'); var values = x.dataSync(); var newValues = new Uint8Array(values.length); for (var i = 0; i < values.length; ++i) { newValues[i] = values[i] ? 0 : 1; } return Tensor.make(x.shape, { values: newValues }, 'bool'); }; MathBackendCPU.prototype.logicalAnd = function (a, b) { this.assertNotComplex([a, b], 'logicalAnd'); return this.broadcastedBinaryOp(a, b, 'bool', function (aVal, bVal) { return aVal && bVal; }); }; MathBackendCPU.prototype.logicalOr = function (a, b) { this.assertNotComplex([a, b], 'logicalOr'); return this.broadcastedBinaryOp(a, b, 'bool', function (aVal, bVal) { return aVal || bVal; }); }; MathBackendCPU.prototype.select = function (condition, a, b) { this.assertNotComplex([condition, a, b], 'select'); var values = condition.dataSync(); var aValues = a.dataSync(); var bValues = b.dataSync(); var result = zeros(a.shape, upcastType(a.dtype, b.dtype)); var newValues = result.dataSync(); var index = 0; var offset = condition.rank === 0 || condition.rank > 1 || a.rank === 1 ? 1 : a.shape[1]; for (var i = 0; i < values.length; i++) { for (var j = 0; j < offset; j++) { if (values[i] === 1) { newValues[index++] = aValues[i]; } else { newValues[index++] = bValues[i]; } } } return result; }; MathBackendCPU.prototype.where = function (condition) { this.assertNotComplex([condition], 'where'); var condVals = condition.dataSync(); return whereImpl(condition.shape, condVals); }; MathBackendCPU.prototype.topk = function (x, k, sorted) { this.assertNotComplex(x, 'topk'); var xVals = x.dataSync(); return topkImpl(xVals, x.shape, x.dtype, k, sorted); }; MathBackendCPU.prototype.min = function (x, axes) { this.assertNotComplex(x, 'min'); assertAxesAreInnerMostDims('min', axes, x.rank); var _a = computeOutAndReduceShapes(x.shape, axes), outShape = _a[0], reduceShape = _a[1]; var result = zeros(outShape, x.dtype); var reduceSize = sizeFromShape(reduceShape); var vals = result.dataSync(); var aVals = x.dataSync(); for (var i = 0; i < vals.length; ++i) { var offset = i * reduceSize; var min$$1 = aVals[offset]; for (var j = 0; j < reduceSize; ++j) { var value = aVals[offset + j]; if (value < min$$1) { min$$1 = value; } } vals[i] = min$$1; } return result; }; MathBackendCPU.prototype.minimum = function (a, b) { this.assertNotComplex([a, b], 'minimum'); return this.broadcastedBinaryOp(a, b, a.dtype, function (aVal, bVal) { return Math.min(aVal, bVal); }); }; MathBackendCPU.prototype.mod = function (a, b) { this.assertNotComplex([a, b], 'mod'); return this.broadcastedBinaryOp(a, b, a.dtype, function (aVal, bVal) { var rem = aVal % bVal; if ((aVal < 0 && bVal < 0) || (aVal >= 0 && bVal >= 0)) { return rem; } else { return (rem + bVal) % bVal; } }); }; MathBackendCPU.prototype.max = function (x, axes) { this.assertNotComplex(x, 'max'); assertAxesAreInnerMostDims('max', axes, x.rank); var _a = computeOutAndReduceShapes(x.shape, axes), outShape = _a[0], reduceShape = _a[1]; var result = zeros(outShape, x.dtype); var reduceSize = sizeFromShape(reduceShape); var vals = result.dataSync(); var aVals = x.dataSync(); for (var i = 0; i < vals.length; ++i) { var offset = i * reduceSize; var max$$1 = aVals[offset]; for (var j = 0; j < reduceSize; ++j) { var value = aVals[offset + j]; if (value > max$$1) { max$$1 = value; } } vals[i] = max$$1; } return result; }; MathBackendCPU.prototype.maximum = function (a, b) { this.assertNotComplex([a, b], 'maximum'); return this.broadcastedBinaryOp(a, b, a.dtype, function (aVal, bVal) { return Math.max(aVal, bVal); }); }; MathBackendCPU.prototype.all = function (x, axes) { this.assertNotComplex(x, 'all'); assertAxesAreInnerMostDims('all', axes, x.rank); var _a = computeOutAndReduceShapes(x.shape, axes), outShape = _a[0], reduceShape = _a[1]; var result = zeros(outShape, x.dtype); var reduceSize = sizeFromShape(reduceShape); var vals = result.dataSync(); var aVals = x.dataSync(); for (var i = 0; i < vals.length; ++i) { var offset = i * reduceSize; var all$$1 = aVals[offset]; for (var j = 0; j < reduceSize; ++j) { var value = aVals[offset + j]; all$$1 = all$$1 && value; } vals[i] = all$$1; } return result; }; MathBackendCPU.prototype.any = function (x, axes) { this.assertNotComplex(x, 'any'); assertAxesAreInnerMostDims('any', axes, x.rank); var _a = computeOutAndReduceShapes(x.shape, axes), outShape = _a[0], reduceShape = _a[1]; var result = zeros(outShape, x.dtype); var reduceSize = sizeFromShape(reduceShape); var vals = result.dataSync(); var aVals = x.dataSync(); for (var i = 0; i < vals.length; ++i) { var offset = i * reduceSize; var anyVal = aVals[offset]; for (var j = 0; j < reduceSize; ++j) { var value = aVals[offset + j]; anyVal = anyVal || value; } vals[i] = anyVal; } return result; }; MathBackendCPU.prototype.squaredDifference = function (a, b) { this.assertNotComplex([a, b], 'squaredDifference'); return this.broadcastedBinaryOp(a, b, a.dtype, function (aVal, bVal) { var diff = aVal - bVal; return diff * diff; }); }; MathBackendCPU.prototype.ceil = function (x) { this.assertNotComplex(x, 'ceil'); var values = x.dataSync(); var newValues = new Float32Array(values.length); for (var i = 0; i < values.length; ++i) { newValues[i] = Math.ceil(values[i]); } return Tensor.make(x.shape, { values: newValues }); }; MathBackendCPU.prototype.floor = function (x) { this.assertNotComplex(x, 'floor'); var values = x.dataSync(); var newValues = new Float32Array(values.length); for (var i = 0; i < values.length; ++i) { newValues[i] = Math.floor(values[i]); } return Tensor.make(x.shape, { values: newValues }); }; MathBackendCPU.prototype.sign = function (x) { this.assertNotComplex(x, 'x'); var values = x.dataSync(); var newValues = new Float32Array(values.length); for (var i = 0; i < values.length; ++i) { if (values[i] < 0) { newValues[i] = -1; } else if (values[i] > 0) { newValues[i] = 1; } else { newValues[i] = 0; } } return Tensor.make(x.shape, { values: newValues }); }; MathBackendCPU.prototype.round = function (x) { this.assertNotComplex(x, 'round'); var values = x.dataSync(); var newValues = new Float32Array(values.length); for (var i = 0; i < values.length; ++i) { var base = Math.floor(values[i]); if (values[i] - base < 0.5) { newValues[i] = Math.floor(values[i]); } else if (values[i] - base > 0.5) { newValues[i] = Math.ceil(values[i]); } else { if (base % 2.0 === 0.0) { newValues[i] = base; } else { newValues[i] = base + 1.0; } } } return Tensor.make(x.shape, { values: newValues }); }; MathBackendCPU.prototype.exp = function (x) { this.assertNotComplex(x, 'exp'); var values = x.dataSync(); var newValues = new Float32Array(values.length); for (var i = 0; i < values.length; ++i) { newValues[i] = Math.exp(values[i]); } return Tensor.make(x.shape, { values: newValues }); }; MathBackendCPU.prototype.expm1 = function (x) { this.assertNotComplex(x, 'expm1'); var values = x.dataSync(); var newValues = new Float32Array(values.length); for (var i = 0; i < values.length; ++i) { newValues[i] = Math.expm1(values[i]); } return Tensor.make(x.shape, { values: newValues }); }; MathBackendCPU.prototype.log = function (x) { this.assertNotComplex(x, 'log'); var values = x.dataSync(); var newValues = new Float32Array(values.length); for (var i = 0; i < values.length; ++i) { var value = values[i]; newValues[i] = Math.log(value); } return Tensor.make(x.shape, { values: newValues }); }; MathBackendCPU.prototype.log1p = function (x) { this.assertNotComplex(x, 'log1p'); var values = x.dataSync(); var newValues = new Float32Array(values.length); for (var i = 0; i < values.length; ++i) { var value = values[i]; newValues[i] = Math.log1p(value); } return Tensor.make(x.shape, { values: newValues }); }; MathBackendCPU.prototype.sqrt = function (x) { this.assertNotComplex(x, 'sqrt'); var values = x.dataSync(); var newValues = new Float32Array(values.length); for (var i = 0; i < values.length; ++i) { var value = values[i]; newValues[i] = Math.sqrt(value); } return Tensor.make(x.shape, { values: newValues }); }; MathBackendCPU.prototype.rsqrt = function (x) { this.assertNotComplex(x, 'rsqrt'); var values = x.dataSync(); var newValues = new Float32Array(values.length); for (var i = 0; i < values.length; ++i) { var value = values[i]; newValues[i] = 1 / Math.sqrt(value); } return Tensor.make(x.shape, { values: newValues }); }; MathBackendCPU.prototype.square = function (x) { this.assertNotComplex(x, 'square'); var values = x.dataSync(); var newValues = new Float32Array(values.length); for (var i = 0; i < values.length; ++i) { var value = values[i]; newValues[i] = value * value; } return Tensor.make(x.shape, { values: newValues }); }; MathBackendCPU.prototype.reciprocal = function (x) { this.assertNotComplex(x, 'reciprocal'); var values = x.dataSync(); var newValues = new Float32Array(values.length); for (var i = 0; i < values.length; ++i) { newValues[i] = 1 / values[i]; } return Tensor.make(x.shape, { values: newValues }); }; MathBackendCPU.prototype.linear = function (x) { return x; }; MathBackendCPU.prototype.relu = function (x) { this.assertNotComplex(x, 'relu'); var res = zeros(x.shape, x.dtype); var resVals = res.dataSync(); var inVals = x.dataSync(); for (var i = 0; i < inVals.length; ++i) { resVals[i] = Math.max(0, inVals[i]); } return res; }; MathBackendCPU.prototype.prelu = function (x, a) { this.assertNotComplex([x, a], 'prelu'); return this.broadcastedBinaryOp(x, a, x.dtype, function (xValue, aValue) { return xValue < 0 ? aValue * xValue : xValue; }); }; MathBackendCPU.prototype.elu = function (x) { this.assertNotComplex(x, 'elu'); var resultValues = new Float32Array(x.size); var values = x.dataSync(); for (var i = 0; i < values.length; ++i) { var v = values[i]; if (v >= 0) { resultValues[i] = v; } else { resultValues[i] = (Math.exp(v) - 1); } } return Tensor.make(x.shape, { values: resultValues }); }; MathBackendCPU.prototype.eluDer = function (dy, y) { this.assertNotComplex([dy, y], 'eluDer'); var resultValues = new Float32Array(y.size); var values = y.dataSync(); var dyValues = dy.dataSync(); for (var i = 0; i < values.length; ++i) { var v = values[i]; if (v >= 1) { resultValues[i] = dyValues[i]; } else { resultValues[i] = dyValues[i] * (v + 1); } } return Tensor.make(y.shape, { values: resultValues }); }; MathBackendCPU.prototype.selu = function (x) { this.assertNotComplex(x, 'selu'); var scaleAlpha = SELU_SCALEALPHA; var scale = SELU_SCALE; var resultValues = new Float32Array(x.size); var values = x.dataSync(); for (var i = 0; i < values.length; ++i) { var v = values[i]; if (v >= 0) { resultValues[i] = scale * v; } else { resultValues[i] = scaleAlpha * (Math.exp(v) - 1); } } return Tensor.make(x.shape, { values: resultValues }); }; MathBackendCPU.prototype.clip = function (x, min$$1, max$$1) { this.assertNotComplex(x, 'clip'); var resultValues = new Float32Array(x.size); var values = x.dataSync(); for (var i = 0; i < values.length; ++i) { var v = values[i]; resultValues[i] = v > max$$1 ? max$$1 : (v < min$$1 ? min$$1 : v); } return Tensor.make(x.shape, { values: resultValues }); }; MathBackendCPU.prototype.abs = function (x) { var resultValues = new Float32Array(x.size); var values = x.dataSync(); for (var i = 0; i < values.length; ++i) { resultValues[i] = Math.abs(values[i]); } return Tensor.make(x.shape, { values: resultValues }); }; MathBackendCPU.prototype.complexAbs = function (x) { var resultValues = new Float32Array(x.size); var values = x.dataSync(); for (var i = 0; i < x.size; ++i) { var real$$1 = values[i * 2]; var imag$$1 = values[i * 2 + 1]; resultValues[i] = Math.hypot(real$$1, imag$$1); } return Tensor.make(x.shape, { values: resultValues }); }; MathBackendCPU.prototype.int = function (x) { this.assertNotComplex(x, 'int'); var resultValues = new Int32Array(x.size); var values = x.dataSync(); for (var i = 0; i < values.length; ++i) { resultValues[i] = values[i]; } return Tensor.make(x.shape, { values: resultValues }, 'int32'); }; MathBackendCPU.prototype.sigmoid = function (x) { this.assertNotComplex(x, 'sigmoid'); var resultValues = new Float32Array(x.size); var values = x.dataSync(); for (var i = 0; i < values.length; ++i) { resultValues[i] = 1 / (1 + Math.exp(-values[i])); } return Tensor.make(x.shape, { values: resultValues }); }; MathBackendCPU.prototype.softplus = function (x) { this.assertNotComplex(x, 'softplus'); var epsilon = 1.1920928955078125e-7; var threshold = Math.log(epsilon) + 2.0; var resultValues = new Float32Array(x.size); var values = x.dataSync(); for (var i = 0; i < values.length; ++i) { var tooLarge = values[i] > -threshold; var tooSmall = values[i] < threshold; var expX = Math.exp(values[i]); var result = void 0; if (tooSmall) { result = expX; } else if (tooLarge) { result = values[i]; } else { result = Math.log(1.0 + expX); } resultValues[i] = result; } return Tensor.make(x.shape, { values: resultValues }); }; MathBackendCPU.prototype.sin = function (x) { this.assertNotComplex(x, 'sin'); var resultValues = new Float32Array(x.size); var values = x.dataSync(); for (var i = 0; i < values.length; ++i) { resultValues[i] = Math.sin(values[i]); } return Tensor.make(x.shape, { values: resultValues }); }; MathBackendCPU.prototype.cos = function (x) { this.assertNotComplex(x, 'cos'); var resultValues = new Float32Array(x.size); var values = x.dataSync(); for (var i = 0; i < values.length; ++i) { resultValues[i] = Math.cos(values[i]); } return Tensor.make(x.shape, { values: resultValues }); }; MathBackendCPU.prototype.tan = function (x) { this.assertNotComplex(x, 'tan'); var resultValues = new Float32Array(x.size); var values = x.dataSync(); for (var i = 0; i < values.length; ++i) { resultValues[i] = Math.tan(values[i]); } return Tensor.make(x.shape, { values: resultValues }); }; MathBackendCPU.prototype.asin = function (x) { this.assertNotComplex(x, 'asin'); var resultValues = new Float32Array(x.size); var values = x.dataSync(); for (var i = 0; i < values.length; ++i) { resultValues[i] = Math.asin(values[i]); } return Tensor.make(x.shape, { values: resultValues }); }; MathBackendCPU.prototype.acos = function (x) { this.assertNotComplex(x, 'acos'); var resultValues = new Float32Array(x.size); var values = x.dataSync(); for (var i = 0; i < values.length; ++i) { resultValues[i] = Math.acos(values[i]); } return Tensor.make(x.shape, { values: resultValues }); }; MathBackendCPU.prototype.atan = function (x) { this.assertNotComplex(x, 'atan'); var resultValues = new Float32Array(x.size); var values = x.dataSync(); for (var i = 0; i < values.length; ++i) { resultValues[i] = Math.atan(values[i]); } return Tensor.make(x.shape, { values: resultValues }); }; MathBackendCPU.prototype.atan2 = function (a, b) { this.assertNotComplex([a, b], 'atan2'); return this.broadcastedBinaryOp(a, b, a.dtype, function (aValue, bValue) { return Math.atan2(aValue, bValue); }); }; MathBackendCPU.prototype.sinh = function (x) { this.assertNotComplex(x, 'sinh'); var resultValues = new Float32Array(x.size); var values = x.dataSync(); for (var i = 0; i < values.length; ++i) { resultValues[i] = Math.sinh(values[i]); } return Tensor.make(x.shape, { values: resultValues }); }; MathBackendCPU.prototype.cosh = function (x) { this.assertNotComplex(x, 'cosh'); var resultValues = new Float32Array(x.size); var values = x.dataSync(); for (var i = 0; i < values.length; ++i) { resultValues[i] = Math.cosh(values[i]); } return Tensor.make(x.shape, { values: resultValues }); }; MathBackendCPU.prototype.tanh = function (x) { this.assertNotComplex(x, 'tanh'); var resultValues = new Float32Array(x.size); var values = x.dataSync(); for (var i = 0; i < values.length; ++i) { resultValues[i] = tanh(values[i]); } return Tensor.make(x.shape, { values: resultValues }); }; MathBackendCPU.prototype.asinh = function (x) { this.assertNotComplex(x, 'asinh'); var resultValues = new Float32Array(x.size); var values = x.dataSync(); for (var i = 0; i < values.length; ++i) { resultValues[i] = Math.asinh(values[i]); } return Tensor.make(x.shape, { values: resultValues }); }; MathBackendCPU.prototype.acosh = function (x) { this.assertNotComplex(x, 'acosh'); var resultValues = new Float32Array(x.size); var values = x.dataSync(); for (var i = 0; i < values.length; ++i) { resultValues[i] = Math.acosh(values[i]); } return Tensor.make(x.shape, { values: resultValues }); }; MathBackendCPU.prototype.atanh = function (x) { this.assertNotComplex(x, 'atanh'); var resultValues = new Float32Array(x.size); var values = x.dataSync(); for (var i = 0; i < values.length; ++i) { resultValues[i] = Math.atanh(values[i]); } return Tensor.make(x.shape, { values: resultValues }); }; MathBackendCPU.prototype.erf = function (x) { this.assertNotComplex(x, 'erf'); var resultValues = new Float32Array(x.size); var values = x.dataSync(); var p = ERF_P; var a1 = ERF_A1; var a2 = ERF_A2; var a3 = ERF_A3; var a4 = ERF_A4; var a5 = ERF_A5; for (var i = 0; i < values.length; ++i) { var v = values[i]; var t = 1.0 / (1.0 + p * v); resultValues[i] = 1.0 - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) * t * Math.exp(-v * v); } return Tensor.make(x.shape, { values: resultValues }); }; MathBackendCPU.prototype.step = function (x, alpha) { if (alpha === void 0) { alpha = 0; } this.assertNotComplex(x, 'step'); var resultValues = new Float32Array(x.size); var values = x.dataSync(); for (var i = 0; i < values.length; ++i) { var value = values[i]; if (isNaN(value)) { resultValues[i] = NaN; } else { resultValues[i] = value > 0 ? 1 : alpha; } } return Tensor.make(x.shape, { values: resultValues }); }; MathBackendCPU.prototype.conv2d = function (x, filter, convInfo) { this.assertNotComplex([x, filter], 'conv2d'); var filterHeight = convInfo.filterHeight; var filterWidth = convInfo.filterWidth; var dilationHeight = convInfo.dilationHeight; var dilationWidth = convInfo.dilationWidth; var padLeft = convInfo.padInfo.left; var padTop = convInfo.padInfo.top; var y = buffer(convInfo.outShape, x.dtype); var xVals = x.dataSync(); var wVals = filter.dataSync(); var yVals = y.values; for (var b = 0; b < convInfo.batchSize; ++b) { var xOffset1 = b * x.strides[0]; var yOffset1 = b * y.strides[0]; for (var yR = 0; yR < convInfo.outHeight; ++yR) { var yOffset2 = yOffset1 + yR * y.strides[1]; var xRCorner = yR * convInfo.strideHeight - padLeft; for (var wR = 0; wR < filterHeight; wR++) { var xR = xRCorner + wR * dilationHeight; if (xR < 0 || xR >= convInfo.inHeight) { continue; } var wOffset1 = wR * filter.strides[0]; var xOffset2 = xOffset1 + xR * x.strides[1]; for (var yC = 0; yC < convInfo.outWidth; ++yC) { var yOffset3 = yOffset2 + yC * convInfo.outChannels; var xCCorner = yC * convInfo.strideWidth - padTop; for (var wC = 0; wC < filterWidth; wC++) { var xC = xCCorner + wC * dilationWidth; if (xC < 0 || xC >= convInfo.inWidth) { continue; } var wOffset2 = wOffset1 + wC * filter.strides[1]; var xOffset3 = xOffset2 + xC * convInfo.inChannels; var wOffset3 = wOffset2; for (var d1 = 0; d1 < convInfo.inChannels; ++d1) { var xVal = xVals[xOffset3 + d1]; for (var d2 = 0; d2 < convInfo.outChannels; ++d2) { yVals[yOffset3 + d2] += xVal * wVals[wOffset3 + d2]; } wOffset3 += convInfo.outChannels; } } } } } } return y.toTensor(); }; MathBackendCPU.prototype.conv3d = function (x, filter, convInfo) { var filterDepth = convInfo.filterDepth; var filterHeight = convInfo.filterHeight; var filterWidth = convInfo.filterWidth; var dilationDepth = convInfo.dilationDepth; var dilationHeight = convInfo.dilationHeight; var dilationWidth = convInfo.dilationWidth; var padFront = convInfo.padInfo.front; var padLeft = convInfo.padInfo.left; var padTop = convInfo.padInfo.top; var y = buffer(convInfo.outShape, x.dtype); var xVals = x.dataSync(); var wVals = filter.dataSync(); var yVals = y.values; for (var b = 0; b < convInfo.batchSize; ++b) { var xOffset1 = b * x.strides[0]; var yOffset1 = b * y.strides[0]; for (var yF = 0; yF < convInfo.outDepth; ++yF) { var yOffset2 = yOffset1 + yF * y.strides[1]; var xFCorner = yF * convInfo.strideDepth - padFront; for (var wF = 0; wF < filterDepth; wF++) { var xF = xFCorner + wF * dilationDepth; if (xF < 0 || xF >= convInfo.inDepth) { continue; } var wOffset1 = wF * filter.strides[0]; var xOffset2 = xOffset1 + xF * x.strides[1]; for (var yR = 0; yR < convInfo.outHeight; ++yR) { var yOffset3 = yOffset2 + yR * y.strides[2]; var xRCorner = yR * convInfo.strideHeight - padTop; for (var wR = 0; wR < filterHeight; wR++) { var xR = xRCorner + wR * dilationHeight; if (xR < 0 || xR >= convInfo.inHeight) { continue; } var wOffset2 = wOffset1 + wR * filter.strides[1]; var xOffset3 = xOffset2 + xR * x.strides[2]; for (var yC = 0; yC < convInfo.outWidth; ++yC) { var yOffset4 = yOffset3 + yC * convInfo.outChannels; var xCCorner = yC * convInfo.strideWidth - padLeft; for (var wC = 0; wC < filterWidth; wC++) { var xC = xCCorner + wC * dilationWidth; if (xC < 0 || xC >= convInfo.inWidth) { continue; } var wOffset3 = wOffset2 + wC * filter.strides[2]; var xOffset4 = xOffset3 + xC * convInfo.inChannels; var wOffset4 = wOffset3; for (var d1 = 0; d1 < convInfo.inChannels; ++d1) { var xVal = xVals[xOffset4 + d1]; for (var d2 = 0; d2 < convInfo.outChannels; ++d2) { yVals[yOffset4 + d2] += xVal * wVals[wOffset4 + d2]; } wOffset4 += convInfo.outChannels; } } } } } } } } return y.toTensor(); }; MathBackendCPU.prototype.conv2dDerInput = function (dy, filter, convInfo) { this.assertNotComplex([dy, filter], 'conv2dDerInput'); var dx = buffer(convInfo.inShape, 'float32'); var dxValues = dx.values; var _a = dx.strides, dxS0 = _a[0], dxS1 = _a[1], dxS2 = _a[2]; var dyValues = dy.dataSync(); var _b = dy.strides, dyS0 = _b[0], dyS1 = _b[1], dyS2 = _b[2]; var fltValues = filter.dataSync(); var _c = filter.strides, fltS0 = _c[0], fltS1 = _c[1], fltS2 = _c[2]; var batchSize = convInfo.batchSize, filterHeight = convInfo.filterHeight, filterWidth = convInfo.filterWidth, inChannels = convInfo.inChannels, inHeight = convInfo.inHeight, inWidth = convInfo.inWidth, outChannels = convInfo.outChannels, outHeight = convInfo.outHeight, outWidth = convInfo.outWidth, strideHeight = convInfo.strideHeight, strideWidth = convInfo.strideWidth; var topPad = filterHeight - 1 - convInfo.padInfo.top; var leftPad = filterWidth - 1 - convInfo.padInfo.left; for (var b = 0; b < batchSize; ++b) { for (var d1 = 0; d1 < inChannels; ++d1) { for (var xR = 0; xR < inHeight; ++xR) { var xRCorner = xR - topPad; var xRMin = Math.max(0, Math.ceil(xRCorner / strideHeight)); var yRMax = Math.min(outHeight, (filterHeight + xRCorner) / strideHeight); for (var xC = 0; xC < inWidth; ++xC) { var xCCorner = xC - leftPad; var xCMin = Math.max(0, Math.ceil(xCCorner / strideWidth)); var yCMax = Math.min(outWidth, (filterWidth + xCCorner) / strideWidth); var dotProd = 0; for (var yR = xRMin; yR < yRMax; ++yR) { var wR = yR * strideHeight - xRCorner; for (var yC = xCMin; yC < yCMax; ++yC) { var wC = yC * strideWidth - xCCorner; var dyOffset = dyS0 * b + dyS1 * yR + dyS2 * yC; var fltOffset = fltS0 * (filterHeight - 1 - wR) + fltS1 * (filterWidth - 1 - wC) + fltS2 * d1; for (var d2 = 0; d2 < outChannels; ++d2) { var pixel = dyValues[dyOffset + d2]; var weight = fltValues[fltOffset + d2]; dotProd += pixel * weight; } } } dxValues[dxS0 * b + dxS1 * xR + dxS2 * xC + d1] = dotProd; } } } } return dx.toTensor(); }; MathBackendCPU.prototype.conv3dDerInput = function (dy, filter, convInfo) { var dx = buffer(convInfo.inShape, 'float32'); var dxValues = dx.values; var _a = dx.strides, dxS0 = _a[0], dxS1 = _a[1], dxS2 = _a[2], dxS3 = _a[3]; var dyValues = dy.dataSync(); var _b = dy.strides, dyS0 = _b[0], dyS1 = _b[1], dyS2 = _b[2], dyS3 = _b[3]; var fltValues = filter.dataSync(); var _c = filter.strides, fltS0 = _c[0], fltS1 = _c[1], fltS2 = _c[2], fltS3 = _c[3]; var batchSize = convInfo.batchSize, filterDepth = convInfo.filterDepth, filterHeight = convInfo.filterHeight, filterWidth = convInfo.filterWidth, inChannels = convInfo.inChannels, inDepth = convInfo.inDepth, inHeight = convInfo.inHeight, inWidth = convInfo.inWidth, outChannels = convInfo.outChannels, outDepth = convInfo.outDepth, outHeight = convInfo.outHeight, outWidth = convInfo.outWidth, strideDepth = convInfo.strideDepth, strideHeight = convInfo.strideHeight, strideWidth = convInfo.strideWidth; var frontPad = filterDepth - 1 - convInfo.padInfo.front; var topPad = filterHeight - 1 - convInfo.padInfo.top; var leftPad = filterWidth - 1 - convInfo.padInfo.left; for (var b = 0; b < batchSize; ++b) { for (var d1 = 0; d1 < inChannels; ++d1) { for (var xF = 0; xF < inDepth; ++xF) { var xFCorner = xF - frontPad; var xFMin = Math.max(0, Math.ceil(xFCorner / strideDepth)); var yFMax = Math.min(outDepth, (filterDepth + xFCorner) / strideDepth); for (var xR = 0; xR < inHeight; ++xR) { var xRCorner = xR - topPad; var xRMin = Math.max(0, Math.ceil(xRCorner / strideHeight)); var yRMax = Math.min(outHeight, (filterHeight + xRCorner) / strideHeight); for (var xC = 0; xC < inWidth; ++xC) { var xCCorner = xC - leftPad; var xCMin = Math.max(0, Math.ceil(xCCorner / strideWidth)); var yCMax = Math.min(outWidth, (filterWidth + xCCorner) / strideWidth); var dotProd = 0; for (var yF = xFMin; yF < yFMax; ++yF) { var wF = yF * strideDepth - xFCorner; for (var yR = xRMin; yR < yRMax; ++yR) { var wR = yR * strideHeight - xRCorner; for (var yC = xCMin; yC < yCMax; ++yC) { var wC = yC * strideWidth - xCCorner; var dyOffset = dyS0 * b + dyS1 * yF + dyS2 * yR + dyS3 * yC; var fltOffset = fltS0 * (filterDepth - 1 - wF) + fltS1 * (filterHeight - 1 - wR) + fltS2 * (filterWidth - 1 - wC) + fltS3 * d1; for (var d2 = 0; d2 < outChannels; ++d2) { var pixel = dyValues[dyOffset + d2]; var weight = fltValues[fltOffset + d2]; dotProd += pixel * weight; } } } } dxValues[dxS0 * b + dxS1 * xF + dxS2 * xR + dxS3 * xC + d1] = dotProd; } } } } } return dx.toTensor(); }; MathBackendCPU.prototype.conv2dDerFilter = function (x, dy, convInfo) { this.assertNotComplex([x, dy], 'conv2dDerFilter'); var strideHeight = convInfo.strideHeight; var strideWidth = convInfo.strideWidth; var filterHeight = convInfo.filterHeight; var filterWidth = convInfo.filterWidth; var dW = buffer(convInfo.filterShape, 'float32'); var leftPad = convInfo.padInfo.left; var topPad = convInfo.padInfo.top; for (var wR = 0; wR < filterHeight; ++wR) { var yRMin = Math.max(0, Math.ceil((topPad - wR) / strideHeight)); var yRMax = Math.min(convInfo.outHeight, (convInfo.inHeight + topPad - wR) / strideHeight); for (var wC = 0; wC < filterWidth; ++wC) { var yCMin = Math.max(0, Math.ceil((leftPad - wC) / strideWidth)); var yCMax = Math.min(convInfo.outWidth, (convInfo.inWidth + leftPad - wC) / strideWidth); for (var d1 = 0; d1 < convInfo.inChannels; ++d1) { for (var d2 = 0; d2 < convInfo.outChannels; ++d2) { var dotProd = 0; for (var b = 0; b < convInfo.batchSize; ++b) { for (var yR = yRMin; yR < yRMax; ++yR) { var xR = wR + yR * strideHeight - topPad; for (var yC = yCMin; yC < yCMax; ++yC) { var xC = wC + yC * strideWidth - leftPad; dotProd += x.get(b, xR, xC, d1) * dy.get(b, yR, yC, d2); } } } dW.set(dotProd, wR, wC, d1, d2); } } } } return dW.toTensor(); }; MathBackendCPU.prototype.conv3dDerFilter = function (x, dy, convInfo) { var strideDepth = convInfo.strideDepth; var strideHeight = convInfo.strideHeight; var strideWidth = convInfo.strideWidth; var filterDepth = convInfo.filterDepth; var filterHeight = convInfo.filterHeight; var filterWidth = convInfo.filterWidth; var dw = buffer(convInfo.filterShape, 'float32'); var dwValues = dw.values; var _a = dw.strides, dwS0 = _a[0], dwS1 = _a[1], dwS2 = _a[2], dwS3 = _a[3]; var dyValues = dy.dataSync(); var _b = dy.strides, dyS0 = _b[0], dyS1 = _b[1], dyS2 = _b[2], dyS3 = _b[3]; var xValues = x.dataSync(); var _c = x.strides, xS0 = _c[0], xS1 = _c[1], xS2 = _c[2], xS3 = _c[3]; var frontPad = convInfo.padInfo.front; var leftPad = convInfo.padInfo.left; var topPad = convInfo.padInfo.top; for (var wF = 0; wF < filterDepth; ++wF) { var yFMin = Math.max(0, Math.ceil((frontPad - wF) / strideDepth)); var yFMax = Math.min(convInfo.outDepth, (convInfo.inDepth + frontPad - wF) / strideDepth); var wOffset1 = wF * dwS0; for (var wR = 0; wR < filterHeight; ++wR) { var yRMin = Math.max(0, Math.ceil((topPad - wR) / strideHeight)); var yRMax = Math.min(convInfo.outHeight, (convInfo.inHeight + topPad - wR) / strideHeight); var wOffset2 = wR * dwS1 + wOffset1; for (var wC = 0; wC < filterWidth; ++wC) { var yCMin = Math.max(0, Math.ceil((leftPad - wC) / strideWidth)); var yCMax = Math.min(convInfo.outWidth, (convInfo.inWidth + leftPad - wC) / strideWidth); var wOffset3 = wC * dwS2 + wOffset2; for (var d1 = 0; d1 < convInfo.inChannels; ++d1) { var wOffset4 = d1 * dwS3 + wOffset3; for (var d2 = 0; d2 < convInfo.outChannels; ++d2) { var dotProd = 0; for (var b = 0; b < convInfo.batchSize; ++b) { var xOffset1 = b * xS0; var yOffset1 = b * dyS0; for (var yF = yFMin; yF < yFMax; ++yF) { var xF = wF + yF * strideDepth - frontPad; var xOffset2 = xF * xS1 + xOffset1; var yOffset2 = yF * dyS1 + yOffset1; for (var yR = yRMin; yR < yRMax; ++yR) { var xR = wR + yR * strideHeight - topPad; var xOffset3 = xR * xS2 + xOffset2; var yOffset3 = yR * dyS2 + yOffset2; for (var yC = yCMin; yC < yCMax; ++yC) { var xC = wC + yC * strideWidth - leftPad; var xOffset4 = xC * xS3 + xOffset3; var yOffset4 = yC * dyS3 + yOffset3; dotProd += xValues[xOffset4 + d1] * dyValues[yOffset4 + d2]; } } } } dwValues[wOffset4 + d2] = dotProd; } } } } } return dw.toTensor(); }; MathBackendCPU.prototype.depthwiseConv2D = function (x, filter, convInfo) { this.assertNotComplex([x, filter], 'depthwiseConv2D'); var filterHeight = convInfo.filterHeight; var filterWidth = convInfo.filterWidth; var dilationHeight = convInfo.dilationHeight; var dilationWidth = convInfo.dilationWidth; var padLeft = convInfo.padInfo.left; var padTop = convInfo.padInfo.top; var chMul = convInfo.outChannels / convInfo.inChannels; var y = buffer(convInfo.outShape, x.dtype); var xVals = x.dataSync(); var wVals = filter.dataSync(); var yVals = y.values; for (var b = 0; b < convInfo.batchSize; ++b) { var xOffset1 = b * x.strides[0]; var yOffset1 = b * y.strides[0]; for (var yR = 0; yR < convInfo.outHeight; ++yR) { var yOffset2 = yOffset1 + yR * y.strides[1]; var xRCorner = yR * convInfo.strideHeight - padLeft; for (var wR = 0; wR < filterHeight; ++wR) { var xR = xRCorner + wR * dilationHeight; if (xR < 0 || xR >= convInfo.inHeight) { continue; } var wOffset1 = wR * filter.strides[0]; var xOffset2 = xOffset1 + xR * x.strides[1]; for (var yC = 0; yC < convInfo.outWidth; ++yC) { var yOffset3 = yOffset2 + yC * y.strides[2]; var xCCorner = yC * convInfo.strideWidth - padTop; for (var wC = 0; wC < filterWidth; ++wC) { var xC = xCCorner + wC * dilationWidth; if (xC < 0 || xC >= convInfo.inWidth) { continue; } var wOffset2 = wOffset1 + wC * filter.strides[1]; var xOffset3 = xOffset2 + xC * convInfo.inChannels; var yOffset4 = yOffset3; var wOffset3 = wOffset2; for (var d1 = 0; d1 < convInfo.inChannels; ++d1) { var xVal = xVals[xOffset3 + d1]; for (var q = 0; q < chMul; ++q) { yVals[yOffset4 + q] += xVal * wVals[wOffset3 + q]; } yOffset4 += chMul; wOffset3 += chMul; } } } } } } return y.toTensor(); }; MathBackendCPU.prototype.depthwiseConv2DDerInput = function (dy, filter, convInfo) { this.assertNotComplex([dy, filter], 'depthwiseConv2DDerInput'); var dx = buffer(convInfo.inShape, 'float32'); var dxValues = dx.values; var _a = dx.strides, dxS0 = _a[0], dxS1 = _a[1], dxS2 = _a[2]; var dyValues = dy.dataSync(); var _b = dy.strides, dyS0 = _b[0], dyS1 = _b[1], dyS2 = _b[2]; var fltValues = filter.dataSync(); var _c = filter.strides, fltS0 = _c[0], fltS1 = _c[1], fltS2 = _c[2]; var batchSize = convInfo.batchSize, filterHeight = convInfo.filterHeight, filterWidth = convInfo.filterWidth, inChannels = convInfo.inChannels, inHeight = convInfo.inHeight, inWidth = convInfo.inWidth, outChannels = convInfo.outChannels, outHeight = convInfo.outHeight, outWidth = convInfo.outWidth, strideHeight = convInfo.strideHeight, strideWidth = convInfo.strideWidth; var topPad = filterHeight - 1 - convInfo.padInfo.top; var leftPad = filterWidth - 1 - convInfo.padInfo.left; var chMul = outChannels / inChannels; for (var b = 0; b < batchSize; ++b) { for (var d1 = 0; d1 < inChannels; ++d1) { for (var xR = 0; xR < inHeight; ++xR) { var xRCorner = xR - topPad; var xRMin = Math.max(0, Math.ceil(xRCorner / strideHeight)); var yRMax = Math.min(outHeight, (filterHeight + xRCorner) / strideHeight); for (var xC = 0; xC < inWidth; ++xC) { var xCCorner = xC - leftPad; var xCMin = Math.max(0, Math.ceil(xCCorner / strideWidth)); var yCMax = Math.min(outWidth, (filterWidth + xCCorner) / strideWidth); var dotProd = 0; for (var yR = xRMin; yR < yRMax; ++yR) { var wR = yR * strideHeight - xRCorner; for (var yC = xCMin; yC < yCMax; ++yC) { var wC = yC * strideWidth - xCCorner; var dyOffset = dyS0 * b + dyS1 * yR + dyS2 * yC; var fltOffset = fltS0 * (filterHeight - 1 - wR) + fltS1 * (filterWidth - 1 - wC) + fltS2 * d1; for (var dm = 0; dm < chMul; ++dm) { var d2 = d1 * chMul + dm; var pixel = dyValues[dyOffset + d2]; var weight = fltValues[fltOffset + dm]; dotProd += pixel * weight; } } } dxValues[dxS0 * b + dxS1 * xR + dxS2 * xC + d1] = dotProd; } } } } return dx.toTensor(); }; MathBackendCPU.prototype.depthwiseConv2DDerFilter = function (x, dy, convInfo) { this.assertNotComplex([x, dy], 'depthwiseConv2DDerFilter'); var strideHeight = convInfo.strideHeight; var strideWidth = convInfo.strideWidth; var filterHeight = convInfo.filterHeight; var filterWidth = convInfo.filterWidth; var dW = buffer(convInfo.filterShape, 'float32'); var leftPad = convInfo.padInfo.left; var topPad = convInfo.padInfo.top; var chMul = convInfo.outChannels / convInfo.inChannels; for (var wR = 0; wR < filterHeight; ++wR) { var yRMin = Math.max(0, Math.ceil((topPad - wR) / strideHeight)); var yRMax = Math.min(convInfo.outHeight, (convInfo.inHeight + topPad - wR) / strideHeight); for (var wC = 0; wC < filterWidth; ++wC) { var yCMin = Math.max(0, Math.ceil((leftPad - wC) / strideWidth)); var yCMax = Math.min(convInfo.outWidth, (convInfo.inWidth + leftPad - wC) / strideWidth); for (var d2 = 0; d2 < convInfo.outChannels; ++d2) { var d1 = Math.trunc(d2 / chMul); var dm = d2 % chMul; var dotProd = 0; for (var b = 0; b < convInfo.batchSize; ++b) { for (var yR = yRMin; yR < yRMax; ++yR) { var xR = wR + yR * strideHeight - topPad; for (var yC = yCMin; yC < yCMax; ++yC) { var xC = wC + yC * strideWidth - leftPad; dotProd += x.get(b, xR, xC, d1) * dy.get(b, yR, yC, d2); } } } dW.set(dotProd, wR, wC, d1, dm); } } } return dW.toTensor(); }; MathBackendCPU.prototype.tile = function (x, reps) { this.assertNotComplex(x, 'tile'); var newShape = new Array(x.rank); for (var i = 0; i < newShape.length; i++) { newShape[i] = x.shape[i] * reps[i]; } var result = buffer(newShape, x.dtype); var xBuf = x.buffer(); for (var i = 0; i < result.values.length; ++i) { var newLoc = result.indexToLoc(i); var originalLoc = new Array(x.rank); for (var i_1 = 0; i_1 < originalLoc.length; i_1++) { originalLoc[i_1] = newLoc[i_1] % x.shape[i_1]; } var originalIndex = xBuf.locToIndex(originalLoc); result.values[i] = xBuf.values[originalIndex]; } return result.toTensor(); }; MathBackendCPU.prototype.pad = function (x, paddings, constantValue) { this.assertNotComplex(x, 'pad'); var outShape = paddings.map(function (p, i) { return p[0] + x.shape[i] + p[1]; }); var start = paddings.map(function (p) { return p[0]; }); var xBuffer = x.buffer(); var buffer$$1 = buffer(outShape, x.dtype); if (constantValue !== 0) { buffer$$1.values.fill(constantValue); } for (var i = 0; i < x.size; i++) { var coords = xBuffer.indexToLoc(i); var outCoords = coords.map(function (c, i) { return c + start[i]; }); buffer$$1.set.apply(buffer$$1, [x.get.apply(x, coords)].concat(outCoords)); } return buffer$$1.toTensor(); }; MathBackendCPU.prototype.transpose = function (x, perm) { this.assertNotComplex(x, 'transpose'); var newShape = new Array(x.rank); for (var i = 0; i < newShape.length; i++) { newShape[i] = x.shape[perm[i]]; } var values = x.dataSync(); var result = buffer(newShape, x.dtype); var xBuf = x.buffer(); for (var i = 0; i < x.size; ++i) { var loc = xBuf.indexToLoc(i); var newLoc = new Array(loc.length); for (var i_2 = 0; i_2 < newLoc.length; i_2++) { newLoc[i_2] = loc[perm[i_2]]; } var newIndex = result.locToIndex(newLoc); result.values[newIndex] = values[i]; } return result.toTensor(); }; MathBackendCPU.prototype.gather = function (x, indices, axis) { this.assertNotComplex([x, indices], 'gather'); var newShape = x.shape.slice(); var indicesValues = indices.dataSync(); newShape[axis] = indicesValues.length; var result = buffer(newShape, x.dtype); var xBuf = x.buffer(); for (var i = 0; i < result.size; ++i) { var newLoc = result.indexToLoc(i); var originalLoc = newLoc.slice(); originalLoc[axis] = indicesValues[newLoc[axis]]; var originalIndex = xBuf.locToIndex(originalLoc); result.values[i] = xBuf.values[originalIndex]; } return result.toTensor(); }; MathBackendCPU.prototype.batchToSpaceND = function (x, blockShape, crops) { this.assertNotComplex([x], 'batchToSpaceND'); var prod$$1 = blockShape.reduce(function (a, b) { return a * b; }); var reshaped = getReshaped(x.shape, blockShape, prod$$1); var permuted = getPermuted(reshaped.length, blockShape.length); var reshapedPermuted = getReshapedPermuted(x.shape, blockShape, prod$$1); var sliceBeginCoords = getSliceBeginCoords(crops, blockShape.length); var sliceSize = getSliceSize(reshapedPermuted, crops, blockShape.length); return x.reshape(reshaped) .transpose(permuted) .reshape(reshapedPermuted) .slice(sliceBeginCoords, sliceSize); }; MathBackendCPU.prototype.spaceToBatchND = function (x, blockShape, paddings) { this.assertNotComplex([x], 'spaceToBatchND'); var prod$$1 = blockShape.reduce(function (a, b) { return a * b; }); var completePaddings = [[0, 0]]; completePaddings.push.apply(completePaddings, paddings); for (var i = 1 + blockShape.length; i < x.shape.length; ++i) { completePaddings.push([0, 0]); } var paddedX = x.pad(completePaddings); var reshapedPaddedShape = getReshaped(paddedX.shape, blockShape, prod$$1, false); var permutedReshapedPaddedPermutation = getPermuted(reshapedPaddedShape.length, blockShape.length, false); var flattenShape = getReshapedPermuted(paddedX.shape, blockShape, prod$$1, false); return paddedX.reshape(reshapedPaddedShape) .transpose(permutedReshapedPaddedPermutation) .reshape(flattenShape); }; MathBackendCPU.prototype.pool = function (x, convInfo, poolType) { this.assertNotComplex(x, 'pool'); var strideHeight = convInfo.strideHeight; var strideWidth = convInfo.strideWidth; var dilationHeight = convInfo.dilationHeight; var dilationWidth = convInfo.dilationWidth; var effectiveFilterHeight = convInfo.effectiveFilterHeight; var effectiveFilterWidth = convInfo.effectiveFilterWidth; var padTop = convInfo.padInfo.top; var padLeft = convInfo.padInfo.left; var initialValue = (poolType === 'max' ? Number.NEGATIVE_INFINITY : Number.POSITIVE_INFINITY); var xValues = x.dataSync(); var output = buffer(convInfo.outShape, x.dtype); var outputVals = output.values; var outputBatchStrides = convInfo.outShape[1] * convInfo.outShape[2] * convInfo.outShape[3]; var outputRowStrides = convInfo.outShape[2] * convInfo.outShape[3]; var outputColStrides = convInfo.outShape[3]; for (var b = 0; b < convInfo.batchSize; ++b) { var outputBatchOffset = b * outputBatchStrides; var inputBatchOffset = b * x.strides[0]; for (var d = 0; d < convInfo.inChannels; ++d) { for (var yR = 0; yR < convInfo.outHeight; ++yR) { var xRCorner = yR * strideHeight - padTop; var xRMin = Math.max(0, xRCorner); var xRMax = Math.min(convInfo.inHeight, effectiveFilterHeight + xRCorner); var outputRowOffset = outputBatchOffset + yR * outputRowStrides; for (var yC = 0; yC < convInfo.outWidth; ++yC) { var xCCorner = yC * strideWidth - padLeft; var xCMin = Math.max(0, xCCorner); var xCMax = Math.min(convInfo.inWidth, effectiveFilterWidth + xCCorner); var minMaxValue = initialValue; var avgValue = 0; var count = 0; for (var xR = xRMin; xR < xRMax; xR += dilationHeight) { var xROffset = inputBatchOffset + xR * x.strides[1]; for (var xC = xCMin; xC < xCMax; xC += dilationWidth) { var xCOffset = xROffset + xC * x.strides[2]; var pixel = xValues[xCOffset + d]; if ((poolType === 'max' && pixel > minMaxValue)) { minMaxValue = pixel; } else if (poolType === 'avg') { avgValue += pixel; count++; } } if (isNaN(minMaxValue)) { break; } } var outputOffset = outputRowOffset + yC * outputColStrides + d; outputVals[outputOffset] = poolType === 'avg' ? avgValue / count : minMaxValue; } } } } return output.toTensor(); }; MathBackendCPU.prototype.maxPool = function (x, convInfo) { return this.pool(x, convInfo, 'max'); }; MathBackendCPU.prototype.maxPoolPositions = function (x, convInfo) { var maxPositions = buffer(convInfo.outShape, 'int32'); var strideHeight = convInfo.strideHeight; var strideWidth = convInfo.strideWidth; var dilationHeight = convInfo.dilationHeight; var dilationWidth = convInfo.dilationWidth; var effectiveFilterHeight = convInfo.effectiveFilterHeight; var effectiveFilterWidth = convInfo.effectiveFilterWidth; var padTop = convInfo.padInfo.top; var padLeft = convInfo.padInfo.left; for (var b = 0; b < convInfo.batchSize; ++b) { for (var d = 0; d < convInfo.inChannels; ++d) { for (var yR = 0; yR < convInfo.outHeight; ++yR) { var xRCorner = yR * strideHeight - padTop; var xRMin = xRCorner; while (xRMin < 0) { xRMin += dilationHeight; } var xRMax = Math.min(convInfo.inHeight, effectiveFilterHeight + xRCorner); for (var yC = 0; yC < convInfo.outWidth; ++yC) { var xCCorner = yC * strideWidth - padLeft; var xCMin = xCCorner; while (xCMin < 0) { xCMin += dilationWidth; } var xCMax = Math.min(convInfo.inWidth, effectiveFilterWidth + xCCorner); var maxValue = Number.NEGATIVE_INFINITY; var maxPosition = -1; for (var xR = xRMin; xR < xRMax; xR += dilationHeight) { var wR = xR - xRCorner; for (var xC = xCMin; xC < xCMax; xC += dilationWidth) { var wC = xC - xCCorner; var pixel = x.get(b, xR, xC, d); if (pixel > maxValue) { maxValue = pixel; maxPosition = wR * effectiveFilterWidth + wC; } } } maxPositions.set(maxPosition, b, yR, yC, d); } } } } return maxPositions.toTensor(); }; MathBackendCPU.prototype.maxPoolBackprop = function (dy, x, y, convInfo) { this.assertNotComplex([x, y], 'maxPoolBackprop'); var maxPositions = this.maxPoolPositions(x, convInfo); var strideHeight = convInfo.strideHeight; var strideWidth = convInfo.strideWidth; var dilationHeight = convInfo.dilationHeight; var dilationWidth = convInfo.dilationWidth; var effectiveFilterHeight = convInfo.effectiveFilterHeight; var effectiveFilterWidth = convInfo.effectiveFilterWidth; var padLeft = effectiveFilterWidth - 1 - convInfo.padInfo.left; var padTop = effectiveFilterHeight - 1 - convInfo.padInfo.top; var dx = buffer(x.shape, 'float32'); for (var b = 0; b < convInfo.batchSize; ++b) { for (var d = 0; d < convInfo.inChannels; ++d) { for (var dxR = 0; dxR < convInfo.inHeight; ++dxR) { for (var dxC = 0; dxC < convInfo.inWidth; ++dxC) { var dyRCorner = dxR - padTop; var dyCCorner = dxC - padLeft; var dotProd = 0; for (var wR = 0; wR < effectiveFilterHeight; wR += dilationHeight) { var dyR = (dyRCorner + wR) / strideHeight; if (dyR < 0 || dyR >= convInfo.outHeight || Math.floor(dyR) !== dyR) { continue; } for (var wC = 0; wC < effectiveFilterWidth; wC += dilationWidth) { var dyC = (dyCCorner + wC) / strideWidth; if (dyC < 0 || dyC >= convInfo.outWidth || Math.floor(dyC) !== dyC) { continue; } var maxPos = effectiveFilterHeight * effectiveFilterWidth - 1 - maxPositions.get(b, dyR, dyC, d); var curPos = wR * effectiveFilterWidth + wC; var mask = maxPos === curPos ? 1 : 0; if (mask === 0) { continue; } var pixel = dy.get(b, dyR, dyC, d); dotProd += pixel * mask; } } dx.set(dotProd, b, dxR, dxC, d); } } } } return dx.toTensor(); }; MathBackendCPU.prototype.avgPoolBackprop = function (dy, x, convInfo) { this.assertNotComplex([dy, x], 'avgPoolBackprop'); var strideHeight = convInfo.strideHeight; var strideWidth = convInfo.strideWidth; var filterHeight = convInfo.filterHeight; var filterWidth = convInfo.filterWidth; var dilationHeight = convInfo.dilationHeight; var dilationWidth = convInfo.dilationWidth; var effectiveFilterHeight = convInfo.effectiveFilterHeight; var effectiveFilterWidth = convInfo.effectiveFilterWidth; var padLeft = effectiveFilterWidth - 1 - convInfo.padInfo.left; var padTop = effectiveFilterHeight - 1 - convInfo.padInfo.top; var dx = buffer(x.shape, 'float32'); var avgMultiplier = 1 / (filterHeight * filterWidth); for (var b = 0; b < convInfo.batchSize; ++b) { for (var d = 0; d < convInfo.inChannels; ++d) { for (var dxR = 0; dxR < convInfo.inHeight; ++dxR) { for (var dxC = 0; dxC < convInfo.inWidth; ++dxC) { var dyRCorner = dxR - padTop; var dyCCorner = dxC - padLeft; var dotProd = 0; for (var wR = 0; wR < effectiveFilterHeight; wR += dilationHeight) { var dyR = (dyRCorner + wR) / strideHeight; if (dyR < 0 || dyR >= convInfo.outHeight || Math.floor(dyR) !== dyR) { continue; } for (var wC = 0; wC < effectiveFilterWidth; wC += dilationWidth) { var dyC = (dyCCorner + wC) / strideWidth; if (dyC < 0 || dyC >= convInfo.outWidth || Math.floor(dyC) !== dyC) { continue; } var pixel = dy.get(b, dyR, dyC, d); dotProd += pixel; } } dx.set(dotProd * avgMultiplier, b, dxR, dxC, d); } } } } return dx.toTensor(); }; MathBackendCPU.prototype.cast = function (x, dtype) { return castTensor(x, dtype, this); }; MathBackendCPU.prototype.reshape = function (x, shape) { return reshapeTensor(x, shape); }; MathBackendCPU.prototype.avgPool = function (x, convInfo) { this.assertNotComplex(x, 'avgPool'); return this.pool(x, convInfo, 'avg').toFloat(); }; MathBackendCPU.prototype.resizeBilinear = function (x, newHeight, newWidth, alignCorners) { this.assertNotComplex(x, 'resizeBilinear'); var _a = x.shape, batch = _a[0], oldHeight = _a[1], oldWidth = _a[2], numChannels = _a[3]; var xValues = x.dataSync(); var result = new Float32Array(sizeFromShape([batch, newHeight, newWidth, numChannels])); var effectiveInputSize = [ (alignCorners && newHeight > 1) ? oldHeight - 1 : oldHeight, (alignCorners && newWidth > 1) ? oldWidth - 1 : oldWidth ]; var effectiveOutputSize = [ (alignCorners && newHeight > 1) ? newHeight - 1 : newHeight, (alignCorners && newWidth > 1) ? newWidth - 1 : newWidth ]; var outputIdx = 0; var effectiveRowSizeRatio = effectiveInputSize[0] / effectiveOutputSize[0]; var effectiveColSizeRatio = effectiveInputSize[1] / effectiveOutputSize[1]; for (var b = 0; b < batch; b++) { for (var r = 0; r < newHeight; r++) { var sourceFracRow = effectiveRowSizeRatio * r; var sourceRowFloor = Math.floor(sourceFracRow); var rowFrac = sourceFracRow - sourceRowFloor; var sourceRowCeil = Math.min(oldHeight - 1, Math.ceil(sourceFracRow)); var topRowOffset = b * x.strides[0] + sourceRowFloor * x.strides[1]; var botRowOffset = b * x.strides[0] + sourceRowCeil * x.strides[1]; for (var c = 0; c < newWidth; c++) { var sourceFracCol = effectiveColSizeRatio * c; var sourceColFloor = Math.floor(sourceFracCol); var colFrac = sourceFracCol - sourceColFloor; var sourceColCeil = Math.min(oldWidth - 1, Math.ceil(sourceFracCol)); var topLeftOffest = topRowOffset + sourceColFloor * x.strides[2]; var botLeftOffset = botRowOffset + sourceColFloor * x.strides[2]; var topRightOffset = topRowOffset + +sourceColCeil * x.strides[2]; var botRightOffest = botRowOffset + sourceColCeil * x.strides[2]; for (var d = 0; d < numChannels; d++) { var topLeft = xValues[topLeftOffest + d]; var bottomLeft = xValues[botLeftOffset + d]; var topRight = xValues[topRightOffset + d]; var bottomRight = xValues[botRightOffest + d]; var top_1 = topLeft + (topRight - topLeft) * colFrac; var bottom = bottomLeft + (bottomRight - bottomLeft) * colFrac; var newValue = top_1 + (bottom - top_1) * rowFrac; result[outputIdx++] = newValue; } } } } return tensor(result, [batch, newHeight, newWidth, numChannels]); }; MathBackendCPU.prototype.resizeBilinearBackprop = function (dy, x, alignCorners) { this.assertNotComplex([dy, x], 'resizeBilinearBackprop'); var _a = x.shape, batch = _a[0], xHeight = _a[1], xWidth = _a[2], depth = _a[3]; var _b = dy.shape, yHeight = _b[1], yWidth = _b[2]; var output = new Float32Array(batch * xHeight * xWidth * depth); var effectiveXSize = [ (alignCorners && yHeight > 1) ? xHeight - 1 : xHeight, (alignCorners && yWidth > 1) ? xWidth - 1 : xWidth ]; var effectiveYSize = [ (alignCorners && yHeight > 1) ? yHeight - 1 : yHeight, (alignCorners && yWidth > 1) ? yWidth - 1 : yWidth ]; var heightScale = effectiveXSize[0] / effectiveYSize[0]; var widthScale = effectiveXSize[1] / effectiveYSize[1]; var dyValues = dy.dataSync(); var offset = 0; for (var b = 0; b < batch; b++) { var bOffset = b * x.strides[0]; for (var r = 0; r < yHeight; r++) { var dxR = r * heightScale; var topDxRIndex = Math.floor(dxR); var bottomDxRIndex = Math.min(Math.ceil(dxR), xHeight - 1); var topDxROffset = bOffset + topDxRIndex * x.strides[1]; var bottomDxROffset = bOffset + bottomDxRIndex * x.strides[1]; var dxRLerp = dxR - topDxRIndex; var inverseDxRLerp = 1.0 - dxRLerp; for (var c = 0; c < yWidth; c++) { var dxC = c * widthScale; var leftDxCIndex = Math.floor(dxC); var rightDxCIndex = Math.min(Math.ceil(dxC), xWidth - 1); var dxCLerp = dxC - leftDxCIndex; var inverseDxCLerp = 1.0 - dxCLerp; var topLeftRCOffset = topDxROffset + leftDxCIndex * x.strides[2]; var topRightRCOffset = topDxROffset + rightDxCIndex * x.strides[2]; var bottomLeftRCOffset = bottomDxROffset + leftDxCIndex * x.strides[2]; var bottomRightRCOffset = bottomDxROffset + rightDxCIndex * x.strides[2]; var inverseDxRLerpTimesInverseDxCLerp = inverseDxRLerp * inverseDxCLerp; var inverseDxRLerpTimesDxCLerp = inverseDxRLerp * dxCLerp; var dxRLerpTimesInverseDxCLerp = dxRLerp * inverseDxCLerp; var dxRLerpTimesDxCLerp = dxRLerp * dxCLerp; for (var d = 0; d < depth; d++) { var dyVal = dyValues[offset++]; output[topLeftRCOffset + d] += dyVal * inverseDxRLerpTimesInverseDxCLerp; output[topRightRCOffset + d] += dyVal * inverseDxRLerpTimesDxCLerp; output[bottomLeftRCOffset + d] += dyVal * dxRLerpTimesInverseDxCLerp; output[bottomRightRCOffset + d] += dyVal * dxRLerpTimesDxCLerp; } } } } return tensor4d(output, [batch, xWidth, xHeight, depth], x.dtype); }; MathBackendCPU.prototype.resizeNearestNeighbor = function (x, newHeight, newWidth, alignCorners) { this.assertNotComplex(x, 'resizeNearestNeighbor'); var _a = x.shape, batch = _a[0], oldHeight = _a[1], oldWidth = _a[2], numChannels = _a[3]; var xValues = x.dataSync(); var output = new Float32Array(batch * newHeight * newWidth * numChannels); var effectiveInputSize = [ (alignCorners && newHeight > 1) ? oldHeight - 1 : oldHeight, (alignCorners && newWidth > 1) ? oldWidth - 1 : oldWidth ]; var effectiveOutputSize = [ (alignCorners && newHeight > 1) ? newHeight - 1 : newHeight, (alignCorners && newWidth > 1) ? newWidth - 1 : newWidth ]; var effectiveRowSizeRatio = effectiveInputSize[0] / effectiveOutputSize[0]; var effectiveColSizeRatio = effectiveInputSize[1] / effectiveOutputSize[1]; var outputOffset = 0; for (var b = 0; b < batch; b++) { var batchOffset = b * x.strides[0]; for (var r = 0; r < newHeight; r++) { var sourceFracRow = effectiveRowSizeRatio * r; var sourceNearestRow = Math.min(oldHeight - 1, alignCorners ? Math.round(sourceFracRow) : Math.floor(sourceFracRow)); var rowOffset = batchOffset + sourceNearestRow * x.strides[1]; for (var c = 0; c < newWidth; c++) { var sourceFracCol = effectiveColSizeRatio * c; var sourceNearestCol = Math.min(oldWidth - 1, alignCorners ? Math.round(sourceFracCol) : Math.floor(sourceFracCol)); var colOffset = rowOffset + sourceNearestCol * x.strides[2]; for (var d = 0; d < numChannels; d++) { var newVal = xValues[colOffset + d]; output[outputOffset++] = newVal; } } } } return tensor(output, [batch, newHeight, newWidth, numChannels], x.dtype); }; MathBackendCPU.prototype.resizeNearestNeighborBackprop = function (dy, x, alignCorners) { this.assertNotComplex([dy, x], 'resizeNearestNeighborBackprop'); var _a = x.shape, batch = _a[0], xHeight = _a[1], xWidth = _a[2], depth = _a[3]; var _b = dy.shape, yHeight = _b[1], yWidth = _b[2]; var output = new Float32Array(batch * xHeight * xWidth * depth); var dyValues = dy.dataSync(); var effectiveXSize = [ (alignCorners && yHeight > 1) ? xHeight - 1 : xHeight, (alignCorners && yWidth > 1) ? xWidth - 1 : xWidth ]; var effectiveYSize = [ (alignCorners && yHeight > 1) ? yHeight - 1 : yHeight, (alignCorners && yWidth > 1) ? yWidth - 1 : yWidth ]; var heightScale = effectiveXSize[0] / effectiveYSize[0]; var widthScale = effectiveXSize[1] / effectiveYSize[1]; var invHeightScale = 1 / heightScale; var invWidthScale = 1 / widthScale; var winHeight = (Math.ceil(invHeightScale) * 2) + 2; var winWidth = (Math.ceil(invWidthScale) * 2) + 2; for (var b = 0; b < batch; b++) { var batchOffset = b * x.strides[0]; for (var r = 0; r < xHeight; r++) { var rowOffset = batchOffset + r * x.strides[1]; var startRLerp = Math.floor(r * invHeightScale); var startDyR = Math.floor(startRLerp - (winHeight / 2)); for (var c = 0; c < xWidth; c++) { var colOffset = rowOffset + c * x.strides[2]; var startCLerp = Math.floor(c * invWidthScale); var startDyC = Math.floor(startCLerp - (winWidth / 2)); for (var d = 0; d < depth; d++) { var accum = 0; for (var dyRIndex = 0; dyRIndex < winHeight; dyRIndex++) { var dyR = dyRIndex + startDyR; if (dyR < 0 || dyR >= yHeight) { continue; } var dyROffset = batchOffset + dyR * dy.strides[1]; var sourceFracRow = dyR * heightScale; var sourceNearestRow = Math.min(xHeight - 1, alignCorners ? Math.round(sourceFracRow) : Math.floor(sourceFracRow)); if (r !== sourceNearestRow) { continue; } for (var dyCIndex = 0; dyCIndex < winWidth; dyCIndex++) { var dyC = dyCIndex + startDyC; if (dyC < 0 || dyC >= yWidth) { continue; } var dyCOffset = dyROffset + dyC * dy.strides[2]; var sourceFracCol = dyC * widthScale; var sourceNearestCol = Math.min(xWidth - 1, alignCorners ? Math.round(sourceFracCol) : Math.floor(sourceFracCol)); if (c === sourceNearestCol) { accum += dyValues[dyCOffset + d]; } } } output[colOffset + d] = accum; } } } } return tensor4d(output, x.shape, x.dtype); }; MathBackendCPU.prototype.batchNormalization = function (x, mean$$1, variance, varianceEpsilon, scale, offset) { this.assertNotComplex([x, mean$$1, variance, scale, offset], 'batchNormalization'); var xVals = x.dataSync(); var mVals = mean$$1.dataSync(); var varVals = variance.dataSync(); var sVals = scale ? scale.dataSync() : new Float32Array([1]); var offVals = offset ? offset.dataSync() : new Float32Array([0]); var outVals = new Float32Array(xVals.length); var offValsLength = offVals.length; var sValsLength = sVals.length; var varValsLength = varVals.length; var mValsLength = mVals.length; var offi = 0; var mi = 0; var si = 0; var vi = 0; for (var i = 0; i < xVals.length; ++i) { outVals[i] = offVals[offi++] + (xVals[i] - mVals[mi++]) * sVals[si++] / Math.sqrt(varVals[vi++] + varianceEpsilon); if (offi >= offValsLength) { offi = 0; } if (mi >= mValsLength) { mi = 0; } if (si >= sValsLength) { si = 0; } if (vi >= varValsLength) { vi = 0; } } return tensor4d(outVals, x.shape); }; MathBackendCPU.prototype.localResponseNormalization4D = function (x, depthRadius, bias, alpha, beta) { this.assertNotComplex(x, 'localResponseNormalization4D'); var channels = x.shape[3]; var maxD = channels - 1; var xValues = x.dataSync(); var size = sizeFromShape(x.shape); var result = new Float32Array(size); function sumAcrossChannels(offset) { var currentChannel = offset % channels; var beginSumOffset = offset - currentChannel + Math.max(0, currentChannel - depthRadius); var endSumOffset = offset - currentChannel + Math.min(currentChannel + depthRadius, maxD); var sum$$1 = 0.0; for (; beginSumOffset <= endSumOffset; beginSumOffset++) { var z = xValues[beginSumOffset]; sum$$1 += z * z; } return sum$$1; } for (var offset = 0; offset < size; offset++) { var sum$$1 = sumAcrossChannels(offset); var val = xValues[offset] * Math.pow(bias + alpha * sum$$1, -beta); result[offset] = val; } return tensor4d(result, x.shape); }; MathBackendCPU.prototype.LRNGrad = function (dy, inputImage, outputImage, depthRadius, bias, alpha, beta) { this.assertNotComplex(dy, 'LRNGrad'); var channels = dy.shape[3]; var dyValues = dy.dataSync(); var inputImageValues = inputImage.dataSync(); var outputImageValues = outputImage.dataSync(); var result = new Float32Array(sizeFromShape(dy.shape)); var size = sizeFromShape(dy.shape); for (var offset = 0; offset < size; offset++) { var currentChannel = offset % channels; var depthBegin = (offset - currentChannel) + Math.max(0, currentChannel - depthRadius); var depthEnd = (offset - currentChannel) + Math.min(channels, currentChannel + depthRadius + 1); var norm$$1 = 0; for (var k = depthBegin; k < depthEnd; k++) { norm$$1 += Math.pow(inputImageValues[k], 2); } norm$$1 = alpha * norm$$1 + bias; for (var k = depthBegin; k < depthEnd; k++) { var dyi = -2 * alpha * beta * inputImageValues[k] * outputImageValues[offset] / norm$$1; if (offset === k) { dyi += Math.pow(norm$$1, -beta); } dyi *= dyValues[offset]; result[k] += dyi; } } return tensor4d(result, dy.shape); }; MathBackendCPU.prototype.multinomial = function (logits, normalized, numSamples, seed) { this.assertNotComplex(logits, 'multinomial'); var probabilities = normalized ? logits : softmax(logits); var batchSize = probabilities.shape[0]; var numEvents = probabilities.shape[1]; var res = zeros([batchSize, numSamples], 'int32'); var resVals = res.dataSync(); var probVals = probabilities.dataSync(); for (var b = 0; b < batchSize; ++b) { var offset = b * numEvents; var cdf = new Float32Array(numEvents - 1); cdf[0] = probVals[offset]; for (var event_1 = 1; event_1 < cdf.length; ++event_1) { cdf[event_1] = cdf[event_1 - 1] + probVals[offset + event_1]; } var random = seedrandom_1(seed.toString()); var outOffset = b * numSamples; for (var sampleId = 0; sampleId < numSamples; ++sampleId) { var r = random(); resVals[outOffset + sampleId] = cdf.length; for (var event_2 = 0; event_2 < cdf.length; event_2++) { if (r < cdf[event_2]) { resVals[outOffset + sampleId] = event_2; break; } } } } return res; }; MathBackendCPU.prototype.oneHot = function (indices, depth, onValue, offValue) { this.assertNotComplex(indices, 'oneHot'); var res = new Float32Array(indices.size * depth); res.fill(offValue); for (var event_3 = 0; event_3 < indices.size; ++event_3) { if (indices.get(event_3) >= 0 && indices.get(event_3) < depth) { res[event_3 * depth + indices.get(event_3)] = onValue; } } return tensor2d(res, [indices.size, depth], 'int32'); }; MathBackendCPU.prototype.nonMaxSuppression = function (boxes, scores, maxOutputSize, iouThreshold, scoreThreshold) { this.assertNotComplex(boxes, 'nonMaxSuppression'); var boxesVals = boxes.dataSync(); var scoresVals = scores.dataSync(); return nonMaxSuppressionImpl(boxesVals, scoresVals, maxOutputSize, iouThreshold, scoreThreshold); }; MathBackendCPU.prototype.fft = function (x) { return this.fftBatch(x, false); }; MathBackendCPU.prototype.ifft = function (x) { return this.fftBatch(x, true); }; MathBackendCPU.prototype.fftBatch = function (x, inverse) { var batch = x.shape[0]; var innerDim = x.shape[1]; var realResult = buffer(x.shape, 'float32'); var imagResult = buffer(x.shape, 'float32'); var real$$1 = real(x).as2D(batch, innerDim); var imag$$1 = imag(x).as2D(batch, innerDim); for (var b = 0; b < batch; b++) { var r = real$$1.slice([b, 0], [1, innerDim]); var i = imag$$1.slice([b, 0], [1, innerDim]); var input = complex(r, i); var res = this.fftImpl(input, inverse).dataSync(); for (var d = 0; d < innerDim; d++) { var c = getComplexWithIndex(res, d); realResult.values[b * innerDim + d] = c.real; imagResult.values[b * innerDim + d] = c.imag; } } var t = complex(realResult.toTensor(), imagResult.toTensor()); return t.as2D(batch, innerDim); }; MathBackendCPU.prototype.fftImpl = function (x, inverse) { var x1D = x.as1D(); var n = x1D.size; if (this.isExponentOf2(n)) { var result = this.fftRadix2(x1D, n, inverse).as2D(x.shape[0], x.shape[1]); if (inverse) { result = complex(real(result).div(scalar(n)), imag(result).div(scalar(n))); } return result; } else { var data = x.dataSync(); var rawOutput = this.fourierTransformByMatmul(data, n, inverse); var output = splitRealAndImagArrays(rawOutput); return complex(output.real, output.imag).as2D(x.shape[0], x.shape[1]); } }; MathBackendCPU.prototype.isExponentOf2 = function (size) { return (size & size - 1) === 0; }; MathBackendCPU.prototype.fftRadix2 = function (input, size, inverse) { if (size === 1) { return input; } var data = input.dataSync(); var half = size / 2; var evenComplex = complexWithEvenIndex(data); var evenTensor = complex(evenComplex.real, evenComplex.imag).as1D(); var oddComplex = complexWithOddIndex(data); var oddTensor = complex(oddComplex.real, oddComplex.imag).as1D(); evenTensor = this.fftRadix2(evenTensor, half, inverse); oddTensor = this.fftRadix2(oddTensor, half, inverse); var e = exponents(size, inverse); var exponent$$1 = complex(e.real, e.imag).mul(oddTensor); var addPart = evenTensor.add(exponent$$1); var subPart = evenTensor.sub(exponent$$1); var realTensor = real(addPart).concat(real(subPart)); var imagTensor = imag(addPart).concat(imag(subPart)); return complex(realTensor, imagTensor).as1D(); }; MathBackendCPU.prototype.fourierTransformByMatmul = function (data, size, inverse) { var ret = new Float32Array(size * 2); for (var r = 0; r < size; r++) { var real$$1 = 0.0; var imag$$1 = 0.0; for (var c = 0; c < size; c++) { var e = exponent(r * c, size, inverse); var term = getComplexWithIndex(data, c); real$$1 += term.real * e.real - term.imag * e.imag; imag$$1 += term.real * e.imag + term.imag * e.real; } if (inverse) { real$$1 /= size; imag$$1 /= size; } assignToTypedArray(ret, real$$1, imag$$1, r); } return ret; }; MathBackendCPU.prototype.depthToSpace = function (x, blockSize, dataFormat) { assert(dataFormat === 'NHWC', "Only NHWC dataFormat supported on CPU for depthToSpace. Got " + dataFormat); assert(blockSize > 1, "blockSize should be > 1 for depthToSpace, but was: " + blockSize); var batchSize = x.shape[0]; var inputHeight = x.shape[1]; var inputWidth = x.shape[2]; var inputDepth = x.shape[3]; var outputHeight = inputHeight * blockSize; var outputWidth = inputWidth * blockSize; var outputDepth = inputDepth / (blockSize * blockSize); var xValues = x.dataSync(); var result = new Float32Array(batchSize * outputHeight * outputWidth * outputDepth); var outputIdx = 0; for (var b = 0; b < batchSize; ++b) { for (var h = 0; h < outputHeight; ++h) { var inH = Math.floor(h / blockSize); var offsetH = (h % blockSize); for (var w = 0; w < outputWidth; ++w) { var inW = Math.floor(w / blockSize); var offsetW = (w % blockSize); var offsetD = (offsetH * blockSize + offsetW) * outputDepth; for (var d = 0; d < outputDepth; ++d) { var inD = d + offsetD; var inputIdx = inD + inputDepth * (inW + inputWidth * (inH + inputHeight * b)); result[outputIdx++] = xValues[inputIdx]; } } } } return tensor4d(result, [batchSize, outputHeight, outputWidth, outputDepth]); }; MathBackendCPU.prototype.broadcastedBinaryOp = function (a, b, dtype, op$$1) { var newShape = assertAndGetBroadcastShape(a.shape, b.shape); var result = buffer(newShape, dtype); var aVals = a.dataSync(); var bVals = b.dataSync(); var aBroadcastDims = getBroadcastDims(a.shape, newShape); var bBroadcastDims = getBroadcastDims(b.shape, newShape); var resVals = result.values; if (aBroadcastDims.length + bBroadcastDims.length === 0) { for (var i = 0; i < resVals.length; ++i) { resVals[i] = op$$1(aVals[i % aVals.length], bVals[i % bVals.length]); } } else { var aBuf = a.buffer(); var bBuf = b.buffer(); var _loop_2 = function (i) { var loc = result.indexToLoc(i); var aLoc = loc.slice(-a.rank); aBroadcastDims.forEach(function (d) { return aLoc[d] = 0; }); var aIndex = aBuf.locToIndex(aLoc); var bLoc = loc.slice(-b.rank); bBroadcastDims.forEach(function (d) { return bLoc[d] = 0; }); var bIndex = bBuf.locToIndex(bLoc); resVals[i] = op$$1(aVals[aIndex], bVals[bIndex]); }; for (var i = 0; i < resVals.length; ++i) { _loop_2(i); } } return result.toTensor(); }; MathBackendCPU.prototype.broadcastedBinaryComplexOp = function (a, b, op$$1) { var newShape = assertAndGetBroadcastShape(a.shape, b.shape); var realResult = buffer(newShape, 'float32'); var imagResult = buffer(newShape, 'float32'); var aVals = a.dataSync(); var bVals = b.dataSync(); var aBroadcastDims = getBroadcastDims(a.shape, newShape); var bBroadcastDims = getBroadcastDims(b.shape, newShape); var realVals = realResult.values; var imagVals = imagResult.values; if (aBroadcastDims.length + bBroadcastDims.length === 0) { for (var i = 0; i < realVals.length; i++) { var aIdx = i % aVals.length; var bIdx = i % bVals.length; var result = op$$1(aVals[aIdx * 2], aVals[aIdx * 2 + 1], bVals[bIdx * 2], bVals[bIdx * 2 + 1]); realVals[i] = result.real; imagVals[i] = result.imag; } } else { var aRealBuf = this.data.get(a.dataId).complexTensors.real.buffer(); var bRealBuf = this.data.get(b.dataId).complexTensors.real.buffer(); var _loop_3 = function (i) { var loc = realResult.indexToLoc(i); var aLoc = loc.slice(-a.rank); aBroadcastDims.forEach(function (d) { return aLoc[d] = 0; }); var aIndex = aRealBuf.locToIndex(aLoc); var bLoc = loc.slice(-b.rank); bBroadcastDims.forEach(function (d) { return bLoc[d] = 0; }); var bIndex = bRealBuf.locToIndex(bLoc); var opResult = op$$1(aVals[aIndex * 2], aVals[aIndex * 2 + 1], bVals[bIndex * 2], bVals[bIndex * 2 + 1]); realVals[i] = opResult.real; imagVals[i] = opResult.imag; }; for (var i = 0; i < realVals.length; i++) { _loop_3(i); } } return this.complex(realResult.toTensor(), imagResult.toTensor()); }; MathBackendCPU.prototype.split = function (x, sizeSplits, axis) { return split(x, sizeSplits, axis); }; MathBackendCPU.prototype.dispose = function () { }; MathBackendCPU.prototype.floatPrecision = function () { return 32; }; MathBackendCPU.prototype.cropAndResize = function (images, boxes, boxIndex, cropSize, method, extrapolationValue) { var _a = images.shape, batch = _a[0], imageHeight = _a[1], imageWidth = _a[2], numChannels = _a[3]; var numBoxes = boxes.shape[0]; var cropHeight = cropSize[0], cropWidth = cropSize[1]; var output = buffer([numBoxes, cropHeight, cropWidth, numChannels], images.dtype); var boxVals = boxes.dataSync(); var boxIndVals = boxIndex.dataSync(); var imageVals = images.dataSync(); var inStride = images.strides; var outStride = output.strides; for (var b = 0; b < numBoxes; b++) { var startInd = b * 4; var y1 = boxVals[startInd]; var x1 = boxVals[startInd + 1]; var y2 = boxVals[startInd + 2]; var x2 = boxVals[startInd + 3]; var bInd = boxIndVals[b]; if (bInd >= batch) { continue; } var heightScale = (cropHeight > 1) ? (y2 - y1) * (imageHeight - 1) / (cropHeight - 1) : 0; var widthScale = (cropWidth > 1) ? (x2 - x1) * (imageWidth - 1) / (cropWidth - 1) : 0; for (var y = 0; y < cropHeight; y++) { var yInd = (cropHeight > 1) ? y1 * (imageHeight - 1) + y * (heightScale) : 0.5 * (y1 + y2) * (imageHeight - 1); if (yInd < 0 || yInd > imageHeight - 1) { for (var x = 0; x < cropWidth; x++) { for (var c = 0; c < numChannels; c++) { var ind = c + x * outStride[2] + y * outStride[1] + b * outStride[0]; output.values[ind] = extrapolationValue; } } continue; } if (method === 'bilinear') { var topInd = Math.floor(yInd); var bottomInd = Math.ceil(yInd); var yLerp = yInd - topInd; for (var x = 0; x < cropWidth; x++) { var xInd = (cropWidth > 1) ? x1 * (imageWidth - 1) + x * widthScale : 0.5 * (x1 + x2) * (imageWidth - 1); if (xInd < 0 || xInd > imageWidth - 1) { for (var c = 0; c < numChannels; c++) { var ind = c + x * outStride[2] + y * outStride[1] + b * outStride[0]; output.values[ind] = extrapolationValue; } continue; } var leftInd = Math.floor(xInd); var rightInd = Math.ceil(xInd); var xLerp = xInd - leftInd; for (var c = 0; c < numChannels; c++) { var ind = c + leftInd * inStride[2] + topInd * inStride[1] + bInd * inStride[0]; var topLeft = imageVals[ind]; ind = c + rightInd * inStride[2] + topInd * inStride[1] + bInd * inStride[0]; var topRight = imageVals[ind]; ind = c + leftInd * inStride[2] + bottomInd * inStride[1] + bInd * inStride[0]; var bottomLeft = imageVals[ind]; ind = c + rightInd * inStride[2] + bottomInd * inStride[1] + bInd * inStride[0]; var bottomRight = imageVals[ind]; var top_2 = topLeft + (topRight - topLeft) * xLerp; var bottom = bottomLeft + (bottomRight - bottomLeft) * xLerp; ind = c + x * outStride[2] + y * outStride[1] + b * outStride[0]; output.values[ind] = top_2 + ((bottom - top_2) * yLerp); } } } else { for (var x = 0; x < cropWidth; ++x) { var xInd = (cropWidth > 1) ? x1 * (imageWidth - 1) + x * widthScale : 0.5 * (x1 + x2) * (imageWidth - 1); if (xInd < 0 || xInd > imageWidth - 1) { for (var c = 0; c < numChannels; c++) { var ind = c + x * outStride[2] + y * outStride[1] + b * outStride[0]; output.values[ind] = extrapolationValue; } continue; } var closestX = Math.round(xInd); var closestY = Math.round(yInd); for (var c = 0; c < numChannels; c++) { var inInd = c + closestX * inStride[2] + closestY * inStride[1] + bInd * inStride[0]; var outInd = c + x * outStride[2] + y * outStride[1] + b * outStride[0]; output.values[outInd] = imageVals[inInd]; } } } } } return output.toTensor(); }; MathBackendCPU.prototype.sparseToDense = function (sparseIndices, sparseValues, outputShape, defaultValue) { var _a = calculateShapes(sparseValues, sparseIndices, outputShape), sliceRank = _a.sliceRank, numUpdates = _a.numUpdates, sliceSize = _a.sliceSize, strides = _a.strides, outputSize = _a.outputSize; var sumDupeIndices = false; return this.scatter(sparseIndices, sparseValues, outputShape, outputSize, sliceSize, numUpdates, sliceRank, strides, defaultValue, sumDupeIndices); }; MathBackendCPU.prototype.gatherND = function (x, indices) { var indicesShape = indices.shape; var sliceRank = indicesShape[indicesShape.length - 1]; var _a = prepareAndValidate(x, indices), resultShape = _a[0], numSlices = _a[1], sliceSize = _a[2], strides = _a[3]; if (numSlices === 0) { return tensor([], resultShape, x.dtype); } var buffer$$1 = new TensorBuffer([numSlices, sliceSize], x.dtype); var indicesData = indices.dataSync(); var xData = x.dataSync(); for (var i = 0; i < numSlices; i++) { var index = []; var flattenIndex = 0; for (var j = 0; j < sliceRank; j++) { var dim = indicesData[i * sliceRank + j]; flattenIndex += dim * strides[j]; index.push(dim); } if (flattenIndex < 0 || flattenIndex >= x.size / sliceSize) { throw new Error("Invalid indices: " + index + " does not index into " + x.shape); } for (var k = 0; k < sliceSize; k++) { buffer$$1.values[i * sliceSize + k] = xData[flattenIndex * sliceSize + k]; } } return buffer$$1.toTensor().reshape(resultShape); }; MathBackendCPU.prototype.scatterND = function (indices, updates, shape) { var _a = calculateShapes(updates, indices, shape), sliceRank = _a.sliceRank, numUpdates = _a.numUpdates, sliceSize = _a.sliceSize, strides = _a.strides, outputSize = _a.outputSize; var defaultValue = scalar(0); var sumDupeIndices = true; return this.scatter(indices, updates, shape, outputSize, sliceSize, numUpdates, sliceRank, strides, defaultValue, sumDupeIndices); }; MathBackendCPU.prototype.scatter = function (indices, updates, shape, outputSize, sliceSize, numUpdates, sliceRank, strides, defaultValue, sumDupeIndices) { var flattenShape = [outputSize / sliceSize, sliceSize]; var indicesData = indices.dataSync(); var updatesData = updates.dataSync(); if (outputSize === 0) { return tensor([], shape, updates.dtype); } var buffer$$1 = new TensorBuffer(flattenShape, updates.dtype); buffer$$1.values.fill(defaultValue.dataSync()[0]); for (var i = 0; i < numUpdates; i++) { var index = []; var flattenIndex = 0; for (var j = 0; j < sliceRank; j++) { var dim = indicesData[i * sliceRank + j]; index.push(dim); flattenIndex += dim * strides[j]; } if (flattenIndex < 0 || flattenIndex >= outputSize / sliceSize) { throw new Error("Invalid indices: " + index + " does not index into " + shape); } for (var k = 0; k < sliceSize; k++) { if (sumDupeIndices) { buffer$$1.values[flattenIndex * sliceSize + k] += updatesData[i * sliceSize + k]; } else { buffer$$1.values[flattenIndex * sliceSize + k] = updates.rank === 0 ? updatesData[0] : updatesData[i * sliceSize + k]; } } } return buffer$$1.toTensor().reshape(shape); }; return MathBackendCPU; }()); ENV.registerBackend('cpu', function () { return new MathBackendCPU(); }, 1, setTensorTracker); var delayCallback = typeof requestAnimationFrame !== 'undefined' ? requestAnimationFrame : setImmediate; function nextFrame() { return new Promise(function (resolve) { return delayCallback(function () { return resolve(); }); }); } var DTYPE_VALUE_SIZE_MAP = { 'float32': 4, 'int32': 4, 'uint16': 2, 'uint8': 1, 'bool': 1, }; function encodeWeights(tensors) { return __awaiter(this, void 0, void 0, function () { var specs, dataPromises, name_1, t, tensorValues; return __generator(this, function (_a) { switch (_a.label) { case 0: specs = []; dataPromises = []; for (name_1 in tensors) { t = tensors[name_1]; if (t.dtype !== 'float32' && t.dtype !== 'int32' && t.dtype !== 'bool') { throw new Error("Unsupported dtype in weight '" + name_1 + "': " + t.dtype); } specs.push({ name: name_1, shape: t.shape, dtype: t.dtype }); dataPromises.push(t.data()); } return [4, Promise.all(dataPromises)]; case 1: tensorValues = _a.sent(); return [2, { data: concatenateTypedArrays(tensorValues), specs: specs }]; } }); }); } function decodeWeights(buffer, specs) { var out = {}; var offset = 0; var _loop_1 = function (spec) { var name_2 = spec.name; var dtype = spec.dtype; var shape = spec.shape; var size = sizeFromShape(shape); var typedArray = void 0; if ('quantization' in spec) { var quantization_1 = spec.quantization; if (quantization_1.dtype !== 'uint8' && quantization_1.dtype !== 'uint16') { throw new Error("Weight " + spec.name + " has unknown " + ("quantization dtype " + quantization_1.dtype + ". ") + "Supported quantization dtypes are: 'uint8' and 'uint16'."); } var quantizationSizeFactor = DTYPE_VALUE_SIZE_MAP[quantization_1.dtype]; var byteBuffer = buffer.slice(offset, offset + size * quantizationSizeFactor); var quantizedArray = (quantization_1.dtype === 'uint8') ? new Uint8Array(byteBuffer) : new Uint16Array(byteBuffer); if (dtype === 'float32') { typedArray = Float32Array.from(quantizedArray, function (v) { return v * quantization_1.scale + quantization_1.min; }); } else if (dtype === 'int32') { typedArray = Int32Array.from(quantizedArray, function (v) { return Math.round(v * quantization_1.scale + quantization_1.min); }); } else { throw new Error("Unsupported dtype in weight '" + name_2 + "': " + dtype); } offset += size * quantizationSizeFactor; } else { var dtypeFactor = DTYPE_VALUE_SIZE_MAP[dtype]; var byteBuffer = buffer.slice(offset, offset + size * dtypeFactor); if (dtype === 'float32') { typedArray = new Float32Array(byteBuffer); } else if (dtype === 'int32') { typedArray = new Int32Array(byteBuffer); } else if (dtype === 'bool') { typedArray = new Uint8Array(byteBuffer); } else { throw new Error("Unsupported dtype in weight '" + name_2 + "': " + dtype); } offset += size * dtypeFactor; } var value = void 0; if (dtype === 'float32') { value = tensor(typedArray, shape, 'float32'); } else if (dtype === 'int32') { value = tensor(typedArray, shape, 'int32'); } else if (dtype === 'bool') { value = tensor(typedArray, shape, 'bool'); } else { throw new Error("Unsupported dtype in weight '" + name_2 + "': " + dtype); } out[name_2] = value; }; for (var _i = 0, specs_1 = specs; _i < specs_1.length; _i++) { var spec = specs_1[_i]; _loop_1(spec); } return out; } function concatenateTypedArrays(xs) { if (xs === null) { throw new Error("Invalid input value: " + JSON.stringify(xs)); } var totalByteLength = 0; var normalizedXs = []; xs.forEach(function (x) { totalByteLength += x.byteLength; normalizedXs.push(x.byteLength === x.buffer.byteLength ? x : new x.constructor(x)); if (!(x instanceof Float32Array || x instanceof Int32Array || x instanceof Uint8Array)) { throw new Error("Unsupported TypedArray subtype: " + x.constructor.name); } }); var y = new Uint8Array(totalByteLength); var offset = 0; normalizedXs.forEach(function (x) { y.set(new Uint8Array(x.buffer), offset); offset += x.byteLength; }); return y.buffer; } var useNodeBuffer = typeof Buffer !== 'undefined' && (typeof Blob === 'undefined' || typeof atob === 'undefined' || typeof btoa === 'undefined'); function stringByteLength(str) { if (useNodeBuffer) { return Buffer.byteLength(str); } return new Blob([str]).size; } function arrayBufferToBase64String(buffer) { if (useNodeBuffer) { return Buffer.from(buffer).toString('base64'); } return btoa(String.fromCharCode.apply(null, new Uint8Array(buffer))); } function base64StringToArrayBuffer(str) { if (useNodeBuffer) { var buf = Buffer.from(str, 'base64'); return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength); } var s = atob(str); var buffer = new Uint8Array(s.length); for (var i = 0; i < s.length; ++i) { buffer.set([s.charCodeAt(i)], i); } return buffer.buffer; } function concatenateArrayBuffers(buffers) { var totalByteLength = 0; buffers.forEach(function (buffer) { totalByteLength += buffer.byteLength; }); var temp = new Uint8Array(totalByteLength); var offset = 0; buffers.forEach(function (buffer) { temp.set(new Uint8Array(buffer), offset); offset += buffer.byteLength; }); return temp.buffer; } function basename(path) { var SEPARATOR = '/'; path = path.trim(); while (path.endsWith(SEPARATOR)) { path = path.slice(0, path.length - 1); } var items = path.split(SEPARATOR); return items[items.length - 1]; } function getModelArtifactsInfoForJSON(modelArtifacts) { if (modelArtifacts.modelTopology instanceof ArrayBuffer) { throw new Error('Expected JSON model topology, received ArrayBuffer.'); } return { dateSaved: new Date(), modelTopologyType: 'JSON', modelTopologyBytes: modelArtifacts.modelTopology == null ? 0 : stringByteLength(JSON.stringify(modelArtifacts.modelTopology)), weightSpecsBytes: modelArtifacts.weightSpecs == null ? 0 : stringByteLength(JSON.stringify(modelArtifacts.weightSpecs)), weightDataBytes: modelArtifacts.weightData == null ? 0 : modelArtifacts.weightData.byteLength, }; } var IORouterRegistry = (function () { function IORouterRegistry() { this.saveRouters = []; this.loadRouters = []; } IORouterRegistry.getInstance = function () { if (IORouterRegistry.instance == null) { IORouterRegistry.instance = new IORouterRegistry(); } return IORouterRegistry.instance; }; IORouterRegistry.registerSaveRouter = function (saveRouter) { IORouterRegistry.getInstance().saveRouters.push(saveRouter); }; IORouterRegistry.registerLoadRouter = function (loadRouter) { IORouterRegistry.getInstance().loadRouters.push(loadRouter); }; IORouterRegistry.getSaveHandlers = function (url) { return IORouterRegistry.getHandlers(url, 'save'); }; IORouterRegistry.getLoadHandlers = function (url, onProgress) { return IORouterRegistry.getHandlers(url, 'load', onProgress); }; IORouterRegistry.getHandlers = function (url, handlerType, onProgress) { var validHandlers = []; var routers = handlerType === 'load' ? this.getInstance().loadRouters : this.getInstance().saveRouters; routers.forEach(function (router) { var handler = router(url, onProgress); if (handler !== null) { validHandlers.push(handler); } }); return validHandlers; }; return IORouterRegistry; }()); var registerSaveRouter = function (loudRouter) { return IORouterRegistry.registerSaveRouter(loudRouter); }; var registerLoadRouter = function (loudRouter) { return IORouterRegistry.registerLoadRouter(loudRouter); }; var getSaveHandlers = function (url) { return IORouterRegistry.getSaveHandlers(url); }; var getLoadHandlers = function (url, onProgress) { return IORouterRegistry.getLoadHandlers(url); }; var URL_SCHEME_SUFFIX = '://'; var ModelStoreManagerRegistry = (function () { function ModelStoreManagerRegistry() { this.managers = {}; } ModelStoreManagerRegistry.getInstance = function () { if (ModelStoreManagerRegistry.instance == null) { ModelStoreManagerRegistry.instance = new ModelStoreManagerRegistry(); } return ModelStoreManagerRegistry.instance; }; ModelStoreManagerRegistry.registerManager = function (scheme, manager) { assert(scheme != null, 'scheme must not be undefined or null.'); if (scheme.endsWith(URL_SCHEME_SUFFIX)) { scheme = scheme.slice(0, scheme.indexOf(URL_SCHEME_SUFFIX)); } assert(scheme.length > 0, 'scheme must not be an empty string.'); var registry = ModelStoreManagerRegistry.getInstance(); assert(registry.managers[scheme] == null, "A model store manager is already registered for scheme '" + scheme + "'."); registry.managers[scheme] = manager; }; ModelStoreManagerRegistry.getManager = function (scheme) { var manager = this.getInstance().managers[scheme]; if (manager == null) { throw new Error("Cannot find model manager for scheme '" + scheme + "'"); } return manager; }; ModelStoreManagerRegistry.getSchemes = function () { return Object.keys(this.getInstance().managers); }; return ModelStoreManagerRegistry; }()); function parseURL(url) { if (url.indexOf(URL_SCHEME_SUFFIX) === -1) { throw new Error("The url string provided does not contain a scheme. " + "Supported schemes are: " + ("" + ModelStoreManagerRegistry.getSchemes().join(','))); } return { scheme: url.split(URL_SCHEME_SUFFIX)[0], path: url.split(URL_SCHEME_SUFFIX)[1], }; } function cloneModelInternal(sourceURL, destURL, deleteSource) { if (deleteSource === void 0) { deleteSource = false; } return __awaiter(this, void 0, void 0, function () { var loadHandlers, loadHandler, saveHandlers, saveHandler, sourceScheme, sourcePath, sameMedium, modelArtifacts, saveResult; return __generator(this, function (_a) { switch (_a.label) { case 0: assert(sourceURL !== destURL, "Old path and new path are the same: '" + sourceURL + "'"); loadHandlers = IORouterRegistry.getLoadHandlers(sourceURL); assert(loadHandlers.length > 0, "Copying failed because no load handler is found for source URL " + sourceURL + "."); assert(loadHandlers.length < 2, "Copying failed because more than one (" + loadHandlers.length + ") " + ("load handlers for source URL " + sourceURL + ".")); loadHandler = loadHandlers[0]; saveHandlers = IORouterRegistry.getSaveHandlers(destURL); assert(saveHandlers.length > 0, "Copying failed because no save handler is found for destination URL " + (destURL + ".")); assert(saveHandlers.length < 2, "Copying failed because more than one (" + loadHandlers.length + ") " + ("save handlers for destination URL " + destURL + ".")); saveHandler = saveHandlers[0]; sourceScheme = parseURL(sourceURL).scheme; sourcePath = parseURL(sourceURL).path; sameMedium = sourceScheme === parseURL(sourceURL).scheme; return [4, loadHandler.load()]; case 1: modelArtifacts = _a.sent(); if (!(deleteSource && sameMedium)) return [3, 3]; return [4, ModelStoreManagerRegistry.getManager(sourceScheme) .removeModel(sourcePath)]; case 2: _a.sent(); _a.label = 3; case 3: return [4, saveHandler.save(modelArtifacts)]; case 4: saveResult = _a.sent(); if (!(deleteSource && !sameMedium)) return [3, 6]; return [4, ModelStoreManagerRegistry.getManager(sourceScheme) .removeModel(sourcePath)]; case 5: _a.sent(); _a.label = 6; case 6: return [2, saveResult.modelArtifactsInfo]; } }); }); } function listModels() { return __awaiter(this, void 0, void 0, function () { var schemes, out, _i, schemes_1, scheme, schemeOut, path, url; return __generator(this, function (_a) { switch (_a.label) { case 0: schemes = ModelStoreManagerRegistry.getSchemes(); out = {}; _i = 0, schemes_1 = schemes; _a.label = 1; case 1: if (!(_i < schemes_1.length)) return [3, 4]; scheme = schemes_1[_i]; return [4, ModelStoreManagerRegistry.getManager(scheme).listModels()]; case 2: schemeOut = _a.sent(); for (path in schemeOut) { url = scheme + URL_SCHEME_SUFFIX + path; out[url] = schemeOut[path]; } _a.label = 3; case 3: _i++; return [3, 1]; case 4: return [2, out]; } }); }); } function removeModel(url) { return __awaiter(this, void 0, void 0, function () { var schemeAndPath, manager; return __generator(this, function (_a) { switch (_a.label) { case 0: schemeAndPath = parseURL(url); manager = ModelStoreManagerRegistry.getManager(schemeAndPath.scheme); return [4, manager.removeModel(schemeAndPath.path)]; case 1: return [2, _a.sent()]; } }); }); } function copyModel(sourceURL, destURL) { return __awaiter(this, void 0, void 0, function () { var deleteSource; return __generator(this, function (_a) { switch (_a.label) { case 0: deleteSource = false; return [4, cloneModelInternal(sourceURL, destURL, deleteSource)]; case 1: return [2, _a.sent()]; } }); }); } function moveModel(sourceURL, destURL) { return __awaiter(this, void 0, void 0, function () { var deleteSource; return __generator(this, function (_a) { switch (_a.label) { case 0: deleteSource = true; return [4, cloneModelInternal(sourceURL, destURL, deleteSource)]; case 1: return [2, _a.sent()]; } }); }); } var DATABASE_NAME = 'tensorflowjs'; var DATABASE_VERSION = 1; var MODEL_STORE_NAME = 'models_store'; var INFO_STORE_NAME = 'model_info_store'; function getIndexedDBFactory() { if (!ENV.get('IS_BROWSER')) { throw new Error('Failed to obtain IndexedDB factory because the current environment' + 'is not a web browser.'); } var theWindow = window; var factory = theWindow.indexedDB || theWindow.mozIndexedDB || theWindow.webkitIndexedDB || theWindow.msIndexedDB || theWindow.shimIndexedDB; if (factory == null) { throw new Error('The current browser does not appear to support IndexedDB.'); } return factory; } function setUpDatabase(openRequest) { var db = openRequest.result; db.createObjectStore(MODEL_STORE_NAME, { keyPath: 'modelPath' }); db.createObjectStore(INFO_STORE_NAME, { keyPath: 'modelPath' }); } var BrowserIndexedDB = (function () { function BrowserIndexedDB(modelPath) { this.indexedDB = getIndexedDBFactory(); if (modelPath == null || !modelPath) { throw new Error('For IndexedDB, modelPath must not be null, undefined or empty.'); } this.modelPath = modelPath; } BrowserIndexedDB.prototype.save = function (modelArtifacts) { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { if (modelArtifacts.modelTopology instanceof ArrayBuffer) { throw new Error('BrowserLocalStorage.save() does not support saving model topology ' + 'in binary formats yet.'); } return [2, this.databaseAction(this.modelPath, modelArtifacts)]; }); }); }; BrowserIndexedDB.prototype.load = function () { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { return [2, this.databaseAction(this.modelPath)]; }); }); }; BrowserIndexedDB.prototype.databaseAction = function (modelPath, modelArtifacts) { var _this = this; return new Promise(function (resolve, reject) { var openRequest = _this.indexedDB.open(DATABASE_NAME, DATABASE_VERSION); openRequest.onupgradeneeded = function () { return setUpDatabase(openRequest); }; openRequest.onsuccess = function () { var db = openRequest.result; if (modelArtifacts == null) { var modelTx = db.transaction(MODEL_STORE_NAME, 'readonly'); var modelStore = modelTx.objectStore(MODEL_STORE_NAME); var getRequest_1 = modelStore.get(_this.modelPath); getRequest_1.onsuccess = function () { if (getRequest_1.result == null) { db.close(); return reject(new Error("Cannot find model with path '" + _this.modelPath + "' " + "in IndexedDB.")); } else { resolve(getRequest_1.result.modelArtifacts); } }; getRequest_1.onerror = function (error) { db.close(); return reject(getRequest_1.error); }; modelTx.oncomplete = function () { return db.close(); }; } else { var modelArtifactsInfo_1 = getModelArtifactsInfoForJSON(modelArtifacts); var infoTx_1 = db.transaction(INFO_STORE_NAME, 'readwrite'); var infoStore_1 = infoTx_1.objectStore(INFO_STORE_NAME); var putInfoRequest_1 = infoStore_1.put({ modelPath: _this.modelPath, modelArtifactsInfo: modelArtifactsInfo_1 }); var modelTx_1; putInfoRequest_1.onsuccess = function () { modelTx_1 = db.transaction(MODEL_STORE_NAME, 'readwrite'); var modelStore = modelTx_1.objectStore(MODEL_STORE_NAME); var putModelRequest = modelStore.put({ modelPath: _this.modelPath, modelArtifacts: modelArtifacts, modelArtifactsInfo: modelArtifactsInfo_1 }); putModelRequest.onsuccess = function () { return resolve({ modelArtifactsInfo: modelArtifactsInfo_1 }); }; putModelRequest.onerror = function (error) { infoStore_1 = infoTx_1.objectStore(INFO_STORE_NAME); var deleteInfoRequest = infoStore_1.delete(_this.modelPath); deleteInfoRequest.onsuccess = function () { db.close(); return reject(putModelRequest.error); }; deleteInfoRequest.onerror = function (error) { db.close(); return reject(putModelRequest.error); }; }; }; putInfoRequest_1.onerror = function (error) { db.close(); return reject(putInfoRequest_1.error); }; infoTx_1.oncomplete = function () { if (modelTx_1 == null) { db.close(); } else { modelTx_1.oncomplete = function () { return db.close(); }; } }; } }; openRequest.onerror = function (error) { return reject(openRequest.error); }; }); }; BrowserIndexedDB.URL_SCHEME = 'indexeddb://'; return BrowserIndexedDB; }()); var indexedDBRouter = function (url) { if (!ENV.get('IS_BROWSER')) { return null; } else { if (!Array.isArray(url) && url.startsWith(BrowserIndexedDB.URL_SCHEME)) { return browserIndexedDB(url.slice(BrowserIndexedDB.URL_SCHEME.length)); } else { return null; } } }; IORouterRegistry.registerSaveRouter(indexedDBRouter); IORouterRegistry.registerLoadRouter(indexedDBRouter); function browserIndexedDB(modelPath) { return new BrowserIndexedDB(modelPath); } function maybeStripScheme(key) { return key.startsWith(BrowserIndexedDB.URL_SCHEME) ? key.slice(BrowserIndexedDB.URL_SCHEME.length) : key; } var BrowserIndexedDBManager = (function () { function BrowserIndexedDBManager() { this.indexedDB = getIndexedDBFactory(); } BrowserIndexedDBManager.prototype.listModels = function () { return __awaiter(this, void 0, void 0, function () { var _this = this; return __generator(this, function (_a) { return [2, new Promise(function (resolve, reject) { var openRequest = _this.indexedDB.open(DATABASE_NAME, DATABASE_VERSION); openRequest.onupgradeneeded = function () { return setUpDatabase(openRequest); }; openRequest.onsuccess = function () { var db = openRequest.result; var tx = db.transaction(INFO_STORE_NAME, 'readonly'); var store = tx.objectStore(INFO_STORE_NAME); var getAllInfoRequest = store.getAll(); getAllInfoRequest.onsuccess = function () { var out = {}; for (var _i = 0, _a = getAllInfoRequest.result; _i < _a.length; _i++) { var item = _a[_i]; out[item.modelPath] = item.modelArtifactsInfo; } resolve(out); }; getAllInfoRequest.onerror = function (error) { db.close(); return reject(getAllInfoRequest.error); }; tx.oncomplete = function () { return db.close(); }; }; openRequest.onerror = function (error) { return reject(openRequest.error); }; })]; }); }); }; BrowserIndexedDBManager.prototype.removeModel = function (path) { return __awaiter(this, void 0, void 0, function () { var _this = this; return __generator(this, function (_a) { path = maybeStripScheme(path); return [2, new Promise(function (resolve, reject) { var openRequest = _this.indexedDB.open(DATABASE_NAME, DATABASE_VERSION); openRequest.onupgradeneeded = function () { return setUpDatabase(openRequest); }; openRequest.onsuccess = function () { var db = openRequest.result; var infoTx = db.transaction(INFO_STORE_NAME, 'readwrite'); var infoStore = infoTx.objectStore(INFO_STORE_NAME); var getInfoRequest = infoStore.get(path); var modelTx; getInfoRequest.onsuccess = function () { if (getInfoRequest.result == null) { db.close(); return reject(new Error("Cannot find model with path '" + path + "' " + "in IndexedDB.")); } else { var deleteInfoRequest = infoStore.delete(path); var deleteModelData_1 = function () { modelTx = db.transaction(MODEL_STORE_NAME, 'readwrite'); var modelStore = modelTx.objectStore(MODEL_STORE_NAME); var deleteModelRequest = modelStore.delete(path); deleteModelRequest.onsuccess = function () { return resolve(getInfoRequest.result.modelArtifactsInfo); }; deleteModelRequest.onerror = function (error) { return reject(getInfoRequest.error); }; }; deleteInfoRequest.onsuccess = deleteModelData_1; deleteInfoRequest.onerror = function (error) { deleteModelData_1(); db.close(); return reject(getInfoRequest.error); }; } }; getInfoRequest.onerror = function (error) { db.close(); return reject(getInfoRequest.error); }; infoTx.oncomplete = function () { if (modelTx == null) { db.close(); } else { modelTx.oncomplete = function () { return db.close(); }; } }; }; openRequest.onerror = function (error) { return reject(openRequest.error); }; })]; }); }); }; return BrowserIndexedDBManager; }()); if (ENV.get('IS_BROWSER')) { try { ModelStoreManagerRegistry.registerManager(BrowserIndexedDB.URL_SCHEME, new BrowserIndexedDBManager()); } catch (err) { } } var PATH_SEPARATOR = '/'; var PATH_PREFIX = 'tensorflowjs_models'; var INFO_SUFFIX = 'info'; var MODEL_TOPOLOGY_SUFFIX = 'model_topology'; var WEIGHT_SPECS_SUFFIX = 'weight_specs'; var WEIGHT_DATA_SUFFIX = 'weight_data'; function getModelKeys(path) { return { info: [PATH_PREFIX, path, INFO_SUFFIX].join(PATH_SEPARATOR), topology: [PATH_PREFIX, path, MODEL_TOPOLOGY_SUFFIX].join(PATH_SEPARATOR), weightSpecs: [PATH_PREFIX, path, WEIGHT_SPECS_SUFFIX].join(PATH_SEPARATOR), weightData: [PATH_PREFIX, path, WEIGHT_DATA_SUFFIX].join(PATH_SEPARATOR) }; } function getModelPathFromKey(key) { var items = key.split(PATH_SEPARATOR); if (items.length < 3) { throw new Error("Invalid key format: " + key); } return items.slice(1, items.length - 1).join(PATH_SEPARATOR); } function maybeStripScheme$1(key) { return key.startsWith(BrowserLocalStorage.URL_SCHEME) ? key.slice(BrowserLocalStorage.URL_SCHEME.length) : key; } var BrowserLocalStorage = (function () { function BrowserLocalStorage(modelPath) { if (!ENV.get('IS_BROWSER') || typeof window.localStorage === 'undefined') { throw new Error('The current environment does not support local storage.'); } this.LS = window.localStorage; if (modelPath == null || !modelPath) { throw new Error('For local storage, modelPath must not be null, undefined or empty.'); } this.modelPath = modelPath; this.keys = getModelKeys(this.modelPath); } BrowserLocalStorage.prototype.save = function (modelArtifacts) { return __awaiter(this, void 0, void 0, function () { var topology, weightSpecs, modelArtifactsInfo, key; return __generator(this, function (_a) { if (modelArtifacts.modelTopology instanceof ArrayBuffer) { throw new Error('BrowserLocalStorage.save() does not support saving model topology ' + 'in binary formats yet.'); } else { topology = JSON.stringify(modelArtifacts.modelTopology); weightSpecs = JSON.stringify(modelArtifacts.weightSpecs); modelArtifactsInfo = getModelArtifactsInfoForJSON(modelArtifacts); try { this.LS.setItem(this.keys.info, JSON.stringify(modelArtifactsInfo)); this.LS.setItem(this.keys.topology, topology); this.LS.setItem(this.keys.weightSpecs, weightSpecs); this.LS.setItem(this.keys.weightData, arrayBufferToBase64String(modelArtifacts.weightData)); return [2, { modelArtifactsInfo: modelArtifactsInfo }]; } catch (err) { for (key in this.keys) { this.LS.removeItem(this.keys[key]); } throw new Error("Failed to save model '" + this.modelPath + "' to local storage: " + "size quota being exceeded is a possible cause of this failure: " + ("modelTopologyBytes=" + modelArtifactsInfo.modelTopologyBytes + ", ") + ("weightSpecsBytes=" + modelArtifactsInfo.weightSpecsBytes + ", ") + ("weightDataBytes=" + modelArtifactsInfo.weightDataBytes + ".")); } } return [2]; }); }); }; BrowserLocalStorage.prototype.load = function () { return __awaiter(this, void 0, void 0, function () { var info, out, topology, weightSpecs, weightDataBase64; return __generator(this, function (_a) { info = JSON.parse(this.LS.getItem(this.keys.info)); if (info == null) { throw new Error("In local storage, there is no model with name '" + this.modelPath + "'"); } if (info.modelTopologyType !== 'JSON') { throw new Error('BrowserLocalStorage does not support loading non-JSON model ' + 'topology yet.'); } out = {}; topology = JSON.parse(this.LS.getItem(this.keys.topology)); if (topology == null) { throw new Error("In local storage, the topology of model '" + this.modelPath + "' " + "is missing."); } out.modelTopology = topology; weightSpecs = JSON.parse(this.LS.getItem(this.keys.weightSpecs)); if (weightSpecs == null) { throw new Error("In local storage, the weight specs of model '" + this.modelPath + "' " + "are missing."); } out.weightSpecs = weightSpecs; weightDataBase64 = this.LS.getItem(this.keys.weightData); if (weightDataBase64 == null) { throw new Error("In local storage, the binary weight values of model " + ("'" + this.modelPath + "' are missing.")); } out.weightData = base64StringToArrayBuffer(weightDataBase64); return [2, out]; }); }); }; BrowserLocalStorage.URL_SCHEME = 'localstorage://'; return BrowserLocalStorage; }()); var localStorageRouter = function (url) { if (!ENV.get('IS_BROWSER')) { return null; } else { if (!Array.isArray(url) && url.startsWith(BrowserLocalStorage.URL_SCHEME)) { return browserLocalStorage(url.slice(BrowserLocalStorage.URL_SCHEME.length)); } else { return null; } } }; IORouterRegistry.registerSaveRouter(localStorageRouter); IORouterRegistry.registerLoadRouter(localStorageRouter); function browserLocalStorage(modelPath) { return new BrowserLocalStorage(modelPath); } var BrowserLocalStorageManager = (function () { function BrowserLocalStorageManager() { assert(ENV.get('IS_BROWSER'), 'Current environment is not a web browser'); assert(typeof window.localStorage !== 'undefined', 'Current browser does not appear to support localStorage'); this.LS = window.localStorage; } BrowserLocalStorageManager.prototype.listModels = function () { return __awaiter(this, void 0, void 0, function () { var out, prefix, suffix, i, key, modelPath; return __generator(this, function (_a) { out = {}; prefix = PATH_PREFIX + PATH_SEPARATOR; suffix = PATH_SEPARATOR + INFO_SUFFIX; for (i = 0; i < this.LS.length; ++i) { key = this.LS.key(i); if (key.startsWith(prefix) && key.endsWith(suffix)) { modelPath = getModelPathFromKey(key); out[modelPath] = JSON.parse(this.LS.getItem(key)); } } return [2, out]; }); }); }; BrowserLocalStorageManager.prototype.removeModel = function (path) { return __awaiter(this, void 0, void 0, function () { var keys, info; return __generator(this, function (_a) { path = maybeStripScheme$1(path); keys = getModelKeys(path); if (this.LS.getItem(keys.info) == null) { throw new Error("Cannot find model at path '" + path + "'"); } info = JSON.parse(this.LS.getItem(keys.info)); this.LS.removeItem(keys.info); this.LS.removeItem(keys.topology); this.LS.removeItem(keys.weightSpecs); this.LS.removeItem(keys.weightData); return [2, info]; }); }); }; return BrowserLocalStorageManager; }()); if (ENV.get('IS_BROWSER')) { try { ModelStoreManagerRegistry.registerManager(BrowserLocalStorage.URL_SCHEME, new BrowserLocalStorageManager()); } catch (err) { } } var DEFAULT_FILE_NAME_PREFIX = 'model'; var DEFAULT_JSON_EXTENSION_NAME = '.json'; var DEFAULT_WEIGHT_DATA_EXTENSION_NAME = '.weights.bin'; var BrowserDownloads = (function () { function BrowserDownloads(fileNamePrefix) { if (!ENV.get('IS_BROWSER')) { throw new Error('browserDownloads() cannot proceed because the current environment ' + 'is not a browser.'); } if (fileNamePrefix.startsWith(BrowserDownloads.URL_SCHEME)) { fileNamePrefix = fileNamePrefix.slice(BrowserDownloads.URL_SCHEME.length); } if (fileNamePrefix == null || fileNamePrefix.length === 0) { fileNamePrefix = DEFAULT_FILE_NAME_PREFIX; } this.modelTopologyFileName = fileNamePrefix + DEFAULT_JSON_EXTENSION_NAME; this.weightDataFileName = fileNamePrefix + DEFAULT_WEIGHT_DATA_EXTENSION_NAME; } BrowserDownloads.prototype.save = function (modelArtifacts) { return __awaiter(this, void 0, void 0, function () { var weightsURL, weightsManifest, modelTopologyAndWeightManifest, modelTopologyAndWeightManifestURL, jsonAnchor, weightDataAnchor; return __generator(this, function (_a) { weightsURL = window.URL.createObjectURL(new Blob([modelArtifacts.weightData], { type: 'application/octet-stream' })); if (modelArtifacts.modelTopology instanceof ArrayBuffer) { throw new Error('BrowserDownloads.save() does not support saving model topology ' + 'in binary formats yet.'); } else { weightsManifest = [{ paths: ['./' + this.weightDataFileName], weights: modelArtifacts.weightSpecs }]; modelTopologyAndWeightManifest = { modelTopology: modelArtifacts.modelTopology, weightsManifest: weightsManifest }; modelTopologyAndWeightManifestURL = window.URL.createObjectURL(new Blob([JSON.stringify(modelTopologyAndWeightManifest)], { type: 'application/json' })); jsonAnchor = this.jsonAnchor == null ? document.createElement('a') : this.jsonAnchor; jsonAnchor.download = this.modelTopologyFileName; jsonAnchor.href = modelTopologyAndWeightManifestURL; jsonAnchor.click(); if (modelArtifacts.weightData != null) { weightDataAnchor = this.weightDataAnchor == null ? document.createElement('a') : this.weightDataAnchor; weightDataAnchor.download = this.weightDataFileName; weightDataAnchor.href = weightsURL; weightDataAnchor.click(); } return [2, { modelArtifactsInfo: getModelArtifactsInfoForJSON(modelArtifacts) }]; } return [2]; }); }); }; BrowserDownloads.URL_SCHEME = 'downloads://'; return BrowserDownloads; }()); var BrowserFiles = (function () { function BrowserFiles(files) { if (files == null || files.length < 1) { throw new Error("When calling browserFiles, at least 1 file is required, " + ("but received " + files)); } this.files = files; } BrowserFiles.prototype.load = function () { return __awaiter(this, void 0, void 0, function () { var jsonFile, weightFiles; var _this = this; return __generator(this, function (_a) { jsonFile = this.files[0]; weightFiles = this.files.slice(1); return [2, new Promise(function (resolve, reject) { var jsonReader = new FileReader(); jsonReader.onload = function (event) { var modelJSON = JSON.parse(event.target.result); var modelTopology = modelJSON.modelTopology; if (modelTopology == null) { reject(new Error("modelTopology field is missing from file " + jsonFile.name)); return; } if (weightFiles.length === 0) { resolve({ modelTopology: modelTopology }); } var weightsManifest = modelJSON.weightsManifest; if (weightsManifest == null) { reject(new Error("weightManifest field is missing from file " + jsonFile.name)); return; } var pathToFile; try { pathToFile = _this.checkManifestAndWeightFiles(weightsManifest, weightFiles); } catch (err) { reject(err); return; } var weightSpecs = []; var paths = []; var perFileBuffers = []; weightsManifest.forEach(function (weightsGroup) { weightsGroup.paths.forEach(function (path) { paths.push(path); perFileBuffers.push(null); }); weightSpecs.push.apply(weightSpecs, weightsGroup.weights); }); weightsManifest.forEach(function (weightsGroup) { weightsGroup.paths.forEach(function (path) { var weightFileReader = new FileReader(); weightFileReader.onload = function (event) { var weightData = event.target.result; var index = paths.indexOf(path); perFileBuffers[index] = weightData; if (perFileBuffers.indexOf(null) === -1) { resolve({ modelTopology: modelTopology, weightSpecs: weightSpecs, weightData: concatenateArrayBuffers(perFileBuffers), }); } }; weightFileReader.onerror = function (error) { return reject("Failed to weights data from file of path '" + path + "'."); }; weightFileReader.readAsArrayBuffer(pathToFile[path]); }); }); }; jsonReader.onerror = function (error) { return reject("Failed to read model topology and weights manifest JSON " + ("from file '" + jsonFile.name + "'. BrowserFiles supports loading ") + "Keras-style tf.Model artifacts only."); }; jsonReader.readAsText(jsonFile); })]; }); }); }; BrowserFiles.prototype.checkManifestAndWeightFiles = function (manifest, files) { var basenames = []; var fileNames = files.map(function (file) { return basename(file.name); }); var pathToFile = {}; for (var _i = 0, manifest_1 = manifest; _i < manifest_1.length; _i++) { var group = manifest_1[_i]; group.paths.forEach(function (path) { var pathBasename = basename(path); if (basenames.indexOf(pathBasename) !== -1) { throw new Error("Duplicate file basename found in weights manifest: " + ("'" + pathBasename + "'")); } basenames.push(pathBasename); if (fileNames.indexOf(pathBasename) === -1) { throw new Error("Weight file with basename '" + pathBasename + "' is not provided."); } else { pathToFile[path] = files[fileNames.indexOf(pathBasename)]; } }); } if (basenames.length !== files.length) { throw new Error("Mismatch in the number of files in weights manifest " + ("(" + basenames.length + ") and the number of weight files provided ") + ("(" + files.length + ").")); } return pathToFile; }; return BrowserFiles; }()); var browserDownloadsRouter = function (url) { if (!ENV.get('IS_BROWSER')) { return null; } else { if (!Array.isArray(url) && url.startsWith(BrowserDownloads.URL_SCHEME)) { return browserDownloads(url.slice(BrowserDownloads.URL_SCHEME.length)); } else { return null; } } }; IORouterRegistry.registerSaveRouter(browserDownloadsRouter); function browserDownloads(fileNamePrefix) { if (fileNamePrefix === void 0) { fileNamePrefix = 'model'; } return new BrowserDownloads(fileNamePrefix); } function browserFiles(files) { return new BrowserFiles(files); } var OCTET_STREAM_TYPE = 'application/octet-stream'; var CONTENT_TYPE = 'Content-type'; function loadWeightsAsArrayBuffer(fetchURLs, requestOptions, fetchFunc, onProgress) { return __awaiter(this, void 0, void 0, function () { var headers, requests, fetchStartFraction, fetchEndFraction, responses, badContentType, bufferPromises, bufferStartFraction, bufferEndFraction, buffers; return __generator(this, function (_a) { switch (_a.label) { case 0: if (fetchFunc == null) { fetchFunc = fetch; } requestOptions = requestOptions || {}; headers = (requestOptions.headers || {}); headers['Accept'] = OCTET_STREAM_TYPE; requestOptions.headers = headers; requests = fetchURLs.map(function (fetchURL) { return fetchFunc(fetchURL, requestOptions); }); fetchStartFraction = 0; fetchEndFraction = 0.5; if (onProgress != null) { monitorPromisesProgress(requests, onProgress, fetchStartFraction, fetchEndFraction); } return [4, Promise.all(requests)]; case 1: responses = _a.sent(); badContentType = responses.filter(function (response) { var contentType = response.headers.get(CONTENT_TYPE); return !contentType || contentType.indexOf(OCTET_STREAM_TYPE) === -1; }); if (badContentType.length > 0) { throw new Error(badContentType .map(function (resp) { return "Request to " + resp.url + " for weight file failed." + (" Expected content type " + OCTET_STREAM_TYPE + " but got " + resp.headers.get(CONTENT_TYPE) + "."); }) .join('\n')); } bufferPromises = responses.map(function (response) { return response.arrayBuffer(); }); bufferStartFraction = 0.5; bufferEndFraction = 1; if (onProgress != null) { monitorPromisesProgress(bufferPromises, onProgress, bufferStartFraction, bufferEndFraction); } return [4, Promise.all(bufferPromises)]; case 2: buffers = _a.sent(); return [2, buffers]; } }); }); } function loadWeights(manifest, filePathPrefix, weightNames, requestOptions) { if (filePathPrefix === void 0) { filePathPrefix = ''; } return __awaiter(this, void 0, void 0, function () { var fetchWeights, loadWeights; return __generator(this, function (_a) { fetchWeights = function (fetchUrls) { return loadWeightsAsArrayBuffer(fetchUrls, requestOptions); }; loadWeights = weightsLoaderFactory(fetchWeights); return [2, loadWeights(manifest, filePathPrefix, weightNames)]; }); }); } function weightsLoaderFactory(fetchWeightsFunction) { var _this = this; return function (manifest, filePathPrefix, weightNames) { if (filePathPrefix === void 0) { filePathPrefix = ''; } return __awaiter(_this, void 0, void 0, function () { var groupIndicesToFetchMap, groupWeightsToFetch, weightsFound, allManifestWeightNames, weightsNotFound, groupIndicesToFetch, fetchUrls, buffers, weightsTensorMap, bufferIndexOffset; return __generator(this, function (_a) { switch (_a.label) { case 0: groupIndicesToFetchMap = manifest.map(function () { return false; }); groupWeightsToFetch = {}; weightsFound = weightNames != null ? weightNames.map(function () { return false; }) : []; allManifestWeightNames = []; manifest.forEach(function (manifestGroupConfig, groupIndex) { var groupOffset = 0; manifestGroupConfig.weights.forEach(function (weightsEntry) { var rawDtype = ('quantization' in weightsEntry) ? weightsEntry.quantization.dtype : weightsEntry.dtype; var weightsBytes = DTYPE_VALUE_SIZE_MAP[rawDtype] * sizeFromShape(weightsEntry.shape); var enqueueWeightsForFetchingFn = function () { groupIndicesToFetchMap[groupIndex] = true; if (groupWeightsToFetch[groupIndex] == null) { groupWeightsToFetch[groupIndex] = []; } groupWeightsToFetch[groupIndex].push({ manifestEntry: weightsEntry, groupOffset: groupOffset, sizeBytes: weightsBytes }); }; if (weightNames != null) { weightNames.forEach(function (weightName, weightIndex) { if (weightName === weightsEntry.name) { enqueueWeightsForFetchingFn(); weightsFound[weightIndex] = true; } }); } else { enqueueWeightsForFetchingFn(); } allManifestWeightNames.push(weightsEntry.name); groupOffset += weightsBytes; }); }); if (!weightsFound.every(function (found) { return found; })) { weightsNotFound = weightNames.filter(function (_, i) { return !weightsFound[i]; }); throw new Error("Could not find weights in manifest with names: " + (weightsNotFound.join(', ') + ". \n") + "Manifest JSON has weights with names: " + (allManifestWeightNames.join(', ') + ".")); } groupIndicesToFetch = groupIndicesToFetchMap.reduce(function (accumulator, shouldFetch, i) { if (shouldFetch) { accumulator.push(i); } return accumulator; }, []); fetchUrls = []; groupIndicesToFetch.forEach(function (i) { manifest[i].paths.forEach(function (filepath) { var fetchUrl = filePathPrefix + (!filePathPrefix.endsWith('/') ? '/' : '') + filepath; fetchUrls.push(fetchUrl); }); }); return [4, fetchWeightsFunction(fetchUrls)]; case 1: buffers = _a.sent(); weightsTensorMap = {}; bufferIndexOffset = 0; groupIndicesToFetch.forEach(function (i) { var numBuffers = manifest[i].paths.length; var groupBytes = 0; for (var i_1 = 0; i_1 < numBuffers; i_1++) { groupBytes += buffers[bufferIndexOffset + i_1].byteLength; } var groupBuffer = new ArrayBuffer(groupBytes); var groupByteBuffer = new Uint8Array(groupBuffer); var groupBufferOffset = 0; for (var i_2 = 0; i_2 < numBuffers; i_2++) { var buffer = new Uint8Array(buffers[bufferIndexOffset + i_2]); groupByteBuffer.set(buffer, groupBufferOffset); groupBufferOffset += buffer.byteLength; } var weightsEntries = groupWeightsToFetch[i]; weightsEntries.forEach(function (weightsEntry) { var byteBuffer = groupBuffer.slice(weightsEntry.groupOffset, weightsEntry.groupOffset + weightsEntry.sizeBytes); var nameToTensorMap = decodeWeights(byteBuffer, [weightsEntry.manifestEntry]); for (var name_1 in nameToTensorMap) { weightsTensorMap[name_1] = nameToTensorMap[name_1]; } }); bufferIndexOffset += numBuffers; }); return [2, weightsTensorMap]; } }); }); }; } var BrowserHTTPRequest = (function () { function BrowserHTTPRequest(path, requestInit, weightPathPrefix, fetchFunc, onProgress) { this.weightPathPrefix = weightPathPrefix; this.onProgress = onProgress; this.DEFAULT_METHOD = 'POST'; if (fetchFunc == null) { if (typeof fetch === 'undefined') { throw new Error('browserHTTPRequest is not supported outside the web browser ' + 'without a fetch polyfill.'); } fetchFunc = fetch.bind(typeof window === 'undefined' ? null : window); } else { assert(typeof fetchFunc === 'function', 'Must pass a function that matches the signature of ' + '`fetch` (see ' + 'https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API)'); } this.fetchFunc = function (path, requestInits) { return fetchFunc(path, requestInits).catch(function (error) { throw new Error("Request for " + path + " failed due to error: " + error); }); }; assert(path != null && path.length > 0, 'URL path for browserHTTPRequest must not be null, undefined or ' + 'empty.'); if (Array.isArray(path)) { assert(path.length === 2, 'URL paths for browserHTTPRequest must have a length of 2, ' + ("(actual length is " + path.length + ").")); } this.path = path; if (requestInit != null && requestInit.body != null) { throw new Error('requestInit is expected to have no pre-existing body, but has one.'); } this.requestInit = requestInit || {}; } BrowserHTTPRequest.prototype.save = function (modelArtifacts) { return __awaiter(this, void 0, void 0, function () { var init, weightsManifest, modelTopologyAndWeightManifest, response; return __generator(this, function (_a) { switch (_a.label) { case 0: if (modelArtifacts.modelTopology instanceof ArrayBuffer) { throw new Error('BrowserHTTPRequest.save() does not support saving model topology ' + 'in binary formats yet.'); } init = Object.assign({ method: this.DEFAULT_METHOD }, this.requestInit); init.body = new FormData(); weightsManifest = [{ paths: ['./model.weights.bin'], weights: modelArtifacts.weightSpecs, }]; modelTopologyAndWeightManifest = { modelTopology: modelArtifacts.modelTopology, weightsManifest: weightsManifest }; init.body.append('model.json', new Blob([JSON.stringify(modelTopologyAndWeightManifest)], { type: 'application/json' }), 'model.json'); if (modelArtifacts.weightData != null) { init.body.append('model.weights.bin', new Blob([modelArtifacts.weightData], { type: 'application/octet-stream' }), 'model.weights.bin'); } return [4, this.getFetchFunc()(this.path, init)]; case 1: response = _a.sent(); if (response.ok) { return [2, { modelArtifactsInfo: getModelArtifactsInfoForJSON(modelArtifacts), responses: [response], }]; } else { throw new Error("BrowserHTTPRequest.save() failed due to HTTP response status " + (response.status + ".")); } return [2]; } }); }); }; BrowserHTTPRequest.prototype.load = function () { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { return [2, Array.isArray(this.path) ? this.loadBinaryModel() : this.loadJSONModel()]; }); }); }; BrowserHTTPRequest.prototype.loadBinaryTopology = function () { return __awaiter(this, void 0, void 0, function () { var response; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4, this.getFetchFunc()(this.path[0], this.addAcceptHeader('application/octet-stream'))]; case 1: response = _a.sent(); this.verifyContentType(response, 'model topology', 'application/octet-stream'); if (!response.ok) { throw new Error("Request to " + this.path[0] + " failed with error: " + response.statusText); } return [4, response.arrayBuffer()]; case 2: return [2, _a.sent()]; } }); }); }; BrowserHTTPRequest.prototype.addAcceptHeader = function (mimeType) { var requestOptions = Object.assign({}, this.requestInit || {}); var headers = Object.assign({}, requestOptions.headers || {}); headers['Accept'] = mimeType; requestOptions.headers = headers; return requestOptions; }; BrowserHTTPRequest.prototype.verifyContentType = function (response, target, type) { var contentType = response.headers.get('content-type'); if (!contentType || contentType.indexOf(type) === -1) { throw new Error("Request to " + response.url + " for " + target + " failed. Expected content type " + type + " but got " + contentType + "."); } }; BrowserHTTPRequest.prototype.loadBinaryModel = function () { return __awaiter(this, void 0, void 0, function () { var graphPromise, manifestPromise, results, modelTopology, weightsManifestResponse, weightsManifest, weightSpecs, weightData, results_1; return __generator(this, function (_a) { switch (_a.label) { case 0: graphPromise = this.loadBinaryTopology(); return [4, this.getFetchFunc()(this.path[1], this.addAcceptHeader('application/json'))]; case 1: manifestPromise = _a.sent(); this.verifyContentType(manifestPromise, 'weights manifest', 'application/json'); if (!manifestPromise.ok) { throw new Error("Request to " + this.path[1] + " failed with error: " + manifestPromise.statusText); } return [4, Promise.all([graphPromise, manifestPromise])]; case 2: results = _a.sent(); modelTopology = results[0], weightsManifestResponse = results[1]; return [4, weightsManifestResponse.json()]; case 3: weightsManifest = _a.sent(); if (!(weightsManifest != null)) return [3, 5]; return [4, this.loadWeights(weightsManifest)]; case 4: results_1 = _a.sent(); weightSpecs = results_1[0], weightData = results_1[1]; _a.label = 5; case 5: return [2, { modelTopology: modelTopology, weightSpecs: weightSpecs, weightData: weightData }]; } }); }); }; BrowserHTTPRequest.prototype.loadJSONModel = function () { return __awaiter(this, void 0, void 0, function () { var modelConfigRequest, modelConfig, modelTopology, weightsManifest, weightSpecs, weightData, weightsManifest_1, results; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4, this.getFetchFunc()(this.path, this.addAcceptHeader('application/json'))]; case 1: modelConfigRequest = _a.sent(); this.verifyContentType(modelConfigRequest, 'model topology', 'application/json'); if (!modelConfigRequest.ok) { throw new Error("Request to " + this.path + " failed with error: " + modelConfigRequest.statusText); } return [4, modelConfigRequest.json()]; case 2: modelConfig = _a.sent(); modelTopology = modelConfig['modelTopology']; weightsManifest = modelConfig['weightsManifest']; if (modelTopology == null && weightsManifest == null) { throw new Error("The JSON from HTTP path " + this.path + " contains neither model " + "topology or manifest for weights."); } if (!(weightsManifest != null)) return [3, 4]; weightsManifest_1 = modelConfig['weightsManifest']; return [4, this.loadWeights(weightsManifest_1)]; case 3: results = _a.sent(); weightSpecs = results[0], weightData = results[1]; _a.label = 4; case 4: return [2, { modelTopology: modelTopology, weightSpecs: weightSpecs, weightData: weightData }]; } }); }); }; BrowserHTTPRequest.prototype.loadWeights = function (weightsManifest) { return __awaiter(this, void 0, void 0, function () { var weightPath, _a, prefix, suffix, pathPrefix, weightSpecs, _i, weightsManifest_2, entry, fetchURLs, _b, _c; return __generator(this, function (_d) { switch (_d.label) { case 0: weightPath = Array.isArray(this.path) ? this.path[1] : this.path; _a = parseUrl(weightPath), prefix = _a[0], suffix = _a[1]; pathPrefix = this.weightPathPrefix || prefix; weightSpecs = []; for (_i = 0, weightsManifest_2 = weightsManifest; _i < weightsManifest_2.length; _i++) { entry = weightsManifest_2[_i]; weightSpecs.push.apply(weightSpecs, entry.weights); } fetchURLs = []; weightsManifest.forEach(function (weightsGroup) { weightsGroup.paths.forEach(function (path) { fetchURLs.push(pathPrefix + path + suffix); }); }); _b = [weightSpecs]; _c = concatenateArrayBuffers; return [4, loadWeightsAsArrayBuffer(fetchURLs, this.requestInit, this.getFetchFunc(), this.onProgress)]; case 1: return [2, _b.concat([ _c.apply(void 0, [_d.sent()]) ])]; } }); }); }; BrowserHTTPRequest.prototype.getFetchFunc = function () { return this.fetchFunc; }; BrowserHTTPRequest.URL_SCHEME_REGEX = /^https?:\/\//; return BrowserHTTPRequest; }()); function parseUrl(url) { var lastSlash = url.lastIndexOf('/'); var lastSearchParam = url.lastIndexOf('?'); var prefix = url.substring(0, lastSlash); var suffix = lastSearchParam > lastSlash ? url.substring(lastSearchParam) : ''; return [prefix + '/', suffix]; } function isHTTPScheme(url) { return url.match(BrowserHTTPRequest.URL_SCHEME_REGEX) != null; } var httpRequestRouter = function (url, onProgress) { if (typeof fetch === 'undefined') { return null; } else { var isHTTP = true; if (Array.isArray(url)) { isHTTP = url.every(function (urlItem) { return isHTTPScheme(urlItem); }); } else { isHTTP = isHTTPScheme(url); } if (isHTTP) { return browserHTTPRequest(url, null, null, null, onProgress); } } return null; }; IORouterRegistry.registerSaveRouter(httpRequestRouter); IORouterRegistry.registerLoadRouter(httpRequestRouter); function browserHTTPRequest(path, requestInit, weightPathPrefix, fetchFunc, onProgress) { return new BrowserHTTPRequest(path, requestInit, weightPathPrefix, fetchFunc, onProgress); } var PassthroughLoader = (function () { function PassthroughLoader(modelTopology, weightSpecs, weightData) { this.modelTopology = modelTopology; this.weightSpecs = weightSpecs; this.weightData = weightData; } PassthroughLoader.prototype.load = function () { return __awaiter(this, void 0, void 0, function () { var result; return __generator(this, function (_a) { result = {}; if (this.modelTopology != null) { result = __assign({ modelTopology: this.modelTopology }, result); } if (this.weightSpecs != null && this.weightSpecs.length > 0) { result = __assign({ weightSpecs: this.weightSpecs }, result); } if (this.weightData != null && this.weightData.byteLength > 0) { result = __assign({ weightData: this.weightData }, result); } return [2, result]; }); }); }; return PassthroughLoader; }()); var PassthroughSaver = (function () { function PassthroughSaver(saveHandler) { this.saveHandler = saveHandler; } PassthroughSaver.prototype.save = function (modelArtifacts) { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { return [2, this.saveHandler(modelArtifacts)]; }); }); }; return PassthroughSaver; }()); function fromMemory(modelTopology, weightSpecs, weightData) { return new PassthroughLoader(modelTopology, weightSpecs, weightData); } function withSaveHandler(saveHandler) { return new PassthroughSaver(saveHandler); } var io = /*#__PURE__*/Object.freeze({ browserFiles: browserFiles, browserHTTPRequest: browserHTTPRequest, concatenateArrayBuffers: concatenateArrayBuffers, decodeWeights: decodeWeights, encodeWeights: encodeWeights, fromMemory: fromMemory, getLoadHandlers: getLoadHandlers, getModelArtifactsInfoForJSON: getModelArtifactsInfoForJSON, getSaveHandlers: getSaveHandlers, isHTTPScheme: isHTTPScheme, loadWeights: loadWeights, registerLoadRouter: registerLoadRouter, registerSaveRouter: registerSaveRouter, weightsLoaderFactory: weightsLoaderFactory, withSaveHandler: withSaveHandler, copyModel: copyModel, listModels: listModels, moveModel: moveModel, removeModel: removeModel }); function confusionMatrix_(labels, predictions, numClasses) { var $labels = convertToTensor(labels, 'labels', 'confusionMatrix'); var $predictions = convertToTensor(predictions, 'predictions', 'confusionMatrix'); assert(numClasses == null || numClasses > 0 && Number.isInteger(numClasses), "If provided, numClasses must be a positive integer, " + ("but got " + numClasses)); assert($labels.rank === 1, "Expected the rank of labels to be 1, but got " + $labels.rank); assert($predictions.rank === 1, "Expected the rank of predictions to be 1, " + ("but got " + $predictions.rank)); assert($labels.shape[0] === $predictions.shape[0], "Mismatch in the number of examples: " + ($labels.shape[0] + " vs. " + $predictions.shape[0] + ". ") + "Labels and predictions should have the same number of elements."); assert(numClasses > 0 && Number.isInteger(numClasses), "numClasses is required to be a positive integer, but got " + numClasses); var oneHotLabels = oneHot($labels.asType('int32'), numClasses); var oneHotPredictions = oneHot($predictions.asType('int32'), numClasses); return oneHotLabels.transpose().matMul(oneHotPredictions).asType('int32'); } var confusionMatrix = op({ confusionMatrix_: confusionMatrix_ }); var math = /*#__PURE__*/Object.freeze({ confusionMatrix: confusionMatrix }); var Serializable = (function () { function Serializable() { } Serializable.prototype.getClassName = function () { return this.constructor .className; }; Serializable.fromConfig = function (cls, config) { return new cls(config); }; return Serializable; }()); var SerializationMap = (function () { function SerializationMap() { this.classNameMap = {}; } SerializationMap.getMap = function () { if (SerializationMap.instance == null) { SerializationMap.instance = new SerializationMap(); } return SerializationMap.instance; }; SerializationMap.register = function (cls) { SerializationMap.getMap().classNameMap[cls.className] = [cls, cls.fromConfig]; }; return SerializationMap; }()); function registerClass(cls) { assert(cls.className != null, "Class being registered does not have the static className property " + "defined."); assert(typeof cls.className === 'string', "className is required to be a string, but got type " + typeof cls.className); assert(cls.className.length > 0, "Class being registered has an empty-string as its className, which " + "is disallowed."); SerializationMap.register(cls); } var serialization = /*#__PURE__*/Object.freeze({ Serializable: Serializable, SerializationMap: SerializationMap, registerClass: registerClass }); var WEBGL_ENVS = { 'HAS_WEBGL': true }; var PACKED_ENVS = { 'WEBGL_PACK': true }; var NODE_ENVS = { 'IS_NODE': true }; var CHROME_ENVS = { 'IS_CHROME': true }; var BROWSER_ENVS = { 'IS_BROWSER': true }; var CPU_ENVS = { 'HAS_WEBGL': false }; var ALL_ENVS = {}; function expectArraysClose(actual, expected, epsilon) { if (epsilon == null) { epsilon = ENV.get('TEST_EPSILON'); } return expectArraysPredicate(actual, expected, function (a, b) { return areClose(a, Number(b), epsilon); }); } function expectArraysPredicate(actual, expected, predicate) { if (!(actual instanceof Tensor) && !(expected instanceof Tensor)) { var aType = actual.constructor.name; var bType = expected.constructor.name; if (aType !== bType) { throw new Error("Arrays are of different type actual: " + aType + " " + ("vs expected: " + bType)); } } else if (actual instanceof Tensor && expected instanceof Tensor) { if (actual.dtype !== expected.dtype) { throw new Error("Arrays are of different type actual: " + actual.dtype + " " + ("vs expected: " + expected.dtype + ".")); } if (!arraysEqual(actual.shape, expected.shape)) { throw new Error("Arrays are of different shape actual: " + actual.shape + " " + ("vs expected: " + expected.shape + ".")); } } var actualValues; var expectedValues; if (actual instanceof Tensor) { actualValues = actual.dataSync(); } else { actualValues = actual; } if (expected instanceof Tensor) { expectedValues = expected.dataSync(); } else { expectedValues = expected; } if (actualValues.length !== expectedValues.length) { throw new Error("Arrays have different lengths actual: " + actualValues.length + " vs " + ("expected: " + expectedValues.length + ".\n") + ("Actual: " + actualValues + ".\n") + ("Expected: " + expectedValues + ".")); } for (var i = 0; i < expectedValues.length; ++i) { var a = actualValues[i]; var e = expectedValues[i]; if (!predicate(a, e)) { throw new Error("Arrays differ: actual[" + i + "] = " + a + ", expected[" + i + "] = " + e + ".\n" + ("Actual: " + actualValues + ".\n") + ("Expected: " + expectedValues + ".")); } } } function expectPromiseToFail(fn, done) { fn().then(function () { return done.fail(); }, function () { return done(); }); } function expectArraysEqual(actual, expected) { if (actual instanceof Tensor && actual.dtype === 'string' || expected instanceof Tensor && expected.dtype === 'string' || Array.isArray(actual) && isString(actual[0]) || Array.isArray(expected) && isString(expected[0])) { return expectArraysPredicate(actual, expected, function (a, b) { return a == b; }); } return expectArraysClose(actual, expected, 0); } function expectNumbersClose(a, e, epsilon) { if (epsilon == null) { epsilon = ENV.get('TEST_EPSILON'); } if (!areClose(a, e, epsilon)) { throw new Error("Numbers differ: actual === " + a + ", expected === " + e); } } function areClose(a, e, epsilon) { if (isNaN(a) && isNaN(e)) { return true; } if (isNaN(a) || isNaN(e) || Math.abs(a - e) > epsilon) { return false; } return true; } function expectValuesInRange(actual, low, high) { var actualVals; if (actual instanceof Tensor) { actualVals = actual.dataSync(); } else { actualVals = actual; } for (var i = 0; i < actualVals.length; i++) { if (actualVals[i] < low || actualVals[i] > high) { throw new Error("Value out of range:" + actualVals[i] + " low: " + low + ", high: " + high); } } } function expectArrayBuffersEqual(actual, expected) { expect(new Float32Array(actual)).toEqual(new Float32Array(expected)); } var test_util = /*#__PURE__*/Object.freeze({ WEBGL_ENVS: WEBGL_ENVS, PACKED_ENVS: PACKED_ENVS, NODE_ENVS: NODE_ENVS, CHROME_ENVS: CHROME_ENVS, BROWSER_ENVS: BROWSER_ENVS, CPU_ENVS: CPU_ENVS, ALL_ENVS: ALL_ENVS, expectArraysClose: expectArraysClose, expectPromiseToFail: expectPromiseToFail, expectArraysEqual: expectArraysEqual, expectNumbersClose: expectNumbersClose, expectValuesInRange: expectValuesInRange, expectArrayBuffersEqual: expectArrayBuffersEqual }); var version = '0.14.5'; var webgl = /*#__PURE__*/Object.freeze({ gpgpu_util: gpgpu_util, webgl_util: webgl_util, MathBackendWebGL: MathBackendWebGL, GPGPUContext: GPGPUContext }); var Optimizer = (function (_super) { __extends(Optimizer, _super); function Optimizer() { return _super !== null && _super.apply(this, arguments) || this; } Optimizer.prototype.minimize = function (f, returnCost, varList) { if (returnCost === void 0) { returnCost = false; } var _a = this.computeGradients(f, varList), value = _a.value, grads$$1 = _a.grads; this.applyGradients(grads$$1); var varNames = Object.keys(grads$$1); varNames.forEach(function (varName) { return grads$$1[varName].dispose(); }); if (returnCost) { return value; } else { value.dispose(); return null; } }; Optimizer.prototype.computeGradients = function (f, varList) { return variableGrads(f, varList); }; return Optimizer; }(Serializable)); Object.defineProperty(Optimizer, Symbol.hasInstance, { value: function (instance) { return instance.minimize != null && instance.computeGradients != null && instance.applyGradients != null; } }); var AdadeltaOptimizer = (function (_super) { __extends(AdadeltaOptimizer, _super); function AdadeltaOptimizer(learningRate, rho, epsilon) { if (epsilon === void 0) { epsilon = null; } var _this = _super.call(this) || this; _this.learningRate = learningRate; _this.rho = rho; _this.epsilon = epsilon; _this.accumulatedGrads = {}; _this.accumulatedUpdates = {}; _this.c = keep(scalar(-learningRate)); _this.rhoScalar = keep(scalar(rho)); _this.oneMinusRho = keep(scalar(1 - rho)); if (epsilon === null) { epsilon = ENV.get('EPSILON'); } _this.epsilonScalar = keep(scalar(epsilon)); return _this; } AdadeltaOptimizer.prototype.applyGradients = function (variableGradients) { var _this = this; var _loop_1 = function (variableName) { var value = ENV.engine.registeredVariables[variableName]; if (this_1.accumulatedGrads[variableName] == null) { var trainable_1 = false; tidy(function () { _this.accumulatedGrads[variableName] = zerosLike(value).variable(trainable_1); }); } if (this_1.accumulatedUpdates[variableName] == null) { var trainable_2 = false; tidy(function () { _this.accumulatedUpdates[variableName] = zerosLike(value).variable(trainable_2); }); } var gradient = variableGradients[variableName]; var accumulatedGrad = this_1.accumulatedGrads[variableName]; var accumulatedUpdate = this_1.accumulatedUpdates[variableName]; tidy(function () { var newAccumulatedGrad = _this.rhoScalar.mul(accumulatedGrad) .add(_this.oneMinusRho.mul(gradient.square())); var updates = accumulatedUpdate.add(_this.epsilonScalar) .sqrt() .div(accumulatedGrad.add(_this.epsilonScalar).sqrt()) .mul(gradient); var newAccumulatedUpdate = _this.rhoScalar.mul(accumulatedUpdate) .add(_this.oneMinusRho.mul(updates.square())); _this.accumulatedGrads[variableName].assign(newAccumulatedGrad); _this.accumulatedUpdates[variableName].assign(newAccumulatedUpdate); var newValue = _this.c.mul(updates).add(value); value.assign(newValue); }); }; var this_1 = this; for (var variableName in variableGradients) { _loop_1(variableName); } }; AdadeltaOptimizer.prototype.dispose = function () { var _this = this; this.c.dispose(); this.epsilonScalar.dispose(); this.rhoScalar.dispose(); this.oneMinusRho.dispose(); if (this.accumulatedUpdates != null) { Object.keys(this.accumulatedUpdates) .forEach(function (name) { return _this.accumulatedUpdates[name].dispose(); }); Object.keys(this.accumulatedGrads) .forEach(function (name) { return _this.accumulatedGrads[name].dispose(); }); } }; AdadeltaOptimizer.prototype.getConfig = function () { return { learningRate: this.learningRate, rho: this.rho, epsilon: this.epsilon }; }; AdadeltaOptimizer.fromConfig = function (cls, config) { return new cls(config.learningRate, config.rho, config.epsilon); }; AdadeltaOptimizer.className = 'AdadeltaOptimizer'; return AdadeltaOptimizer; }(Optimizer)); registerClass(AdadeltaOptimizer); var AdagradOptimizer = (function (_super) { __extends(AdagradOptimizer, _super); function AdagradOptimizer(learningRate, initialAccumulatorValue) { if (initialAccumulatorValue === void 0) { initialAccumulatorValue = 0.1; } var _this = _super.call(this) || this; _this.learningRate = learningRate; _this.initialAccumulatorValue = initialAccumulatorValue; _this.accumulatedGrads = {}; _this.c = keep(scalar(-learningRate)); _this.epsilon = keep(scalar(ENV.get('EPSILON'))); return _this; } AdagradOptimizer.prototype.applyGradients = function (variableGradients) { var _this = this; var _loop_1 = function (variableName) { var value = ENV.engine.registeredVariables[variableName]; if (this_1.accumulatedGrads[variableName] == null) { var trainable_1 = false; tidy(function () { _this.accumulatedGrads[variableName] = fill(value.shape, _this.initialAccumulatorValue) .variable(trainable_1); }); } var gradient = variableGradients[variableName]; var accumulatedGrad = this_1.accumulatedGrads[variableName]; tidy(function () { var newAccumulatedGrad = accumulatedGrad.add(gradient.square()); _this.accumulatedGrads[variableName].assign(newAccumulatedGrad); var newValue = _this.c .mul(gradient.div(newAccumulatedGrad.add(_this.epsilon).sqrt())) .add(value); value.assign(newValue); }); }; var this_1 = this; for (var variableName in variableGradients) { _loop_1(variableName); } }; AdagradOptimizer.prototype.dispose = function () { var _this = this; this.epsilon.dispose(); this.c.dispose(); if (this.accumulatedGrads != null) { Object.keys(this.accumulatedGrads) .forEach(function (name) { return _this.accumulatedGrads[name].dispose(); }); } }; AdagradOptimizer.prototype.getConfig = function () { return { learningRate: this.learningRate, initialAccumulatorValue: this.initialAccumulatorValue, }; }; AdagradOptimizer.fromConfig = function (cls, config) { return new cls(config.learningRate, config.initialAccumulatorValue); }; AdagradOptimizer.className = 'AdagradOptimizer'; return AdagradOptimizer; }(Optimizer)); registerClass(AdagradOptimizer); var AdamOptimizer = (function (_super) { __extends(AdamOptimizer, _super); function AdamOptimizer(learningRate, beta1, beta2, epsilon) { if (epsilon === void 0) { epsilon = null; } var _this = _super.call(this) || this; _this.learningRate = learningRate; _this.beta1 = beta1; _this.beta2 = beta2; _this.epsilon = epsilon; _this.accumulatedFirstMoment = {}; _this.accumulatedSecondMoment = {}; _this.c = keep(scalar(-learningRate)); _this.beta1Scalar = keep(scalar(beta1)); _this.beta2Scalar = keep(scalar(beta2)); tidy(function () { _this.accBeta1 = scalar(beta1).variable(); _this.accBeta2 = scalar(beta2).variable(); }); _this.oneMinusBeta1 = keep(scalar(1 - beta1)); _this.oneMinusBeta2 = keep(scalar(1 - beta2)); _this.one = keep(scalar(1)); if (epsilon === null) { epsilon = ENV.get('EPSILON'); } _this.epsScalar = keep(scalar(epsilon)); return _this; } AdamOptimizer.prototype.applyGradients = function (variableGradients) { var _this = this; tidy(function () { var oneMinusAccBeta1 = _this.one.sub(_this.accBeta1); var oneMinusAccBeta2 = _this.one.sub(_this.accBeta2); for (var variableName in variableGradients) { var value = ENV.engine.registeredVariables[variableName]; if (_this.accumulatedFirstMoment[variableName] == null) { var trainable = false; _this.accumulatedFirstMoment[variableName] = zerosLike(value).variable(trainable); } if (_this.accumulatedSecondMoment[variableName] == null) { var trainable = false; _this.accumulatedSecondMoment[variableName] = zerosLike(value).variable(trainable); } var gradient = variableGradients[variableName]; var firstMoment = _this.accumulatedFirstMoment[variableName]; var secondMoment = _this.accumulatedSecondMoment[variableName]; var newFirstMoment = _this.beta1Scalar.mul(firstMoment) .add(_this.oneMinusBeta1.mul(gradient)); var newSecondMoment = _this.beta2Scalar.mul(secondMoment) .add(_this.oneMinusBeta2.mul(gradient.square())); var biasCorrectedFirstMoment = newFirstMoment.div(oneMinusAccBeta1); var biasCorrectedSecondMoment = newSecondMoment.div(oneMinusAccBeta2); _this.accumulatedFirstMoment[variableName].assign(newFirstMoment); _this.accumulatedSecondMoment[variableName].assign(newSecondMoment); var newValue = _this.c .mul(biasCorrectedFirstMoment.div(_this.epsScalar.add(biasCorrectedSecondMoment.sqrt()))) .add(value); value.assign(newValue); } _this.accBeta1.assign(_this.accBeta1.mul(_this.beta1Scalar)); _this.accBeta2.assign(_this.accBeta2.mul(_this.beta2Scalar)); }); }; AdamOptimizer.prototype.dispose = function () { var _this = this; this.c.dispose(); this.epsScalar.dispose(); this.beta1Scalar.dispose(); this.beta2Scalar.dispose(); this.accBeta1.dispose(); this.accBeta2.dispose(); this.oneMinusBeta1.dispose(); this.oneMinusBeta2.dispose(); this.one.dispose(); if (this.accumulatedFirstMoment != null) { Object.keys(this.accumulatedFirstMoment) .forEach(function (name) { return _this.accumulatedFirstMoment[name].dispose(); }); } if (this.accumulatedSecondMoment != null) { Object.keys(this.accumulatedSecondMoment) .forEach(function (name) { return _this.accumulatedSecondMoment[name].dispose(); }); } }; AdamOptimizer.prototype.getConfig = function () { return { learningRate: this.learningRate, beta1: this.beta1, beta2: this.beta2, epsilon: this.epsilon, }; }; AdamOptimizer.fromConfig = function (cls, config) { return new cls(config.learningRate, config.beta1, config.beta2, config.epsilon); }; AdamOptimizer.className = 'AdamOptimizer'; return AdamOptimizer; }(Optimizer)); registerClass(AdamOptimizer); var AdamaxOptimizer = (function (_super) { __extends(AdamaxOptimizer, _super); function AdamaxOptimizer(learningRate, beta1, beta2, epsilon, decay) { if (epsilon === void 0) { epsilon = null; } if (decay === void 0) { decay = 0.0; } var _this = _super.call(this) || this; _this.learningRate = learningRate; _this.beta1 = beta1; _this.beta2 = beta2; _this.epsilon = epsilon; _this.decay = decay; _this.accumulatedFirstMoment = {}; _this.accumulatedWeightedInfNorm = {}; _this.c = keep(scalar(-learningRate)); _this.beta1Scalar = keep(scalar(beta1)); _this.beta2Scalar = keep(scalar(beta2)); _this.decayScalar = keep(scalar(decay)); tidy(function () { _this.iteration = scalar(0).variable(); _this.accBeta1 = scalar(beta1).variable(); }); _this.oneMinusBeta1 = keep(scalar(1 - beta1)); _this.one = keep(scalar(1)); if (epsilon === null) { epsilon = ENV.get('EPSILON'); } _this.epsScalar = keep(scalar(epsilon)); return _this; } AdamaxOptimizer.prototype.applyGradients = function (variableGradients) { var _this = this; tidy(function () { var oneMinusAccBeta1 = _this.one.sub(_this.accBeta1); var lr = _this.c.div(_this.one.add(_this.decayScalar.mul(_this.iteration))); for (var variableName in variableGradients) { var value = ENV.engine.registeredVariables[variableName]; if (_this.accumulatedFirstMoment[variableName] == null) { var trainable = false; _this.accumulatedFirstMoment[variableName] = zerosLike(value).variable(trainable); } if (_this.accumulatedWeightedInfNorm[variableName] == null) { var trainable = false; _this.accumulatedWeightedInfNorm[variableName] = zerosLike(value).variable(trainable); } var gradient = variableGradients[variableName]; var firstMoment = _this.accumulatedFirstMoment[variableName]; var weightedInfNorm = _this.accumulatedWeightedInfNorm[variableName]; var newFirstMoment = _this.beta1Scalar.mul(firstMoment) .add(_this.oneMinusBeta1.mul(gradient)); var ut0 = _this.beta2Scalar.mul(weightedInfNorm); var ut1 = gradient.abs(); var newWeightedInfNorm = ut0.maximum(ut1); _this.accumulatedFirstMoment[variableName].assign(newFirstMoment); _this.accumulatedWeightedInfNorm[variableName].assign(newWeightedInfNorm); var newValue = lr.div(oneMinusAccBeta1) .mul(newFirstMoment.div(_this.epsScalar.add(newWeightedInfNorm))) .add(value); value.assign(newValue); } _this.iteration.assign(_this.iteration.add(_this.one)); _this.accBeta1.assign(_this.accBeta1.mul(_this.beta1Scalar)); }); }; AdamaxOptimizer.prototype.dispose = function () { var _this = this; this.c.dispose(); this.epsScalar.dispose(); this.accBeta1.dispose(); this.beta1Scalar.dispose(); this.beta2Scalar.dispose(); this.oneMinusBeta1.dispose(); this.decayScalar.dispose(); this.iteration.dispose(); this.one.dispose(); if (this.accumulatedFirstMoment != null) { Object.keys(this.accumulatedFirstMoment) .forEach(function (name) { return _this.accumulatedFirstMoment[name].dispose(); }); } if (this.accumulatedWeightedInfNorm != null) { Object.keys(this.accumulatedWeightedInfNorm) .forEach(function (name) { return _this.accumulatedWeightedInfNorm[name].dispose(); }); } }; AdamaxOptimizer.prototype.getConfig = function () { return { learningRate: this.learningRate, beta1: this.beta1, beta2: this.beta2, epsilon: this.epsilon, decay: this.decay }; }; AdamaxOptimizer.fromConfig = function (cls, config) { return new cls(config.learningRate, config.beta1, config.beta2, config.epsilon, config.decay); }; AdamaxOptimizer.className = 'AdamaxOptimizer'; return AdamaxOptimizer; }(Optimizer)); registerClass(AdamaxOptimizer); var SGDOptimizer = (function (_super) { __extends(SGDOptimizer, _super); function SGDOptimizer(learningRate) { var _this = _super.call(this) || this; _this.learningRate = learningRate; _this.setLearningRate(learningRate); return _this; } SGDOptimizer.prototype.applyGradients = function (variableGradients) { var _this = this; var varNames = Object.keys(variableGradients); varNames.forEach(function (varName) { var gradient = variableGradients[varName]; var value = ENV.engine.registeredVariables[varName]; tidy(function () { var newValue = _this.c.mul(gradient).add(value); value.assign(newValue); }); }); }; SGDOptimizer.prototype.setLearningRate = function (learningRate) { this.learningRate = learningRate; if (this.c != null) { this.c.dispose(); } this.c = keep(scalar(-learningRate)); }; SGDOptimizer.prototype.dispose = function () { this.c.dispose(); }; SGDOptimizer.prototype.getConfig = function () { return { learningRate: this.learningRate }; }; SGDOptimizer.fromConfig = function (cls, config) { return new cls(config.learningRate); }; SGDOptimizer.className = 'SGDOptimizer'; return SGDOptimizer; }(Optimizer)); registerClass(SGDOptimizer); var MomentumOptimizer = (function (_super) { __extends(MomentumOptimizer, _super); function MomentumOptimizer(learningRate, momentum, useNesterov) { if (useNesterov === void 0) { useNesterov = false; } var _this = _super.call(this, learningRate) || this; _this.learningRate = learningRate; _this.momentum = momentum; _this.useNesterov = useNesterov; _this.m = scalar(_this.momentum); _this.accumulations = {}; return _this; } MomentumOptimizer.prototype.applyGradients = function (variableGradients) { var _this = this; var _loop_1 = function (variableName) { var value = ENV.engine.registeredVariables[variableName]; if (this_1.accumulations[variableName] == null) { var trainable_1 = false; tidy(function () { _this.accumulations[variableName] = zerosLike(value).variable(trainable_1); }); } var accumulation = this_1.accumulations[variableName]; var gradient = variableGradients[variableName]; tidy(function () { var newValue; var newAccumulation = _this.m.mul(accumulation).add(gradient); if (_this.useNesterov) { newValue = _this.c.mul(gradient.add(newAccumulation.mul(_this.m))).add(value); } else { newValue = _this.c.mul(newAccumulation).add(value); } _this.accumulations[variableName].assign(newAccumulation); value.assign(newValue); }); }; var this_1 = this; for (var variableName in variableGradients) { _loop_1(variableName); } }; MomentumOptimizer.prototype.dispose = function () { _super.prototype.dispose.call(this); this.m.dispose(); if (this.accumulations != null) { for (var variableName in this.accumulations) { this.accumulations[variableName].dispose(); } } }; MomentumOptimizer.prototype.setMomentum = function (momentum) { this.momentum = momentum; }; MomentumOptimizer.prototype.getConfig = function () { return { learningRate: this.learningRate, momentum: this.momentum, useNesterov: this.useNesterov }; }; MomentumOptimizer.fromConfig = function (cls, config) { return new cls(config.learningRate, config.momentum, config.useNesterov); }; MomentumOptimizer.className = 'MomentumOptimizer'; return MomentumOptimizer; }(SGDOptimizer)); registerClass(MomentumOptimizer); var RMSPropOptimizer = (function (_super) { __extends(RMSPropOptimizer, _super); function RMSPropOptimizer(learningRate, decay, momentum, epsilon, centered) { if (decay === void 0) { decay = 0.9; } if (momentum === void 0) { momentum = 0.0; } if (epsilon === void 0) { epsilon = null; } if (centered === void 0) { centered = false; } var _this = _super.call(this) || this; _this.learningRate = learningRate; _this.decay = decay; _this.momentum = momentum; _this.epsilon = epsilon; _this.accumulatedMeanSquares = {}; _this.accumulatedMeanGrads = {}; _this.accumulatedMoments = {}; _this.c = keep(scalar(learningRate)); _this.decayScalar = keep(scalar(decay)); _this.momentumScalar = keep(scalar(momentum)); _this.oneMinusDecay = keep(scalar(1 - decay)); _this.centered = centered; if (epsilon === null) { epsilon = ENV.get('EPSILON'); } _this.epsilonScalar = keep(scalar(epsilon)); return _this; } RMSPropOptimizer.prototype.applyGradients = function (variableGradients) { var _this = this; var _loop_1 = function (variableName) { var value = ENV.engine.registeredVariables[variableName]; if (this_1.accumulatedMeanSquares[variableName] == null) { var trainable_1 = false; tidy(function () { _this.accumulatedMeanSquares[variableName] = zerosLike(value).variable(trainable_1); }); } if (this_1.accumulatedMeanGrads[variableName] == null && this_1.centered) { var trainable_2 = false; tidy(function () { _this.accumulatedMeanGrads[variableName] = zerosLike(value).variable(trainable_2); }); } if (this_1.accumulatedMoments[variableName] == null) { var trainable_3 = false; tidy(function () { _this.accumulatedMoments[variableName] = zerosLike(value).variable(trainable_3); }); } var accumulatedMeanSquare = this_1.accumulatedMeanSquares[variableName]; var accumulatedMeanGrad = this_1.accumulatedMeanGrads[variableName]; var accumulatedMoments = this_1.accumulatedMoments[variableName]; var gradient = variableGradients[variableName]; tidy(function () { var newAccumulatedMeanSquare = _this.decayScalar.mul(accumulatedMeanSquare) .add(_this.oneMinusDecay.mul(gradient.square())); if (_this.centered) { var newAccumulatedMeanGrad = _this.decayScalar.mul(accumulatedMeanGrad) .add(_this.oneMinusDecay.mul(gradient)); var newAccumulatedMoments = _this.momentumScalar.mul(accumulatedMoments) .add(_this.c.mul(gradient).div(newAccumulatedMeanSquare .sub(newAccumulatedMeanGrad.square().add(_this.epsilonScalar)) .sqrt())); _this.accumulatedMeanSquares[variableName].assign(newAccumulatedMeanSquare); _this.accumulatedMeanGrads[variableName].assign(newAccumulatedMeanGrad); _this.accumulatedMoments[variableName].assign(newAccumulatedMoments); var newValue = value.sub(newAccumulatedMoments); value.assign(newValue); } else { var newAccumulatedMeanSquare_1 = _this.decayScalar.mul(accumulatedMeanSquare) .add(_this.oneMinusDecay.mul(gradient.square())); var newAccumulatedMoments = _this.momentumScalar.mul(accumulatedMoments) .add(_this.c.mul(gradient).div(newAccumulatedMeanSquare_1.add(_this.epsilonScalar).sqrt())); _this.accumulatedMeanSquares[variableName].assign(newAccumulatedMeanSquare_1); _this.accumulatedMoments[variableName].assign(newAccumulatedMoments); var newValue = value.sub(newAccumulatedMoments); value.assign(newValue); } }); }; var this_1 = this; for (var variableName in variableGradients) { _loop_1(variableName); } }; RMSPropOptimizer.prototype.dispose = function () { var _this = this; this.c.dispose(); this.epsilonScalar.dispose(); this.decayScalar.dispose(); this.momentumScalar.dispose(); this.oneMinusDecay.dispose(); if (this.accumulatedMeanSquares != null) { Object.keys(this.accumulatedMeanSquares) .forEach(function (name) { return _this.accumulatedMeanSquares[name].dispose(); }); } if (this.accumulatedMeanGrads != null && this.centered) { Object.keys(this.accumulatedMeanGrads) .forEach(function (name) { return _this.accumulatedMeanGrads[name].dispose(); }); } if (this.accumulatedMoments != null) { Object.keys(this.accumulatedMoments) .forEach(function (name) { return _this.accumulatedMoments[name].dispose(); }); } }; RMSPropOptimizer.prototype.getConfig = function () { return { learningRate: this.learningRate, decay: this.decay, momentum: this.momentum, epsilon: this.epsilon, centered: this.centered }; }; RMSPropOptimizer.fromConfig = function (cls, config) { return new cls(config.learningRate, config.decay, config.momentum, config.epsilon, config.centered); }; RMSPropOptimizer.className = 'RMSPropOptimizer'; return RMSPropOptimizer; }(Optimizer)); registerClass(RMSPropOptimizer); var OptimizerConstructors = (function () { function OptimizerConstructors() { } OptimizerConstructors.sgd = function (learningRate) { return new SGDOptimizer(learningRate); }; OptimizerConstructors.momentum = function (learningRate, momentum, useNesterov) { if (useNesterov === void 0) { useNesterov = false; } return new MomentumOptimizer(learningRate, momentum, useNesterov); }; OptimizerConstructors.rmsprop = function (learningRate, decay, momentum, epsilon, centered) { if (decay === void 0) { decay = .9; } if (momentum === void 0) { momentum = 0.0; } if (epsilon === void 0) { epsilon = null; } if (centered === void 0) { centered = false; } return new RMSPropOptimizer(learningRate, decay, momentum, epsilon, centered); }; OptimizerConstructors.adam = function (learningRate, beta1, beta2, epsilon) { if (learningRate === void 0) { learningRate = 0.001; } if (beta1 === void 0) { beta1 = 0.9; } if (beta2 === void 0) { beta2 = 0.999; } if (epsilon === void 0) { epsilon = null; } return new AdamOptimizer(learningRate, beta1, beta2, epsilon); }; OptimizerConstructors.adadelta = function (learningRate, rho, epsilon) { if (learningRate === void 0) { learningRate = .001; } if (rho === void 0) { rho = .95; } if (epsilon === void 0) { epsilon = null; } return new AdadeltaOptimizer(learningRate, rho, epsilon); }; OptimizerConstructors.adamax = function (learningRate, beta1, beta2, epsilon, decay) { if (learningRate === void 0) { learningRate = 0.002; } if (beta1 === void 0) { beta1 = 0.9; } if (beta2 === void 0) { beta2 = 0.999; } if (epsilon === void 0) { epsilon = null; } if (decay === void 0) { decay = 0.0; } return new AdamaxOptimizer(learningRate, beta1, beta2, epsilon, decay); }; OptimizerConstructors.adagrad = function (learningRate, initialAccumulatorValue) { if (initialAccumulatorValue === void 0) { initialAccumulatorValue = 0.1; } return new AdagradOptimizer(learningRate, initialAccumulatorValue); }; return OptimizerConstructors; }()); var train = { sgd: OptimizerConstructors.sgd, momentum: OptimizerConstructors.momentum, adadelta: OptimizerConstructors.adadelta, adagrad: OptimizerConstructors.adagrad, rmsprop: OptimizerConstructors.rmsprop, adamax: OptimizerConstructors.adamax, adam: OptimizerConstructors.adam }; var setBackend = Environment.setBackend; var getBackend = Environment.getBackend; var disposeVariables = Environment.disposeVariables; var memory = Environment.memory; setOpHandler(ops); exports.setBackend = setBackend; exports.getBackend = getBackend; exports.disposeVariables = disposeVariables; exports.memory = memory; exports.version_core = version; exports.nextFrame = nextFrame; exports.environment = environment; exports.io = io; exports.math = math; exports.serialization = serialization; exports.test_util = test_util; exports.util = util; exports.webgl = webgl; exports.enableProdMode = enableProdMode; exports.AdadeltaOptimizer = AdadeltaOptimizer; exports.AdagradOptimizer = AdagradOptimizer; exports.AdamOptimizer = AdamOptimizer; exports.AdamaxOptimizer = AdamaxOptimizer; exports.MomentumOptimizer = MomentumOptimizer; exports.Optimizer = Optimizer; exports.RMSPropOptimizer = RMSPropOptimizer; exports.SGDOptimizer = SGDOptimizer; exports.Tensor = Tensor; exports.TensorBuffer = TensorBuffer; exports.variable = variable; exports.Variable = Variable; exports.ENV = ENV; exports.Environment = Environment; exports.KernelBackend = KernelBackend; exports.DataStorage = DataStorage; exports.image = image_ops; exports.linalg = linalg_ops; exports.losses = loss_ops; exports.spectral = spectral_ops; exports.fused = fused_ops; exports.op = op; exports.batchNormalization2d = batchNormalization2d; exports.batchNormalization3d = batchNormalization3d; exports.batchNormalization4d = batchNormalization4d; exports.batchNormalization = batchNormalization; exports.complex = complex; exports.real = real; exports.imag = imag; exports.concat = concat; exports.concat1d = concat1d; exports.concat2d = concat2d; exports.concat3d = concat3d; exports.concat4d = concat4d; exports.split = split$1; exports.conv1d = conv1d; exports.conv2d = conv2d; exports.conv3d = conv3d; exports.conv2dDerFilter = conv2dDerFilter; exports.depthwiseConv2d = depthwiseConv2d; exports.separableConv2d = separableConv2d; exports.conv2dTranspose = conv2dTranspose; exports.matMul = matMul; exports.dot = dot; exports.outerProduct = outerProduct; exports.reverse = reverse; exports.reverse1d = reverse1d; exports.reverse2d = reverse2d; exports.reverse3d = reverse3d; exports.reverse4d = reverse4d; exports.maxPool = maxPool; exports.avgPool = avgPool; exports.pool = pool; exports.slice = slice; exports.slice1d = slice1d; exports.slice2d = slice2d; exports.slice3d = slice3d; exports.slice4d = slice4d; exports.abs = abs; exports.acos = acos; exports.acosh = acosh; exports.asin = asin; exports.asinh = asinh; exports.atan = atan; exports.atanh = atanh; exports.ceil = ceil; exports.clipByValue = clipByValue; exports.cos = cos; exports.cosh = cosh; exports.erf = erf; exports.exp = exp; exports.expm1 = expm1; exports.floor = floor; exports.log = log$1; exports.log1p = log1p; exports.logSigmoid = logSigmoid; exports.neg = neg; exports.reciprocal = reciprocal; exports.round = round; exports.rsqrt = rsqrt; exports.sigmoid = sigmoid; exports.sign = sign; exports.sin = sin; exports.sinh = sinh; exports.softplus = softplus; exports.sqrt = sqrt; exports.square = square; exports.step = step; exports.tan = tan; exports.tanh = tanh$1; exports.all = all; exports.any = any; exports.argMax = argMax; exports.argMin = argMin; exports.logSumExp = logSumExp; exports.max = max; exports.mean = mean; exports.min = min; exports.moments = moments; exports.sum = sum$1; exports.prod = prod; exports.equal = equal; exports.equalStrict = equalStrict; exports.greater = greater; exports.greaterEqual = greaterEqual; exports.greaterEqualStrict = greaterEqualStrict; exports.greaterStrict = greaterStrict; exports.less = less; exports.lessEqual = lessEqual; exports.lessEqualStrict = lessEqualStrict; exports.lessStrict = lessStrict; exports.notEqual = notEqual; exports.notEqualStrict = notEqualStrict; exports.add = add; exports.addN = addN; exports.addStrict = addStrict; exports.atan2 = atan2; exports.div = div; exports.divStrict = divStrict; exports.floorDiv = floorDiv; exports.maximum = maximum; exports.maximumStrict = maximumStrict; exports.minimum = minimum; exports.minimumStrict = minimumStrict; exports.mod = mod; exports.modStrict = modStrict; exports.mul = mul; exports.mulStrict = mulStrict; exports.pow = pow; exports.powStrict = powStrict; exports.squaredDifference = squaredDifference; exports.squaredDifferenceStrict = squaredDifferenceStrict; exports.sub = sub; exports.subStrict = subStrict; exports.elu = elu; exports.leakyRelu = leakyRelu; exports.prelu = prelu; exports.relu = relu; exports.selu = selu; exports.logicalAnd = logicalAnd; exports.logicalNot = logicalNot; exports.logicalOr = logicalOr; exports.logicalXor = logicalXor; exports.where = where; exports.whereAsync = whereAsync; exports.buffer = buffer; exports.toPixels = toPixels; exports.print = print; exports.batchToSpaceND = batchToSpaceND; exports.cast = cast; exports.clone = clone; exports.cumsum = cumsum; exports.depthToSpace = depthToSpace; exports.expandDims = expandDims; exports.eye = eye; exports.fromPixels = fromPixels; exports.multinomial = multinomial; exports.oneHot = oneHot; exports.pad = pad; exports.pad1d = pad1d; exports.pad2d = pad2d; exports.pad3d = pad3d; exports.pad4d = pad4d; exports.rand = rand; exports.randomNormal = randomNormal; exports.randomUniform = randomUniform; exports.reshape = reshape; exports.spaceToBatchND = spaceToBatchND; exports.squeeze = squeeze; exports.stack = stack; exports.tile = tile; exports.truncatedNormal = truncatedNormal; exports.unstack = unstack; exports.setdiff1dAsync = setdiff1dAsync; exports.fill = fill; exports.linspace = linspace; exports.ones = ones$1; exports.range = range; exports.scalar = scalar; exports.tensor = tensor; exports.tensor1d = tensor1d; exports.tensor2d = tensor2d; exports.tensor3d = tensor3d; exports.tensor4d = tensor4d; exports.tensor5d = tensor5d; exports.tensor6d = tensor6d; exports.zeros = zeros; exports.onesLike = onesLike; exports.zerosLike = zerosLike; exports.transpose = transpose; exports.softmax = softmax; exports.logSoftmax = logSoftmax; exports.localResponseNormalization = localResponseNormalization; exports.norm = norm; exports.gather = gather; exports.unsortedSegmentSum = unsortedSegmentSum; exports.basicLSTMCell = basicLSTMCell; exports.multiRNNCell = multiRNNCell; exports.movingAverage = movingAverage; exports.stridedSlice = stridedSlice; exports.topk = topk; exports.scatterND = scatterND; exports.fft = fft; exports.ifft = ifft; exports.rfft = rfft; exports.irfft = irfft; exports.sparseToDense = sparseToDense; exports.gatherND = gatherND; exports.train = train; exports.tidy = tidy; exports.keep = keep; exports.dispose = dispose; exports.time = time; exports.profile = profile; exports.customGrad = customGrad; exports.grad = grad; exports.grads = grads; exports.valueAndGrad = valueAndGrad; exports.valueAndGrads = valueAndGrads; exports.variableGrads = variableGrads; Object.defineProperty(exports, '__esModule', { value: true }); }))); //# sourceMappingURL=tf-core.js.map ================================================ FILE: tools/generate_concentric_circles_indices.js ================================================ /*jshint esversion: 6 */ function generateAllArrays() { var xs = []; var ys = []; var towards_center = []; var kernel = 60; var index = 0; var count_per_radius = []; var radius_offset = []; var all_indices = []; function addElementIfOnDistance(i, j, distance) { var d = Math.floor(Math.sqrt(i*i + j*j)); if (d == distance) { xs.push(i); ys.push(j); all_indices[(kernel + j) * (2 * kernel + 1) + kernel + i] = index++; var x_of_element_towards_center = i - Math.sign(i); var y_of_element_towards_center = j - Math.sign(j); // Get the index of element with towards center x and y. var found = false; for (var z = xs.length - 1; z >= 0; z--) { if (xs[z] == x_of_element_towards_center && ys[z] == y_of_element_towards_center) { towards_center.push(z); found = true; break; } } if (!found) console.error("DCHECK failed:" + x_of_element_towards_center + "x" + y_of_element_towards_center); return 1; } return 0; } var t0 = performance.now(); var offset = 0; for (var k = 0; k < kernel; k++) { var counter = 0; // Generate elements in such way that all the points on the circle with // radius k are pushed to the array in clockwise order. // 12:00 -> 3:00 for (var j = -k; j <= 0; j++) { for (var i = 0; i <= k; i++) { counter += addElementIfOnDistance(i, j, k); } } // 3:00 -> 6:00 for (var j = 1; j <= k; j++) { for (var i = k; i >= 1; i--) { counter += addElementIfOnDistance(i, j, k); } } // 6:00 -> 9:00 for (var j = k; j >= 1; j--) { for (var i = 0; i >= -k; i--) { counter += addElementIfOnDistance(i, j, k); } } // 9:00 -> 12:00 for (var j = 0; j >= -k; j--) { for (var i = -k; i <= -1; i++) { counter += addElementIfOnDistance(i, j, k); } } radius_offset.push(offset); offset += counter; count_per_radius.push(counter); } var t1 = performance.now(); console.log("Call to generateAllArrays took " + (t1 - t0) + " milliseconds."); console.log("var xs = [" + xs.join() + "];"); console.log("var ys = [" + ys.join() + "];"); console.log("var towards_center = [" + towards_center.join() + "];"); console.log("var count_per_radius = [" + count_per_radius.join() + "];"); console.log("var radius_offset = [" + radius_offset.join() + "];"); // Test. if (xs.length != towards_center.length) console.error("DCHECK failed on length"); for (var i = 0; i < towards_center.length; ++i) { var ti = towards_center[i]; xdiff = xs[i] - xs[ti]; ydiff = ys[i] - ys[ti]; if (xs[i] > 0) { if (xdiff > 1 || xdiff < 0) console.error("DCHECK failed on positive xdiff"); } if (xs[i] < 0) { if (xdiff < -1 || xdiff > 0) console.error("DCHECK failed on negative xdiff"); } if (ys[i] > 0) { if (ydiff > 1 || ydiff < 0) console.error("DCHECK failed on positive xdiff"); } if (ys[i] < 0) { if (ydiff < -1 || ydiff > 0) console.error("DCHECK failed on negative xdiff"); } } for (var i = 0; i < radius_offset.length; i++) { for (var l = radius_offset[i]; l < radius_offset[i] + count_per_radius[i]; l++) { var d = Math.floor(Math.sqrt(xs[l]*xs[l] + ys[l]*ys[l])); if (d != i) console.error("DCHECK failed on distance"); } } for (var i = 0; i < radius_offset.length; i++) { for (var l = radius_offset[i]; l < radius_offset[i] + count_per_radius[i] - 1; l++) { if (Math.abs(xs[l] - xs[l + 1]) > 1) console.error("DCHECK failed on neigbouring xs:" + xs[l] + "-" + xs[l + 1] + ", ys:" + ys[l] + "-" + ys[l + 1] + " on radius:" + i); if (Math.abs(ys[l] - ys[l + 1]) > 1) console.error("DCHECK failed on neigbouring ys"); } } } ================================================ FILE: typing_in_the_air/doc/tutorial.html ================================================

Tutorial: Typing in the air using Chrome, depth camera and WebGL transform feedback

aleksandar.stojiljkovic@intel.com

Note: this tutorial is work in progress and the final version is expected to be published on 01.org in following days.

Introduction

When I showed this first to my friend Babu, he said something on the line of “that’s not convenient”. Well, though I can type on it faster than scrolling through the character grid, it is correct - it is not convenient, but it is a good illustration for a tutorial.

Few words about the setup first. Plug the Intel® RealSense™

 SR300 to USB 3.0 port of your Linux/Windows or Chrome OS machine. As a near-range camera, SR300 fits well for the use case here. The camera should point towards you, like in the photo. Once you get the hands closer to the camera, you’ll notice they become visible over keyboard and then the closest fingertip movement is analyzed; if there is down-up movement and what is the key pressed.

 

The approach could be improved, but that would be out of scope of this tutorial.

Eventually, you’ll manage to type with not that many mistakes. Use the Delete key to fix them; this is the reason why I made it a bit larger and easier to hit. The captured screenshot animation shows how it works.

The algorithm and the API used

The approach has two steps; pre-process every pixel on GPU and identify potential candidates and then process them on CPU. The algorithms are expected to be highly tailored for the use cases. The algorithm split could be explained like this:

GPU:

  • Process every pixel or tile.
  • Map depth to color.
  • Sample around both depth and color texture, try to recognize features in shader.
  • Prepare the output result, either as transform feedback output or render to texture followed by readPixels.

CPU:

  • After selecting interest area, handle the results.
  • It is essential that GPU (shader code) reduces number of candidates or the area to post-process on CPU, but that it is still robust enough to avoid missing the feature.

WebGL API used for this is presented on the picture.

The part using video and texImage2D is described in previous tutorial. In short, we follow this steps:

1. Create HTML <video> tag.

2. Call getUserMedia(constraint_to_depth_stream) to get the depth stream. If algorithm requires it, get the color stream, too.

3. Set the stream as video source, e.g. video.srcObject = stream;

4. Upload the latest captured depth video frame to texture, e.g.

gl.texImage2D(gl.TEXTURE_2D, 0, gl.R32F, gl.RED, gl.FLOAT, video);

Step 1: GPU part of algorithm

This tutorial is describing transform feedback path. In the example we follow here, vertex shader code detects the points that are the centers of the area, as described by the picture:

So, we sample around and increase the distance of samples to the point. The idea is that on distance D (the green dots on the picture), all of the samples are inside the finger area, but on the distance D + 3, three or four out of four samples (the red dots) are outside the area.

The part of vertex shader code doing this is:

// Vertex shader code; transform feedback returns |depth|.

// We have previously checked that the point is at least 3

// pixels away from the edge, so start from i = 4.0.

float i = 4.0;

float number_of_dots_inside = 4.0;

for (; i < MAX_DISTANCE; i += 3.0) {

  // Sample the texels like on the picture on the left.

  d_0 = texture(s_depth, depth_tex_pos + vec2(i, 0.0) * step).r;

  d_90 = texture(s_depth, depth_tex_pos + vec2(0.0, i) * step).r;

  d_180 = texture(s_depth, depth_tex_pos - vec2(i, 0.0) * step).r;

  d_270 = texture(s_depth, depth_tex_pos - vec2(0.0, i) * step).r;

  if (d_0 * d_90 * d_180 * d_270 == 0.0) {

    number_of_dots_inside = sign(d_0) + sign(d_90) +

                            sign(d_180) + sign(d_270);

    break;

  }

}

// > 7.0 serves to eliminate "thin" areas. We pass depth > 1.0 through

// transform feedback, so that CPU side of algorithm would understands

// that this point is "center of fingertip" point and process it further.

if (number_of_dots_inside <= 1.0 && i > MIN_DISTANCE) {

  // Found it! Pack also the distance in the returned value.

  depth = i + depth;

}

Step 2: CPU part of algorithm

We start this by getting the transform feedback buffer data. The code includes the calls issuing the Step 1 and getting the buffer data, using getBufferSubData, and looks like:

        gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, gl.transform_feedback)

        gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, gl.tf_bo)

        gl.beginTransformFeedback(gl.POINTS);

        gl.drawArrays(gl.POINTS, 0, tf_output.length);

        gl.endTransformFeedback();

        gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, null)

        gl.disable(gl.RASTERIZER_DISCARD);

        gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, gl.tf_bo);

        gl.getBufferSubData(gl.TRANSFORM_FEEDBACK_BUFFER, 0, tf_output, 0, tf_output.length);

        gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, null);

After that, on CPU side, we:

1. attempt to compensate for the noise and identify the fingertip closest to the camera,

2. pass the position of the fingertip to the shader rendering it,

3. find the keyboard key under the fingertip and display it as hovered,

4. detect press-down-and-up gesture of the single fingertip and

5. issue a key click if detecting press-down-and-up gesture.

Let’s start with the data we get from GPU (Step 1). White dots are identified as centers of the area, fingertip candidates. The red dot is the one among them that is the closest to the camera.

 

In the CPU side step, we take only that one, the red dot, and try to further stabilize it by calculating the center of mass (this would be the yellow dot on the pictures) of the shape around it. This step helps in reducing the noise, that is intrinsic to the infrared based depth sensing camera technology. Roughly speaking, the yellow dot is then the calculated center of mass of all connected points to the red point. When the finger is not moving, the yellow dot is more stable than the red, like on the picture.

The algorithm implementing this is given in extractMinumums() function. Starting from the red dot, we enumerate the surrounding points on the same distance, as if spreading the waves of concentrical circles. For each point of the circle, we access the elements that is towards the center (the red point) to check if the point is connected to the red point. This way, we enumerate all the connected points to the red and calculate the average coordinate (i.e. the center of mass).

Summary

The approach could be improved by tracking all of the fingers; not only that it would enable simultaneous key presses, but the click detection would be more robust as we would not only analyze the single closest point to to camera. Instead, it might make more sense to spend some time on different gesture recognition, e.g. low latency hand gesture click made of quick contact of thumb and pointing finger and try to incorporate it in a game. The next tutorial should be about it.

================================================ FILE: typing_in_the_air/front_capture_typing.html ================================================
  • Tab
  • q1
  • w2
  • e3
  • r4
  • t5
  • y6
  • u7
  • i8
  • o9
  • p0
  • :~
  • Delete
  • CapsLock
  • a[
  • s]
  • d{
  • f}
  • g\
  • h|
  • j!
  • k#
  • l$
  • ;£
  • @^
  • Shift
  • z(
  • x)
  • c*
  • v+
  • b%
  • n/
  • m?
  • "&
  • -_
  • ,<
  • .>
  • Return
  •  
================================================ FILE: typing_in_the_air/front_capture_typing.js ================================================ /*jshint esversion: 6 */ var depth_x = 640; var depth_y = 480; var plane = 0.042; // On SR300 around 35 cm // Creates WebGL/WebGL2 context used to upload depth video to texture. // Algorithm is split to two steps: // On GPU (in vertex shader) // 1. Detect “fingertip points.” // 2. How thick the finger is around the fingertip points. // // On CPU: // 1. Retrieve the data from GPU using transform feedback. // 2. Select the closest "fingertip point" to camera. // 3. Calculate the “center of fingertip”, as a center of mass of all // connected points to the fingertip. // 4. Detect down+up movement on the keyboard. // function GL(canvasId) { var canvas = document.getElementById(canvasId); canvas.addEventListener("click", onVideoTouchStart, false); var gl = canvas.getContext("webgl2", { antialias: true }); if (gl) { // EXT_color_buffer_float to use single component R32F texture format. gl.color_buffer_float_ext = gl.getExtension('EXT_color_buffer_float'); gl.WEBGL_get_buffer_sub_data_async = gl.getExtension("WEBGL_get_buffer_sub_data_async"); gl.texture_float_linear = gl.getExtension("OES_texture_float_linear"); } if (!gl || !gl.color_buffer_float_ext) { alert("The demo doesn't run because it requires WebGL2 support."); return; } gl.enable(gl.BLEND); gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); // Transform feedback vertex shader is used for detecting "finger tip" // candidate points. Passes the result as "depth": if it is > 1.0 then the // point is potential "center of area" to analyse further on CPU side of // algorithm. var tf_vertex_shader = gl.createShader(gl.VERTEX_SHADER); gl.shaderSource(tf_vertex_shader, `#version 300 es uniform sampler2D s_depth; uniform vec2 u_depth_size; uniform float u_plane; out float depth; void main() { vec2 depth_pixel; depth_pixel.x = mod(float(gl_VertexID) + 0.5, u_depth_size.x); depth_pixel.y = clamp(floor(float(gl_VertexID) / u_depth_size.x) + 0.5, 0.0, u_depth_size.y); vec2 tex_pos = depth_pixel / u_depth_size; // As camera face towards user, mirror the display. tex_pos.x = 1.0 - tex_pos.x; depth = texture(s_depth, tex_pos).r; if (depth <= u_plane * 0.1 || depth >= u_plane) return; vec2 step = vec2(1.0, 1.0) / u_depth_size; float d_0 = texture(s_depth, tex_pos + vec2( 2.0, 0.0) * step).r; float d_90 = texture(s_depth, tex_pos + vec2( 0.0, 2.0) * step).r; float d_180 = texture(s_depth, tex_pos - vec2( 2.0, 0.0) * step).r; float d_270 = texture(s_depth, tex_pos - vec2( 0.0, 2.0) * step).r; float d_45_1 = texture(s_depth, tex_pos + vec2( 1.0, 1.0) * step).r; float d_135_1 = texture(s_depth, tex_pos + vec2( -1.0, 1.0) * step).r; float d_225_1 = texture(s_depth, tex_pos + vec2( -1.0, -1.0) * step).r; float d_315_1 = texture(s_depth, tex_pos + vec2( 1.0, -1.0) * step).r; // if one these is 0, it means that the point is around the border. Skip // as we want to get points in the center of the area. if (d_0 * d_90 * d_180 * d_270 * d_45_1 * d_135_1 * d_225_1 * d_315_1 == 0.0) { depth = 0.0; return; } // Sample around and increase the distance of samples to the point. // The idea is that on distance D all the samples are inside the area // but on the distance D + 3, 3 or 4 out of 4 are outside the area. This // would make the point "fingertip point" (e.g part of the skeleton) for // the area. float i = 4.0; float inside_count = 4.0; for (; i < 30.0; i += 3.0) { d_0 = texture(s_depth, tex_pos + vec2( i, 0.0) * step).r; d_90 = texture(s_depth, tex_pos + vec2( 0.0, i) * step).r; d_180 = texture(s_depth, tex_pos - vec2( i, 0.0) * step).r; d_270 = texture(s_depth, tex_pos - vec2( 0.0, i) * step).r; if (d_0 * d_90 * d_180 * d_270 == 0.0) { inside_count = sign(d_0) + sign(d_90) + sign(d_180) + sign(d_270); break; } } // > 7.0 serves to eliminate "thin" areas. We pass depth > 1.0 through // transform feedback, so that CPU side of algorithm would understands // that this point is "center of fingertip" point and process it further. if (inside_count <= 1.0 && i > 7.0) { depth = i + depth; } }` ); gl.compileShader(tf_vertex_shader); var tf_dummy_pixel_shader = gl.createShader(gl.FRAGMENT_SHADER); gl.shaderSource(tf_dummy_pixel_shader, `#version 300 es precision mediump float; in float depth; void main() { }` ); gl.compileShader(tf_dummy_pixel_shader); var compute_program = gl.createProgram(); gl.attachShader(compute_program, tf_vertex_shader); gl.attachShader(compute_program, tf_dummy_pixel_shader); gl.transformFeedbackVaryings(compute_program, ["depth"], gl.INTERLEAVED_ATTRIBS); gl.linkProgram(compute_program); console.log(gl.getShaderInfoLog(tf_vertex_shader)); gl.useProgram(compute_program); var process_vao = gl.createVertexArray(); gl.bindVertexArray(process_vao); var tf_bo = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, tf_bo); var transform_feedback = gl.createTransformFeedback(); gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, transform_feedback) gl.uniform1i(gl.getUniformLocation(compute_program, "s_depth"), 0); gl.uniform2f(gl.getUniformLocation(compute_program, "u_depth_size"), depth_x, depth_y); gl.uniform1f(gl.getUniformLocation(compute_program, "u_plane"), plane); // Rendering vertex and fragment shaders are simple. Fragment shader only // needs to display position of calculated "closest fingertip to the camera". var render_vao = gl.createVertexArray(); gl.bindVertexArray(render_vao); var vertex_shader = gl.createShader(gl.VERTEX_SHADER); gl.shaderSource(vertex_shader, `#version 300 es in vec2 v; uniform vec2 u_depth_size; out vec2 t; void main() { gl_Position = vec4(1.0 - v.x * 2.0, 1.0 - v.y * 2.0, 0, 1); t = v; }` ); gl.compileShader(vertex_shader); var pixel_shader = gl.createShader(gl.FRAGMENT_SHADER); gl.shaderSource(pixel_shader, `#version 300 es precision mediump float; uniform sampler2D s_depth; uniform vec2 u_pointer; uniform float u_plane; in vec2 t; out vec4 fragColor; void main() { vec4 tex = texture(s_depth, t); float a = sign(tex.r) * ((tex.r < u_plane) ? 0.5 : clamp((1.0 / (tex.r - u_plane) * 0.003), 0.0, 0.25)); // TODO: render "closest fingertip to the camera" using texture. float d = distance(t, u_pointer); if (u_pointer.x > -1.0 && d < 0.01) { a = 1.0 - 50.0 * d; } fragColor = vec4(tex.r, tex.r, tex.r, a); }` ); gl.compileShader(pixel_shader); var program = gl.createProgram(); gl.attachShader(program, vertex_shader); gl.attachShader(program, pixel_shader); gl.linkProgram(program); gl.useProgram(program); var vertex_location = gl.getAttribLocation(program, "v"); gl.enableVertexAttribArray(vertex_location); gl.uniform1i(gl.getUniformLocation(program, "s_depth"), 0); gl.uniform2f(gl.getUniformLocation(program, "u_depth_size"), depth_x, depth_y); gl.uniform1f(gl.getUniformLocation(program, "u_plane"), plane); gl.uniform2f(gl.getUniformLocation(program, "u_pointer"), -1.0, -1.0); var vertex_buffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([0,0,1,0,1,1,0,1]), gl.STATIC_DRAW); var index_buffer= gl.createBuffer(); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, index_buffer); gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array([0,1,2,0,2,3]), gl.STATIC_DRAW); gl.bindVertexArray(null); // Upload the latest depth frame to this texture. var depth_texture = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, depth_texture); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); const filtering = gl.texture_float_linear ? gl.LINEAR : gl.NEAREST; gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, filtering); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, filtering); // Framebuffer for reading back the texture. var framebuffer = gl.createFramebuffer(); gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer); gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, depth_texture, 0); gl.bindFramebuffer(gl.FRAMEBUFFER, null); gl.vertex_buffer = vertex_buffer; gl.vertex_location = vertex_location; gl.index_buffer = index_buffer; gl.depth_texture = depth_texture; gl.framebuffer = framebuffer; gl.render_program = program; gl.compute_program = compute_program; gl.process_vao = process_vao; gl.render_vao = render_vao; gl.transform_feedback = transform_feedback; gl.tf_bo = tf_bo; gl.videoLoaded = videoLoaded; return gl; } var gl; var u_plane; var tf_output; function videoLoaded(video) { gl.bindBuffer(gl.ARRAY_BUFFER, gl.tf_bo); tf_output = new Float32Array(video.videoWidth * video.videoHeight); gl.bufferData(gl.ARRAY_BUFFER, tf_output.length * 4, gl.DYNAMIC_READ); gl.bindBuffer(gl.ARRAY_BUFFER, null); } var minimum; var minimum_previous; var distance_to_previous_minimum; var current_pos; // For each element we keep the index of element in direction towards central // point. When computing if the element is connected to central point, we just // check the connectivity of element with towards_center. function extractMinumums() { minimum_previous = minimum ? minimum : {"x": -65535, "y": -65535, "depth": 1.0}; minimum = {"x": -65535, "y": -65535, "depth": 1.0}; distance_to_previous_minimum = Number.MAX_VALUE; // If with the same value and no break, we replace all mins in kernel x kernel // square, that have the same value, with one. This is useful for the use case // here (typing) - in some other cases (e.g. sculpting) we might want all of // the values. for(var i = 0; i < tf_output.length; ++i) { if (tf_output[i] > 1.0) addMinimum(i); } // Average minimum to compensate for the noise: go through all connected // points and calculate the "center of mass" for the shape. In this // calculation we disregard the depth values of the points but calculate // the shape center only. if (minimum.x >= 0) { connected.fill(false); var cx = minimum.x; var cy = minimum.y; var count = 1; var xsum = cx; var ysum = cy; var dsum = minimum.depth; connected[0] = true; // Filtered width is the width of the area we get from GPU. If the change is // < 9 we keep the original so that the results look smooth. // TODO: calculate multiple minimum averages for different radius and // incorporate constant radius in key press analysis. const filtered_width = (current_pos && Math.abs(current_pos.filtered_width - minimum.width) < 9) ? Math.max(current_pos.filtered_width, minimum.width) : minimum.width; const radius = Math.min(radius_offset.length, filtered_width * 1.3); for (var j = 1; j < radius; j++) { const r_start = radius_offset[j]; const r_end = r_start + count_per_radius[j]; for (var i = r_start; i < r_end; i++) { const element_towards_center = towards_center[i]; // We get the index of element towards center. If the point is not // connected to the center, don't bother analyzing it. if (!connected[element_towards_center]) continue; var x = cx + xs[i]; var y = cy + ys[i]; if (x < 0 || y < 0 || x >= depth_x || y >= depth_y) continue; const l = tf_output[x + y * depth_x]; if (l > 0.0) { connected[i] = true; xsum += x; ysum += y; dsum += l % 1.0; count++; } } } current_pos = { "x": Math.round(xsum / count), "y": Math.round(ysum / count), "depth" : minimum.depth, "width": minimum.width, "filtered_width": filtered_width }; // Render the position. gl.uniform2f(gl.getUniformLocation(gl.render_program, "u_pointer"), 1.0 - current_pos.x / depth_x, current_pos.y / depth_y); } else { current_pos = undefined; gl.uniform2f(gl.getUniformLocation(gl.render_program, "u_pointer"), -1.0, -1.0); } } function addMinimum(index) { var width_depth = tf_output[index]; var depth = width_depth % 1.0; if (depth < minimum.depth) { var x = index % depth_x; var y = Math.floor(index / depth_x); minimum = {"x": x, "y": y, "depth": depth, "width": Math.floor(width_depth)}; } } // Generated using generate_concentric_circles_indices.js for kernel 60. // This is used to enumerate through points on the same distance from the center, // similar to spreading of vawes of concentrical circles and for each point of // the circle, use towards_center to calculate if the point is connected to the // center. var xs = [0,0,1,1,1,0,-1,-1,-1,0,1,2,2,2,2,2,1,0,-1,-2,-2,-2,-2,-2,-1,0,1,2,3,3,3,3,3,2,1,0,-1,-2,-3,-3,-3,-3,-3,-2,-1,0,1,2,3,4,4,4,4,4,3,2,1,0,-1,-2,-3,-4,-4,-4,-4,-4,-3,-2,-1,0,1,2,3,3,4,4,5,5,5,5,5,5,5,4,4,3,3,2,1,0,-1,-2,-3,-3,-4,-4,-5,-5,-5,-5,-5,-5,-5,-4,-4,-3,-3,-2,-1,0,1,2,3,4,5,6,6,6,6,6,6,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-6,-6,-6,-6,-6,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,5,6,6,7,7,7,7,7,7,7,6,6,5,5,4,3,2,1,0,-1,-2,-3,-4,-5,-5,-6,-6,-7,-7,-7,-7,-7,-7,-7,-6,-6,-5,-5,-4,-3,-2,-1,0,1,2,3,4,4,5,6,7,7,8,8,8,8,8,8,8,8,8,7,7,6,5,4,4,3,2,1,0,-1,-2,-3,-4,-4,-5,-6,-7,-7,-8,-8,-8,-8,-8,-8,-8,-8,-8,-7,-7,-6,-5,-4,-4,-3,-2,-1,0,1,2,3,4,5,6,7,7,8,9,9,9,9,9,9,9,9,9,8,7,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-7,-8,-9,-9,-9,-9,-9,-9,-9,-9,-9,-8,-7,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,6,7,8,8,9,9,10,10,10,10,10,10,10,10,10,9,9,8,8,7,6,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-6,-7,-8,-8,-9,-9,-10,-10,-10,-10,-10,-10,-10,-10,-10,-9,-9,-8,-8,-7,-6,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,10,11,11,11,11,11,11,11,11,11,10,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-10,-11,-11,-11,-11,-11,-11,-11,-11,-11,-10,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,8,9,9,10,10,11,11,12,12,12,12,12,12,12,12,12,11,11,10,10,9,9,8,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-8,-9,-9,-10,-10,-11,-11,-12,-12,-12,-12,-12,-12,-12,-12,-12,-11,-11,-10,-10,-9,-9,-8,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,5,6,7,7,8,9,10,11,11,12,12,12,13,13,13,13,13,13,13,13,13,13,13,12,12,12,11,11,10,9,8,7,7,6,5,5,4,3,2,1,0,-1,-2,-3,-4,-5,-5,-6,-7,-7,-8,-9,-10,-11,-11,-12,-12,-12,-13,-13,-13,-13,-13,-13,-13,-13,-13,-13,-13,-12,-12,-12,-11,-11,-10,-9,-8,-7,-7,-6,-5,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,10,11,11,12,13,13,14,14,14,14,14,14,14,14,14,14,14,13,13,12,11,11,10,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-10,-11,-11,-12,-13,-13,-14,-14,-14,-14,-14,-14,-14,-14,-14,-14,-14,-13,-13,-12,-11,-11,-10,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,9,10,11,12,12,13,13,14,14,15,15,15,15,15,15,15,15,15,15,15,14,14,13,13,12,12,11,10,9,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-9,-10,-11,-12,-12,-13,-13,-14,-14,-15,-15,-15,-15,-15,-15,-15,-15,-15,-15,-15,-14,-14,-13,-13,-12,-12,-11,-10,-9,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,12,13,14,14,15,15,16,16,16,16,16,16,16,16,16,16,16,15,15,14,14,13,12,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-12,-13,-14,-14,-15,-15,-16,-16,-16,-16,-16,-16,-16,-16,-16,-16,-16,-15,-15,-14,-14,-13,-12,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,8,9,10,11,11,12,13,13,14,14,15,15,16,16,16,17,17,17,17,17,17,17,17,17,17,17,16,16,16,15,15,14,14,13,13,12,11,11,10,9,8,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-8,-9,-10,-11,-11,-12,-13,-13,-14,-14,-15,-15,-16,-16,-16,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-16,-16,-16,-15,-15,-14,-14,-13,-13,-12,-11,-11,-10,-9,-8,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,6,7,8,9,10,10,11,12,13,14,15,15,16,16,17,17,17,18,18,18,18,18,18,18,18,18,18,18,18,18,17,17,17,16,16,15,15,14,13,12,11,10,10,9,8,7,6,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-6,-7,-8,-9,-10,-10,-11,-12,-13,-14,-15,-15,-16,-16,-17,-17,-17,-18,-18,-18,-18,-18,-18,-18,-18,-18,-18,-18,-18,-18,-17,-17,-17,-16,-16,-15,-15,-14,-13,-12,-11,-10,-10,-9,-8,-7,-6,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,13,14,14,15,15,16,17,17,18,18,19,19,19,19,19,19,19,19,19,19,19,19,19,18,18,17,17,16,15,15,14,14,13,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-13,-14,-14,-15,-15,-16,-17,-17,-18,-18,-19,-19,-19,-19,-19,-19,-19,-19,-19,-19,-19,-19,-19,-18,-18,-17,-17,-16,-15,-15,-14,-14,-13,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,12,13,14,15,16,16,17,17,18,18,19,19,20,20,20,20,20,20,20,20,20,20,20,20,20,19,19,18,18,17,17,16,16,15,14,13,12,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-12,-13,-14,-15,-16,-16,-17,-17,-18,-18,-19,-19,-20,-20,-20,-20,-20,-20,-20,-20,-20,-20,-20,-20,-20,-19,-19,-18,-18,-17,-17,-16,-16,-15,-14,-13,-12,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,9,10,11,11,12,13,14,15,15,16,16,17,18,18,19,19,19,20,20,20,21,21,21,21,21,21,21,21,21,21,21,21,21,20,20,20,19,19,19,18,18,17,16,16,15,15,14,13,12,11,11,10,9,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-9,-10,-11,-11,-12,-13,-14,-15,-15,-16,-16,-17,-18,-18,-19,-19,-19,-20,-20,-20,-21,-21,-21,-21,-21,-21,-21,-21,-21,-21,-21,-21,-21,-20,-20,-20,-19,-19,-19,-18,-18,-17,-16,-16,-15,-15,-14,-13,-12,-11,-11,-10,-9,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,14,15,16,17,17,18,18,19,20,20,21,21,21,22,22,22,22,22,22,22,22,22,22,22,22,22,21,21,21,20,20,19,18,18,17,17,16,15,14,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-14,-15,-16,-17,-17,-18,-18,-19,-20,-20,-21,-21,-21,-22,-22,-22,-22,-22,-22,-22,-22,-22,-22,-22,-22,-22,-21,-21,-21,-20,-20,-19,-18,-18,-17,-17,-16,-15,-14,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,13,14,15,16,17,18,19,19,20,20,21,21,22,22,22,23,23,23,23,23,23,23,23,23,23,23,23,23,22,22,22,21,21,20,20,19,19,18,17,16,15,14,13,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-13,-14,-15,-16,-17,-18,-19,-19,-20,-20,-21,-21,-22,-22,-22,-23,-23,-23,-23,-23,-23,-23,-23,-23,-23,-23,-23,-23,-22,-22,-22,-21,-21,-20,-20,-19,-19,-18,-17,-16,-15,-14,-13,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,16,17,17,18,18,19,19,20,21,21,22,22,23,23,23,24,24,24,24,24,24,24,24,24,24,24,24,24,23,23,23,22,22,21,21,20,19,19,18,18,17,17,16,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-16,-17,-17,-18,-18,-19,-19,-20,-21,-21,-22,-22,-23,-23,-23,-24,-24,-24,-24,-24,-24,-24,-24,-24,-24,-24,-24,-24,-23,-23,-23,-22,-22,-21,-21,-20,-19,-19,-18,-18,-17,-17,-16,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,7,8,9,10,11,12,12,13,14,15,15,16,17,18,19,20,20,21,21,22,22,23,23,23,24,24,24,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,24,24,24,23,23,23,22,22,21,21,20,20,19,18,17,16,15,15,14,13,12,12,11,10,9,8,7,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-7,-8,-9,-10,-11,-12,-12,-13,-14,-15,-15,-16,-17,-18,-19,-20,-20,-21,-21,-22,-22,-23,-23,-23,-24,-24,-24,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-24,-24,-24,-23,-23,-23,-22,-22,-21,-21,-20,-20,-19,-18,-17,-16,-15,-15,-14,-13,-12,-12,-11,-10,-9,-8,-7,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,10,11,12,13,14,14,15,16,17,18,18,19,19,20,20,21,22,22,23,23,24,24,24,25,25,25,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,25,25,25,24,24,24,23,23,22,22,21,20,20,19,19,18,18,17,16,15,14,14,13,12,11,10,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-10,-11,-12,-13,-14,-14,-15,-16,-17,-18,-18,-19,-19,-20,-20,-21,-22,-22,-23,-23,-24,-24,-24,-25,-25,-25,-26,-26,-26,-26,-26,-26,-26,-26,-26,-26,-26,-26,-26,-26,-26,-25,-25,-25,-24,-24,-24,-23,-23,-22,-22,-21,-20,-20,-19,-19,-18,-18,-17,-16,-15,-14,-14,-13,-12,-11,-10,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,17,18,19,20,21,21,22,22,23,24,24,25,25,26,26,26,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,26,26,26,25,25,24,24,23,22,22,21,21,20,19,18,17,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-17,-18,-19,-20,-21,-21,-22,-22,-23,-24,-24,-25,-25,-26,-26,-26,-27,-27,-27,-27,-27,-27,-27,-27,-27,-27,-27,-27,-27,-27,-27,-26,-26,-26,-25,-25,-24,-24,-23,-22,-22,-21,-21,-20,-19,-18,-17,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,16,17,18,19,20,21,22,23,23,24,24,25,25,26,26,27,27,27,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,27,27,27,26,26,25,25,24,24,23,23,22,21,20,19,18,17,16,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-16,-17,-18,-19,-20,-21,-22,-23,-23,-24,-24,-25,-25,-26,-26,-27,-27,-27,-28,-28,-28,-28,-28,-28,-28,-28,-28,-28,-28,-28,-28,-28,-28,-27,-27,-27,-26,-26,-25,-25,-24,-24,-23,-23,-22,-21,-20,-19,-18,-17,-16,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,13,14,15,16,17,18,19,19,20,20,21,21,22,22,23,23,24,25,25,26,26,27,27,27,28,28,28,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,28,28,28,27,27,27,26,26,25,25,24,23,23,22,22,21,21,20,20,19,19,18,17,16,15,14,13,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-13,-14,-15,-16,-17,-18,-19,-19,-20,-20,-21,-21,-22,-22,-23,-23,-24,-25,-25,-26,-26,-27,-27,-27,-28,-28,-28,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-28,-28,-28,-27,-27,-27,-26,-26,-25,-25,-24,-23,-23,-22,-22,-21,-21,-20,-20,-19,-19,-18,-17,-16,-15,-14,-13,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,15,16,17,18,18,19,20,21,22,23,24,24,25,25,26,26,27,27,28,28,28,29,29,29,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,29,29,29,28,28,28,27,27,26,26,25,25,24,24,23,22,21,20,19,18,18,17,16,15,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-15,-16,-17,-18,-18,-19,-20,-21,-22,-23,-24,-24,-25,-25,-26,-26,-27,-27,-28,-28,-28,-29,-29,-29,-30,-30,-30,-30,-30,-30,-30,-30,-30,-30,-30,-30,-30,-30,-30,-29,-29,-29,-28,-28,-28,-27,-27,-26,-26,-25,-25,-24,-24,-23,-22,-21,-20,-19,-18,-18,-17,-16,-15,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,11,12,13,14,15,16,17,17,18,19,20,21,21,22,22,23,23,24,24,25,26,26,27,27,28,28,29,29,29,30,30,30,30,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,30,30,30,30,29,29,29,28,28,27,27,26,26,25,24,24,23,23,22,22,21,21,20,19,18,17,17,16,15,14,13,12,11,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-11,-12,-13,-14,-15,-16,-17,-17,-18,-19,-20,-21,-21,-22,-22,-23,-23,-24,-24,-25,-26,-26,-27,-27,-28,-28,-29,-29,-29,-30,-30,-30,-30,-31,-31,-31,-31,-31,-31,-31,-31,-31,-31,-31,-31,-31,-31,-31,-30,-30,-30,-30,-29,-29,-29,-28,-28,-27,-27,-26,-26,-25,-24,-24,-23,-23,-22,-22,-21,-21,-20,-19,-18,-17,-17,-16,-15,-14,-13,-12,-11,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,8,9,10,11,12,13,14,15,16,17,18,19,20,20,21,22,23,24,25,25,26,26,27,28,28,29,29,30,30,31,31,31,31,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,31,31,31,31,30,30,29,29,28,28,27,26,26,25,25,24,23,22,21,20,20,19,18,17,16,15,14,13,12,11,10,9,8,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-20,-20,-21,-22,-23,-24,-25,-25,-26,-26,-27,-28,-28,-29,-29,-30,-30,-31,-31,-31,-31,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-31,-31,-31,-31,-30,-30,-29,-29,-28,-28,-27,-26,-26,-25,-25,-24,-23,-22,-21,-20,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,19,20,21,22,23,23,24,24,25,25,26,27,27,28,28,29,29,30,30,31,31,32,32,32,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,32,32,32,31,31,30,30,29,29,28,28,27,27,26,25,25,24,24,23,23,22,21,20,19,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-19,-20,-21,-22,-23,-23,-24,-24,-25,-25,-26,-27,-27,-28,-28,-29,-29,-30,-30,-31,-31,-32,-32,-32,-33,-33,-33,-33,-33,-33,-33,-33,-33,-33,-33,-33,-33,-33,-33,-33,-33,-32,-32,-32,-31,-31,-30,-30,-29,-29,-28,-28,-27,-27,-26,-25,-25,-24,-24,-23,-23,-22,-21,-20,-19,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,14,15,16,16,17,18,18,19,20,21,22,22,23,24,25,26,26,27,27,28,29,29,30,30,30,31,31,31,32,32,32,33,33,33,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,33,33,33,32,32,32,31,31,31,30,30,30,29,29,28,27,27,26,26,25,24,23,22,22,21,20,19,18,18,17,16,16,15,14,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-14,-15,-16,-16,-17,-18,-18,-19,-20,-21,-22,-22,-23,-24,-25,-26,-26,-27,-27,-28,-29,-29,-30,-30,-30,-31,-31,-31,-32,-32,-32,-33,-33,-33,-34,-34,-34,-34,-34,-34,-34,-34,-34,-34,-34,-34,-34,-34,-34,-34,-34,-33,-33,-33,-32,-32,-32,-31,-31,-31,-30,-30,-30,-29,-29,-28,-27,-27,-26,-26,-25,-24,-23,-22,-22,-21,-20,-19,-18,-18,-17,-16,-16,-15,-14,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,21,22,23,24,25,26,27,28,28,29,29,30,31,31,32,32,33,33,33,34,34,34,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,34,34,34,33,33,33,32,32,31,31,30,29,29,28,28,27,26,25,24,23,22,21,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-20,-21,-21,-22,-23,-24,-25,-26,-27,-28,-28,-29,-29,-30,-31,-31,-32,-32,-33,-33,-33,-34,-34,-34,-35,-35,-35,-35,-35,-35,-35,-35,-35,-35,-35,-35,-35,-35,-35,-35,-35,-34,-34,-34,-33,-33,-33,-32,-32,-31,-31,-30,-29,-29,-28,-28,-27,-26,-25,-24,-23,-22,-21,-21,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,20,21,22,23,24,24,25,25,26,26,27,27,28,28,29,30,30,31,31,32,32,33,33,34,34,34,35,35,35,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,35,35,35,34,34,34,33,33,32,32,31,31,30,30,29,28,28,27,27,26,26,25,25,24,24,23,22,21,20,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-20,-20,-21,-22,-23,-24,-24,-25,-25,-26,-26,-27,-27,-28,-28,-29,-30,-30,-31,-31,-32,-32,-33,-33,-34,-34,-34,-35,-35,-35,-36,-36,-36,-36,-36,-36,-36,-36,-36,-36,-36,-36,-36,-36,-36,-36,-36,-35,-35,-35,-34,-34,-34,-33,-33,-32,-32,-31,-31,-30,-30,-29,-28,-28,-27,-27,-26,-26,-25,-25,-24,-24,-23,-22,-21,-20,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,12,13,14,15,16,17,18,19,20,21,22,23,23,24,25,26,27,28,29,29,30,30,31,32,32,33,33,34,34,35,35,35,36,36,36,36,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,36,36,36,36,35,35,35,34,34,33,33,32,32,31,30,30,29,29,28,27,26,25,24,23,23,22,21,20,19,18,17,16,15,14,13,12,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-12,-13,-14,-15,-16,-17,-18,-19,-20,-21,-22,-23,-23,-24,-25,-26,-27,-28,-29,-29,-30,-30,-31,-32,-32,-33,-33,-34,-34,-35,-35,-35,-36,-36,-36,-36,-37,-37,-37,-37,-37,-37,-37,-37,-37,-37,-37,-37,-37,-37,-37,-37,-37,-36,-36,-36,-36,-35,-35,-35,-34,-34,-33,-33,-32,-32,-31,-30,-30,-29,-29,-28,-27,-26,-25,-24,-23,-23,-22,-21,-20,-19,-18,-17,-16,-15,-14,-13,-12,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,17,18,19,19,20,21,22,22,23,24,25,26,26,27,27,28,28,29,29,30,31,31,32,32,33,33,34,34,34,35,35,35,36,36,37,37,37,37,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,37,37,37,37,36,36,35,35,35,34,34,34,33,33,32,32,31,31,30,29,29,28,28,27,27,26,26,25,24,23,22,22,21,20,19,19,18,17,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-17,-18,-19,-19,-20,-21,-22,-22,-23,-24,-25,-26,-26,-27,-27,-28,-28,-29,-29,-30,-31,-31,-32,-32,-33,-33,-34,-34,-34,-35,-35,-35,-36,-36,-37,-37,-37,-37,-38,-38,-38,-38,-38,-38,-38,-38,-38,-38,-38,-38,-38,-38,-38,-38,-38,-37,-37,-37,-37,-36,-36,-35,-35,-35,-34,-34,-34,-33,-33,-32,-32,-31,-31,-30,-29,-29,-28,-28,-27,-27,-26,-26,-25,-24,-23,-22,-22,-21,-20,-19,-19,-18,-17,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,15,16,17,18,19,20,21,21,22,23,24,25,25,26,27,28,29,30,30,31,31,32,33,33,34,34,35,35,36,36,36,37,37,37,38,38,38,38,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,38,38,38,38,37,37,37,36,36,36,35,35,34,34,33,33,32,31,31,30,30,29,28,27,26,25,25,24,23,22,21,21,20,19,18,17,16,15,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-15,-16,-17,-18,-19,-20,-21,-21,-22,-23,-24,-25,-25,-26,-27,-28,-29,-30,-30,-31,-31,-32,-33,-33,-34,-34,-35,-35,-36,-36,-36,-37,-37,-37,-38,-38,-38,-38,-39,-39,-39,-39,-39,-39,-39,-39,-39,-39,-39,-39,-39,-39,-39,-39,-39,-38,-38,-38,-38,-37,-37,-37,-36,-36,-36,-35,-35,-34,-34,-33,-33,-32,-31,-31,-30,-30,-29,-28,-27,-26,-25,-25,-24,-23,-22,-21,-21,-20,-19,-18,-17,-16,-15,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,24,25,26,27,28,29,30,31,32,32,33,33,34,35,35,36,36,37,37,38,38,38,39,39,39,39,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,39,39,39,39,38,38,38,37,37,36,36,35,35,34,33,33,32,32,31,30,29,28,27,26,25,24,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-20,-21,-22,-23,-24,-24,-25,-26,-27,-28,-29,-30,-31,-32,-32,-33,-33,-34,-35,-35,-36,-36,-37,-37,-38,-38,-38,-39,-39,-39,-39,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-39,-39,-39,-39,-38,-38,-38,-37,-37,-36,-36,-35,-35,-34,-33,-33,-32,-32,-31,-30,-29,-28,-27,-26,-25,-24,-24,-23,-22,-21,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,23,24,25,26,27,27,28,28,29,29,30,30,31,31,32,32,33,34,34,35,35,36,36,37,37,38,38,39,39,39,40,40,40,40,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,40,40,40,40,39,39,39,38,38,37,37,36,36,35,35,34,34,33,32,32,31,31,30,30,29,29,28,28,27,27,26,25,24,23,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-20,-21,-22,-23,-23,-24,-25,-26,-27,-27,-28,-28,-29,-29,-30,-30,-31,-31,-32,-32,-33,-34,-34,-35,-35,-36,-36,-37,-37,-38,-38,-39,-39,-39,-40,-40,-40,-40,-41,-41,-41,-41,-41,-41,-41,-41,-41,-41,-41,-41,-41,-41,-41,-41,-41,-41,-41,-40,-40,-40,-40,-39,-39,-39,-38,-38,-37,-37,-36,-36,-35,-35,-34,-34,-33,-32,-32,-31,-31,-30,-30,-29,-29,-28,-28,-27,-27,-26,-25,-24,-23,-23,-22,-21,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,18,19,20,20,21,22,23,24,25,26,26,27,28,29,30,31,32,33,33,34,34,35,36,36,37,37,38,38,38,39,39,39,40,40,40,41,41,41,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,41,41,41,40,40,40,39,39,39,38,38,38,37,37,36,36,35,34,34,33,33,32,31,30,29,28,27,26,26,25,24,23,22,21,20,20,19,18,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-18,-19,-20,-20,-21,-22,-23,-24,-25,-26,-26,-27,-28,-29,-30,-31,-32,-33,-33,-34,-34,-35,-36,-36,-37,-37,-38,-38,-38,-39,-39,-39,-40,-40,-40,-41,-41,-41,-42,-42,-42,-42,-42,-42,-42,-42,-42,-42,-42,-42,-42,-42,-42,-42,-42,-42,-42,-41,-41,-41,-40,-40,-40,-39,-39,-39,-38,-38,-38,-37,-37,-36,-36,-35,-34,-34,-33,-33,-32,-31,-30,-29,-28,-27,-26,-26,-25,-24,-23,-22,-21,-20,-20,-19,-18,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,13,14,15,16,17,18,19,20,21,22,22,23,24,25,25,26,27,28,29,29,30,30,31,31,32,32,33,33,34,35,35,36,36,37,37,38,38,39,39,40,40,40,41,41,41,42,42,42,42,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,42,42,42,42,41,41,41,40,40,40,39,39,38,38,37,37,36,36,35,35,34,33,33,32,32,31,31,30,30,29,29,28,27,26,25,25,24,23,22,22,21,20,19,18,17,16,15,14,13,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-13,-14,-15,-16,-17,-18,-19,-20,-21,-22,-22,-23,-24,-25,-25,-26,-27,-28,-29,-29,-30,-30,-31,-31,-32,-32,-33,-33,-34,-35,-35,-36,-36,-37,-37,-38,-38,-39,-39,-40,-40,-40,-41,-41,-41,-42,-42,-42,-42,-43,-43,-43,-43,-43,-43,-43,-43,-43,-43,-43,-43,-43,-43,-43,-43,-43,-43,-43,-42,-42,-42,-42,-41,-41,-41,-40,-40,-40,-39,-39,-38,-38,-37,-37,-36,-36,-35,-35,-34,-33,-33,-32,-32,-31,-31,-30,-30,-29,-29,-28,-27,-26,-25,-25,-24,-23,-22,-22,-21,-20,-19,-18,-17,-16,-15,-14,-13,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,16,17,18,19,20,21,22,23,24,24,25,26,27,28,28,29,30,31,32,33,34,34,35,35,36,37,37,38,38,39,39,40,40,41,41,41,42,42,42,43,43,43,43,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,43,43,43,43,42,42,42,41,41,41,40,40,39,39,38,38,37,37,36,35,35,34,34,33,32,31,30,29,28,28,27,26,25,24,24,23,22,21,20,19,18,17,16,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-16,-17,-18,-19,-20,-21,-22,-23,-24,-24,-25,-26,-27,-28,-28,-29,-30,-31,-32,-33,-34,-34,-35,-35,-36,-37,-37,-38,-38,-39,-39,-40,-40,-41,-41,-41,-42,-42,-42,-43,-43,-43,-43,-44,-44,-44,-44,-44,-44,-44,-44,-44,-44,-44,-44,-44,-44,-44,-44,-44,-44,-44,-43,-43,-43,-43,-42,-42,-42,-41,-41,-41,-40,-40,-39,-39,-38,-38,-37,-37,-36,-35,-35,-34,-34,-33,-32,-31,-30,-29,-28,-28,-27,-26,-25,-24,-24,-23,-22,-21,-20,-19,-18,-17,-16,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,27,28,29,30,31,32,32,33,33,34,35,36,36,37,37,38,39,39,40,40,41,41,42,42,43,43,43,44,44,44,44,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,44,44,44,44,43,43,43,42,42,41,41,40,40,39,39,38,37,37,36,36,35,34,33,33,32,32,31,30,29,28,27,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-20,-21,-22,-23,-24,-25,-26,-27,-27,-28,-29,-30,-31,-32,-32,-33,-33,-34,-35,-36,-36,-37,-37,-38,-39,-39,-40,-40,-41,-41,-42,-42,-43,-43,-43,-44,-44,-44,-44,-45,-45,-45,-45,-45,-45,-45,-45,-45,-45,-45,-45,-45,-45,-45,-45,-45,-45,-45,-44,-44,-44,-44,-43,-43,-43,-42,-42,-41,-41,-40,-40,-39,-39,-38,-37,-37,-36,-36,-35,-34,-33,-33,-32,-32,-31,-30,-29,-28,-27,-27,-26,-25,-24,-23,-22,-21,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,21,22,23,24,25,26,26,27,28,29,30,30,31,31,32,33,34,34,35,35,36,36,37,38,38,39,39,40,40,41,41,42,42,42,43,43,44,44,44,45,45,45,45,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,45,45,45,45,44,44,44,43,43,42,42,42,41,41,40,40,39,39,38,38,37,36,36,35,35,34,34,33,32,31,31,30,30,29,28,27,26,26,25,24,23,22,21,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-20,-21,-21,-22,-23,-24,-25,-26,-26,-27,-28,-29,-30,-30,-31,-31,-32,-33,-34,-34,-35,-35,-36,-36,-37,-38,-38,-39,-39,-40,-40,-41,-41,-42,-42,-42,-43,-43,-44,-44,-44,-45,-45,-45,-45,-46,-46,-46,-46,-46,-46,-46,-46,-46,-46,-46,-46,-46,-46,-46,-46,-46,-46,-46,-45,-45,-45,-45,-44,-44,-44,-43,-43,-42,-42,-42,-41,-41,-40,-40,-39,-39,-38,-38,-37,-36,-36,-35,-35,-34,-34,-33,-32,-31,-31,-30,-30,-29,-28,-27,-26,-26,-25,-24,-23,-22,-21,-21,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,19,20,21,22,23,23,24,25,26,27,28,29,29,30,31,32,33,34,35,36,37,37,38,38,39,40,40,41,41,42,42,43,43,43,44,44,44,45,45,45,46,46,46,46,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,46,46,46,46,45,45,45,44,44,44,43,43,43,42,42,41,41,40,40,39,38,38,37,37,36,35,34,33,32,31,30,29,29,28,27,26,25,24,23,23,22,21,20,19,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-19,-20,-21,-22,-23,-23,-24,-25,-26,-27,-28,-29,-29,-30,-31,-32,-33,-34,-35,-36,-37,-37,-38,-38,-39,-40,-40,-41,-41,-42,-42,-43,-43,-43,-44,-44,-44,-45,-45,-45,-46,-46,-46,-46,-47,-47,-47,-47,-47,-47,-47,-47,-47,-47,-47,-47,-47,-47,-47,-47,-47,-47,-47,-46,-46,-46,-46,-45,-45,-45,-44,-44,-44,-43,-43,-43,-42,-42,-41,-41,-40,-40,-39,-38,-38,-37,-37,-36,-35,-34,-33,-32,-31,-30,-29,-29,-28,-27,-26,-25,-24,-23,-23,-22,-21,-20,-19,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,25,26,27,28,28,29,30,31,32,32,33,33,34,34,35,35,36,36,37,37,38,39,39,40,40,41,41,42,42,43,43,44,44,45,45,45,46,46,46,47,47,47,47,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,47,47,47,47,46,46,46,45,45,45,44,44,43,43,42,42,41,41,40,40,39,39,38,37,37,36,36,35,35,34,34,33,33,32,32,31,30,29,28,28,27,26,25,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-20,-21,-22,-23,-24,-25,-25,-26,-27,-28,-28,-29,-30,-31,-32,-32,-33,-33,-34,-34,-35,-35,-36,-36,-37,-37,-38,-39,-39,-40,-40,-41,-41,-42,-42,-43,-43,-44,-44,-45,-45,-45,-46,-46,-46,-47,-47,-47,-47,-48,-48,-48,-48,-48,-48,-48,-48,-48,-48,-48,-48,-48,-48,-48,-48,-48,-48,-48,-47,-47,-47,-47,-46,-46,-46,-45,-45,-45,-44,-44,-43,-43,-42,-42,-41,-41,-40,-40,-39,-39,-38,-37,-37,-36,-36,-35,-35,-34,-34,-33,-33,-32,-32,-31,-30,-29,-28,-28,-27,-26,-25,-25,-24,-23,-22,-21,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,17,18,19,20,21,22,23,24,25,26,27,27,28,29,30,31,31,32,33,34,35,36,37,38,38,39,39,40,41,41,42,42,43,43,44,44,45,45,46,46,46,47,47,47,47,48,48,48,48,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,48,48,48,48,47,47,47,47,46,46,46,45,45,44,44,43,43,42,42,41,41,40,39,39,38,38,37,36,35,34,33,32,31,31,30,29,28,27,27,26,25,24,23,22,21,20,19,18,17,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-17,-18,-19,-20,-21,-22,-23,-24,-25,-26,-27,-27,-28,-29,-30,-31,-31,-32,-33,-34,-35,-36,-37,-38,-38,-39,-39,-40,-41,-41,-42,-42,-43,-43,-44,-44,-45,-45,-46,-46,-46,-47,-47,-47,-47,-48,-48,-48,-48,-49,-49,-49,-49,-49,-49,-49,-49,-49,-49,-49,-49,-49,-49,-49,-49,-49,-49,-49,-48,-48,-48,-48,-47,-47,-47,-47,-46,-46,-46,-45,-45,-44,-44,-43,-43,-42,-42,-41,-41,-40,-39,-39,-38,-38,-37,-36,-35,-34,-33,-32,-31,-31,-30,-29,-28,-27,-27,-26,-25,-24,-23,-22,-21,-20,-19,-18,-17,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,10,11,12,13,14,14,15,16,17,18,19,20,21,22,22,23,24,25,26,27,28,29,30,30,31,32,33,34,34,35,35,36,36,37,37,38,38,39,40,40,41,41,42,43,43,44,44,45,45,46,46,46,47,47,48,48,48,48,49,49,49,49,49,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,49,49,49,49,49,48,48,48,48,47,47,46,46,46,45,45,44,44,43,43,42,41,41,40,40,39,38,38,37,37,36,36,35,35,34,34,33,32,31,30,30,29,28,27,26,25,24,23,22,22,21,20,19,18,17,16,15,14,14,13,12,11,10,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-10,-11,-12,-13,-14,-14,-15,-16,-17,-18,-19,-20,-21,-22,-22,-23,-24,-25,-26,-27,-28,-29,-30,-30,-31,-32,-33,-34,-34,-35,-35,-36,-36,-37,-37,-38,-38,-39,-40,-40,-41,-41,-42,-43,-43,-44,-44,-45,-45,-46,-46,-46,-47,-47,-48,-48,-48,-48,-49,-49,-49,-49,-49,-50,-50,-50,-50,-50,-50,-50,-50,-50,-50,-50,-50,-50,-50,-50,-50,-50,-50,-50,-50,-50,-49,-49,-49,-49,-49,-48,-48,-48,-48,-47,-47,-46,-46,-46,-45,-45,-44,-44,-43,-43,-42,-41,-41,-40,-40,-39,-38,-38,-37,-37,-36,-36,-35,-35,-34,-34,-33,-32,-31,-30,-30,-29,-28,-27,-26,-25,-24,-23,-22,-22,-21,-20,-19,-18,-17,-16,-15,-14,-14,-13,-12,-11,-10,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,24,25,26,26,27,28,29,29,30,31,32,33,33,34,35,36,37,38,39,39,40,40,41,42,42,43,43,44,44,45,45,45,46,46,47,47,47,48,48,49,49,49,50,50,50,50,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,50,50,50,50,49,49,49,48,48,47,47,47,46,46,45,45,45,44,44,43,43,42,42,41,40,40,39,39,38,37,36,35,34,33,33,32,31,30,29,29,28,27,26,26,25,24,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-20,-21,-22,-23,-24,-24,-25,-26,-26,-27,-28,-29,-29,-30,-31,-32,-33,-33,-34,-35,-36,-37,-38,-39,-39,-40,-40,-41,-42,-42,-43,-43,-44,-44,-45,-45,-45,-46,-46,-47,-47,-47,-48,-48,-49,-49,-49,-50,-50,-50,-50,-51,-51,-51,-51,-51,-51,-51,-51,-51,-51,-51,-51,-51,-51,-51,-51,-51,-51,-51,-51,-51,-50,-50,-50,-50,-49,-49,-49,-48,-48,-47,-47,-47,-46,-46,-45,-45,-45,-44,-44,-43,-43,-42,-42,-41,-40,-40,-39,-39,-38,-37,-36,-35,-34,-33,-33,-32,-31,-30,-29,-29,-28,-27,-26,-26,-25,-24,-24,-23,-22,-21,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,20,21,22,23,24,25,26,27,28,29,30,31,32,32,33,34,35,36,37,38,39,40,41,41,42,42,43,44,44,45,46,46,47,47,48,48,48,49,49,49,50,50,50,51,51,51,51,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,51,51,51,51,50,50,50,49,49,49,48,48,48,47,47,46,46,45,44,44,43,42,42,41,41,40,39,38,37,36,35,34,33,32,32,31,30,29,28,27,26,25,24,23,22,21,20,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-20,-20,-21,-22,-23,-24,-25,-26,-27,-28,-29,-30,-31,-32,-32,-33,-34,-35,-36,-37,-38,-39,-40,-41,-41,-42,-42,-43,-44,-44,-45,-46,-46,-47,-47,-48,-48,-48,-49,-49,-49,-50,-50,-50,-51,-51,-51,-51,-52,-52,-52,-52,-52,-52,-52,-52,-52,-52,-52,-52,-52,-52,-52,-52,-52,-52,-52,-52,-52,-51,-51,-51,-51,-50,-50,-50,-49,-49,-49,-48,-48,-48,-47,-47,-46,-46,-45,-44,-44,-43,-42,-42,-41,-41,-40,-39,-38,-37,-36,-35,-34,-33,-32,-32,-31,-30,-29,-28,-27,-26,-25,-24,-23,-22,-21,-20,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,28,29,30,31,31,32,33,34,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,43,43,44,44,45,45,46,46,47,47,48,48,49,49,50,50,50,51,51,51,52,52,52,52,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,52,52,52,52,51,51,51,50,50,50,49,49,48,48,47,47,46,46,45,45,44,44,43,43,42,41,41,40,40,39,39,38,38,37,37,36,36,35,35,34,33,32,31,31,30,29,28,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-20,-21,-22,-23,-24,-25,-26,-27,-28,-28,-29,-30,-31,-31,-32,-33,-34,-35,-35,-36,-36,-37,-37,-38,-38,-39,-39,-40,-40,-41,-41,-42,-43,-43,-44,-44,-45,-45,-46,-46,-47,-47,-48,-48,-49,-49,-50,-50,-50,-51,-51,-51,-52,-52,-52,-52,-53,-53,-53,-53,-53,-53,-53,-53,-53,-53,-53,-53,-53,-53,-53,-53,-53,-53,-53,-53,-53,-52,-52,-52,-52,-51,-51,-51,-50,-50,-50,-49,-49,-48,-48,-47,-47,-46,-46,-45,-45,-44,-44,-43,-43,-42,-41,-41,-40,-40,-39,-39,-38,-38,-37,-37,-36,-36,-35,-35,-34,-33,-32,-31,-31,-30,-29,-28,-28,-27,-26,-25,-24,-23,-22,-21,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,30,31,32,33,34,34,35,36,37,38,39,40,41,42,42,43,43,44,45,45,46,46,47,47,48,48,49,49,50,50,51,51,51,52,52,52,53,53,53,53,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,53,53,53,53,52,52,52,51,51,51,50,50,49,49,48,48,47,47,46,46,45,45,44,43,43,42,42,41,40,39,38,37,36,35,34,34,33,32,31,30,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-20,-21,-22,-23,-24,-25,-26,-27,-28,-29,-30,-30,-31,-32,-33,-34,-34,-35,-36,-37,-38,-39,-40,-41,-42,-42,-43,-43,-44,-45,-45,-46,-46,-47,-47,-48,-48,-49,-49,-50,-50,-51,-51,-51,-52,-52,-52,-53,-53,-53,-53,-54,-54,-54,-54,-54,-54,-54,-54,-54,-54,-54,-54,-54,-54,-54,-54,-54,-54,-54,-54,-54,-53,-53,-53,-53,-52,-52,-52,-51,-51,-51,-50,-50,-49,-49,-48,-48,-47,-47,-46,-46,-45,-45,-44,-43,-43,-42,-42,-41,-40,-39,-38,-37,-36,-35,-34,-34,-33,-32,-31,-30,-30,-29,-28,-27,-26,-25,-24,-23,-22,-21,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,18,19,20,21,22,23,23,24,25,25,26,27,27,28,29,30,31,32,33,33,34,35,36,37,37,38,38,39,39,40,40,41,41,42,42,43,44,44,45,45,46,47,47,48,48,49,49,49,50,50,50,51,51,51,52,52,52,53,53,53,53,54,54,54,54,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,54,54,54,54,53,53,53,53,52,52,52,51,51,51,50,50,50,49,49,49,48,48,47,47,46,45,45,44,44,43,42,42,41,41,40,40,39,39,38,38,37,37,36,35,34,33,33,32,31,30,29,28,27,27,26,25,25,24,23,23,22,21,20,19,18,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-18,-19,-20,-21,-22,-23,-23,-24,-25,-25,-26,-27,-27,-28,-29,-30,-31,-32,-33,-33,-34,-35,-36,-37,-37,-38,-38,-39,-39,-40,-40,-41,-41,-42,-42,-43,-44,-44,-45,-45,-46,-47,-47,-48,-48,-49,-49,-49,-50,-50,-50,-51,-51,-51,-52,-52,-52,-53,-53,-53,-53,-54,-54,-54,-54,-55,-55,-55,-55,-55,-55,-55,-55,-55,-55,-55,-55,-55,-55,-55,-55,-55,-55,-55,-55,-55,-54,-54,-54,-54,-53,-53,-53,-53,-52,-52,-52,-51,-51,-51,-50,-50,-50,-49,-49,-49,-48,-48,-47,-47,-46,-45,-45,-44,-44,-43,-42,-42,-41,-41,-40,-40,-39,-39,-38,-38,-37,-37,-36,-35,-34,-33,-33,-32,-31,-30,-29,-28,-27,-27,-26,-25,-25,-24,-23,-23,-22,-21,-20,-19,-18,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,29,30,31,32,32,33,34,35,36,36,37,38,39,40,41,42,43,43,44,44,45,46,46,47,47,48,48,49,49,50,50,51,51,52,52,52,53,53,54,54,54,54,55,55,55,55,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,55,55,55,55,54,54,54,54,53,53,52,52,52,51,51,50,50,49,49,48,48,47,47,46,46,45,44,44,43,43,42,41,40,39,38,37,36,36,35,34,33,32,32,31,30,29,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-20,-21,-22,-23,-24,-25,-26,-27,-28,-29,-29,-30,-31,-32,-32,-33,-34,-35,-36,-36,-37,-38,-39,-40,-41,-42,-43,-43,-44,-44,-45,-46,-46,-47,-47,-48,-48,-49,-49,-50,-50,-51,-51,-52,-52,-52,-53,-53,-54,-54,-54,-54,-55,-55,-55,-55,-56,-56,-56,-56,-56,-56,-56,-56,-56,-56,-56,-56,-56,-56,-56,-56,-56,-56,-56,-56,-56,-55,-55,-55,-55,-54,-54,-54,-54,-53,-53,-52,-52,-52,-51,-51,-50,-50,-49,-49,-48,-48,-47,-47,-46,-46,-45,-44,-44,-43,-43,-42,-41,-40,-39,-38,-37,-36,-36,-35,-34,-33,-32,-32,-31,-30,-29,-29,-28,-27,-26,-25,-24,-23,-22,-21,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,15,16,17,18,19,20,21,21,22,23,24,25,26,27,28,29,30,31,31,32,33,34,35,35,36,37,38,39,40,41,41,42,43,44,45,45,46,46,47,48,48,49,49,50,50,51,51,52,52,53,53,53,54,54,54,55,55,55,55,56,56,56,56,56,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,56,56,56,56,56,55,55,55,55,54,54,54,53,53,53,52,52,51,51,50,50,49,49,48,48,47,46,46,45,45,44,43,42,41,41,40,39,38,37,36,35,35,34,33,32,31,31,30,29,28,27,26,25,24,23,22,21,21,20,19,18,17,16,15,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-15,-16,-17,-18,-19,-20,-21,-21,-22,-23,-24,-25,-26,-27,-28,-29,-30,-31,-31,-32,-33,-34,-35,-35,-36,-37,-38,-39,-40,-41,-41,-42,-43,-44,-45,-45,-46,-46,-47,-48,-48,-49,-49,-50,-50,-51,-51,-52,-52,-53,-53,-53,-54,-54,-54,-55,-55,-55,-55,-56,-56,-56,-56,-56,-57,-57,-57,-57,-57,-57,-57,-57,-57,-57,-57,-57,-57,-57,-57,-57,-57,-57,-57,-57,-57,-56,-56,-56,-56,-56,-55,-55,-55,-55,-54,-54,-54,-53,-53,-53,-52,-52,-51,-51,-50,-50,-49,-49,-48,-48,-47,-46,-46,-45,-45,-44,-43,-42,-41,-41,-40,-39,-38,-37,-36,-35,-35,-34,-33,-32,-31,-31,-30,-29,-28,-27,-26,-25,-24,-23,-22,-21,-21,-20,-19,-18,-17,-16,-15,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,34,35,36,37,38,38,39,39,40,40,41,42,42,43,43,44,44,45,45,46,47,47,48,48,49,50,50,51,51,52,52,53,53,54,54,55,55,55,56,56,56,57,57,57,57,57,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,57,57,57,57,57,56,56,56,55,55,55,54,54,53,53,52,52,51,51,50,50,49,48,48,47,47,46,45,45,44,44,43,43,42,42,41,40,40,39,39,38,38,37,36,35,34,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-20,-21,-22,-23,-24,-25,-26,-27,-28,-29,-30,-31,-32,-33,-34,-34,-35,-36,-37,-38,-38,-39,-39,-40,-40,-41,-42,-42,-43,-43,-44,-44,-45,-45,-46,-47,-47,-48,-48,-49,-50,-50,-51,-51,-52,-52,-53,-53,-54,-54,-55,-55,-55,-56,-56,-56,-57,-57,-57,-57,-57,-58,-58,-58,-58,-58,-58,-58,-58,-58,-58,-58,-58,-58,-58,-58,-58,-58,-58,-58,-58,-58,-57,-57,-57,-57,-57,-56,-56,-56,-55,-55,-55,-54,-54,-53,-53,-52,-52,-51,-51,-50,-50,-49,-48,-48,-47,-47,-46,-45,-45,-44,-44,-43,-43,-42,-42,-41,-40,-40,-39,-39,-38,-38,-37,-36,-35,-34,-34,-33,-32,-31,-30,-29,-28,-27,-26,-25,-24,-23,-22,-21,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,26,27,28,28,29,30,31,32,33,33,34,35,36,37,37,38,39,40,41,42,43,44,45,46,46,47,47,48,49,49,50,50,51,51,52,52,53,53,53,54,54,54,55,55,56,56,56,57,57,57,58,58,58,58,58,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,58,58,58,58,58,57,57,57,56,56,56,55,55,54,54,54,53,53,53,52,52,51,51,50,50,49,49,48,47,47,46,46,45,44,43,42,41,40,39,38,37,37,36,35,34,33,33,32,31,30,29,28,28,27,26,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-20,-21,-22,-23,-24,-25,-26,-26,-27,-28,-28,-29,-30,-31,-32,-33,-33,-34,-35,-36,-37,-37,-38,-39,-40,-41,-42,-43,-44,-45,-46,-46,-47,-47,-48,-49,-49,-50,-50,-51,-51,-52,-52,-53,-53,-53,-54,-54,-54,-55,-55,-56,-56,-56,-57,-57,-57,-58,-58,-58,-58,-58,-59,-59,-59,-59,-59,-59,-59,-59,-59,-59,-59,-59,-59,-59,-59,-59,-59,-59,-59,-59,-59,-58,-58,-58,-58,-58,-57,-57,-57,-56,-56,-56,-55,-55,-54,-54,-54,-53,-53,-53,-52,-52,-51,-51,-50,-50,-49,-49,-48,-47,-47,-46,-46,-45,-44,-43,-42,-41,-40,-39,-38,-37,-37,-36,-35,-34,-33,-33,-32,-31,-30,-29,-28,-28,-27,-26,-26,-25,-24,-23,-22,-21,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1]; var ys = [0,-1,-1,0,1,1,1,0,-1,-2,-2,-2,-1,0,1,2,2,2,2,2,1,0,-1,-2,-2,-3,-3,-3,-2,-1,0,1,2,3,3,3,3,3,2,1,0,-1,-2,-3,-3,-4,-4,-4,-3,-2,-1,0,1,2,3,4,4,4,4,4,3,2,1,0,-1,-2,-3,-4,-4,-5,-5,-5,-5,-4,-4,-3,-3,-2,-1,0,1,2,3,3,4,4,5,5,5,5,5,5,5,4,4,3,3,2,1,0,-1,-2,-3,-3,-4,-4,-5,-5,-5,-6,-6,-6,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,6,6,6,6,6,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-6,-6,-7,-7,-7,-7,-6,-6,-5,-5,-4,-3,-2,-1,0,1,2,3,4,5,5,6,6,7,7,7,7,7,7,7,6,6,5,5,4,3,2,1,0,-1,-2,-3,-4,-5,-5,-6,-6,-7,-7,-7,-8,-8,-8,-8,-8,-7,-7,-6,-5,-4,-4,-3,-2,-1,0,1,2,3,4,4,5,6,7,7,8,8,8,8,8,8,8,8,8,7,7,6,5,4,4,3,2,1,0,-1,-2,-3,-4,-4,-5,-6,-7,-7,-8,-8,-8,-8,-9,-9,-9,-9,-9,-8,-7,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,7,8,9,9,9,9,9,9,9,9,9,8,7,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-7,-8,-9,-9,-9,-9,-10,-10,-10,-10,-10,-9,-9,-8,-8,-7,-6,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,6,7,8,8,9,9,10,10,10,10,10,10,10,10,10,9,9,8,8,7,6,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-6,-7,-8,-8,-9,-9,-10,-10,-10,-10,-11,-11,-11,-11,-11,-10,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,10,11,11,11,11,11,11,11,11,11,10,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-10,-11,-11,-11,-11,-12,-12,-12,-12,-12,-11,-11,-10,-10,-9,-9,-8,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,8,9,9,10,10,11,11,12,12,12,12,12,12,12,12,12,11,11,10,10,9,9,8,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-8,-9,-9,-10,-10,-11,-11,-12,-12,-12,-12,-13,-13,-13,-13,-13,-13,-12,-12,-12,-11,-11,-10,-9,-8,-7,-7,-6,-5,-5,-4,-3,-2,-1,0,1,2,3,4,5,5,6,7,7,8,9,10,11,11,12,12,12,13,13,13,13,13,13,13,13,13,13,13,12,12,12,11,11,10,9,8,7,7,6,5,5,4,3,2,1,0,-1,-2,-3,-4,-5,-5,-6,-7,-7,-8,-9,-10,-11,-11,-12,-12,-12,-13,-13,-13,-13,-13,-14,-14,-14,-14,-14,-14,-13,-13,-12,-11,-11,-10,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,10,11,11,12,13,13,14,14,14,14,14,14,14,14,14,14,14,13,13,12,11,11,10,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-10,-11,-11,-12,-13,-13,-14,-14,-14,-14,-14,-15,-15,-15,-15,-15,-15,-14,-14,-13,-13,-12,-12,-11,-10,-9,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,9,10,11,12,12,13,13,14,14,15,15,15,15,15,15,15,15,15,15,15,14,14,13,13,12,12,11,10,9,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-9,-10,-11,-12,-12,-13,-13,-14,-14,-15,-15,-15,-15,-15,-16,-16,-16,-16,-16,-16,-15,-15,-14,-14,-13,-12,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,12,13,14,14,15,15,16,16,16,16,16,16,16,16,16,16,16,15,15,14,14,13,12,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-12,-13,-14,-14,-15,-15,-16,-16,-16,-16,-16,-17,-17,-17,-17,-17,-17,-16,-16,-16,-15,-15,-14,-14,-13,-13,-12,-11,-11,-10,-9,-8,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,8,9,10,11,11,12,13,13,14,14,15,15,16,16,16,17,17,17,17,17,17,17,17,17,17,17,16,16,16,15,15,14,14,13,13,12,11,11,10,9,8,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-8,-9,-10,-11,-11,-12,-13,-13,-14,-14,-15,-15,-16,-16,-16,-17,-17,-17,-17,-17,-18,-18,-18,-18,-18,-18,-18,-17,-17,-17,-16,-16,-15,-15,-14,-13,-12,-11,-10,-10,-9,-8,-7,-6,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,6,7,8,9,10,10,11,12,13,14,15,15,16,16,17,17,17,18,18,18,18,18,18,18,18,18,18,18,18,18,17,17,17,16,16,15,15,14,13,12,11,10,10,9,8,7,6,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-6,-7,-8,-9,-10,-10,-11,-12,-13,-14,-15,-15,-16,-16,-17,-17,-17,-18,-18,-18,-18,-18,-18,-19,-19,-19,-19,-19,-19,-19,-18,-18,-17,-17,-16,-15,-15,-14,-14,-13,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,13,14,14,15,15,16,17,17,18,18,19,19,19,19,19,19,19,19,19,19,19,19,19,18,18,17,17,16,15,15,14,14,13,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-13,-14,-14,-15,-15,-16,-17,-17,-18,-18,-19,-19,-19,-19,-19,-19,-20,-20,-20,-20,-20,-20,-20,-19,-19,-18,-18,-17,-17,-16,-16,-15,-14,-13,-12,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,12,13,14,15,16,16,17,17,18,18,19,19,20,20,20,20,20,20,20,20,20,20,20,20,20,19,19,18,18,17,17,16,16,15,14,13,12,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-12,-13,-14,-15,-16,-16,-17,-17,-18,-18,-19,-19,-20,-20,-20,-20,-20,-20,-21,-21,-21,-21,-21,-21,-21,-20,-20,-20,-19,-19,-19,-18,-18,-17,-16,-16,-15,-15,-14,-13,-12,-11,-11,-10,-9,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,9,10,11,11,12,13,14,15,15,16,16,17,18,18,19,19,19,20,20,20,21,21,21,21,21,21,21,21,21,21,21,21,21,20,20,20,19,19,19,18,18,17,16,16,15,15,14,13,12,11,11,10,9,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-9,-10,-11,-11,-12,-13,-14,-15,-15,-16,-16,-17,-18,-18,-19,-19,-19,-20,-20,-20,-21,-21,-21,-21,-21,-21,-22,-22,-22,-22,-22,-22,-22,-21,-21,-21,-20,-20,-19,-18,-18,-17,-17,-16,-15,-14,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,14,15,16,17,17,18,18,19,20,20,21,21,21,22,22,22,22,22,22,22,22,22,22,22,22,22,21,21,21,20,20,19,18,18,17,17,16,15,14,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-14,-15,-16,-17,-17,-18,-18,-19,-20,-20,-21,-21,-21,-22,-22,-22,-22,-22,-22,-23,-23,-23,-23,-23,-23,-23,-22,-22,-22,-21,-21,-20,-20,-19,-19,-18,-17,-16,-15,-14,-13,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,13,14,15,16,17,18,19,19,20,20,21,21,22,22,22,23,23,23,23,23,23,23,23,23,23,23,23,23,22,22,22,21,21,20,20,19,19,18,17,16,15,14,13,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-13,-14,-15,-16,-17,-18,-19,-19,-20,-20,-21,-21,-22,-22,-22,-23,-23,-23,-23,-23,-23,-24,-24,-24,-24,-24,-24,-24,-23,-23,-23,-22,-22,-21,-21,-20,-19,-19,-18,-18,-17,-17,-16,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,16,17,17,18,18,19,19,20,21,21,22,22,23,23,23,24,24,24,24,24,24,24,24,24,24,24,24,24,23,23,23,22,22,21,21,20,19,19,18,18,17,17,16,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-16,-17,-17,-18,-18,-19,-19,-20,-21,-21,-22,-22,-23,-23,-23,-24,-24,-24,-24,-24,-24,-25,-25,-25,-25,-25,-25,-25,-25,-24,-24,-24,-23,-23,-23,-22,-22,-21,-21,-20,-20,-19,-18,-17,-16,-15,-15,-14,-13,-12,-12,-11,-10,-9,-8,-7,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,7,8,9,10,11,12,12,13,14,15,15,16,17,18,19,20,20,21,21,22,22,23,23,23,24,24,24,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,24,24,24,23,23,23,22,22,21,21,20,20,19,18,17,16,15,15,14,13,12,12,11,10,9,8,7,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-7,-8,-9,-10,-11,-12,-12,-13,-14,-15,-15,-16,-17,-18,-19,-20,-20,-21,-21,-22,-22,-23,-23,-23,-24,-24,-24,-25,-25,-25,-25,-25,-25,-25,-26,-26,-26,-26,-26,-26,-26,-26,-25,-25,-25,-24,-24,-24,-23,-23,-22,-22,-21,-20,-20,-19,-19,-18,-18,-17,-16,-15,-14,-14,-13,-12,-11,-10,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,10,11,12,13,14,14,15,16,17,18,18,19,19,20,20,21,22,22,23,23,24,24,24,25,25,25,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,25,25,25,24,24,24,23,23,22,22,21,20,20,19,19,18,18,17,16,15,14,14,13,12,11,10,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-10,-11,-12,-13,-14,-14,-15,-16,-17,-18,-18,-19,-19,-20,-20,-21,-22,-22,-23,-23,-24,-24,-24,-25,-25,-25,-26,-26,-26,-26,-26,-26,-26,-27,-27,-27,-27,-27,-27,-27,-27,-26,-26,-26,-25,-25,-24,-24,-23,-22,-22,-21,-21,-20,-19,-18,-17,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,17,18,19,20,21,21,22,22,23,24,24,25,25,26,26,26,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,26,26,26,25,25,24,24,23,22,22,21,21,20,19,18,17,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-17,-18,-19,-20,-21,-21,-22,-22,-23,-24,-24,-25,-25,-26,-26,-26,-27,-27,-27,-27,-27,-27,-27,-28,-28,-28,-28,-28,-28,-28,-28,-27,-27,-27,-26,-26,-25,-25,-24,-24,-23,-23,-22,-21,-20,-19,-18,-17,-16,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,16,17,18,19,20,21,22,23,23,24,24,25,25,26,26,27,27,27,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,27,27,27,26,26,25,25,24,24,23,23,22,21,20,19,18,17,16,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-16,-17,-18,-19,-20,-21,-22,-23,-23,-24,-24,-25,-25,-26,-26,-27,-27,-27,-28,-28,-28,-28,-28,-28,-28,-29,-29,-29,-29,-29,-29,-29,-29,-28,-28,-28,-27,-27,-27,-26,-26,-25,-25,-24,-23,-23,-22,-22,-21,-21,-20,-20,-19,-19,-18,-17,-16,-15,-14,-13,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,13,14,15,16,17,18,19,19,20,20,21,21,22,22,23,23,24,25,25,26,26,27,27,27,28,28,28,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,28,28,28,27,27,27,26,26,25,25,24,23,23,22,22,21,21,20,20,19,19,18,17,16,15,14,13,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-13,-14,-15,-16,-17,-18,-19,-19,-20,-20,-21,-21,-22,-22,-23,-23,-24,-25,-25,-26,-26,-27,-27,-27,-28,-28,-28,-29,-29,-29,-29,-29,-29,-29,-30,-30,-30,-30,-30,-30,-30,-30,-29,-29,-29,-28,-28,-28,-27,-27,-26,-26,-25,-25,-24,-24,-23,-22,-21,-20,-19,-18,-18,-17,-16,-15,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,15,16,17,18,18,19,20,21,22,23,24,24,25,25,26,26,27,27,28,28,28,29,29,29,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,29,29,29,28,28,28,27,27,26,26,25,25,24,24,23,22,21,20,19,18,18,17,16,15,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-15,-16,-17,-18,-18,-19,-20,-21,-22,-23,-24,-24,-25,-25,-26,-26,-27,-27,-28,-28,-28,-29,-29,-29,-30,-30,-30,-30,-30,-30,-30,-31,-31,-31,-31,-31,-31,-31,-31,-30,-30,-30,-30,-29,-29,-29,-28,-28,-27,-27,-26,-26,-25,-24,-24,-23,-23,-22,-22,-21,-21,-20,-19,-18,-17,-17,-16,-15,-14,-13,-12,-11,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,11,12,13,14,15,16,17,17,18,19,20,21,21,22,22,23,23,24,24,25,26,26,27,27,28,28,29,29,29,30,30,30,30,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,30,30,30,30,29,29,29,28,28,27,27,26,26,25,24,24,23,23,22,22,21,21,20,19,18,17,17,16,15,14,13,12,11,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-11,-12,-13,-14,-15,-16,-17,-17,-18,-19,-20,-21,-21,-22,-22,-23,-23,-24,-24,-25,-26,-26,-27,-27,-28,-28,-29,-29,-29,-30,-30,-30,-30,-31,-31,-31,-31,-31,-31,-31,-32,-32,-32,-32,-32,-32,-32,-32,-32,-31,-31,-31,-31,-30,-30,-29,-29,-28,-28,-27,-26,-26,-25,-25,-24,-23,-22,-21,-20,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,8,9,10,11,12,13,14,15,16,17,18,19,20,20,21,22,23,24,25,25,26,26,27,28,28,29,29,30,30,31,31,31,31,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,31,31,31,31,30,30,29,29,28,28,27,26,26,25,25,24,23,22,21,20,20,19,18,17,16,15,14,13,12,11,10,9,8,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-20,-20,-21,-22,-23,-24,-25,-25,-26,-26,-27,-28,-28,-29,-29,-30,-30,-31,-31,-31,-31,-32,-32,-32,-32,-32,-32,-32,-32,-33,-33,-33,-33,-33,-33,-33,-33,-33,-32,-32,-32,-31,-31,-30,-30,-29,-29,-28,-28,-27,-27,-26,-25,-25,-24,-24,-23,-23,-22,-21,-20,-19,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,19,20,21,22,23,23,24,24,25,25,26,27,27,28,28,29,29,30,30,31,31,32,32,32,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,32,32,32,31,31,30,30,29,29,28,28,27,27,26,25,25,24,24,23,23,22,21,20,19,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-19,-20,-21,-22,-23,-23,-24,-24,-25,-25,-26,-27,-27,-28,-28,-29,-29,-30,-30,-31,-31,-32,-32,-32,-33,-33,-33,-33,-33,-33,-33,-33,-34,-34,-34,-34,-34,-34,-34,-34,-34,-33,-33,-33,-32,-32,-32,-31,-31,-31,-30,-30,-30,-29,-29,-28,-27,-27,-26,-26,-25,-24,-23,-22,-22,-21,-20,-19,-18,-18,-17,-16,-16,-15,-14,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,14,15,16,16,17,18,18,19,20,21,22,22,23,24,25,26,26,27,27,28,29,29,30,30,30,31,31,31,32,32,32,33,33,33,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,33,33,33,32,32,32,31,31,31,30,30,30,29,29,28,27,27,26,26,25,24,23,22,22,21,20,19,18,18,17,16,16,15,14,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-14,-15,-16,-16,-17,-18,-18,-19,-20,-21,-22,-22,-23,-24,-25,-26,-26,-27,-27,-28,-29,-29,-30,-30,-30,-31,-31,-31,-32,-32,-32,-33,-33,-33,-34,-34,-34,-34,-34,-34,-34,-34,-35,-35,-35,-35,-35,-35,-35,-35,-35,-34,-34,-34,-33,-33,-33,-32,-32,-31,-31,-30,-29,-29,-28,-28,-27,-26,-25,-24,-23,-22,-21,-21,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,21,22,23,24,25,26,27,28,28,29,29,30,31,31,32,32,33,33,33,34,34,34,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,34,34,34,33,33,33,32,32,31,31,30,29,29,28,28,27,26,25,24,23,22,21,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-20,-21,-21,-22,-23,-24,-25,-26,-27,-28,-28,-29,-29,-30,-31,-31,-32,-32,-33,-33,-33,-34,-34,-34,-35,-35,-35,-35,-35,-35,-35,-35,-36,-36,-36,-36,-36,-36,-36,-36,-36,-35,-35,-35,-34,-34,-34,-33,-33,-32,-32,-31,-31,-30,-30,-29,-28,-28,-27,-27,-26,-26,-25,-25,-24,-24,-23,-22,-21,-20,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,20,21,22,23,24,24,25,25,26,26,27,27,28,28,29,30,30,31,31,32,32,33,33,34,34,34,35,35,35,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,35,35,35,34,34,34,33,33,32,32,31,31,30,30,29,28,28,27,27,26,26,25,25,24,24,23,22,21,20,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-20,-20,-21,-22,-23,-24,-24,-25,-25,-26,-26,-27,-27,-28,-28,-29,-30,-30,-31,-31,-32,-32,-33,-33,-34,-34,-34,-35,-35,-35,-36,-36,-36,-36,-36,-36,-36,-36,-37,-37,-37,-37,-37,-37,-37,-37,-37,-36,-36,-36,-36,-35,-35,-35,-34,-34,-33,-33,-32,-32,-31,-30,-30,-29,-29,-28,-27,-26,-25,-24,-23,-23,-22,-21,-20,-19,-18,-17,-16,-15,-14,-13,-12,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,12,13,14,15,16,17,18,19,20,21,22,23,23,24,25,26,27,28,29,29,30,30,31,32,32,33,33,34,34,35,35,35,36,36,36,36,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,36,36,36,36,35,35,35,34,34,33,33,32,32,31,30,30,29,29,28,27,26,25,24,23,23,22,21,20,19,18,17,16,15,14,13,12,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-12,-13,-14,-15,-16,-17,-18,-19,-20,-21,-22,-23,-23,-24,-25,-26,-27,-28,-29,-29,-30,-30,-31,-32,-32,-33,-33,-34,-34,-35,-35,-35,-36,-36,-36,-36,-37,-37,-37,-37,-37,-37,-37,-37,-38,-38,-38,-38,-38,-38,-38,-38,-38,-37,-37,-37,-37,-36,-36,-35,-35,-35,-34,-34,-34,-33,-33,-32,-32,-31,-31,-30,-29,-29,-28,-28,-27,-27,-26,-26,-25,-24,-23,-22,-22,-21,-20,-19,-19,-18,-17,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,17,18,19,19,20,21,22,22,23,24,25,26,26,27,27,28,28,29,29,30,31,31,32,32,33,33,34,34,34,35,35,35,36,36,37,37,37,37,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,37,37,37,37,36,36,35,35,35,34,34,34,33,33,32,32,31,31,30,29,29,28,28,27,27,26,26,25,24,23,22,22,21,20,19,19,18,17,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-17,-18,-19,-19,-20,-21,-22,-22,-23,-24,-25,-26,-26,-27,-27,-28,-28,-29,-29,-30,-31,-31,-32,-32,-33,-33,-34,-34,-34,-35,-35,-35,-36,-36,-37,-37,-37,-37,-38,-38,-38,-38,-38,-38,-38,-38,-39,-39,-39,-39,-39,-39,-39,-39,-39,-38,-38,-38,-38,-37,-37,-37,-36,-36,-36,-35,-35,-34,-34,-33,-33,-32,-31,-31,-30,-30,-29,-28,-27,-26,-25,-25,-24,-23,-22,-21,-21,-20,-19,-18,-17,-16,-15,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,15,16,17,18,19,20,21,21,22,23,24,25,25,26,27,28,29,30,30,31,31,32,33,33,34,34,35,35,36,36,36,37,37,37,38,38,38,38,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,38,38,38,38,37,37,37,36,36,36,35,35,34,34,33,33,32,31,31,30,30,29,28,27,26,25,25,24,23,22,21,21,20,19,18,17,16,15,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-15,-16,-17,-18,-19,-20,-21,-21,-22,-23,-24,-25,-25,-26,-27,-28,-29,-30,-30,-31,-31,-32,-33,-33,-34,-34,-35,-35,-36,-36,-36,-37,-37,-37,-38,-38,-38,-38,-39,-39,-39,-39,-39,-39,-39,-39,-40,-40,-40,-40,-40,-40,-40,-40,-40,-39,-39,-39,-39,-38,-38,-38,-37,-37,-36,-36,-35,-35,-34,-33,-33,-32,-32,-31,-30,-29,-28,-27,-26,-25,-24,-24,-23,-22,-21,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,24,25,26,27,28,29,30,31,32,32,33,33,34,35,35,36,36,37,37,38,38,38,39,39,39,39,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,39,39,39,39,38,38,38,37,37,36,36,35,35,34,33,33,32,32,31,30,29,28,27,26,25,24,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-20,-21,-22,-23,-24,-24,-25,-26,-27,-28,-29,-30,-31,-32,-32,-33,-33,-34,-35,-35,-36,-36,-37,-37,-38,-38,-38,-39,-39,-39,-39,-40,-40,-40,-40,-40,-40,-40,-40,-41,-41,-41,-41,-41,-41,-41,-41,-41,-41,-40,-40,-40,-40,-39,-39,-39,-38,-38,-37,-37,-36,-36,-35,-35,-34,-34,-33,-32,-32,-31,-31,-30,-30,-29,-29,-28,-28,-27,-27,-26,-25,-24,-23,-23,-22,-21,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,23,24,25,26,27,27,28,28,29,29,30,30,31,31,32,32,33,34,34,35,35,36,36,37,37,38,38,39,39,39,40,40,40,40,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,40,40,40,40,39,39,39,38,38,37,37,36,36,35,35,34,34,33,32,32,31,31,30,30,29,29,28,28,27,27,26,25,24,23,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-20,-21,-22,-23,-23,-24,-25,-26,-27,-27,-28,-28,-29,-29,-30,-30,-31,-31,-32,-32,-33,-34,-34,-35,-35,-36,-36,-37,-37,-38,-38,-39,-39,-39,-40,-40,-40,-40,-41,-41,-41,-41,-41,-41,-41,-41,-41,-42,-42,-42,-42,-42,-42,-42,-42,-42,-42,-41,-41,-41,-40,-40,-40,-39,-39,-39,-38,-38,-38,-37,-37,-36,-36,-35,-34,-34,-33,-33,-32,-31,-30,-29,-28,-27,-26,-26,-25,-24,-23,-22,-21,-20,-20,-19,-18,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,18,19,20,20,21,22,23,24,25,26,26,27,28,29,30,31,32,33,33,34,34,35,36,36,37,37,38,38,38,39,39,39,40,40,40,41,41,41,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,41,41,41,40,40,40,39,39,39,38,38,38,37,37,36,36,35,34,34,33,33,32,31,30,29,28,27,26,26,25,24,23,22,21,20,20,19,18,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-18,-19,-20,-20,-21,-22,-23,-24,-25,-26,-26,-27,-28,-29,-30,-31,-32,-33,-33,-34,-34,-35,-36,-36,-37,-37,-38,-38,-38,-39,-39,-39,-40,-40,-40,-41,-41,-41,-42,-42,-42,-42,-42,-42,-42,-42,-42,-43,-43,-43,-43,-43,-43,-43,-43,-43,-43,-42,-42,-42,-42,-41,-41,-41,-40,-40,-40,-39,-39,-38,-38,-37,-37,-36,-36,-35,-35,-34,-33,-33,-32,-32,-31,-31,-30,-30,-29,-29,-28,-27,-26,-25,-25,-24,-23,-22,-22,-21,-20,-19,-18,-17,-16,-15,-14,-13,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,13,14,15,16,17,18,19,20,21,22,22,23,24,25,25,26,27,28,29,29,30,30,31,31,32,32,33,33,34,35,35,36,36,37,37,38,38,39,39,40,40,40,41,41,41,42,42,42,42,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,42,42,42,42,41,41,41,40,40,40,39,39,38,38,37,37,36,36,35,35,34,33,33,32,32,31,31,30,30,29,29,28,27,26,25,25,24,23,22,22,21,20,19,18,17,16,15,14,13,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-13,-14,-15,-16,-17,-18,-19,-20,-21,-22,-22,-23,-24,-25,-25,-26,-27,-28,-29,-29,-30,-30,-31,-31,-32,-32,-33,-33,-34,-35,-35,-36,-36,-37,-37,-38,-38,-39,-39,-40,-40,-40,-41,-41,-41,-42,-42,-42,-42,-43,-43,-43,-43,-43,-43,-43,-43,-43,-44,-44,-44,-44,-44,-44,-44,-44,-44,-44,-43,-43,-43,-43,-42,-42,-42,-41,-41,-41,-40,-40,-39,-39,-38,-38,-37,-37,-36,-35,-35,-34,-34,-33,-32,-31,-30,-29,-28,-28,-27,-26,-25,-24,-24,-23,-22,-21,-20,-19,-18,-17,-16,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,16,17,18,19,20,21,22,23,24,24,25,26,27,28,28,29,30,31,32,33,34,34,35,35,36,37,37,38,38,39,39,40,40,41,41,41,42,42,42,43,43,43,43,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,43,43,43,43,42,42,42,41,41,41,40,40,39,39,38,38,37,37,36,35,35,34,34,33,32,31,30,29,28,28,27,26,25,24,24,23,22,21,20,19,18,17,16,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-16,-17,-18,-19,-20,-21,-22,-23,-24,-24,-25,-26,-27,-28,-28,-29,-30,-31,-32,-33,-34,-34,-35,-35,-36,-37,-37,-38,-38,-39,-39,-40,-40,-41,-41,-41,-42,-42,-42,-43,-43,-43,-43,-44,-44,-44,-44,-44,-44,-44,-44,-44,-45,-45,-45,-45,-45,-45,-45,-45,-45,-45,-44,-44,-44,-44,-43,-43,-43,-42,-42,-41,-41,-40,-40,-39,-39,-38,-37,-37,-36,-36,-35,-34,-33,-33,-32,-32,-31,-30,-29,-28,-27,-27,-26,-25,-24,-23,-22,-21,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,27,28,29,30,31,32,32,33,33,34,35,36,36,37,37,38,39,39,40,40,41,41,42,42,43,43,43,44,44,44,44,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,44,44,44,44,43,43,43,42,42,41,41,40,40,39,39,38,37,37,36,36,35,34,33,33,32,32,31,30,29,28,27,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-20,-21,-22,-23,-24,-25,-26,-27,-27,-28,-29,-30,-31,-32,-32,-33,-33,-34,-35,-36,-36,-37,-37,-38,-39,-39,-40,-40,-41,-41,-42,-42,-43,-43,-43,-44,-44,-44,-44,-45,-45,-45,-45,-45,-45,-45,-45,-45,-46,-46,-46,-46,-46,-46,-46,-46,-46,-46,-45,-45,-45,-45,-44,-44,-44,-43,-43,-42,-42,-42,-41,-41,-40,-40,-39,-39,-38,-38,-37,-36,-36,-35,-35,-34,-34,-33,-32,-31,-31,-30,-30,-29,-28,-27,-26,-26,-25,-24,-23,-22,-21,-21,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,21,22,23,24,25,26,26,27,28,29,30,30,31,31,32,33,34,34,35,35,36,36,37,38,38,39,39,40,40,41,41,42,42,42,43,43,44,44,44,45,45,45,45,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,45,45,45,45,44,44,44,43,43,42,42,42,41,41,40,40,39,39,38,38,37,36,36,35,35,34,34,33,32,31,31,30,30,29,28,27,26,26,25,24,23,22,21,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-20,-21,-21,-22,-23,-24,-25,-26,-26,-27,-28,-29,-30,-30,-31,-31,-32,-33,-34,-34,-35,-35,-36,-36,-37,-38,-38,-39,-39,-40,-40,-41,-41,-42,-42,-42,-43,-43,-44,-44,-44,-45,-45,-45,-45,-46,-46,-46,-46,-46,-46,-46,-46,-46,-47,-47,-47,-47,-47,-47,-47,-47,-47,-47,-46,-46,-46,-46,-45,-45,-45,-44,-44,-44,-43,-43,-43,-42,-42,-41,-41,-40,-40,-39,-38,-38,-37,-37,-36,-35,-34,-33,-32,-31,-30,-29,-29,-28,-27,-26,-25,-24,-23,-23,-22,-21,-20,-19,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,19,20,21,22,23,23,24,25,26,27,28,29,29,30,31,32,33,34,35,36,37,37,38,38,39,40,40,41,41,42,42,43,43,43,44,44,44,45,45,45,46,46,46,46,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,46,46,46,46,45,45,45,44,44,44,43,43,43,42,42,41,41,40,40,39,38,38,37,37,36,35,34,33,32,31,30,29,29,28,27,26,25,24,23,23,22,21,20,19,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-19,-20,-21,-22,-23,-23,-24,-25,-26,-27,-28,-29,-29,-30,-31,-32,-33,-34,-35,-36,-37,-37,-38,-38,-39,-40,-40,-41,-41,-42,-42,-43,-43,-43,-44,-44,-44,-45,-45,-45,-46,-46,-46,-46,-47,-47,-47,-47,-47,-47,-47,-47,-47,-48,-48,-48,-48,-48,-48,-48,-48,-48,-48,-47,-47,-47,-47,-46,-46,-46,-45,-45,-45,-44,-44,-43,-43,-42,-42,-41,-41,-40,-40,-39,-39,-38,-37,-37,-36,-36,-35,-35,-34,-34,-33,-33,-32,-32,-31,-30,-29,-28,-28,-27,-26,-25,-25,-24,-23,-22,-21,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,25,26,27,28,28,29,30,31,32,32,33,33,34,34,35,35,36,36,37,37,38,39,39,40,40,41,41,42,42,43,43,44,44,45,45,45,46,46,46,47,47,47,47,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,47,47,47,47,46,46,46,45,45,45,44,44,43,43,42,42,41,41,40,40,39,39,38,37,37,36,36,35,35,34,34,33,33,32,32,31,30,29,28,28,27,26,25,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-20,-21,-22,-23,-24,-25,-25,-26,-27,-28,-28,-29,-30,-31,-32,-32,-33,-33,-34,-34,-35,-35,-36,-36,-37,-37,-38,-39,-39,-40,-40,-41,-41,-42,-42,-43,-43,-44,-44,-45,-45,-45,-46,-46,-46,-47,-47,-47,-47,-48,-48,-48,-48,-48,-48,-48,-48,-48,-49,-49,-49,-49,-49,-49,-49,-49,-49,-49,-48,-48,-48,-48,-47,-47,-47,-47,-46,-46,-46,-45,-45,-44,-44,-43,-43,-42,-42,-41,-41,-40,-39,-39,-38,-38,-37,-36,-35,-34,-33,-32,-31,-31,-30,-29,-28,-27,-27,-26,-25,-24,-23,-22,-21,-20,-19,-18,-17,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,17,18,19,20,21,22,23,24,25,26,27,27,28,29,30,31,31,32,33,34,35,36,37,38,38,39,39,40,41,41,42,42,43,43,44,44,45,45,46,46,46,47,47,47,47,48,48,48,48,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,48,48,48,48,47,47,47,47,46,46,46,45,45,44,44,43,43,42,42,41,41,40,39,39,38,38,37,36,35,34,33,32,31,31,30,29,28,27,27,26,25,24,23,22,21,20,19,18,17,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-17,-18,-19,-20,-21,-22,-23,-24,-25,-26,-27,-27,-28,-29,-30,-31,-31,-32,-33,-34,-35,-36,-37,-38,-38,-39,-39,-40,-41,-41,-42,-42,-43,-43,-44,-44,-45,-45,-46,-46,-46,-47,-47,-47,-47,-48,-48,-48,-48,-49,-49,-49,-49,-49,-49,-49,-49,-49,-50,-50,-50,-50,-50,-50,-50,-50,-50,-50,-50,-49,-49,-49,-49,-49,-48,-48,-48,-48,-47,-47,-46,-46,-46,-45,-45,-44,-44,-43,-43,-42,-41,-41,-40,-40,-39,-38,-38,-37,-37,-36,-36,-35,-35,-34,-34,-33,-32,-31,-30,-30,-29,-28,-27,-26,-25,-24,-23,-22,-22,-21,-20,-19,-18,-17,-16,-15,-14,-14,-13,-12,-11,-10,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,10,11,12,13,14,14,15,16,17,18,19,20,21,22,22,23,24,25,26,27,28,29,30,30,31,32,33,34,34,35,35,36,36,37,37,38,38,39,40,40,41,41,42,43,43,44,44,45,45,46,46,46,47,47,48,48,48,48,49,49,49,49,49,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,49,49,49,49,49,48,48,48,48,47,47,46,46,46,45,45,44,44,43,43,42,41,41,40,40,39,38,38,37,37,36,36,35,35,34,34,33,32,31,30,30,29,28,27,26,25,24,23,22,22,21,20,19,18,17,16,15,14,14,13,12,11,10,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-10,-11,-12,-13,-14,-14,-15,-16,-17,-18,-19,-20,-21,-22,-22,-23,-24,-25,-26,-27,-28,-29,-30,-30,-31,-32,-33,-34,-34,-35,-35,-36,-36,-37,-37,-38,-38,-39,-40,-40,-41,-41,-42,-43,-43,-44,-44,-45,-45,-46,-46,-46,-47,-47,-48,-48,-48,-48,-49,-49,-49,-49,-49,-50,-50,-50,-50,-50,-50,-50,-50,-50,-50,-51,-51,-51,-51,-51,-51,-51,-51,-51,-51,-51,-50,-50,-50,-50,-49,-49,-49,-48,-48,-47,-47,-47,-46,-46,-45,-45,-45,-44,-44,-43,-43,-42,-42,-41,-40,-40,-39,-39,-38,-37,-36,-35,-34,-33,-33,-32,-31,-30,-29,-29,-28,-27,-26,-26,-25,-24,-24,-23,-22,-21,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,24,25,26,26,27,28,29,29,30,31,32,33,33,34,35,36,37,38,39,39,40,40,41,42,42,43,43,44,44,45,45,45,46,46,47,47,47,48,48,49,49,49,50,50,50,50,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,50,50,50,50,49,49,49,48,48,47,47,47,46,46,45,45,45,44,44,43,43,42,42,41,40,40,39,39,38,37,36,35,34,33,33,32,31,30,29,29,28,27,26,26,25,24,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-20,-21,-22,-23,-24,-24,-25,-26,-26,-27,-28,-29,-29,-30,-31,-32,-33,-33,-34,-35,-36,-37,-38,-39,-39,-40,-40,-41,-42,-42,-43,-43,-44,-44,-45,-45,-45,-46,-46,-47,-47,-47,-48,-48,-49,-49,-49,-50,-50,-50,-50,-51,-51,-51,-51,-51,-51,-51,-51,-51,-51,-52,-52,-52,-52,-52,-52,-52,-52,-52,-52,-52,-51,-51,-51,-51,-50,-50,-50,-49,-49,-49,-48,-48,-48,-47,-47,-46,-46,-45,-44,-44,-43,-42,-42,-41,-41,-40,-39,-38,-37,-36,-35,-34,-33,-32,-32,-31,-30,-29,-28,-27,-26,-25,-24,-23,-22,-21,-20,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,20,21,22,23,24,25,26,27,28,29,30,31,32,32,33,34,35,36,37,38,39,40,41,41,42,42,43,44,44,45,46,46,47,47,48,48,48,49,49,49,50,50,50,51,51,51,51,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,51,51,51,51,50,50,50,49,49,49,48,48,48,47,47,46,46,45,44,44,43,42,42,41,41,40,39,38,37,36,35,34,33,32,32,31,30,29,28,27,26,25,24,23,22,21,20,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-20,-20,-21,-22,-23,-24,-25,-26,-27,-28,-29,-30,-31,-32,-32,-33,-34,-35,-36,-37,-38,-39,-40,-41,-41,-42,-42,-43,-44,-44,-45,-46,-46,-47,-47,-48,-48,-48,-49,-49,-49,-50,-50,-50,-51,-51,-51,-51,-52,-52,-52,-52,-52,-52,-52,-52,-52,-52,-53,-53,-53,-53,-53,-53,-53,-53,-53,-53,-53,-52,-52,-52,-52,-51,-51,-51,-50,-50,-50,-49,-49,-48,-48,-47,-47,-46,-46,-45,-45,-44,-44,-43,-43,-42,-41,-41,-40,-40,-39,-39,-38,-38,-37,-37,-36,-36,-35,-35,-34,-33,-32,-31,-31,-30,-29,-28,-28,-27,-26,-25,-24,-23,-22,-21,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,28,29,30,31,31,32,33,34,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,43,43,44,44,45,45,46,46,47,47,48,48,49,49,50,50,50,51,51,51,52,52,52,52,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,52,52,52,52,51,51,51,50,50,50,49,49,48,48,47,47,46,46,45,45,44,44,43,43,42,41,41,40,40,39,39,38,38,37,37,36,36,35,35,34,33,32,31,31,30,29,28,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-20,-21,-22,-23,-24,-25,-26,-27,-28,-28,-29,-30,-31,-31,-32,-33,-34,-35,-35,-36,-36,-37,-37,-38,-38,-39,-39,-40,-40,-41,-41,-42,-43,-43,-44,-44,-45,-45,-46,-46,-47,-47,-48,-48,-49,-49,-50,-50,-50,-51,-51,-51,-52,-52,-52,-52,-53,-53,-53,-53,-53,-53,-53,-53,-53,-53,-54,-54,-54,-54,-54,-54,-54,-54,-54,-54,-54,-53,-53,-53,-53,-52,-52,-52,-51,-51,-51,-50,-50,-49,-49,-48,-48,-47,-47,-46,-46,-45,-45,-44,-43,-43,-42,-42,-41,-40,-39,-38,-37,-36,-35,-34,-34,-33,-32,-31,-30,-30,-29,-28,-27,-26,-25,-24,-23,-22,-21,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,30,31,32,33,34,34,35,36,37,38,39,40,41,42,42,43,43,44,45,45,46,46,47,47,48,48,49,49,50,50,51,51,51,52,52,52,53,53,53,53,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,53,53,53,53,52,52,52,51,51,51,50,50,49,49,48,48,47,47,46,46,45,45,44,43,43,42,42,41,40,39,38,37,36,35,34,34,33,32,31,30,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-20,-21,-22,-23,-24,-25,-26,-27,-28,-29,-30,-30,-31,-32,-33,-34,-34,-35,-36,-37,-38,-39,-40,-41,-42,-42,-43,-43,-44,-45,-45,-46,-46,-47,-47,-48,-48,-49,-49,-50,-50,-51,-51,-51,-52,-52,-52,-53,-53,-53,-53,-54,-54,-54,-54,-54,-54,-54,-54,-54,-54,-55,-55,-55,-55,-55,-55,-55,-55,-55,-55,-55,-54,-54,-54,-54,-53,-53,-53,-53,-52,-52,-52,-51,-51,-51,-50,-50,-50,-49,-49,-49,-48,-48,-47,-47,-46,-45,-45,-44,-44,-43,-42,-42,-41,-41,-40,-40,-39,-39,-38,-38,-37,-37,-36,-35,-34,-33,-33,-32,-31,-30,-29,-28,-27,-27,-26,-25,-25,-24,-23,-23,-22,-21,-20,-19,-18,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,18,19,20,21,22,23,23,24,25,25,26,27,27,28,29,30,31,32,33,33,34,35,36,37,37,38,38,39,39,40,40,41,41,42,42,43,44,44,45,45,46,47,47,48,48,49,49,49,50,50,50,51,51,51,52,52,52,53,53,53,53,54,54,54,54,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,54,54,54,54,53,53,53,53,52,52,52,51,51,51,50,50,50,49,49,49,48,48,47,47,46,45,45,44,44,43,42,42,41,41,40,40,39,39,38,38,37,37,36,35,34,33,33,32,31,30,29,28,27,27,26,25,25,24,23,23,22,21,20,19,18,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-18,-19,-20,-21,-22,-23,-23,-24,-25,-25,-26,-27,-27,-28,-29,-30,-31,-32,-33,-33,-34,-35,-36,-37,-37,-38,-38,-39,-39,-40,-40,-41,-41,-42,-42,-43,-44,-44,-45,-45,-46,-47,-47,-48,-48,-49,-49,-49,-50,-50,-50,-51,-51,-51,-52,-52,-52,-53,-53,-53,-53,-54,-54,-54,-54,-55,-55,-55,-55,-55,-55,-55,-55,-55,-55,-56,-56,-56,-56,-56,-56,-56,-56,-56,-56,-56,-55,-55,-55,-55,-54,-54,-54,-54,-53,-53,-52,-52,-52,-51,-51,-50,-50,-49,-49,-48,-48,-47,-47,-46,-46,-45,-44,-44,-43,-43,-42,-41,-40,-39,-38,-37,-36,-36,-35,-34,-33,-32,-32,-31,-30,-29,-29,-28,-27,-26,-25,-24,-23,-22,-21,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,29,30,31,32,32,33,34,35,36,36,37,38,39,40,41,42,43,43,44,44,45,46,46,47,47,48,48,49,49,50,50,51,51,52,52,52,53,53,54,54,54,54,55,55,55,55,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,55,55,55,55,54,54,54,54,53,53,52,52,52,51,51,50,50,49,49,48,48,47,47,46,46,45,44,44,43,43,42,41,40,39,38,37,36,36,35,34,33,32,32,31,30,29,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-20,-21,-22,-23,-24,-25,-26,-27,-28,-29,-29,-30,-31,-32,-32,-33,-34,-35,-36,-36,-37,-38,-39,-40,-41,-42,-43,-43,-44,-44,-45,-46,-46,-47,-47,-48,-48,-49,-49,-50,-50,-51,-51,-52,-52,-52,-53,-53,-54,-54,-54,-54,-55,-55,-55,-55,-56,-56,-56,-56,-56,-56,-56,-56,-56,-56,-57,-57,-57,-57,-57,-57,-57,-57,-57,-57,-57,-56,-56,-56,-56,-56,-55,-55,-55,-55,-54,-54,-54,-53,-53,-53,-52,-52,-51,-51,-50,-50,-49,-49,-48,-48,-47,-46,-46,-45,-45,-44,-43,-42,-41,-41,-40,-39,-38,-37,-36,-35,-35,-34,-33,-32,-31,-31,-30,-29,-28,-27,-26,-25,-24,-23,-22,-21,-21,-20,-19,-18,-17,-16,-15,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,15,16,17,18,19,20,21,21,22,23,24,25,26,27,28,29,30,31,31,32,33,34,35,35,36,37,38,39,40,41,41,42,43,44,45,45,46,46,47,48,48,49,49,50,50,51,51,52,52,53,53,53,54,54,54,55,55,55,55,56,56,56,56,56,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,56,56,56,56,56,55,55,55,55,54,54,54,53,53,53,52,52,51,51,50,50,49,49,48,48,47,46,46,45,45,44,43,42,41,41,40,39,38,37,36,35,35,34,33,32,31,31,30,29,28,27,26,25,24,23,22,21,21,20,19,18,17,16,15,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-15,-16,-17,-18,-19,-20,-21,-21,-22,-23,-24,-25,-26,-27,-28,-29,-30,-31,-31,-32,-33,-34,-35,-35,-36,-37,-38,-39,-40,-41,-41,-42,-43,-44,-45,-45,-46,-46,-47,-48,-48,-49,-49,-50,-50,-51,-51,-52,-52,-53,-53,-53,-54,-54,-54,-55,-55,-55,-55,-56,-56,-56,-56,-56,-57,-57,-57,-57,-57,-57,-57,-57,-57,-57,-58,-58,-58,-58,-58,-58,-58,-58,-58,-58,-58,-57,-57,-57,-57,-57,-56,-56,-56,-55,-55,-55,-54,-54,-53,-53,-52,-52,-51,-51,-50,-50,-49,-48,-48,-47,-47,-46,-45,-45,-44,-44,-43,-43,-42,-42,-41,-40,-40,-39,-39,-38,-38,-37,-36,-35,-34,-34,-33,-32,-31,-30,-29,-28,-27,-26,-25,-24,-23,-22,-21,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,34,35,36,37,38,38,39,39,40,40,41,42,42,43,43,44,44,45,45,46,47,47,48,48,49,50,50,51,51,52,52,53,53,54,54,55,55,55,56,56,56,57,57,57,57,57,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,57,57,57,57,57,56,56,56,55,55,55,54,54,53,53,52,52,51,51,50,50,49,48,48,47,47,46,45,45,44,44,43,43,42,42,41,40,40,39,39,38,38,37,36,35,34,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-20,-21,-22,-23,-24,-25,-26,-27,-28,-29,-30,-31,-32,-33,-34,-34,-35,-36,-37,-38,-38,-39,-39,-40,-40,-41,-42,-42,-43,-43,-44,-44,-45,-45,-46,-47,-47,-48,-48,-49,-50,-50,-51,-51,-52,-52,-53,-53,-54,-54,-55,-55,-55,-56,-56,-56,-57,-57,-57,-57,-57,-58,-58,-58,-58,-58,-58,-58,-58,-58,-58,-59,-59,-59,-59,-59,-59,-59,-59,-59,-59,-59,-58,-58,-58,-58,-58,-57,-57,-57,-56,-56,-56,-55,-55,-54,-54,-54,-53,-53,-53,-52,-52,-51,-51,-50,-50,-49,-49,-48,-47,-47,-46,-46,-45,-44,-43,-42,-41,-40,-39,-38,-37,-37,-36,-35,-34,-33,-33,-32,-31,-30,-29,-28,-28,-27,-26,-26,-25,-24,-23,-22,-21,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,26,27,28,28,29,30,31,32,33,33,34,35,36,37,37,38,39,40,41,42,43,44,45,46,46,47,47,48,49,49,50,50,51,51,52,52,53,53,53,54,54,54,55,55,56,56,56,57,57,57,58,58,58,58,58,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,58,58,58,58,58,57,57,57,56,56,56,55,55,54,54,54,53,53,53,52,52,51,51,50,50,49,49,48,47,47,46,46,45,44,43,42,41,40,39,38,37,37,36,35,34,33,33,32,31,30,29,28,28,27,26,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-20,-21,-22,-23,-24,-25,-26,-26,-27,-28,-28,-29,-30,-31,-32,-33,-33,-34,-35,-36,-37,-37,-38,-39,-40,-41,-42,-43,-44,-45,-46,-46,-47,-47,-48,-49,-49,-50,-50,-51,-51,-52,-52,-53,-53,-53,-54,-54,-54,-55,-55,-56,-56,-56,-57,-57,-57,-58,-58,-58,-58,-58,-59,-59,-59,-59,-59,-59,-59,-59,-59,-59]; var towards_center = [0,0,0,0,0,0,0,0,0,1,1,2,3,3,3,4,5,5,5,6,7,7,7,8,1,9,9,10,12,13,13,13,14,16,17,17,17,18,20,21,21,21,22,24,9,25,25,26,11,29,30,30,30,31,15,34,35,35,35,36,19,39,40,40,40,41,23,44,25,45,45,46,47,27,48,28,49,50,51,51,51,52,53,32,54,33,55,56,57,57,57,58,59,37,60,38,61,62,63,63,63,64,65,42,66,43,67,68,45,69,69,70,71,73,75,77,78,79,79,79,80,81,83,85,87,88,89,89,89,90,91,93,95,97,98,99,99,99,100,101,103,105,107,108,69,109,109,110,111,72,113,74,114,76,116,117,118,118,118,119,120,82,122,84,123,86,125,126,127,127,127,128,129,92,131,94,132,96,134,135,136,136,136,137,138,102,140,104,141,106,143,144,109,145,145,146,147,148,112,149,151,153,115,154,155,156,157,157,157,158,159,160,121,161,163,165,124,166,167,168,169,169,169,170,171,172,130,173,175,177,133,178,179,180,181,181,181,182,183,184,139,185,187,189,142,190,191,192,145,193,193,194,195,196,198,150,200,152,202,204,205,206,207,207,207,208,209,210,212,162,214,164,216,218,219,220,221,221,221,222,223,224,226,174,228,176,230,232,233,234,235,235,235,236,237,238,240,186,242,188,244,246,247,248,193,249,249,250,251,252,197,254,199,255,257,201,258,203,260,261,262,263,263,263,264,265,266,211,268,213,269,271,215,272,217,274,275,276,277,277,277,278,279,280,225,282,227,283,285,229,286,231,288,289,290,291,291,291,292,293,294,239,296,241,297,299,243,300,245,302,303,304,249,305,305,306,307,308,253,310,312,256,315,317,259,319,320,321,322,322,322,323,324,325,267,327,329,270,332,334,273,336,337,338,339,339,339,340,341,342,281,344,346,284,349,351,287,353,354,355,356,356,356,357,358,359,295,361,363,298,366,368,301,370,371,372,305,373,373,374,375,376,309,378,311,380,313,381,314,382,316,384,318,386,387,388,389,389,389,390,391,392,326,394,328,396,330,397,331,398,333,400,335,402,403,404,405,405,405,406,407,408,343,410,345,412,347,413,348,414,350,416,352,418,419,420,421,421,421,422,423,424,360,426,362,428,364,429,365,430,367,432,369,434,435,436,373,437,437,438,439,440,441,377,442,443,379,444,446,448,450,383,451,452,385,453,454,455,456,457,457,457,458,459,460,461,393,462,463,395,464,466,468,470,399,471,472,401,473,474,475,476,477,477,477,478,479,480,481,409,482,483,411,484,486,488,490,415,491,492,417,493,494,495,496,497,497,497,498,499,500,501,425,502,503,427,504,506,508,510,431,511,512,433,513,514,515,516,437,517,517,518,519,520,521,523,524,526,445,528,447,529,449,531,533,534,536,537,538,539,540,540,540,541,542,543,544,546,547,549,465,551,467,552,469,554,556,557,559,560,561,562,563,563,563,564,565,566,567,569,570,572,485,574,487,575,489,577,579,580,582,583,584,585,586,586,586,587,588,589,590,592,593,595,505,597,507,598,509,600,602,603,605,606,607,608,517,609,609,610,611,612,613,522,615,525,617,527,618,620,622,530,623,532,625,535,627,628,629,630,631,631,631,632,633,634,635,545,637,548,639,550,640,642,644,553,645,555,647,558,649,650,651,652,653,653,653,654,655,656,657,568,659,571,661,573,662,664,666,576,667,578,669,581,671,672,673,674,675,675,675,676,677,678,679,591,681,594,683,596,684,686,688,599,689,601,691,604,693,694,695,696,609,697,697,698,699,700,701,614,703,616,705,707,619,709,621,711,713,624,715,626,717,718,719,720,721,721,721,722,723,724,725,636,727,638,729,731,641,733,643,735,737,646,739,648,741,742,743,744,745,745,745,746,747,748,749,658,751,660,753,755,663,757,665,759,761,668,763,670,765,766,767,768,769,769,769,770,771,772,773,680,775,682,777,779,685,781,687,783,785,690,787,692,789,790,791,792,697,793,793,794,795,796,797,702,799,800,704,801,706,803,708,804,806,710,807,712,809,714,810,811,716,813,814,815,816,817,817,817,818,819,820,821,726,823,824,728,825,730,827,732,828,830,734,831,736,833,738,834,835,740,837,838,839,840,841,841,841,842,843,844,845,750,847,848,752,849,754,851,756,852,854,758,855,760,857,762,858,859,764,861,862,863,864,865,865,865,866,867,868,869,774,871,872,776,873,778,875,780,876,878,782,879,784,881,786,882,883,788,885,886,887,888,793,889,889,890,891,892,893,894,798,895,896,898,899,802,900,902,805,905,907,808,908,909,911,912,812,913,914,915,916,917,918,918,918,919,920,921,922,923,822,924,925,927,928,826,929,931,829,934,936,832,937,938,940,941,836,942,943,944,945,946,947,947,947,948,949,950,951,952,846,953,954,956,957,850,958,960,853,963,965,856,966,967,969,970,860,971,972,973,974,975,976,976,976,977,978,979,980,981,870,982,983,985,986,874,987,989,877,992,994,880,995,996,998,999,884,1000,1001,1002,1003,1004,889,1005,1005,1006,1007,1008,1009,1010,1012,1013,897,1015,1017,901,1019,903,1020,904,1021,906,1023,1025,910,1027,1028,1030,1031,1032,1033,1034,1035,1035,1035,1036,1037,1038,1039,1040,1042,1043,926,1045,1047,930,1049,932,1050,933,1051,935,1053,1055,939,1057,1058,1060,1061,1062,1063,1064,1065,1065,1065,1066,1067,1068,1069,1070,1072,1073,955,1075,1077,959,1079,961,1080,962,1081,964,1083,1085,968,1087,1088,1090,1091,1092,1093,1094,1095,1095,1095,1096,1097,1098,1099,1100,1102,1103,984,1105,1107,988,1109,990,1110,991,1111,993,1113,1115,997,1117,1118,1120,1121,1122,1123,1124,1005,1125,1125,1126,1127,1128,1129,1130,1011,1132,1014,1134,1016,1136,1018,1137,1139,1141,1143,1022,1144,1024,1146,1026,1148,1029,1150,1151,1152,1153,1154,1155,1155,1155,1156,1157,1158,1159,1160,1041,1162,1044,1164,1046,1166,1048,1167,1169,1171,1173,1052,1174,1054,1176,1056,1178,1059,1180,1181,1182,1183,1184,1185,1185,1185,1186,1187,1188,1189,1190,1071,1192,1074,1194,1076,1196,1078,1197,1199,1201,1203,1082,1204,1084,1206,1086,1208,1089,1210,1211,1212,1213,1214,1215,1215,1215,1216,1217,1218,1219,1220,1101,1222,1104,1224,1106,1226,1108,1227,1229,1231,1233,1112,1234,1114,1236,1116,1238,1119,1240,1241,1242,1243,1244,1125,1245,1245,1246,1247,1248,1249,1250,1131,1252,1253,1133,1254,1255,1135,1256,1258,1138,1260,1140,1261,1142,1263,1265,1145,1266,1267,1147,1268,1269,1149,1271,1272,1273,1274,1275,1276,1276,1276,1277,1278,1279,1280,1281,1161,1283,1284,1163,1285,1286,1165,1287,1289,1168,1291,1170,1292,1172,1294,1296,1175,1297,1298,1177,1299,1300,1179,1302,1303,1304,1305,1306,1307,1307,1307,1308,1309,1310,1311,1312,1191,1314,1315,1193,1316,1317,1195,1318,1320,1198,1322,1200,1323,1202,1325,1327,1205,1328,1329,1207,1330,1331,1209,1333,1334,1335,1336,1337,1338,1338,1338,1339,1340,1341,1342,1343,1221,1345,1346,1223,1347,1348,1225,1349,1351,1228,1353,1230,1354,1232,1356,1358,1235,1359,1360,1237,1361,1362,1239,1364,1365,1366,1367,1368,1245,1369,1369,1370,1371,1372,1373,1374,1251,1376,1377,1379,1380,1382,1257,1384,1259,1385,1387,1389,1262,1390,1264,1392,1394,1395,1397,1398,1270,1400,1401,1402,1403,1404,1405,1405,1405,1406,1407,1408,1409,1410,1282,1412,1413,1415,1416,1418,1288,1420,1290,1421,1423,1425,1293,1426,1295,1428,1430,1431,1433,1434,1301,1436,1437,1438,1439,1440,1441,1441,1441,1442,1443,1444,1445,1446,1313,1448,1449,1451,1452,1454,1319,1456,1321,1457,1459,1461,1324,1462,1326,1464,1466,1467,1469,1470,1332,1472,1473,1474,1475,1476,1477,1477,1477,1478,1479,1480,1481,1482,1344,1484,1485,1487,1488,1490,1350,1492,1352,1493,1495,1497,1355,1498,1357,1500,1502,1503,1505,1506,1363,1508,1509,1510,1511,1512,1369,1513,1513,1514,1515,1516,1517,1518,1375,1520,1521,1378,1523,1381,1525,1383,1526,1528,1386,1388,1532,1534,1391,1535,1393,1537,1396,1539,1540,1399,1542,1543,1544,1545,1546,1547,1547,1547,1548,1549,1550,1551,1552,1411,1554,1555,1414,1557,1417,1559,1419,1560,1562,1422,1424,1566,1568,1427,1569,1429,1571,1432,1573,1574,1435,1576,1577,1578,1579,1580,1581,1581,1581,1582,1583,1584,1585,1586,1447,1588,1589,1450,1591,1453,1593,1455,1594,1596,1458,1460,1600,1602,1463,1603,1465,1605,1468,1607,1608,1471,1610,1611,1612,1613,1614,1615,1615,1615,1616,1617,1618,1619,1620,1483,1622,1623,1486,1625,1489,1627,1491,1628,1630,1494,1496,1634,1636,1499,1637,1501,1639,1504,1641,1642,1507,1644,1645,1646,1647,1648,1513,1649,1649,1650,1651,1652,1653,1654,1519,1656,1657,1522,1659,1524,1661,1663,1527,1665,1529,1666,1530,1667,1531,1668,1533,1670,1672,1536,1674,1538,1676,1677,1541,1679,1680,1681,1682,1683,1684,1684,1684,1685,1686,1687,1688,1689,1553,1691,1692,1556,1694,1558,1696,1698,1561,1700,1563,1701,1564,1702,1565,1703,1567,1705,1707,1570,1709,1572,1711,1712,1575,1714,1715,1716,1717,1718,1719,1719,1719,1720,1721,1722,1723,1724,1587,1726,1727,1590,1729,1592,1731,1733,1595,1735,1597,1736,1598,1737,1599,1738,1601,1740,1742,1604,1744,1606,1746,1747,1609,1749,1750,1751,1752,1753,1754,1754,1754,1755,1756,1757,1758,1759,1621,1761,1762,1624,1764,1626,1766,1768,1629,1770,1631,1771,1632,1772,1633,1773,1635,1775,1777,1638,1779,1640,1781,1782,1643,1784,1785,1786,1787,1788,1649,1789,1789,1790,1791,1792,1793,1794,1795,1655,1796,1797,1658,1799,1800,1660,1801,1662,1803,1664,1804,1806,1808,1810,1812,1669,1813,1671,1815,1673,1816,1817,1675,1819,1820,1678,1821,1822,1823,1824,1825,1826,1827,1827,1827,1828,1829,1830,1831,1832,1833,1690,1834,1835,1693,1837,1838,1695,1839,1697,1841,1699,1842,1844,1846,1848,1850,1704,1851,1706,1853,1708,1854,1855,1710,1857,1858,1713,1859,1860,1861,1862,1863,1864,1865,1865,1865,1866,1867,1868,1869,1870,1871,1725,1872,1873,1728,1875,1876,1730,1877,1732,1879,1734,1880,1882,1884,1886,1888,1739,1889,1741,1891,1743,1892,1893,1745,1895,1896,1748,1897,1898,1899,1900,1901,1902,1903,1903,1903,1904,1905,1906,1907,1908,1909,1760,1910,1911,1763,1913,1914,1765,1915,1767,1917,1769,1918,1920,1922,1924,1926,1774,1927,1776,1929,1778,1930,1931,1780,1933,1934,1783,1935,1936,1937,1938,1939,1940,1789,1941,1941,1942,1943,1944,1945,1946,1947,1949,1950,1951,1798,1952,1953,1955,1956,1802,1957,1959,1805,1961,1807,1962,1809,1963,1811,1965,1967,1814,1968,1969,1971,1972,1818,1973,1974,1975,1977,1978,1979,1980,1981,1982,1983,1983,1983,1984,1985,1986,1987,1988,1989,1991,1992,1993,1836,1994,1995,1997,1998,1840,1999,2001,1843,2003,1845,2004,1847,2005,1849,2007,2009,1852,2010,2011,2013,2014,1856,2015,2016,2017,2019,2020,2021,2022,2023,2024,2025,2025,2025,2026,2027,2028,2029,2030,2031,2033,2034,2035,1874,2036,2037,2039,2040,1878,2041,2043,1881,2045,1883,2046,1885,2047,1887,2049,2051,1890,2052,2053,2055,2056,1894,2057,2058,2059,2061,2062,2063,2064,2065,2066,2067,2067,2067,2068,2069,2070,2071,2072,2073,2075,2076,2077,1912,2078,2079,2081,2082,1916,2083,2085,1919,2087,1921,2088,1923,2089,1925,2091,2093,1928,2094,2095,2097,2098,1932,2099,2100,2101,2103,2104,2105,2106,2107,2108,1941,2109,2109,2110,2111,2112,2113,2114,2115,1948,2117,2118,2120,2121,1954,2123,2125,1958,2127,1960,2128,2130,2132,2134,1964,2135,1966,2137,2139,1970,2141,2142,2144,2145,1976,2147,2148,2149,2150,2151,2152,2153,2153,2153,2154,2155,2156,2157,2158,2159,1990,2161,2162,2164,2165,1996,2167,2169,2000,2171,2002,2172,2174,2176,2178,2006,2179,2008,2181,2183,2012,2185,2186,2188,2189,2018,2191,2192,2193,2194,2195,2196,2197,2197,2197,2198,2199,2200,2201,2202,2203,2032,2205,2206,2208,2209,2038,2211,2213,2042,2215,2044,2216,2218,2220,2222,2048,2223,2050,2225,2227,2054,2229,2230,2232,2233,2060,2235,2236,2237,2238,2239,2240,2241,2241,2241,2242,2243,2244,2245,2246,2247,2074,2249,2250,2252,2253,2080,2255,2257,2084,2259,2086,2260,2262,2264,2266,2090,2267,2092,2269,2271,2096,2273,2274,2276,2277,2102,2279,2280,2281,2282,2283,2284,2109,2285,2285,2286,2287,2288,2289,2290,2291,2116,2293,2294,2119,2296,2122,2298,2124,2300,2126,2301,2303,2129,2131,2133,2308,2310,2136,2311,2138,2313,2140,2315,2143,2317,2318,2146,2320,2321,2322,2323,2324,2325,2326,2326,2326,2327,2328,2329,2330,2331,2332,2160,2334,2335,2163,2337,2166,2339,2168,2341,2170,2342,2344,2173,2175,2177,2349,2351,2180,2352,2182,2354,2184,2356,2187,2358,2359,2190,2361,2362,2363,2364,2365,2366,2367,2367,2367,2368,2369,2370,2371,2372,2373,2204,2375,2376,2207,2378,2210,2380,2212,2382,2214,2383,2385,2217,2219,2221,2390,2392,2224,2393,2226,2395,2228,2397,2231,2399,2400,2234,2402,2403,2404,2405,2406,2407,2408,2408,2408,2409,2410,2411,2412,2413,2414,2248,2416,2417,2251,2419,2254,2421,2256,2423,2258,2424,2426,2261,2263,2265,2431,2433,2268,2434,2270,2436,2272,2438,2275,2440,2441,2278,2443,2444,2445,2446,2447,2448,2285,2449,2449,2450,2451,2452,2453,2454,2455,2292,2457,2458,2295,2460,2461,2297,2462,2299,2464,2466,2302,2468,2304,2469,2305,2470,2306,2471,2307,2472,2309,2474,2476,2312,2478,2314,2479,2480,2316,2482,2483,2319,2485,2486,2487,2488,2489,2490,2491,2491,2491,2492,2493,2494,2495,2496,2497,2333,2499,2500,2336,2502,2503,2338,2504,2340,2506,2508,2343,2510,2345,2511,2346,2512,2347,2513,2348,2514,2350,2516,2518,2353,2520,2355,2521,2522,2357,2524,2525,2360,2527,2528,2529,2530,2531,2532,2533,2533,2533,2534,2535,2536,2537,2538,2539,2374,2541,2542,2377,2544,2545,2379,2546,2381,2548,2550,2384,2552,2386,2553,2387,2554,2388,2555,2389,2556,2391,2558,2560,2394,2562,2396,2563,2564,2398,2566,2567,2401,2569,2570,2571,2572,2573,2574,2575,2575,2575,2576,2577,2578,2579,2580,2581,2415,2583,2584,2418,2586,2587,2420,2588,2422,2590,2592,2425,2594,2427,2595,2428,2596,2429,2597,2430,2598,2432,2600,2602,2435,2604,2437,2605,2606,2439,2608,2609,2442,2611,2612,2613,2614,2615,2616,2449,2617,2617,2618,2619,2620,2621,2622,2623,2456,2625,2626,2459,2628,2629,2631,2632,2463,2633,2465,2635,2467,2636,2638,2640,2642,2644,2646,2473,2647,2475,2649,2477,2650,2651,2653,2654,2481,2656,2657,2484,2659,2660,2661,2662,2663,2664,2665,2665,2665,2666,2667,2668,2669,2670,2671,2498,2673,2674,2501,2676,2677,2679,2680,2505,2681,2507,2683,2509,2684,2686,2688,2690,2692,2694,2515,2695,2517,2697,2519,2698,2699,2701,2702,2523,2704,2705,2526,2707,2708,2709,2710,2711,2712,2713,2713,2713,2714,2715,2716,2717,2718,2719,2540,2721,2722,2543,2724,2725,2727,2728,2547,2729,2549,2731,2551,2732,2734,2736,2738,2740,2742,2557,2743,2559,2745,2561,2746,2747,2749,2750,2565,2752,2753,2568,2755,2756,2757,2758,2759,2760,2761,2761,2761,2762,2763,2764,2765,2766,2767,2582,2769,2770,2585,2772,2773,2775,2776,2589,2777,2591,2779,2593,2780,2782,2784,2786,2788,2790,2599,2791,2601,2793,2603,2794,2795,2797,2798,2607,2800,2801,2610,2803,2804,2805,2806,2807,2808,2617,2809,2809,2810,2811,2812,2813,2814,2815,2624,2817,2818,2819,2627,2820,2821,2630,2823,2825,2826,2634,2827,2829,2637,2831,2639,2832,2641,2833,2643,2834,2645,2836,2838,2648,2839,2840,2842,2652,2844,2845,2655,2846,2847,2848,2658,2850,2851,2852,2853,2854,2855,2856,2856,2856,2857,2858,2859,2860,2861,2862,2672,2864,2865,2866,2675,2867,2868,2678,2870,2872,2873,2682,2874,2876,2685,2878,2687,2879,2689,2880,2691,2881,2693,2883,2885,2696,2886,2887,2889,2700,2891,2892,2703,2893,2894,2895,2706,2897,2898,2899,2900,2901,2902,2903,2903,2903,2904,2905,2906,2907,2908,2909,2720,2911,2912,2913,2723,2914,2915,2726,2917,2919,2920,2730,2921,2923,2733,2925,2735,2926,2737,2927,2739,2928,2741,2930,2932,2744,2933,2934,2936,2748,2938,2939,2751,2940,2941,2942,2754,2944,2945,2946,2947,2948,2949,2950,2950,2950,2951,2952,2953,2954,2955,2956,2768,2958,2959,2960,2771,2961,2962,2774,2964,2966,2967,2778,2968,2970,2781,2972,2783,2973,2785,2974,2787,2975,2789,2977,2979,2792,2980,2981,2983,2796,2985,2986,2799,2987,2988,2989,2802,2991,2992,2993,2994,2995,2996,2809,2997,2997,2998,2999,3000,3001,3002,3003,3004,2816,3005,3006,3007,3009,3010,2822,3012,2824,3014,3016,2828,3018,2830,3019,3021,3023,3025,3027,2835,3028,2837,3030,3032,2841,3034,2843,3036,3037,3039,3040,3041,2849,3042,3043,3044,3045,3046,3047,3048,3049,3049,3049,3050,3051,3052,3053,3054,3055,3056,2863,3057,3058,3059,3061,3062,2869,3064,2871,3066,3068,2875,3070,2877,3071,3073,3075,3077,3079,2882,3080,2884,3082,3084,2888,3086,2890,3088,3089,3091,3092,3093,2896,3094,3095,3096,3097,3098,3099,3100,3101,3101,3101,3102,3103,3104,3105,3106,3107,3108,2910,3109,3110,3111,3113,3114,2916,3116,2918,3118,3120,2922,3122,2924,3123,3125,3127,3129,3131,2929,3132,2931,3134,3136,2935,3138,2937,3140,3141,3143,3144,3145,2943,3146,3147,3148,3149,3150,3151,3152,3153,3153,3153,3154,3155,3156,3157,3158,3159,3160,2957,3161,3162,3163,3165,3166,2963,3168,2965,3170,3172,2969,3174,2971,3175,3177,3179,3181,3183,2976,3184,2978,3186,3188,2982,3190,2984,3192,3193,3195,3196,3197,2990,3198,3199,3200,3201,3202,3203,3204,2997,3205,3205,3206,3207,3208,3209,3210,3211,3212,3214,3215,3216,3008,3218,3011,3220,3013,3222,3015,3224,3017,3225,3227,3020,3229,3022,3230,3024,3231,3026,3233,3235,3029,3236,3031,3238,3033,3240,3035,3242,3038,3244,3245,3246,3248,3249,3250,3251,3252,3253,3254,3255,3255,3255,3256,3257,3258,3259,3260,3261,3262,3264,3265,3266,3060,3268,3063,3270,3065,3272,3067,3274,3069,3275,3277,3072,3279,3074,3280,3076,3281,3078,3283,3285,3081,3286,3083,3288,3085,3290,3087,3292,3090,3294,3295,3296,3298,3299,3300,3301,3302,3303,3304,3305,3305,3305,3306,3307,3308,3309,3310,3311,3312,3314,3315,3316,3112,3318,3115,3320,3117,3322,3119,3324,3121,3325,3327,3124,3329,3126,3330,3128,3331,3130,3333,3335,3133,3336,3135,3338,3137,3340,3139,3342,3142,3344,3345,3346,3348,3349,3350,3351,3352,3353,3354,3355,3355,3355,3356,3357,3358,3359,3360,3361,3362,3364,3365,3366,3164,3368,3167,3370,3169,3372,3171,3374,3173,3375,3377,3176,3379,3178,3380,3180,3381,3182,3383,3385,3185,3386,3187,3388,3189,3390,3191,3392,3194,3394,3395,3396,3398,3399,3400,3401,3402,3403,3404,3205,3405,3405,3406,3407,3408,3409,3410,3411,3412,3213,3414,3415,3217,3417,3418,3219,3419,3420,3221,3421,3422,3223,3423,3425,3226,3427,3228,3428,3430,3432,3434,3232,3435,3234,3437,3439,3237,3440,3441,3239,3442,3443,3241,3444,3445,3243,3447,3448,3247,3450,3451,3452,3453,3454,3455,3456,3457,3457,3457,3458,3459,3460,3461,3462,3463,3464,3263,3466,3467,3267,3469,3470,3269,3471,3472,3271,3473,3474,3273,3475,3477,3276,3479,3278,3480,3482,3484,3486,3282,3487,3284,3489,3491,3287,3492,3493,3289,3494,3495,3291,3496,3497,3293,3499,3500,3297,3502,3503,3504,3505,3506,3507,3508,3509,3509,3509,3510,3511,3512,3513,3514,3515,3516,3313,3518,3519,3317,3521,3522,3319,3523,3524,3321,3525,3526,3323,3527,3529,3326,3531,3328,3532,3534,3536,3538,3332,3539,3334,3541,3543,3337,3544,3545,3339,3546,3547,3341,3548,3549,3343,3551,3552,3347,3554,3555,3556,3557,3558,3559,3560,3561,3561,3561,3562,3563,3564,3565,3566,3567,3568,3363,3570,3571,3367,3573,3574,3369,3575,3576,3371,3577,3578,3373,3579,3581,3376,3583,3378,3584,3586,3588,3590,3382,3591,3384,3593,3595,3387,3596,3597,3389,3598,3599,3391,3600,3601,3393,3603,3604,3397,3606,3607,3608,3609,3610,3611,3612,3405,3613,3613,3614,3615,3616,3617,3618,3619,3620,3413,3622,3623,3416,3625,3626,3628,3629,3631,3632,3634,3424,3636,3426,3637,3639,3429,3431,3433,3644,3646,3436,3647,3438,3649,3651,3652,3654,3655,3657,3658,3446,3660,3661,3449,3663,3664,3665,3666,3667,3668,3669,3670,3670,3670,3671,3672,3673,3674,3675,3676,3677,3465,3679,3680,3468,3682,3683,3685,3686,3688,3689,3691,3476,3693,3478,3694,3696,3481,3483,3485,3701,3703,3488,3704,3490,3706,3708,3709,3711,3712,3714,3715,3498,3717,3718,3501,3720,3721,3722,3723,3724,3725,3726,3727,3727,3727,3728,3729,3730,3731,3732,3733,3734,3517,3736,3737,3520,3739,3740,3742,3743,3745,3746,3748,3528,3750,3530,3751,3753,3533,3535,3537,3758,3760,3540,3761,3542,3763,3765,3766,3768,3769,3771,3772,3550,3774,3775,3553,3777,3778,3779,3780,3781,3782,3783,3784,3784,3784,3785,3786,3787,3788,3789,3790,3791,3569,3793,3794,3572,3796,3797,3799,3800,3802,3803,3805,3580,3807,3582,3808,3810,3585,3587,3589,3815,3817,3592,3818,3594,3820,3822,3823,3825,3826,3828,3829,3602,3831,3832,3605,3834,3835,3836,3837,3838,3839,3840,3613,3841,3841,3842,3843,3844,3845,3846,3847,3848,3621,3850,3851,3624,3853,3854,3627,3856,3630,3858,3633,3860,3635,3861,3863,3638,3865,3640,3866,3641,3867,3642,3868,3643,3869,3645,3871,3873,3648,3874,3650,3876,3653,3878,3656,3880,3881,3659,3883,3884,3662,3886,3887,3888,3889,3890,3891,3892,3893,3893,3893,3894,3895,3896,3897,3898,3899,3900,3678,3902,3903,3681,3905,3906,3684,3908,3687,3910,3690,3912,3692,3913,3915,3695,3917,3697,3918,3698,3919,3699,3920,3700,3921,3702,3923,3925,3705,3926,3707,3928,3710,3930,3713,3932,3933,3716,3935,3936,3719,3938,3939,3940,3941,3942,3943,3944,3945,3945,3945,3946,3947,3948,3949,3950,3951,3952,3735,3954,3955,3738,3957,3958,3741,3960,3744,3962,3747,3964,3749,3965,3967,3752,3969,3754,3970,3755,3971,3756,3972,3757,3973,3759,3975,3977,3762,3978,3764,3980,3767,3982,3770,3984,3985,3773,3987,3988,3776,3990,3991,3992,3993,3994,3995,3996,3997,3997,3997,3998,3999,4000,4001,4002,4003,4004,3792,4006,4007,3795,4009,4010,3798,4012,3801,4014,3804,4016,3806,4017,4019,3809,4021,3811,4022,3812,4023,3813,4024,3814,4025,3816,4027,4029,3819,4030,3821,4032,3824,4034,3827,4036,4037,3830,4039,4040,3833,4042,4043,4044,4045,4046,4047,4048,3841,4049,4049,4050,4051,4052,4053,4054,4055,4056,3849,4058,4059,4060,3852,4061,4062,3855,4064,3857,4066,3859,4068,4070,3862,4072,3864,4073,4075,4077,4079,4081,4083,3870,4084,3872,4086,4088,3875,4090,3877,4092,3879,4094,4095,3882,4096,4097,4098,3885,4100,4101,4102,4103,4104,4105,4106,4107,4107,4107,4108,4109,4110,4111,4112,4113,4114,3901,4116,4117,4118,3904,4119,4120,3907,4122,3909,4124,3911,4126,4128,3914,4130,3916,4131,4133,4135,4137,4139,4141,3922,4142,3924,4144,4146,3927,4148,3929,4150,3931,4152,4153,3934,4154,4155,4156,3937,4158,4159,4160,4161,4162,4163,4164,4165,4165,4165,4166,4167,4168,4169,4170,4171,4172,3953,4174,4175,4176,3956,4177,4178,3959,4180,3961,4182,3963,4184,4186,3966,4188,3968,4189,4191,4193,4195,4197,4199,3974,4200,3976,4202,4204,3979,4206,3981,4208,3983,4210,4211,3986,4212,4213,4214,3989,4216,4217,4218,4219,4220,4221,4222,4223,4223,4223,4224,4225,4226,4227,4228,4229,4230,4005,4232,4233,4234,4008,4235,4236,4011,4238,4013,4240,4015,4242,4244,4018,4246,4020,4247,4249,4251,4253,4255,4257,4026,4258,4028,4260,4262,4031,4264,4033,4266,4035,4268,4269,4038,4270,4271,4272,4041,4274,4275,4276,4277,4278,4279,4280,4049,4281,4281,4282,4283,4284,4285,4286,4287,4288,4057,4290,4291,4292,4294,4295,4063,4297,4298,4065,4299,4300,4067,4301,4069,4303,4071,4304,4306,4074,4308,4076,4309,4078,4310,4080,4311,4082,4313,4315,4085,4316,4087,4318,4089,4319,4320,4091,4321,4322,4093,4324,4325,4327,4328,4329,4099,4331,4332,4333,4334,4335,4336,4337,4338,4338,4338,4339,4340,4341,4342,4343,4344,4345,4115,4347,4348,4349,4351,4352,4121,4354,4355,4123,4356,4357,4125,4358,4127,4360,4129,4361,4363,4132,4365,4134,4366,4136,4367,4138,4368,4140,4370,4372,4143,4373,4145,4375,4147,4376,4377,4149,4378,4379,4151,4381,4382,4384,4385,4386,4157,4388,4389,4390,4391,4392,4393,4394,4395,4395,4395,4396,4397,4398,4399,4400,4401,4402,4173,4404,4405,4406,4408,4409,4179,4411,4412,4181,4413,4414,4183,4415,4185,4417,4187,4418,4420,4190,4422,4192,4423,4194,4424,4196,4425,4198,4427,4429,4201,4430,4203,4432,4205,4433,4434,4207,4435,4436,4209,4438,4439,4441,4442,4443,4215,4445,4446,4447,4448,4449,4450,4451,4452,4452,4452,4453,4454,4455,4456,4457,4458,4459,4231,4461,4462,4463,4465,4466,4237,4468,4469,4239,4470,4471,4241,4472,4243,4474,4245,4475,4477,4248,4479,4250,4480,4252,4481,4254,4482,4256,4484,4486,4259,4487,4261,4489,4263,4490,4491,4265,4492,4493,4267,4495,4496,4498,4499,4500,4273,4502,4503,4504,4505,4506,4507,4508,4281,4509,4509,4510,4511,4512,4513,4514,4515,4516,4289,4518,4519,4520,4293,4522,4523,4296,4524,4525,4527,4528,4530,4531,4302,4532,4534,4305,4536,4307,4537,4539,4541,4543,4545,4312,4546,4314,4548,4550,4317,4551,4552,4554,4555,4557,4558,4323,4559,4560,4326,4562,4563,4564,4330,4566,4567,4568,4569,4570,4571,4572,4573,4573,4573,4574,4575,4576,4577,4578,4579,4580,4346,4582,4583,4584,4350,4586,4587,4353,4588,4589,4591,4592,4594,4595,4359,4596,4598,4362,4600,4364,4601,4603,4605,4607,4609,4369,4610,4371,4612,4614,4374,4615,4616,4618,4619,4621,4622,4380,4623,4624,4383,4626,4627,4628,4387,4630,4631,4632,4633,4634,4635,4636,4637,4637,4637,4638,4639,4640,4641,4642,4643,4644,4403,4646,4647,4648,4407,4650,4651,4410,4652,4653,4655,4656,4658,4659,4416,4660,4662,4419,4664,4421,4665,4667,4669,4671,4673,4426,4674,4428,4676,4678,4431,4679,4680,4682,4683,4685,4686,4437,4687,4688,4440,4690,4691,4692,4444,4694,4695,4696,4697,4698,4699,4700,4701,4701,4701,4702,4703,4704,4705,4706,4707,4708,4460,4710,4711,4712,4464,4714,4715,4467,4716,4717,4719,4720,4722,4723,4473,4724,4726,4476,4728,4478,4729,4731,4733,4735,4737,4483,4738,4485,4740,4742,4488,4743,4744,4746,4747,4749,4750,4494,4751,4752,4497,4754,4755,4756,4501,4758,4759,4760,4761,4762,4763,4764,4509,4765,4765,4766,4767,4768,4769,4770,4771,4772,4517,4774,4775,4776,4521,4778,4779,4781,4782,4526,4784,4529,4786,4788,4533,4790,4535,4791,4793,4538,4540,4542,4544,4799,4801,4547,4802,4549,4804,4806,4553,4808,4556,4810,4811,4813,4814,4561,4816,4817,4818,4565,4820,4821,4822,4823,4824,4825,4826,4827,4827,4827,4828,4829,4830,4831,4832,4833,4834,4581,4836,4837,4838,4585,4840,4841,4843,4844,4590,4846,4593,4848,4850,4597,4852,4599,4853,4855,4602,4604,4606,4608,4861,4863,4611,4864,4613,4866,4868,4617,4870,4620,4872,4873,4875,4876,4625,4878,4879,4880,4629,4882,4883,4884,4885,4886,4887,4888,4889,4889,4889,4890,4891,4892,4893,4894,4895,4896,4645,4898,4899,4900,4649,4902,4903,4905,4906,4654,4908,4657,4910,4912,4661,4914,4663,4915,4917,4666,4668,4670,4672,4923,4925,4675,4926,4677,4928,4930,4681,4932,4684,4934,4935,4937,4938,4689,4940,4941,4942,4693,4944,4945,4946,4947,4948,4949,4950,4951,4951,4951,4952,4953,4954,4955,4956,4957,4958,4709,4960,4961,4962,4713,4964,4965,4967,4968,4718,4970,4721,4972,4974,4725,4976,4727,4977,4979,4730,4732,4734,4736,4985,4987,4739,4988,4741,4990,4992,4745,4994,4748,4996,4997,4999,5000,4753,5002,5003,5004,4757,5006,5007,5008,5009,5010,5011,5012,4765,5013,5013,5014,5015,5016,5017,5018,5019,5020,5021,4773,5022,5023,5024,4777,5026,5027,4780,5029,4783,5031,4785,5033,4787,5035,4789,5036,5038,4792,5040,4794,5041,4795,5042,4796,5043,4797,5044,4798,5045,4800,5047,5049,4803,5050,4805,5052,4807,5054,4809,5056,4812,5058,5059,4815,5061,5062,5063,4819,5064,5065,5066,5067,5068,5069,5070,5071,5072,5072,5072,5073,5074,5075,5076,5077,5078,5079,5080,4835,5081,5082,5083,4839,5085,5086,4842,5088,4845,5090,4847,5092,4849,5094,4851,5095,5097,4854,5099,4856,5100,4857,5101,4858,5102,4859,5103,4860,5104,4862,5106,5108,4865,5109,4867,5111,4869,5113,4871,5115,4874,5117,5118,4877,5120,5121,5122,4881,5123,5124,5125,5126,5127,5128,5129,5130,5131,5131,5131,5132,5133,5134,5135,5136,5137,5138,5139,4897,5140,5141,5142,4901,5144,5145,4904,5147,4907,5149,4909,5151,4911,5153,4913,5154,5156,4916,5158,4918,5159,4919,5160,4920,5161,4921,5162,4922,5163,4924,5165,5167,4927,5168,4929,5170,4931,5172,4933,5174,4936,5176,5177,4939,5179,5180,5181,4943,5182,5183,5184,5185,5186,5187,5188,5189,5190,5190,5190,5191,5192,5193,5194,5195,5196,5197,5198,4959,5199,5200,5201,4963,5203,5204,4966,5206,4969,5208,4971,5210,4973,5212,4975,5213,5215,4978,5217,4980,5218,4981,5219,4982,5220,4983,5221,4984,5222,4986,5224,5226,4989,5227,4991,5229,4993,5231,4995,5233,4998,5235,5236,5001,5238,5239,5240,5005,5241,5242,5243,5244,5245,5246,5247,5248,5013,5249,5249,5250,5251,5252,5253,5254,5255,5256,5257,5259,5260,5261,5025,5263,5264,5028,5266,5267,5030,5268,5269,5032,5270,5034,5272,5274,5037,5276,5039,5277,5279,5281,5283,5285,5287,5289,5046,5290,5048,5292,5294,5051,5296,5053,5297,5298,5055,5299,5300,5057,5302,5303,5060,5305,5306,5307,5309,5310,5311,5312,5313,5314,5315,5316,5317,5317,5317,5318,5319,5320,5321,5322,5323,5324,5325,5327,5328,5329,5084,5331,5332,5087,5334,5335,5089,5336,5337,5091,5338,5093,5340,5342,5096,5344,5098,5345,5347,5349,5351,5353,5355,5357,5105,5358,5107,5360,5362,5110,5364,5112,5365,5366,5114,5367,5368,5116,5370,5371,5119,5373,5374,5375,5377,5378,5379,5380,5381,5382,5383,5384,5385,5385,5385,5386,5387,5388,5389,5390,5391,5392,5393,5395,5396,5397,5143,5399,5400,5146,5402,5403,5148,5404,5405,5150,5406,5152,5408,5410,5155,5412,5157,5413,5415,5417,5419,5421,5423,5425,5164,5426,5166,5428,5430,5169,5432,5171,5433,5434,5173,5435,5436,5175,5438,5439,5178,5441,5442,5443,5445,5446,5447,5448,5449,5450,5451,5452,5453,5453,5453,5454,5455,5456,5457,5458,5459,5460,5461,5463,5464,5465,5202,5467,5468,5205,5470,5471,5207,5472,5473,5209,5474,5211,5476,5478,5214,5480,5216,5481,5483,5485,5487,5489,5491,5493,5223,5494,5225,5496,5498,5228,5500,5230,5501,5502,5232,5503,5504,5234,5506,5507,5237,5509,5510,5511,5513,5514,5515,5516,5517,5518,5519,5520,5249,5521,5521,5522,5523,5524,5525,5526,5527,5528,5529,5258,5531,5532,5533,5262,5534,5535,5265,5537,5538,5540,5541,5543,5544,5271,5545,5273,5547,5275,5548,5550,5278,5552,5280,5553,5282,5554,5284,5555,5286,5556,5288,5558,5560,5291,5561,5293,5563,5295,5564,5565,5567,5568,5570,5571,5301,5573,5574,5304,5575,5576,5577,5308,5579,5580,5581,5582,5583,5584,5585,5586,5587,5587,5587,5588,5589,5590,5591,5592,5593,5594,5595,5326,5597,5598,5599,5330,5600,5601,5333,5603,5604,5606,5607,5609,5610,5339,5611,5341,5613,5343,5614,5616,5346,5618,5348,5619,5350,5620,5352,5621,5354,5622,5356,5624,5626,5359,5627,5361,5629,5363,5630,5631,5633,5634,5636,5637,5369,5639,5640,5372,5641,5642,5643,5376,5645,5646,5647,5648,5649,5650,5651,5652,5653,5653,5653,5654,5655,5656,5657,5658,5659,5660,5661,5394,5663,5664,5665,5398,5666,5667,5401,5669,5670,5672,5673,5675,5676,5407,5677,5409,5679,5411,5680,5682,5414,5684,5416,5685,5418,5686,5420,5687,5422,5688,5424,5690,5692,5427,5693,5429,5695,5431,5696,5697,5699,5700,5702,5703,5437,5705,5706,5440,5707,5708,5709,5444,5711,5712,5713,5714,5715,5716,5717,5718,5719,5719,5719,5720,5721,5722,5723,5724,5725,5726,5727,5462,5729,5730,5731,5466,5732,5733,5469,5735,5736,5738,5739,5741,5742,5475,5743,5477,5745,5479,5746,5748,5482,5750,5484,5751,5486,5752,5488,5753,5490,5754,5492,5756,5758,5495,5759,5497,5761,5499,5762,5763,5765,5766,5768,5769,5505,5771,5772,5508,5773,5774,5775,5512,5777,5778,5779,5780,5781,5782,5783,5784,5521,5785,5785,5786,5787,5788,5789,5790,5791,5792,5793,5530,5795,5796,5797,5799,5800,5801,5536,5802,5803,5539,5805,5542,5807,5809,5810,5546,5811,5813,5549,5815,5551,5816,5818,5820,5822,5824,5826,5557,5827,5559,5829,5831,5562,5832,5833,5835,5566,5837,5569,5839,5840,5572,5841,5842,5843,5845,5846,5847,5578,5849,5850,5851,5852,5853,5854,5855,5856,5857,5857,5857,5858,5859,5860,5861,5862,5863,5864,5865,5596,5867,5868,5869,5871,5872,5873,5602,5874,5875,5605,5877,5608,5879,5881,5882,5612,5883,5885,5615,5887,5617,5888,5890,5892,5894,5896,5898,5623,5899,5625,5901,5903,5628,5904,5905,5907,5632,5909,5635,5911,5912,5638,5913,5914,5915,5917,5918,5919,5644,5921,5922,5923,5924,5925,5926,5927,5928,5929,5929,5929,5930,5931,5932,5933,5934,5935,5936,5937,5662,5939,5940,5941,5943,5944,5945,5668,5946,5947,5671,5949,5674,5951,5953,5954,5678,5955,5957,5681,5959,5683,5960,5962,5964,5966,5968,5970,5689,5971,5691,5973,5975,5694,5976,5977,5979,5698,5981,5701,5983,5984,5704,5985,5986,5987,5989,5990,5991,5710,5993,5994,5995,5996,5997,5998,5999,6000,6001,6001,6001,6002,6003,6004,6005,6006,6007,6008,6009,5728,6011,6012,6013,6015,6016,6017,5734,6018,6019,5737,6021,5740,6023,6025,6026,5744,6027,6029,5747,6031,5749,6032,6034,6036,6038,6040,6042,5755,6043,5757,6045,6047,5760,6048,6049,6051,5764,6053,5767,6055,6056,5770,6057,6058,6059,6061,6062,6063,5776,6065,6066,6067,6068,6069,6070,6071,6072,5785,6073,6073,6074,6075,6076,6077,6078,6079,6080,6081,5794,6083,6084,6085,5798,6087,6088,6090,6091,5804,6093,5806,6095,5808,6097,6099,5812,6101,5814,6102,6104,5817,5819,6107,5821,6108,5823,5825,6111,6113,5828,6114,5830,6116,6118,5834,6120,5836,6122,5838,6124,6125,6127,6128,5844,6130,6131,6132,5848,6134,6135,6136,6137,6138,6139,6140,6141,6142,6142,6142,6143,6144,6145,6146,6147,6148,6149,6150,5866,6152,6153,6154,5870,6156,6157,6159,6160,5876,6162,5878,6164,5880,6166,6168,5884,6170,5886,6171,6173,5889,5891,6176,5893,6177,5895,5897,6180,6182,5900,6183,5902,6185,6187,5906,6189,5908,6191,5910,6193,6194,6196,6197,5916,6199,6200,6201,5920,6203,6204,6205,6206,6207,6208,6209,6210,6211,6211,6211,6212,6213,6214,6215,6216,6217,6218,6219,5938,6221,6222,6223,5942,6225,6226,6228,6229,5948,6231,5950,6233,5952,6235,6237,5956,6239,5958,6240,6242,5961,5963,6245,5965,6246,5967,5969,6249,6251,5972,6252,5974,6254,6256,5978,6258,5980,6260,5982,6262,6263,6265,6266,5988,6268,6269,6270,5992,6272,6273,6274,6275,6276,6277,6278,6279,6280,6280,6280,6281,6282,6283,6284,6285,6286,6287,6288,6010,6290,6291,6292,6014,6294,6295,6297,6298,6020,6300,6022,6302,6024,6304,6306,6028,6308,6030,6309,6311,6033,6035,6314,6037,6315,6039,6041,6318,6320,6044,6321,6046,6323,6325,6050,6327,6052,6329,6054,6331,6332,6334,6335,6060,6337,6338,6339,6064,6341,6342,6343,6344,6345,6346,6347,6348,6073,6349,6349,6350,6351,6352,6353,6354,6355,6356,6357,6082,6359,6360,6361,6086,6363,6364,6089,6366,6092,6368,6369,6094,6370,6096,6372,6098,6374,6100,6375,6377,6103,6379,6105,6380,6106,6381,6383,6385,6109,6386,6110,6387,6112,6389,6391,6115,6392,6117,6394,6119,6396,6121,6397,6398,6123,6400,6126,6402,6403,6129,6405,6406,6407,6133,6409,6410,6411,6412,6413,6414,6415,6416,6417,6417,6417,6418,6419,6420,6421,6422,6423,6424,6425,6151,6427,6428,6429,6155,6431,6432,6158,6434,6161,6436,6437,6163,6438,6165,6440,6167,6442,6169,6443,6445,6172,6447,6174,6448,6175,6449,6451,6453,6178,6454,6179,6455,6181,6457,6459,6184,6460,6186,6462,6188,6464,6190,6465,6466,6192,6468,6195,6470,6471,6198,6473,6474,6475,6202,6477,6478,6479,6480,6481,6482,6483,6484,6485,6485,6485,6486,6487,6488,6489,6490,6491,6492,6493,6220,6495,6496,6497,6224,6499,6500,6227,6502,6230,6504,6505,6232,6506,6234,6508,6236,6510,6238,6511,6513,6241,6515,6243,6516,6244,6517,6519,6521,6247,6522,6248,6523,6250,6525,6527,6253,6528,6255,6530,6257,6532,6259,6533,6534,6261,6536,6264,6538,6539,6267,6541,6542,6543,6271,6545,6546,6547,6548,6549,6550,6551,6552,6553,6553,6553,6554,6555,6556,6557,6558,6559,6560,6561,6289,6563,6564,6565,6293,6567,6568,6296,6570,6299,6572,6573,6301,6574,6303,6576,6305,6578,6307,6579,6581,6310,6583,6312,6584,6313,6585,6587,6589,6316,6590,6317,6591,6319,6593,6595,6322,6596,6324,6598,6326,6600,6328,6601,6602,6330,6604,6333,6606,6607,6336,6609,6610,6611,6340,6613,6614,6615,6616,6617,6618,6619,6620,6349,6621,6621,6622,6623,6624,6625,6626,6627,6628,6629,6358,6631,6632,6633,6362,6635,6636,6365,6638,6639,6367,6640,6641,6643,6644,6371,6645,6373,6647,6649,6376,6651,6378,6652,6654,6656,6382,6384,6660,6662,6664,6388,6665,6390,6667,6669,6393,6671,6395,6672,6673,6675,6676,6399,6677,6678,6401,6680,6681,6404,6683,6684,6685,6408,6687,6688,6689,6690,6691,6692,6693,6694,6695,6695,6695,6696,6697,6698,6699,6700,6701,6702,6703,6426,6705,6706,6707,6430,6709,6710,6433,6712,6713,6435,6714,6715,6717,6718,6439,6719,6441,6721,6723,6444,6725,6446,6726,6728,6730,6450,6452,6734,6736,6738,6456,6739,6458,6741,6743,6461,6745,6463,6746,6747,6749,6750,6467,6751,6752,6469,6754,6755,6472,6757,6758,6759,6476,6761,6762,6763,6764,6765,6766,6767,6768,6769,6769,6769,6770,6771,6772,6773,6774,6775,6776,6777,6494,6779,6780,6781,6498,6783,6784,6501,6786,6787,6503,6788,6789,6791,6792,6507,6793,6509,6795,6797,6512,6799,6514,6800,6802,6804,6518,6520,6808,6810,6812,6524,6813,6526,6815,6817,6529,6819,6531,6820,6821,6823,6824,6535,6825,6826,6537,6828,6829,6540,6831,6832,6833,6544,6835,6836,6837,6838,6839,6840,6841,6842,6843,6843,6843,6844,6845,6846,6847,6848,6849,6850,6851,6562,6853,6854,6855,6566,6857,6858,6569,6860,6861,6571,6862,6863,6865,6866,6575,6867,6577,6869,6871,6580,6873,6582,6874,6876,6878,6586,6588,6882,6884,6886,6592,6887,6594,6889,6891,6597,6893,6599,6894,6895,6897,6898,6603,6899,6900,6605,6902,6903,6608,6905,6906,6907,6612,6909,6910,6911,6912,6913,6914,6915,6916,6621,6917,6917,6918,6919,6920,6921,6922,6923,6924,6925,6630,6927,6928,6929,6634,6931,6932,6637,6934,6935,6937,6938,6642,6940,6942,6943,6646,6944,6648,6946,6650,6947,6949,6653,6951,6655,6952,6657,6953,6658,6954,6659,6955,6661,6956,6663,6958,6960,6666,6961,6668,6963,6670,6964,6965,6967,6674,6969,6970,6972,6973,6679,6975,6976,6682,6978,6979,6980,6686,6982,6983,6984,6985,6986,6987,6988,6989,6990,6990,6990,6991,6992,6993,6994,6995,6996,6997,6998,6704,7000,7001,7002,6708,7004,7005,6711,7007,7008,7010,7011,6716,7013,7015,7016,6720,7017,6722,7019,6724,7020,7022,6727,7024,6729,7025,6731,7026,6732,7027,6733,7028,6735,7029,6737,7031,7033,6740,7034,6742,7036,6744,7037,7038,7040,6748,7042,7043,7045,7046,6753,7048,7049,6756,7051,7052,7053,6760,7055,7056,7057,7058,7059,7060,7061,7062,7063,7063,7063,7064,7065,7066,7067,7068,7069,7070,7071,6778,7073,7074,7075,6782,7077,7078,6785,7080,7081,7083,7084,6790,7086,7088,7089,6794,7090,6796,7092,6798,7093,7095,6801,7097,6803,7098,6805,7099,6806,7100,6807,7101,6809,7102,6811,7104,7106,6814,7107,6816,7109,6818,7110,7111,7113,6822,7115,7116,7118,7119,6827,7121,7122,6830,7124,7125,7126,6834,7128,7129,7130,7131,7132,7133,7134,7135,7136,7136,7136,7137,7138,7139,7140,7141,7142,7143,7144,6852,7146,7147,7148,6856,7150,7151,6859,7153,7154,7156,7157,6864,7159,7161,7162,6868,7163,6870,7165,6872,7166,7168,6875,7170,6877,7171,6879,7172,6880,7173,6881,7174,6883,7175,6885,7177,7179,6888,7180,6890,7182,6892,7183,7184,7186,6896,7188,7189,7191,7192,6901,7194,7195,6904,7197,7198,7199,6908,7201,7202,7203,7204,7205,7206,7207,7208,6917,7209,7209,7210,7211,7212,7213,7214,7215,7216,7217,6926,7219,7220,7221,6930,7223,7224,7225,6933,7226,7227,6936,7229,6939,7231,6941,7233,7235,7236,6945,7237,7239,6948,7241,6950,7242,7244,7246,7248,7250,7252,7254,6957,7255,6959,7257,7259,6962,7260,7261,7263,6966,7265,6968,7267,6971,7269,7270,6974,7271,7272,7273,6977,7275,7276,7277,6981,7279,7280,7281,7282,7283,7284,7285,7286,7287,7287,7287,7288,7289,7290,7291,7292,7293,7294,7295,6999,7297,7298,7299,7003,7301,7302,7303,7006,7304,7305,7009,7307,7012,7309,7014,7311,7313,7314,7018,7315,7317,7021,7319,7023,7320,7322,7324,7326,7328,7330,7332,7030,7333,7032,7335,7337,7035,7338,7339,7341,7039,7343,7041,7345,7044,7347,7348,7047,7349,7350,7351,7050,7353,7354,7355,7054,7357,7358,7359,7360,7361,7362,7363,7364,7365,7365,7365,7366,7367,7368,7369,7370,7371,7372,7373,7072,7375,7376,7377,7076,7379,7380,7381,7079,7382,7383,7082,7385,7085,7387,7087,7389,7391,7392,7091,7393,7395,7094,7397,7096,7398,7400,7402,7404,7406,7408,7410,7103,7411,7105,7413,7415,7108,7416,7417,7419,7112,7421,7114,7423,7117,7425,7426,7120,7427,7428,7429,7123,7431,7432,7433,7127,7435,7436,7437,7438,7439,7440,7441,7442,7443,7443,7443,7444,7445,7446,7447,7448,7449,7450,7451,7145,7453,7454,7455,7149,7457,7458,7459,7152,7460,7461,7155,7463,7158,7465,7160,7467,7469,7470,7164,7471,7473,7167,7475,7169,7476,7478,7480,7482,7484,7486,7488,7176,7489,7178,7491,7493,7181,7494,7495,7497,7185,7499,7187,7501,7190,7503,7504,7193,7505,7506,7507,7196,7509,7510,7511,7200,7513,7514,7515,7516,7517,7518,7519,7520,7209,7521,7521,7522,7523,7524,7525,7526,7527,7528,7529,7530,7218,7531,7532,7533,7534,7222,7535,7536,7537,7539,7540,7228,7542,7543,7230,7544,7232,7546,7234,7548,7550,7238,7552,7240,7553,7555,7243,7557,7245,7558,7247,7559,7249,7560,7251,7561,7253,7563,7565,7256,7566,7258,7568,7570,7262,7572,7264,7574,7266,7575,7576,7268,7578,7579,7581,7582,7583,7274,7584,7585,7586,7587,7278,7588,7589,7590,7591,7592,7593,7594,7595,7596,7597,7597,7597,7598,7599,7600,7601,7602,7603,7604,7605,7606,7296,7607,7608,7609,7610,7300,7611,7612,7613,7615,7616,7306,7618,7619,7308,7620,7310,7622,7312,7624,7626,7316,7628,7318,7629,7631,7321,7633,7323,7634,7325,7635,7327,7636,7329,7637,7331,7639,7641,7334,7642,7336,7644,7646,7340,7648,7342,7650,7344,7651,7652,7346,7654,7655,7657,7658,7659,7352,7660,7661,7662,7663,7356,7664,7665,7666,7667,7668,7669,7670,7671,7672,7673,7673,7673,7674,7675,7676,7677,7678,7679,7680,7681,7682,7374,7683,7684,7685,7686,7378,7687,7688,7689,7691,7692,7384,7694,7695,7386,7696,7388,7698,7390,7700,7702,7394,7704,7396,7705,7707,7399,7709,7401,7710,7403,7711,7405,7712,7407,7713,7409,7715,7717,7412,7718,7414,7720,7722,7418,7724,7420,7726,7422,7727,7728,7424,7730,7731,7733,7734,7735,7430,7736,7737,7738,7739,7434,7740,7741,7742,7743,7744,7745,7746,7747,7748,7749,7749,7749,7750,7751,7752,7753,7754,7755,7756,7757,7758,7452,7759,7760,7761,7762,7456,7763,7764,7765,7767,7768,7462,7770,7771,7464,7772,7466,7774,7468,7776,7778,7472,7780,7474,7781,7783,7477,7785,7479,7786,7481,7787,7483,7788,7485,7789,7487,7791,7793,7490,7794,7492,7796,7798,7496,7800,7498,7802,7500,7803,7804,7502,7806,7807,7809,7810,7811,7508,7812,7813,7814,7815,7512,7816,7817,7818,7819,7820,7821,7822,7823,7824,7521,7825,7825,7826,7827,7828,7829,7830,7831,7832,7833,7834,7836,7837,7838,7839,7841,7842,7843,7538,7845,7541,7847,7848,7850,7851,7545,7852,7853,7547,7854,7549,7856,7551,7857,7859,7554,7861,7556,7862,7864,7866,7868,7870,7872,7562,7873,7564,7875,7877,7567,7878,7569,7880,7571,7881,7882,7573,7883,7884,7886,7887,7577,7889,7580,7891,7892,7893,7895,7896,7897,7898,7900,7901,7902,7903,7904,7905,7906,7907,7908,7909,7909,7909,7910,7911,7912,7913,7914,7915,7916,7917,7918,7920,7921,7922,7923,7925,7926,7927,7614,7929,7617,7931,7932,7934,7935,7621,7936,7937,7623,7938,7625,7940,7627,7941,7943,7630,7945,7632,7946,7948,7950,7952,7954,7956,7638,7957,7640,7959,7961,7643,7962,7645,7964,7647,7965,7966,7649,7967,7968,7970,7971,7653,7973,7656,7975,7976,7977,7979,7980,7981,7982,7984,7985,7986,7987,7988,7989,7990,7991,7992,7993,7993,7993,7994,7995,7996,7997,7998,7999,8000,8001,8002,8004,8005,8006,8007,8009,8010,8011,7690,8013,7693,8015,8016,8018,8019,7697,8020,8021,7699,8022,7701,8024,7703,8025,8027,7706,8029,7708,8030,8032,8034,8036,8038,8040,7714,8041,7716,8043,8045,7719,8046,7721,8048,7723,8049,8050,7725,8051,8052,8054,8055,7729,8057,7732,8059,8060,8061,8063,8064,8065,8066,8068,8069,8070,8071,8072,8073,8074,8075,8076,8077,8077,8077,8078,8079,8080,8081,8082,8083,8084,8085,8086,8088,8089,8090,8091,8093,8094,8095,7766,8097,7769,8099,8100,8102,8103,7773,8104,8105,7775,8106,7777,8108,7779,8109,8111,7782,8113,7784,8114,8116,8118,8120,8122,8124,7790,8125,7792,8127,8129,7795,8130,7797,8132,7799,8133,8134,7801,8135,8136,8138,8139,7805,8141,7808,8143,8144,8145,8147,8148,8149,8150,8152,8153,8154,8155,8156,8157,8158,8159,8160,7825,8161,8161,8162,8163,8164,8165,8166,8167,8168,8169,8170,7835,8172,8173,8174,7840,8176,8177,7844,8179,8180,7846,8181,8182,7849,8184,8186,8187,8189,7855,8191,8193,7858,8195,7860,8196,8198,7863,7865,7867,7869,7871,8205,8207,7874,8208,7876,8210,8212,7879,8214,8216,8217,8219,7885,8221,8222,7888,8223,8224,7890,8226,8227,7894,8229,8230,8231,7899,8233,8234,8235,8236,8237,8238,8239,8240,8241,8242,8242,8242,8243,8244,8245,8246,8247,8248,8249,8250,8251,7919,8253,8254,8255,7924,8257,8258,7928,8260,8261,7930,8262,8263,7933,8265,8267,8268,8270,7939,8272,8274,7942,8276,7944,8277,8279,7947,7949,7951,7953,7955,8286,8288,7958,8289,7960,8291,8293,7963,8295,8297,8298,8300,7969,8302,8303,7972,8304,8305,7974,8307,8308,7978,8310,8311,8312,7983,8314,8315,8316,8317,8318,8319,8320,8321,8322,8323,8323,8323,8324,8325,8326,8327,8328,8329,8330,8331,8332,8003,8334,8335,8336,8008,8338,8339,8012,8341,8342,8014,8343,8344,8017,8346,8348,8349,8351,8023,8353,8355,8026,8357,8028,8358,8360,8031,8033,8035,8037,8039,8367,8369,8042,8370,8044,8372,8374,8047,8376,8378,8379,8381,8053,8383,8384,8056,8385,8386,8058,8388,8389,8062,8391,8392,8393,8067,8395,8396,8397,8398,8399,8400,8401,8402,8403,8404,8404,8404,8405,8406,8407,8408,8409,8410,8411,8412,8413,8087,8415,8416,8417,8092,8419,8420,8096,8422,8423,8098,8424,8425,8101,8427,8429,8430,8432,8107,8434,8436,8110,8438,8112,8439,8441,8115,8117,8119,8121,8123,8448,8450,8126,8451,8128,8453,8455,8131,8457,8459,8460,8462,8137,8464,8465,8140,8466,8467,8142,8469,8470,8146,8472,8473,8474,8151,8476,8477,8478,8479,8480,8481,8482,8483,8484,8161,8485,8485,8486,8487,8488,8489,8490,8491,8492,8493,8494,8171,8496,8497,8498,8175,8500,8501,8178,8503,8504,8506,8507,8183,8509,8185,8511,8188,8513,8190,8514,8192,8516,8194,8517,8519,8197,8521,8199,8522,8200,8523,8201,8524,8202,8525,8203,8526,8204,8527,8206,8529,8531,8209,8532,8211,8534,8213,8535,8215,8537,8218,8539,8220,8541,8542,8544,8545,8225,8547,8548,8228,8550,8551,8552,8232,8554,8555,8556,8557,8558,8559,8560,8561,8562,8563,8563,8563,8564,8565,8566,8567,8568,8569,8570,8571,8572,8252,8574,8575,8576,8256,8578,8579,8259,8581,8582,8584,8585,8264,8587,8266,8589,8269,8591,8271,8592,8273,8594,8275,8595,8597,8278,8599,8280,8600,8281,8601,8282,8602,8283,8603,8284,8604,8285,8605,8287,8607,8609,8290,8610,8292,8612,8294,8613,8296,8615,8299,8617,8301,8619,8620,8622,8623,8306,8625,8626,8309,8628,8629,8630,8313,8632,8633,8634,8635,8636,8637,8638,8639,8640,8641,8641,8641,8642,8643,8644,8645,8646,8647,8648,8649,8650,8333,8652,8653,8654,8337,8656,8657,8340,8659,8660,8662,8663,8345,8665,8347,8667,8350,8669,8352,8670,8354,8672,8356,8673,8675,8359,8677,8361,8678,8362,8679,8363,8680,8364,8681,8365,8682,8366,8683,8368,8685,8687,8371,8688,8373,8690,8375,8691,8377,8693,8380,8695,8382,8697,8698,8700,8701,8387,8703,8704,8390,8706,8707,8708,8394,8710,8711,8712,8713,8714,8715,8716,8717,8718,8719,8719,8719,8720,8721,8722,8723,8724,8725,8726,8727,8728,8414,8730,8731,8732,8418,8734,8735,8421,8737,8738,8740,8741,8426,8743,8428,8745,8431,8747,8433,8748,8435,8750,8437,8751,8753,8440,8755,8442,8756,8443,8757,8444,8758,8445,8759,8446,8760,8447,8761,8449,8763,8765,8452,8766,8454,8768,8456,8769,8458,8771,8461,8773,8463,8775,8776,8778,8779,8468,8781,8782,8471,8784,8785,8786,8475,8788,8789,8790,8791,8792,8793,8794,8795,8796,8485,8797,8797,8798,8799,8800,8801,8802,8803,8804,8805,8806,8495,8808,8809,8810,8499,8812,8813,8502,8815,8816,8505,8818,8508,8820,8510,8822,8512,8824,8826,8827,8515,8828,8830,8518,8832,8520,8833,8835,8837,8839,8841,8843,8845,8847,8528,8848,8530,8850,8852,8533,8853,8854,8856,8536,8858,8538,8860,8540,8862,8543,8864,8865,8546,8867,8868,8549,8870,8871,8872,8553,8874,8875,8876,8877,8878,8879,8880,8881,8882,8883,8883,8883,8884,8885,8886,8887,8888,8889,8890,8891,8892,8573,8894,8895,8896,8577,8898,8899,8580,8901,8902,8583,8904,8586,8906,8588,8908,8590,8910,8912,8913,8593,8914,8916,8596,8918,8598,8919,8921,8923,8925,8927,8929,8931,8933,8606,8934,8608,8936,8938,8611,8939,8940,8942,8614,8944,8616,8946,8618,8948,8621,8950,8951,8624,8953,8954,8627,8956,8957,8958,8631,8960,8961,8962,8963,8964,8965,8966,8967,8968,8969,8969,8969,8970,8971,8972,8973,8974,8975,8976,8977,8978,8651,8980,8981,8982,8655,8984,8985,8658,8987,8988,8661,8990,8664,8992,8666,8994,8668,8996,8998,8999,8671,9000,9002,8674,9004,8676,9005,9007,9009,9011,9013,9015,9017,9019,8684,9020,8686,9022,9024,8689,9025,9026,9028,8692,9030,8694,9032,8696,9034,8699,9036,9037,8702,9039,9040,8705,9042,9043,9044,8709,9046,9047,9048,9049,9050,9051,9052,9053,9054,9055,9055,9055,9056,9057,9058,9059,9060,9061,9062,9063,9064,8729,9066,9067,9068,8733,9070,9071,8736,9073,9074,8739,9076,8742,9078,8744,9080,8746,9082,9084,9085,8749,9086,9088,8752,9090,8754,9091,9093,9095,9097,9099,9101,9103,9105,8762,9106,8764,9108,9110,8767,9111,9112,9114,8770,9116,8772,9118,8774,9120,8777,9122,9123,8780,9125,9126,8783,9128,9129,9130,8787,9132,9133,9134,9135,9136,9137,9138,9139,9140,8797,9141,9141,9142,9143,9144,9145,9146,9147,9148,9149,9150,8807,9152,9153,9154,8811,9156,9157,9158,8814,9159,9160,8817,9162,9163,8819,9164,9165,8821,9166,9167,8823,9168,8825,9170,9172,8829,9174,8831,9175,9177,8834,9179,8836,9180,8838,9181,8840,9182,8842,9183,8844,9184,8846,9186,9188,8849,9189,8851,9191,9193,8855,9195,8857,9196,9197,8859,9198,9199,8861,9200,9201,8863,9203,9204,8866,9205,9206,9207,8869,9209,9210,9211,8873,9213,9214,9215,9216,9217,9218,9219,9220,9221,9222,9222,9222,9223,9224,9225,9226,9227,9228,9229,9230,9231,8893,9233,9234,9235,8897,9237,9238,9239,8900,9240,9241,8903,9243,9244,8905,9245,9246,8907,9247,9248,8909,9249,8911,9251,9253,8915,9255,8917,9256,9258,8920,9260,8922,9261,8924,9262,8926,9263,8928,9264,8930,9265,8932,9267,9269,8935,9270,8937,9272,9274,8941,9276,8943,9277,9278,8945,9279,9280,8947,9281,9282,8949,9284,9285,8952,9286,9287,9288,8955,9290,9291,9292,8959,9294,9295,9296,9297,9298,9299,9300,9301,9302,9303,9303,9303,9304,9305,9306,9307,9308,9309,9310,9311,9312,8979,9314,9315,9316,8983,9318,9319,9320,8986,9321,9322,8989,9324,9325,8991,9326,9327,8993,9328,9329,8995,9330,8997,9332,9334,9001,9336,9003,9337,9339,9006,9341,9008,9342,9010,9343,9012,9344,9014,9345,9016,9346,9018,9348,9350,9021,9351,9023,9353,9355,9027,9357,9029,9358,9359,9031,9360,9361,9033,9362,9363,9035,9365,9366,9038,9367,9368,9369,9041,9371,9372,9373,9045,9375,9376,9377,9378,9379,9380,9381,9382,9383,9384,9384,9384,9385,9386,9387,9388,9389,9390,9391,9392,9393,9065,9395,9396,9397,9069,9399,9400,9401,9072,9402,9403,9075,9405,9406,9077,9407,9408,9079,9409,9410,9081,9411,9083,9413,9415,9087,9417,9089,9418,9420,9092,9422,9094,9423,9096,9424,9098,9425,9100,9426,9102,9427,9104,9429,9431,9107,9432,9109,9434,9436,9113,9438,9115,9439,9440,9117,9441,9442,9119,9443,9444,9121,9446,9447,9124,9448,9449,9450,9127,9452,9453,9454,9131,9456,9457,9458,9459,9460,9461,9462,9463,9464,9141,9465,9465,9466,9467,9468,9469,9470,9471,9472,9473,9474,9151,9476,9477,9478,9155,9480,9481,9482,9484,9485,9161,9487,9488,9490,9491,9493,9494,9496,9497,9169,9498,9171,9500,9173,9501,9503,9176,9505,9178,9506,9508,9510,9512,9514,9516,9518,9185,9519,9187,9521,9523,9190,9524,9192,9526,9194,9527,9528,9530,9531,9533,9534,9536,9537,9202,9539,9540,9542,9543,9544,9208,9546,9547,9548,9212,9550,9551,9552,9553,9554,9555,9556,9557,9558,9559,9559,9559,9560,9561,9562,9563,9564,9565,9566,9567,9568,9232,9570,9571,9572,9236,9574,9575,9576,9578,9579,9242,9581,9582,9584,9585,9587,9588,9590,9591,9250,9592,9252,9594,9254,9595,9597,9257,9599,9259,9600,9602,9604,9606,9608,9610,9612,9266,9613,9268,9615,9617,9271,9618,9273,9620,9275,9621,9622,9624,9625,9627,9628,9630,9631,9283,9633,9634,9636,9637,9638,9289,9640,9641,9642,9293,9644,9645,9646,9647,9648,9649,9650,9651,9652,9653,9653,9653,9654,9655,9656,9657,9658,9659,9660,9661,9662,9313,9664,9665,9666,9317,9668,9669,9670,9672,9673,9323,9675,9676,9678,9679,9681,9682,9684,9685,9331,9686,9333,9688,9335,9689,9691,9338,9693,9340,9694,9696,9698,9700,9702,9704,9706,9347,9707,9349,9709,9711,9352,9712,9354,9714,9356,9715,9716,9718,9719,9721,9722,9724,9725,9364,9727,9728,9730,9731,9732,9370,9734,9735,9736,9374,9738,9739,9740,9741,9742,9743,9744,9745,9746,9747,9747,9747,9748,9749,9750,9751,9752,9753,9754,9755,9756,9394,9758,9759,9760,9398,9762,9763,9764,9766,9767,9404,9769,9770,9772,9773,9775,9776,9778,9779,9412,9780,9414,9782,9416,9783,9785,9419,9787,9421,9788,9790,9792,9794,9796,9798,9800,9428,9801,9430,9803,9805,9433,9806,9435,9808,9437,9809,9810,9812,9813,9815,9816,9818,9819,9445,9821,9822,9824,9825,9826,9451,9828,9829,9830,9455,9832,9833,9834,9835,9836,9837,9838,9839,9840,9465,9841,9841,9842,9843,9844,9845,9846,9847,9848,9849,9850,9475,9852,9853,9854,9855,9479,9856,9857,9858,9483,9860,9861,9486,9862,9863,9489,9865,9492,9867,9495,9869,9871,9872,9499,9873,9875,9502,9877,9504,9878,9880,9507,9509,9511,9884,9513,9515,9517,9888,9890,9520,9891,9522,9893,9895,9525,9896,9897,9899,9529,9901,9532,9903,9535,9905,9906,9538,9907,9908,9541,9910,9911,9912,9545,9913,9914,9915,9916,9549,9918,9919,9920,9921,9922,9923,9924,9925,9926,9927,9927,9927,9928,9929,9930,9931,9932,9933,9934,9935,9936,9569,9938,9939,9940,9941,9573,9942,9943,9944,9577,9946,9947,9580,9948,9949,9583,9951,9586,9953,9589,9955,9957,9958,9593,9959,9961,9596,9963,9598,9964,9966,9601,9603,9605,9970,9607,9609,9611,9974,9976,9614,9977,9616,9979,9981,9619,9982,9983,9985,9623,9987,9626,9989,9629,9991,9992,9632,9993,9994,9635,9996,9997,9998,9639,9999,10000,10001,10002,9643,10004,10005,10006,10007,10008,10009,10010,10011,10012,10013,10013,10013,10014,10015,10016,10017,10018,10019,10020,10021,10022,9663,10024,10025,10026,10027,9667,10028,10029,10030,9671,10032,10033,9674,10034,10035,9677,10037,9680,10039,9683,10041,10043,10044,9687,10045,10047,9690,10049,9692,10050,10052,9695,9697,9699,10056,9701,9703,9705,10060,10062,9708,10063,9710,10065,10067,9713,10068,10069,10071,9717,10073,9720,10075,9723,10077,10078,9726,10079,10080,9729,10082,10083,10084,9733,10085,10086,10087,10088,9737,10090,10091,10092,10093,10094,10095,10096,10097,10098,10099,10099,10099,10100,10101,10102,10103,10104,10105,10106,10107,10108,9757,10110,10111,10112,10113,9761,10114,10115,10116,9765,10118,10119,9768,10120,10121,9771,10123,9774,10125,9777,10127,10129,10130,9781,10131,10133,9784,10135,9786,10136,10138,9789,9791,9793,10142,9795,9797,9799,10146,10148,9802,10149,9804,10151,10153,9807,10154,10155,10157,9811,10159,9814,10161,9817,10163,10164,9820,10165,10166,9823,10168,10169,10170,9827,10171,10172,10173,10174,9831,10176,10177,10178,10179,10180,10181,10182,10183,10184,9841,10185,10185,10186,10187,10188,10189,10190,10191,10192,10193,10194,9851,10196,10197,10198,10199,10201,10202,10203,9859,10205,10206,10208,10209,9864,10211,9866,10213,9868,10215,9870,10217,10219,9874,10221,9876,10222,10224,9879,10226,9881,10227,9882,10228,9883,10229,10231,9885,10232,9886,10233,9887,10234,9889,10236,10238,9892,10239,9894,10241,10243,9898,10245,9900,10247,9902,10249,9904,10251,10252,10254,10255,9909,10257,10258,10259,10261,10262,10263,10264,9917,10266,10267,10268,10269,10270,10271,10272,10273,10274,10275,10275,10275,10276,10277,10278,10279,10280,10281,10282,10283,10284,9937,10286,10287,10288,10289,10291,10292,10293,9945,10295,10296,10298,10299,9950,10301,9952,10303,9954,10305,9956,10307,10309,9960,10311,9962,10312,10314,9965,10316,9967,10317,9968,10318,9969,10319,10321,9971,10322,9972,10323,9973,10324,9975,10326,10328,9978,10329,9980,10331,10333,9984,10335,9986,10337,9988,10339,9990,10341,10342,10344,10345,9995,10347,10348,10349,10351,10352,10353,10354,10003,10356,10357,10358,10359,10360,10361,10362,10363,10364,10365,10365,10365,10366,10367,10368,10369,10370,10371,10372,10373,10374,10023,10376,10377,10378,10379,10381,10382,10383,10031,10385,10386,10388,10389,10036,10391,10038,10393,10040,10395,10042,10397,10399,10046,10401,10048,10402,10404,10051,10406,10053,10407,10054,10408,10055,10409,10411,10057,10412,10058,10413,10059,10414,10061,10416,10418,10064,10419,10066,10421,10423,10070,10425,10072,10427,10074,10429,10076,10431,10432,10434,10435,10081,10437,10438,10439,10441,10442,10443,10444,10089,10446,10447,10448,10449,10450,10451,10452,10453,10454,10455,10455,10455,10456,10457,10458,10459,10460,10461,10462,10463,10464,10109,10466,10467,10468,10469,10471,10472,10473,10117,10475,10476,10478,10479,10122,10481,10124,10483,10126,10485,10128,10487,10489,10132,10491,10134,10492,10494,10137,10496,10139,10497,10140,10498,10141,10499,10501,10143,10502,10144,10503,10145,10504,10147,10506,10508,10150,10509,10152,10511,10513,10156,10515,10158,10517,10160,10519,10162,10521,10522,10524,10525,10167,10527,10528,10529,10531,10532,10533,10534,10175,10536,10537,10538,10539,10540,10541,10542,10543,10544,10185,10545,10545,10546,10547,10548,10549,10550,10551,10552,10553,10554,10195,10556,10557,10558,10559,10200,10561,10562,10204,10564,10565,10207,10567,10210,10569,10570,10212,10571,10572,10214,10573,10216,10575,10218,10577,10220,10578,10580,10223,10582,10225,10583,10585,10587,10589,10230,10592,10594,10596,10598,10235,10599,10237,10601,10603,10240,10604,10242,10606,10244,10608,10246,10609,10610,10248,10611,10612,10250,10614,10253,10616,10617,10256,10619,10620,10260,10622,10623,10624,10625,10265,10627,10628,10629,10630,10631,10632,10633,10634,10635,10636,10636,10636,10637,10638,10639,10640,10641,10642,10643,10644,10645,10285,10647,10648,10649,10650,10290,10652,10653,10294,10655,10656,10297,10658,10300,10660,10661,10302,10662,10663,10304,10664,10306,10666,10308,10668,10310,10669,10671,10313,10673,10315,10674,10676,10678,10680,10320,10683,10685,10687,10689,10325,10690,10327,10692,10694,10330,10695,10332,10697,10334,10699,10336,10700,10701,10338,10702,10703,10340,10705,10343,10707,10708,10346,10710,10711,10350,10713,10714,10715,10716,10355,10718,10719,10720,10721,10722,10723,10724,10725,10726,10727,10727,10727,10728,10729,10730,10731,10732,10733,10734,10735,10736,10375,10738,10739,10740,10741,10380,10743,10744,10384,10746,10747,10387,10749,10390,10751,10752,10392,10753,10754,10394,10755,10396,10757,10398,10759,10400,10760,10762,10403,10764,10405,10765,10767,10769,10771,10410,10774,10776,10778,10780,10415,10781,10417,10783,10785,10420,10786,10422,10788,10424,10790,10426,10791,10792,10428,10793,10794,10430,10796,10433,10798,10799,10436,10801,10802,10440,10804,10805,10806,10807,10445,10809,10810,10811,10812,10813,10814,10815,10816,10817,10818,10818,10818,10819,10820,10821,10822,10823,10824,10825,10826,10827,10465,10829,10830,10831,10832,10470,10834,10835,10474,10837,10838,10477,10840,10480,10842,10843,10482,10844,10845,10484,10846,10486,10848,10488,10850,10490,10851,10853,10493,10855,10495,10856,10858,10860,10862,10500,10865,10867,10869,10871,10505,10872,10507,10874,10876,10510,10877,10512,10879,10514,10881,10516,10882,10883,10518,10884,10885,10520,10887,10523,10889,10890,10526,10892,10893,10530,10895,10896,10897,10898,10535,10900,10901,10902,10903,10904,10905,10906,10907,10908,10545]; var count_per_radius = [1,8,16,20,24,40,36,48,56,56,68,64,80,92,88,96,96,116,120,120,124,144,136,140,152,168,176,164,168,192,188,208,200,208,228,208,232,228,256,248,236,272,264,288,276,272,296,292,312,304,336,324,312,344,324,376,344,360,364,368]; var radius_offset = [0,1,9,25,45,69,109,145,193,249,305,373,437,517,609,697,793,889,1005,1125,1245,1369,1513,1649,1789,1941,2109,2285,2449,2617,2809,2997,3205,3405,3613,3841,4049,4281,4509,4765,5013,5249,5521,5785,6073,6349,6621,6917,7209,7521,7825,8161,8485,8797,9141,9465,9841,10185,10545,10909]; // End of generated. var connected = new Array(towards_center.length).fill(true);