@tensorflow/tfjs-data
Version:
TensorFlow Data API in JavaScript
267 lines • 23.6 kB
JavaScript
/**
* @license
* Copyright 2018 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.
*
* =============================================================================
*/
import { datasetFromIteratorFn } from './dataset';
import { CSVDataset } from './datasets/csv_dataset';
import { iteratorFromFunction } from './iterators/lazy_iterator';
import { MicrophoneIterator } from './iterators/microphone_iterator';
import { WebcamIterator } from './iterators/webcam_iterator';
import { URLDataSource } from './sources/url_data_source';
/**
* Create a `CSVDataset` by reading and decoding CSV file(s) from provided URL
* or local path if it's in Node environment.
*
* Note: If isLabel in columnConfigs is `true` for at least one column, the
* element in returned `CSVDataset` will be an object of
* `{xs:features, ys:labels}`: xs is a dict of features key/value pairs, ys
* is a dict of labels key/value pairs. If no column is marked as label,
* returns a dict of features only.
*
* ```js
* const csvUrl =
* 'https://storage.googleapis.com/tfjs-examples/multivariate-linear-regression/data/boston-housing-train.csv';
*
* async function run() {
* // We want to predict the column "medv", which represents a median value of
* // a home (in $1000s), so we mark it as a label.
* const csvDataset = tf.data.csv(
* csvUrl, {
* columnConfigs: {
* medv: {
* isLabel: true
* }
* }
* });
*
* // Number of features is the number of column names minus one for the label
* // column.
* const numOfFeatures = (await csvDataset.columnNames()).length - 1;
*
* // Prepare the Dataset for training.
* const flattenedDataset =
* csvDataset
* .map(({xs, ys}) =>
* {
* // Convert xs(features) and ys(labels) from object form (keyed by
* // column name) to array form.
* return {xs:Object.values(xs), ys:Object.values(ys)};
* })
* .batch(10);
*
* // Define the model.
* const model = tf.sequential();
* model.add(tf.layers.dense({
* inputShape: [numOfFeatures],
* units: 1
* }));
* model.compile({
* optimizer: tf.train.sgd(0.000001),
* loss: 'meanSquaredError'
* });
*
* // Fit the model using the prepared Dataset
* return model.fitDataset(flattenedDataset, {
* epochs: 10,
* callbacks: {
* onEpochEnd: async (epoch, logs) => {
* console.log(epoch + ':' + logs.loss);
* }
* }
* });
* }
*
* await run();
* ```
*
* @param source URL or local path to get CSV file. If it's a local path, it
* must have prefix `file://` and it only works in node environment.
* @param csvConfig (Optional) A CSVConfig object that contains configurations
* of reading and decoding from CSV file(s).
*
* @doc {
* heading: 'Data',
* subheading: 'Creation',
* namespace: 'data',
* configParamIndices: [1]
* }
*/
export function csv(source, csvConfig = {}) {
return new CSVDataset(new URLDataSource(source), csvConfig);
}
/**
* Create a `Dataset` that produces each element by calling a provided function.
*
* Note that repeated iterations over this `Dataset` may produce different
* results, because the function will be called anew for each element of each
* iteration.
*
* Also, beware that the sequence of calls to this function may be out of order
* in time with respect to the logical order of the Dataset. This is due to the
* asynchronous lazy nature of stream processing, and depends on downstream
* transformations (e.g. .shuffle()). If the provided function is pure, this is
* no problem, but if it is a closure over a mutable state (e.g., a traversal
* pointer), then the order of the produced elements may be scrambled.
*
* ```js
* let i = -1;
* const func = () =>
* ++i < 5 ? {value: i, done: false} : {value: null, done: true};
* const ds = tf.data.func(func);
* await ds.forEachAsync(e => console.log(e));
* ```
*
* @param f A function that produces one data element on each call.
*/
export function func(f) {
const iter = iteratorFromFunction(f);
return datasetFromIteratorFn(async () => iter);
}
/**
* Create a `Dataset` that produces each element from provided JavaScript
* generator, which is a function that returns a (potentially async) iterator.
*
* For more information on iterators and generators, see
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators .
* For the iterator protocol, see
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols .
*
* Example of creating a dataset from an iterator factory:
* ```js
* function makeIterator() {
* const numElements = 10;
* let index = 0;
*
* const iterator = {
* next: () => {
* let result;
* if (index < numElements) {
* result = {value: index, done: false};
* index++;
* return result;
* }
* return {value: index, done: true};
* }
* };
* return iterator;
* }
* const ds = tf.data.generator(makeIterator);
* await ds.forEachAsync(e => console.log(e));
* ```
*
* Example of creating a dataset from a generator:
* ```js
* function* dataGenerator() {
* const numElements = 10;
* let index = 0;
* while (index < numElements) {
* const x = index;
* index++;
* yield x;
* }
* }
*
* const ds = tf.data.generator(dataGenerator);
* await ds.forEachAsync(e => console.log(e));
* ```
*
* @param generator A JavaScript function that returns
* a (potentially async) JavaScript iterator.
*
* @doc {
* heading: 'Data',
* subheading: 'Creation',
* namespace: 'data',
* configParamIndices: [1]
* }
*/
export function generator(generator) {
return datasetFromIteratorFn(async () => {
const gen = await generator();
return iteratorFromFunction(() => gen.next());
});
}
/**
* Create an iterator that generates `Tensor`s from webcam video stream. This
* API only works in Browser environment when the device has webcam.
*
* Note: this code snippet only works when the device has a webcam. It will
* request permission to open the webcam when running.
* ```js
* const videoElement = document.createElement('video');
* videoElement.width = 100;
* videoElement.height = 100;
* const cam = await tf.data.webcam(videoElement);
* const img = await cam.capture();
* img.print();
* cam.stop();
* ```
*
* @param webcamVideoElement A `HTMLVideoElement` used to play video from
* webcam. If this element is not provided, a hidden `HTMLVideoElement` will
* be created. In that case, `resizeWidth` and `resizeHeight` must be
* provided to set the generated tensor shape.
* @param webcamConfig A `WebcamConfig` object that contains configurations of
* reading and manipulating data from webcam video stream.
*
* @doc {
* heading: 'Data',
* subheading: 'Creation',
* namespace: 'data',
* ignoreCI: true
* }
*/
export async function webcam(webcamVideoElement, webcamConfig) {
return WebcamIterator.create(webcamVideoElement, webcamConfig);
}
/**
* Create an iterator that generates frequency-domain spectrogram `Tensor`s from
* microphone audio stream with browser's native FFT. This API only works in
* browser environment when the device has microphone.
*
* Note: this code snippet only works when the device has a microphone. It will
* request permission to open the microphone when running.
* ```js
* const mic = await tf.data.microphone({
* fftSize: 1024,
* columnTruncateLength: 232,
* numFramesPerSpectrogram: 43,
* sampleRateHz:44100,
* includeSpectrogram: true,
* includeWaveform: true
* });
* const audioData = await mic.capture();
* const spectrogramTensor = audioData.spectrogram;
* spectrogramTensor.print();
* const waveformTensor = audioData.waveform;
* waveformTensor.print();
* mic.stop();
* ```
*
* @param microphoneConfig A `MicrophoneConfig` object that contains
* configurations of reading audio data from microphone.
*
* @doc {
* heading: 'Data',
* subheading: 'Creation',
* namespace: 'data',
* ignoreCI: true
* }
*/
export async function microphone(microphoneConfig) {
return MicrophoneIterator.create(microphoneConfig);
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVhZGVycy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3RmanMtZGF0YS9zcmMvcmVhZGVycy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7Ozs7OztHQWdCRztBQUdILE9BQU8sRUFBVSxxQkFBcUIsRUFBQyxNQUFNLFdBQVcsQ0FBQztBQUN6RCxPQUFPLEVBQUMsVUFBVSxFQUFDLE1BQU0sd0JBQXdCLENBQUM7QUFDbEQsT0FBTyxFQUFDLG9CQUFvQixFQUFDLE1BQU0sMkJBQTJCLENBQUM7QUFDL0QsT0FBTyxFQUFDLGtCQUFrQixFQUFDLE1BQU0saUNBQWlDLENBQUM7QUFDbkUsT0FBTyxFQUFDLGNBQWMsRUFBQyxNQUFNLDZCQUE2QixDQUFDO0FBQzNELE9BQU8sRUFBQyxhQUFhLEVBQUMsTUFBTSwyQkFBMkIsQ0FBQztBQUd4RDs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0E2RUc7QUFDSCxNQUFNLFVBQVUsR0FBRyxDQUNmLE1BQW1CLEVBQUUsWUFBdUIsRUFBRTtJQUNoRCxPQUFPLElBQUksVUFBVSxDQUFDLElBQUksYUFBYSxDQUFDLE1BQU0sQ0FBQyxFQUFFLFNBQVMsQ0FBQyxDQUFDO0FBQzlELENBQUM7QUFFRDs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0F1Qkc7QUFDSCxNQUFNLFVBQVUsSUFBSSxDQUNoQixDQUFzRDtJQUN4RCxNQUFNLElBQUksR0FBRyxvQkFBb0IsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUNyQyxPQUFPLHFCQUFxQixDQUFDLEtBQUssSUFBSSxFQUFFLENBQUMsSUFBSSxDQUFDLENBQUM7QUFDakQsQ0FBQztBQUVEOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0F5REc7QUFDSCxNQUFNLFVBQVUsU0FBUyxDQUN2QixTQUFzRTtJQUV0RSxPQUFPLHFCQUFxQixDQUFDLEtBQUssSUFBSSxFQUFFO1FBQ3RDLE1BQU0sR0FBRyxHQUFHLE1BQU0sU0FBUyxFQUFFLENBQUM7UUFDOUIsT0FBTyxvQkFBb0IsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxHQUFHLENBQUMsSUFBSSxFQUFFLENBQUMsQ0FBQztJQUNoRCxDQUFDLENBQUMsQ0FBQztBQUNMLENBQUM7QUFFRDs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0E2Qkc7QUFDSCxNQUFNLENBQUMsS0FBSyxVQUFVLE1BQU0sQ0FDeEIsa0JBQXFDLEVBQ3JDLFlBQTJCO0lBQzdCLE9BQU8sY0FBYyxDQUFDLE1BQU0sQ0FBQyxrQkFBa0IsRUFBRSxZQUFZLENBQUMsQ0FBQztBQUNqRSxDQUFDO0FBRUQ7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQWlDRztBQUNILE1BQU0sQ0FBQyxLQUFLLFVBQVUsVUFBVSxDQUFDLGdCQUFtQztJQUVsRSxPQUFPLGtCQUFrQixDQUFDLE1BQU0sQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDO0FBQ3JELENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIEBsaWNlbnNlXG4gKiBDb3B5cmlnaHQgMjAxOCBHb29nbGUgTExDLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuICogTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbiAqIHlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbiAqIFlvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuICpcbiAqIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuICpcbiAqIFVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbiAqIGRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbiAqIFdJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuICogU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxuICogbGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG4gKlxuICogPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAqL1xuXG5pbXBvcnQge1RlbnNvckNvbnRhaW5lcn0gZnJvbSAnQHRlbnNvcmZsb3cvdGZqcy1jb3JlJztcbmltcG9ydCB7RGF0YXNldCwgZGF0YXNldEZyb21JdGVyYXRvckZufSBmcm9tICcuL2RhdGFzZXQnO1xuaW1wb3J0IHtDU1ZEYXRhc2V0fSBmcm9tICcuL2RhdGFzZXRzL2Nzdl9kYXRhc2V0JztcbmltcG9ydCB7aXRlcmF0b3JGcm9tRnVuY3Rpb259IGZyb20gJy4vaXRlcmF0b3JzL2xhenlfaXRlcmF0b3InO1xuaW1wb3J0IHtNaWNyb3Bob25lSXRlcmF0b3J9IGZyb20gJy4vaXRlcmF0b3JzL21pY3JvcGhvbmVfaXRlcmF0b3InO1xuaW1wb3J0IHtXZWJjYW1JdGVyYXRvcn0gZnJvbSAnLi9pdGVyYXRvcnMvd2ViY2FtX2l0ZXJhdG9yJztcbmltcG9ydCB7VVJMRGF0YVNvdXJjZX0gZnJvbSAnLi9zb3VyY2VzL3VybF9kYXRhX3NvdXJjZSc7XG5pbXBvcnQge0NTVkNvbmZpZywgTWljcm9waG9uZUNvbmZpZywgV2ViY2FtQ29uZmlnfSBmcm9tICcuL3R5cGVzJztcblxuLyoqXG4gKiBDcmVhdGUgYSBgQ1NWRGF0YXNldGAgYnkgcmVhZGluZyBhbmQgZGVjb2RpbmcgQ1NWIGZpbGUocykgZnJvbSBwcm92aWRlZCBVUkxcbiAqIG9yIGxvY2FsIHBhdGggaWYgaXQncyBpbiBOb2RlIGVudmlyb25tZW50LlxuICpcbiAqIE5vdGU6IElmIGlzTGFiZWwgaW4gY29sdW1uQ29uZmlncyBpcyBgdHJ1ZWAgZm9yIGF0IGxlYXN0IG9uZSBjb2x1bW4sIHRoZVxuICogZWxlbWVudCBpbiByZXR1cm5lZCBgQ1NWRGF0YXNldGAgd2lsbCBiZSBhbiBvYmplY3Qgb2ZcbiAqIGB7eHM6ZmVhdHVyZXMsIHlzOmxhYmVsc31gOiB4cyBpcyBhIGRpY3Qgb2YgZmVhdHVyZXMga2V5L3ZhbHVlIHBhaXJzLCB5c1xuICogaXMgYSBkaWN0IG9mIGxhYmVscyBrZXkvdmFsdWUgcGFpcnMuIElmIG5vIGNvbHVtbiBpcyBtYXJrZWQgYXMgbGFiZWwsXG4gKiByZXR1cm5zIGEgZGljdCBvZiBmZWF0dXJlcyBvbmx5LlxuICpcbiAqIGBgYGpzXG4gKiBjb25zdCBjc3ZVcmwgPVxuICogJ2h0dHBzOi8vc3RvcmFnZS5nb29nbGVhcGlzLmNvbS90ZmpzLWV4YW1wbGVzL211bHRpdmFyaWF0ZS1saW5lYXItcmVncmVzc2lvbi9kYXRhL2Jvc3Rvbi1ob3VzaW5nLXRyYWluLmNzdic7XG4gKlxuICogYXN5bmMgZnVuY3Rpb24gcnVuKCkge1xuICogICAvLyBXZSB3YW50IHRvIHByZWRpY3QgdGhlIGNvbHVtbiBcIm1lZHZcIiwgd2hpY2ggcmVwcmVzZW50cyBhIG1lZGlhbiB2YWx1ZSBvZlxuICogICAvLyBhIGhvbWUgKGluICQxMDAwcyksIHNvIHdlIG1hcmsgaXQgYXMgYSBsYWJlbC5cbiAqICAgY29uc3QgY3N2RGF0YXNldCA9IHRmLmRhdGEuY3N2KFxuICogICAgIGNzdlVybCwge1xuICogICAgICAgY29sdW1uQ29uZmlnczoge1xuICogICAgICAgICBtZWR2OiB7XG4gKiAgICAgICAgICAgaXNMYWJlbDogdHJ1ZVxuICogICAgICAgICB9XG4gKiAgICAgICB9XG4gKiAgICAgfSk7XG4gKlxuICogICAvLyBOdW1iZXIgb2YgZmVhdHVyZXMgaXMgdGhlIG51bWJlciBvZiBjb2x1bW4gbmFtZXMgbWludXMgb25lIGZvciB0aGUgbGFiZWxcbiAqICAgLy8gY29sdW1uLlxuICogICBjb25zdCBudW1PZkZlYXR1cmVzID0gKGF3YWl0IGNzdkRhdGFzZXQuY29sdW1uTmFtZXMoKSkubGVuZ3RoIC0gMTtcbiAqXG4gKiAgIC8vIFByZXBhcmUgdGhlIERhdGFzZXQgZm9yIHRyYWluaW5nLlxuICogICBjb25zdCBmbGF0dGVuZWREYXRhc2V0ID1cbiAqICAgICBjc3ZEYXRhc2V0XG4gKiAgICAgLm1hcCgoe3hzLCB5c30pID0+XG4gKiAgICAgICB7XG4gKiAgICAgICAgIC8vIENvbnZlcnQgeHMoZmVhdHVyZXMpIGFuZCB5cyhsYWJlbHMpIGZyb20gb2JqZWN0IGZvcm0gKGtleWVkIGJ5XG4gKiAgICAgICAgIC8vIGNvbHVtbiBuYW1lKSB0byBhcnJheSBmb3JtLlxuICogICAgICAgICByZXR1cm4ge3hzOk9iamVjdC52YWx1ZXMoeHMpLCB5czpPYmplY3QudmFsdWVzKHlzKX07XG4gKiAgICAgICB9KVxuICogICAgIC5iYXRjaCgxMCk7XG4gKlxuICogICAvLyBEZWZpbmUgdGhlIG1vZGVsLlxuICogICBjb25zdCBtb2RlbCA9IHRmLnNlcXVlbnRpYWwoKTtcbiAqICAgbW9kZWwuYWRkKHRmLmxheWVycy5kZW5zZSh7XG4gKiAgICAgaW5wdXRTaGFwZTogW251bU9mRmVhdHVyZXNdLFxuICogICAgIHVuaXRzOiAxXG4gKiAgIH0pKTtcbiAqICAgbW9kZWwuY29tcGlsZSh7XG4gKiAgICAgb3B0aW1pemVyOiB0Zi50cmFpbi5zZ2QoMC4wMDAwMDEpLFxuICogICAgIGxvc3M6ICdtZWFuU3F1YXJlZEVycm9yJ1xuICogICB9KTtcbiAqXG4gKiAgIC8vIEZpdCB0aGUgbW9kZWwgdXNpbmcgdGhlIHByZXBhcmVkIERhdGFzZXRcbiAqICAgcmV0dXJuIG1vZGVsLmZpdERhdGFzZXQoZmxhdHRlbmVkRGF0YXNldCwge1xuICogICAgIGVwb2NoczogMTAsXG4gKiAgICAgY2FsbGJhY2tzOiB7XG4gKiAgICAgICBvbkVwb2NoRW5kOiBhc3luYyAoZXBvY2gsIGxvZ3MpID0+IHtcbiAqICAgICAgICAgY29uc29sZS5sb2coZXBvY2ggKyAnOicgKyBsb2dzLmxvc3MpO1xuICogICAgICAgfVxuICogICAgIH1cbiAqICAgfSk7XG4gKiB9XG4gKlxuICogYXdhaXQgcnVuKCk7XG4gKiBgYGBcbiAqXG4gKiBAcGFyYW0gc291cmNlIFVSTCBvciBsb2NhbCBwYXRoIHRvIGdldCBDU1YgZmlsZS4gSWYgaXQncyBhIGxvY2FsIHBhdGgsIGl0XG4gKiBtdXN0IGhhdmUgcHJlZml4IGBmaWxlOi8vYCBhbmQgaXQgb25seSB3b3JrcyBpbiBub2RlIGVudmlyb25tZW50LlxuICogQHBhcmFtIGNzdkNvbmZpZyAoT3B0aW9uYWwpIEEgQ1NWQ29uZmlnIG9iamVjdCB0aGF0IGNvbnRhaW5zIGNvbmZpZ3VyYXRpb25zXG4gKiAgICAgb2YgcmVhZGluZyBhbmQgZGVjb2RpbmcgZnJvbSBDU1YgZmlsZShzKS5cbiAqXG4gKiBAZG9jIHtcbiAqICAgaGVhZGluZzogJ0RhdGEnLFxuICogICBzdWJoZWFkaW5nOiAnQ3JlYXRpb24nLFxuICogICBuYW1lc3BhY2U6ICdkYXRhJyxcbiAqICAgY29uZmlnUGFyYW1JbmRpY2VzOiBbMV1cbiAqICB9XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBjc3YoXG4gICAgc291cmNlOiBSZXF1ZXN0SW5mbywgY3N2Q29uZmlnOiBDU1ZDb25maWcgPSB7fSk6IENTVkRhdGFzZXQge1xuICByZXR1cm4gbmV3IENTVkRhdGFzZXQobmV3IFVSTERhdGFTb3VyY2Uoc291cmNlKSwgY3N2Q29uZmlnKTtcbn1cblxuLyoqXG4gKiBDcmVhdGUgYSBgRGF0YXNldGAgdGhhdCBwcm9kdWNlcyBlYWNoIGVsZW1lbnQgYnkgY2FsbGluZyBhIHByb3ZpZGVkIGZ1bmN0aW9uLlxuICpcbiAqIE5vdGUgdGhhdCByZXBlYXRlZCBpdGVyYXRpb25zIG92ZXIgdGhpcyBgRGF0YXNldGAgbWF5IHByb2R1Y2UgZGlmZmVyZW50XG4gKiByZXN1bHRzLCBiZWNhdXNlIHRoZSBmdW5jdGlvbiB3aWxsIGJlIGNhbGxlZCBhbmV3IGZvciBlYWNoIGVsZW1lbnQgb2YgZWFjaFxuICogaXRlcmF0aW9uLlxuICpcbiAqIEFsc28sIGJld2FyZSB0aGF0IHRoZSBzZXF1ZW5jZSBvZiBjYWxscyB0byB0aGlzIGZ1bmN0aW9uIG1heSBiZSBvdXQgb2Ygb3JkZXJcbiAqIGluIHRpbWUgd2l0aCByZXNwZWN0IHRvIHRoZSBsb2dpY2FsIG9yZGVyIG9mIHRoZSBEYXRhc2V0LiBUaGlzIGlzIGR1ZSB0byB0aGVcbiAqIGFzeW5jaHJvbm91cyBsYXp5IG5hdHVyZSBvZiBzdHJlYW0gcHJvY2Vzc2luZywgYW5kIGRlcGVuZHMgb24gZG93bnN0cmVhbVxuICogdHJhbnNmb3JtYXRpb25zIChlLmcuIC5zaHVmZmxlKCkpLiBJZiB0aGUgcHJvdmlkZWQgZnVuY3Rpb24gaXMgcHVyZSwgdGhpcyBpc1xuICogbm8gcHJvYmxlbSwgYnV0IGlmIGl0IGlzIGEgY2xvc3VyZSBvdmVyIGEgbXV0YWJsZSBzdGF0ZSAoZS5nLiwgYSB0cmF2ZXJzYWxcbiAqIHBvaW50ZXIpLCB0aGVuIHRoZSBvcmRlciBvZiB0aGUgcHJvZHVjZWQgZWxlbWVudHMgbWF5IGJlIHNjcmFtYmxlZC5cbiAqXG4gKiBgYGBqc1xuICogbGV0IGkgPSAtMTtcbiAqIGNvbnN0IGZ1bmMgPSAoKSA9PlxuICogICAgKytpIDwgNSA/IHt2YWx1ZTogaSwgZG9uZTogZmFsc2V9IDoge3ZhbHVlOiBudWxsLCBkb25lOiB0cnVlfTtcbiAqIGNvbnN0IGRzID0gdGYuZGF0YS5mdW5jKGZ1bmMpO1xuICogYXdhaXQgZHMuZm9yRWFjaEFzeW5jKGUgPT4gY29uc29sZS5sb2coZSkpO1xuICogYGBgXG4gKlxuICogQHBhcmFtIGYgQSBmdW5jdGlvbiB0aGF0IHByb2R1Y2VzIG9uZSBkYXRhIGVsZW1lbnQgb24gZWFjaCBjYWxsLlxuICovXG5leHBvcnQgZnVuY3Rpb24gZnVuYzxUIGV4dGVuZHMgVGVuc29yQ29udGFpbmVyPihcbiAgICBmOiAoKSA9PiBJdGVyYXRvclJlc3VsdDxUPnwgUHJvbWlzZTxJdGVyYXRvclJlc3VsdDxUPj4pOiBEYXRhc2V0PFQ+IHtcbiAgY29uc3QgaXRlciA9IGl0ZXJhdG9yRnJvbUZ1bmN0aW9uKGYpO1xuICByZXR1cm4gZGF0YXNldEZyb21JdGVyYXRvckZuKGFzeW5jICgpID0+IGl0ZXIpO1xufVxuXG4vKipcbiAqIENyZWF0ZSBhIGBEYXRhc2V0YCB0aGF0IHByb2R1Y2VzIGVhY2ggZWxlbWVudCBmcm9tIHByb3ZpZGVkIEphdmFTY3JpcHRcbiAqIGdlbmVyYXRvciwgd2hpY2ggaXMgYSBmdW5jdGlvbiB0aGF0IHJldHVybnMgYSAocG90ZW50aWFsbHkgYXN5bmMpIGl0ZXJhdG9yLlxuICpcbiAqIEZvciBtb3JlIGluZm9ybWF0aW9uIG9uIGl0ZXJhdG9ycyBhbmQgZ2VuZXJhdG9ycywgc2VlXG4gKiBodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9lbi1VUy9kb2NzL1dlYi9KYXZhU2NyaXB0L0d1aWRlL0l0ZXJhdG9yc19hbmRfR2VuZXJhdG9ycyAuXG4gKiBGb3IgdGhlIGl0ZXJhdG9yIHByb3RvY29sLCBzZWVcbiAqIGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2VuLVVTL2RvY3MvV2ViL0phdmFTY3JpcHQvUmVmZXJlbmNlL0l0ZXJhdGlvbl9wcm90b2NvbHMgLlxuICpcbiAqIEV4YW1wbGUgb2YgY3JlYXRpbmcgYSBkYXRhc2V0IGZyb20gYW4gaXRlcmF0b3IgZmFjdG9yeTpcbiAqIGBgYGpzXG4gKiBmdW5jdGlvbiBtYWtlSXRlcmF0b3IoKSB7XG4gKiAgIGNvbnN0IG51bUVsZW1lbnRzID0gMTA7XG4gKiAgIGxldCBpbmRleCA9IDA7XG4gKlxuICogICBjb25zdCBpdGVyYXRvciA9IHtcbiAqICAgICBuZXh0OiAoKSA9PiB7XG4gKiAgICAgICBsZXQgcmVzdWx0O1xuICogICAgICAgaWYgKGluZGV4IDwgbnVtRWxlbWVudHMpIHtcbiAqICAgICAgICAgcmVzdWx0ID0ge3ZhbHVlOiBpbmRleCwgZG9uZTogZmFsc2V9O1xuICogICAgICAgICBpbmRleCsrO1xuICogICAgICAgICByZXR1cm4gcmVzdWx0O1xuICogICAgICAgfVxuICogICAgICAgcmV0dXJuIHt2YWx1ZTogaW5kZXgsIGRvbmU6IHRydWV9O1xuICogICAgIH1cbiAqICAgfTtcbiAqICAgcmV0dXJuIGl0ZXJhdG9yO1xuICogfVxuICogY29uc3QgZHMgPSB0Zi5kYXRhLmdlbmVyYXRvcihtYWtlSXRlcmF0b3IpO1xuICogYXdhaXQgZHMuZm9yRWFjaEFzeW5jKGUgPT4gY29uc29sZS5sb2coZSkpO1xuICogYGBgXG4gKlxuICogRXhhbXBsZSBvZiBjcmVhdGluZyBhIGRhdGFzZXQgZnJvbSBhIGdlbmVyYXRvcjpcbiAqIGBgYGpzXG4gKiBmdW5jdGlvbiogZGF0YUdlbmVyYXRvcigpIHtcbiAqICAgY29uc3QgbnVtRWxlbWVudHMgPSAxMDtcbiAqICAgbGV0IGluZGV4ID0gMDtcbiAqICAgd2hpbGUgKGluZGV4IDwgbnVtRWxlbWVudHMpIHtcbiAqICAgICBjb25zdCB4ID0gaW5kZXg7XG4gKiAgICAgaW5kZXgrKztcbiAqICAgICB5aWVsZCB4O1xuICogICB9XG4gKiB9XG4gKlxuICogY29uc3QgZHMgPSB0Zi5kYXRhLmdlbmVyYXRvcihkYXRhR2VuZXJhdG9yKTtcbiAqIGF3YWl0IGRzLmZvckVhY2hBc3luYyhlID0+IGNvbnNvbGUubG9nKGUpKTtcbiAqIGBgYFxuICpcbiAqIEBwYXJhbSBnZW5lcmF0b3IgQSBKYXZhU2NyaXB0IGZ1bmN0aW9uIHRoYXQgcmV0dXJuc1xuICogICAgIGEgKHBvdGVudGlhbGx5IGFzeW5jKSBKYXZhU2NyaXB0IGl0ZXJhdG9yLlxuICpcbiAqIEBkb2Mge1xuICogICBoZWFkaW5nOiAnRGF0YScsXG4gKiAgIHN1YmhlYWRpbmc6ICdDcmVhdGlvbicsXG4gKiAgIG5hbWVzcGFjZTogJ2RhdGEnLFxuICogICBjb25maWdQYXJhbUluZGljZXM6IFsxXVxuICogIH1cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdlbmVyYXRvcjxUIGV4dGVuZHMgVGVuc29yQ29udGFpbmVyPihcbiAgZ2VuZXJhdG9yOiAoKSA9PiBJdGVyYXRvcjxUPiB8IFByb21pc2U8SXRlcmF0b3I8VD4+IHwgQXN5bmNJdGVyYXRvcjxUPixcbik6IERhdGFzZXQ8VD4ge1xuICByZXR1cm4gZGF0YXNldEZyb21JdGVyYXRvckZuKGFzeW5jICgpID0+IHtcbiAgICBjb25zdCBnZW4gPSBhd2FpdCBnZW5lcmF0b3IoKTtcbiAgICByZXR1cm4gaXRlcmF0b3JGcm9tRnVuY3Rpb24oKCkgPT4gZ2VuLm5leHQoKSk7XG4gIH0pO1xufVxuXG4vKipcbiAqIENyZWF0ZSBhbiBpdGVyYXRvciB0aGF0IGdlbmVyYXRlcyBgVGVuc29yYHMgZnJvbSB3ZWJjYW0gdmlkZW8gc3RyZWFtLiBUaGlzXG4gKiBBUEkgb25seSB3b3JrcyBpbiBCcm93c2VyIGVudmlyb25tZW50IHdoZW4gdGhlIGRldmljZSBoYXMgd2ViY2FtLlxuICpcbiAqIE5vdGU6IHRoaXMgY29kZSBzbmlwcGV0IG9ubHkgd29ya3Mgd2hlbiB0aGUgZGV2aWNlIGhhcyBhIHdlYmNhbS4gSXQgd2lsbFxuICogcmVxdWVzdCBwZXJtaXNzaW9uIHRvIG9wZW4gdGhlIHdlYmNhbSB3aGVuIHJ1bm5pbmcuXG4gKiBgYGBqc1xuICogY29uc3QgdmlkZW9FbGVtZW50ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgndmlkZW8nKTtcbiAqIHZpZGVvRWxlbWVudC53aWR0aCA9IDEwMDtcbiAqIHZpZGVvRWxlbWVudC5oZWlnaHQgPSAxMDA7XG4gKiBjb25zdCBjYW0gPSBhd2FpdCB0Zi5kYXRhLndlYmNhbSh2aWRlb0VsZW1lbnQpO1xuICogY29uc3QgaW1nID0gYXdhaXQgY2FtLmNhcHR1cmUoKTtcbiAqIGltZy5wcmludCgpO1xuICogY2FtLnN0b3AoKTtcbiAqIGBgYFxuICpcbiAqIEBwYXJhbSB3ZWJjYW1WaWRlb0VsZW1lbnQgQSBgSFRNTFZpZGVvRWxlbWVudGAgdXNlZCB0byBwbGF5IHZpZGVvIGZyb21cbiAqICAgICB3ZWJjYW0uIElmIHRoaXMgZWxlbWVudCBpcyBub3QgcHJvdmlkZWQsIGEgaGlkZGVuIGBIVE1MVmlkZW9FbGVtZW50YCB3aWxsXG4gKiAgICAgYmUgY3JlYXRlZC4gSW4gdGhhdCBjYXNlLCBgcmVzaXplV2lkdGhgIGFuZCBgcmVzaXplSGVpZ2h0YCBtdXN0IGJlXG4gKiAgICAgcHJvdmlkZWQgdG8gc2V0IHRoZSBnZW5lcmF0ZWQgdGVuc29yIHNoYXBlLlxuICogQHBhcmFtIHdlYmNhbUNvbmZpZyBBIGBXZWJjYW1Db25maWdgIG9iamVjdCB0aGF0IGNvbnRhaW5zIGNvbmZpZ3VyYXRpb25zIG9mXG4gKiAgICAgcmVhZGluZyBhbmQgbWFuaXB1bGF0aW5nIGRhdGEgZnJvbSB3ZWJjYW0gdmlkZW8gc3RyZWFtLlxuICpcbiAqIEBkb2Mge1xuICogICBoZWFkaW5nOiAnRGF0YScsXG4gKiAgIHN1YmhlYWRpbmc6ICdDcmVhdGlvbicsXG4gKiAgIG5hbWVzcGFjZTogJ2RhdGEnLFxuICogICBpZ25vcmVDSTogdHJ1ZVxuICogIH1cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHdlYmNhbShcbiAgICB3ZWJjYW1WaWRlb0VsZW1lbnQ/OiBIVE1MVmlkZW9FbGVtZW50LFxuICAgIHdlYmNhbUNvbmZpZz86IFdlYmNhbUNvbmZpZyk6IFByb21pc2U8V2ViY2FtSXRlcmF0b3I+IHtcbiAgcmV0dXJuIFdlYmNhbUl0ZXJhdG9yLmNyZWF0ZSh3ZWJjYW1WaWRlb0VsZW1lbnQsIHdlYmNhbUNvbmZpZyk7XG59XG5cbi8qKlxuICogQ3JlYXRlIGFuIGl0ZXJhdG9yIHRoYXQgZ2VuZXJhdGVzIGZyZXF1ZW5jeS1kb21haW4gc3BlY3Ryb2dyYW0gYFRlbnNvcmBzIGZyb21cbiAqIG1pY3JvcGhvbmUgYXVkaW8gc3RyZWFtIHdpdGggYnJvd3NlcidzIG5hdGl2ZSBGRlQuIFRoaXMgQVBJIG9ubHkgd29ya3MgaW5cbiAqIGJyb3dzZXIgZW52aXJvbm1lbnQgd2hlbiB0aGUgZGV2aWNlIGhhcyBtaWNyb3Bob25lLlxuICpcbiAqIE5vdGU6IHRoaXMgY29kZSBzbmlwcGV0IG9ubHkgd29ya3Mgd2hlbiB0aGUgZGV2aWNlIGhhcyBhIG1pY3JvcGhvbmUuIEl0IHdpbGxcbiAqIHJlcXVlc3QgcGVybWlzc2lvbiB0byBvcGVuIHRoZSBtaWNyb3Bob25lIHdoZW4gcnVubmluZy5cbiAqIGBgYGpzXG4gKiBjb25zdCBtaWMgPSBhd2FpdCB0Zi5kYXRhLm1pY3JvcGhvbmUoe1xuICogICBmZnRTaXplOiAxMDI0LFxuICogICBjb2x1bW5UcnVuY2F0ZUxlbmd0aDogMjMyLFxuICogICBudW1GcmFtZXNQZXJTcGVjdHJvZ3JhbTogNDMsXG4gKiAgIHNhbXBsZVJhdGVIejo0NDEwMCxcbiAqICAgaW5jbHVkZVNwZWN0cm9ncmFtOiB0cnVlLFxuICogICBpbmNsdWRlV2F2ZWZvcm06IHRydWVcbiAqIH0pO1xuICogY29uc3QgYXVkaW9EYXRhID0gYXdhaXQgbWljLmNhcHR1cmUoKTtcbiAqIGNvbnN0IHNwZWN0cm9ncmFtVGVuc29yID0gYXVkaW9EYXRhLnNwZWN0cm9ncmFtO1xuICogc3BlY3Ryb2dyYW1UZW5zb3IucHJpbnQoKTtcbiAqIGNvbnN0IHdhdmVmb3JtVGVuc29yID0gYXVkaW9EYXRhLndhdmVmb3JtO1xuICogd2F2ZWZvcm1UZW5zb3IucHJpbnQoKTtcbiAqIG1pYy5zdG9wKCk7XG4gKiBgYGBcbiAqXG4gKiBAcGFyYW0gbWljcm9waG9uZUNvbmZpZyBBIGBNaWNyb3Bob25lQ29uZmlnYCBvYmplY3QgdGhhdCBjb250YWluc1xuICogICAgIGNvbmZpZ3VyYXRpb25zIG9mIHJlYWRpbmcgYXVkaW8gZGF0YSBmcm9tIG1pY3JvcGhvbmUuXG4gKlxuICogQGRvYyB7XG4gKiAgIGhlYWRpbmc6ICdEYXRhJyxcbiAqICAgc3ViaGVhZGluZzogJ0NyZWF0aW9uJyxcbiAqICAgbmFtZXNwYWNlOiAnZGF0YScsXG4gKiAgIGlnbm9yZUNJOiB0cnVlXG4gKiAgfVxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gbWljcm9waG9uZShtaWNyb3Bob25lQ29uZmlnPzogTWljcm9waG9uZUNvbmZpZyk6XG4gICAgUHJvbWlzZTxNaWNyb3Bob25lSXRlcmF0b3I+IHtcbiAgcmV0dXJuIE1pY3JvcGhvbmVJdGVyYXRvci5jcmVhdGUobWljcm9waG9uZUNvbmZpZyk7XG59XG4iXX0=