#!/usr/bin/env node /** * @license Apache-2.0 * * Copyright (c) 2018 The Stdlib 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. */ 'use strict'; // MODULES // var proc = require( 'process' ); var resolve = require( 'path' ).resolve; var readFileSync = require( '@stdlib/fs/read-file' ).sync; var writeFileSync = require( '@stdlib/fs/write-file' ).sync; var CLI = require( '@stdlib/cli/ctor' ); var stdout = require( '@stdlib/streams/node/stdout' ); var cwd = require( '@stdlib/process/cwd' ); var Uint8Array = require( '@stdlib/array/uint8' ); var Uint32Array = require( '@stdlib/array/uint32' ); var isUint8Array = require( '@stdlib/assert/is-uint8array' ); var isInteger = require( '@stdlib/assert/is-integer' ).isPrimitive; var gcopy = require( '@stdlib/blas/base/gcopy' ); var array2buffer = require( '@stdlib/buffer/from-array' ); var randomStream = require( './../lib' ); // FUNCTIONS // /** * Callback invoked once a source stream ends. * * @private */ function onEnd() { // Append a trailing newline in accordance with standard POSIX behavior: console.log( '' ); // eslint-disable-line no-console } /** * Attempts to load a PRNG state. * * @private * @param {string} filepath - absolute path to file containing PRNG state * @returns {(Uint32Array|Error)} PRNG state or an error */ function loadState( filepath ) { var state; var len; state = readFileSync( filepath ); if ( state instanceof Error ) { return state; } len = state.length; // For older Node.js environments, convert the `Buffer` to a `Uint8Array`... if ( !isUint8Array( state ) ) { state = gcopy( len, state, 1, new Uint8Array( len ), 1 ); } // Create a PRNG state array "view": len /= Uint32Array.BYTES_PER_ELEMENT; if ( !isInteger( len ) ) { return new RangeError( 'invalid option. `state` has an invalid length.' ); } return new Uint32Array( state.buffer, state.byteOffset, len ); } // MAIN // /** * Main execution sequence. * * @private * @returns {void} */ function main() { var stream; var flags; var opts; var args; var cli; var err; var dir; var i; // Create a command-line interface: cli = new CLI({ 'pkg': require( './../package.json' ), 'options': require( './../etc/cli_opts.json' ), 'help': readFileSync( resolve( __dirname, '..', 'docs', 'usage.txt' ), { 'encoding': 'utf8' }) }); // Get any provided command-line options: flags = cli.flags(); if ( flags.help || flags.version ) { return; } // Get the current working directory: dir = cwd(); // Get any provided command-line arguments: args = cli.args(); if ( args.length < 3 ) { err = new Error( 'insufficient arguments. Must provide distribution parameters.' ); return onError( err ); } args[ 0 ] = parseInt( args[ 0 ], 10 ); args[ 1 ] = parseInt( args[ 1 ], 10 ); args[ 2 ] = parseInt( args[ 2 ], 10 ); opts = {}; if ( flags.iter ) { opts.iter = parseInt( flags.iter, 10 ); } if ( flags.sep ) { opts.sep = flags.sep; } if ( flags.state ) { opts.state = loadState( resolve( dir, flags.state ) ); if ( opts.state instanceof Error ) { return onError( opts.state ); } } else if ( flags.seed ) { opts.seed = flags.seed.split( ',' ); for ( i = 0; i < opts.seed.length; i++ ) { opts.seed[ i ] = parseInt( opts.seed[ i ], 10 ); } } if ( flags.snapshot ) { proc.on( 'exit', onExit ); } // Create a source stream and pipe to `stdout`: stream = randomStream( args[ 0 ], args[ 1 ], args[ 2 ], opts ); stream.on( 'end', onEnd ); stream.pipe( stdout ); /** * Callback invoked upon exiting the process. * * @private * @param {integer} code - exit code */ function onExit( code ) { var state; var err; // Get the current PRNG state: state = stream.state; // Create a byte array "view": state = new Uint8Array( state.buffer, state.byteOffset, stream.byteLength ); // eslint-disable-line max-len // Convert the byte array to a `Buffer` (with support for older Node.js environments): state = array2buffer( state ); // Attempt to write the state to file: err = writeFileSync( resolve( dir, flags.snapshot ), state ); if ( err ) { onError( err, code || 1 ); } } /** * Callback invoked upon encountering an error. * * @private * @param {Error} error - error * @param {integer} [code] - exit code */ function onError( error, code ) { cli.error( error, code || 1 ); } } main();