function chi2test( x, options ) {
	var correction;
	var diffs;
	var csums;
	var rsums;
	var means;
	var opts;
	var pval;
	var stat;
	var err;
	var cnt;
	var buf;
	var dof;
	var sh;
	var M;
	var N;

	if ( isndarrayLike( x ) ) {
		sh = x.shape;
		if ( sh.length !== 2 ) {
			throw new TypeError( format( 'invalid argument. First argument must be an array of arrays or a two-dimensional ndarray-like object. Number of input array dimensions: %u.', sh.length ) );
		}
		buf = copy2buffer( x, sh );
	} else if ( isCollection( x ) ) {
		sh = arrayShape( x );
		if ( sh.length !== 2 ) {
			throw new TypeError( format( 'invalid argument. First argument must be an array of arrays or a two-dimensional ndarray-like object. Number of input array dimensions: %u.', sh.length ) );
		}
		buf = new Float64Array( numel( sh ) );
		flatten2dBy( x, sh, false, buf, 1, 0, flattenClbk );
	} else {
		throw new TypeError( format( 'invalid argument. First argument must be an array of arrays or a two-dimensional ndarray-like object. Value: `%s`.', x ) );
	}
	// NOTE: `buf` is now a single-segment contiguous `Float64Array` containing nonnegative integer values...

	opts = defaults();
	if ( arguments.length > 1 ) {
		err = validate( opts, options );
		if ( err ) {
			throw err;
		}
	}
	// Extract the array dimensions:
	M = sh[ 0 ]; // number of rows
	N = sh[ 1 ]; // number of columns

	// Compute the total number of observations:
	cnt = dsumpw( M*N, buf, 1 );

	// Compute marginal sums:
	rsums = axisSum( buf, M, N, 0 ); // sum across the rows (length: M)
	csums = axisSum( buf, M, N, 1 ); // sum down the columns (length: N)

	// Compute the outer product:
	means = outer( rsums, csums ); // MxN

	// Compute the expected frequencies:
	means = dscal( means.length, 1.0/cnt, means, 1 );

	// Compute the absolute differences between the observed and expected frequencies:
	diffs = absdiff( buf, means );

	// Apply Yates' continuity correction...
	if ( M === 2 && N === 2 && opts.correct ) {
		correction = min( 0.5, dmin( diffs.length, diffs, 1 ) );
		dapx( diffs.length, -correction, diffs, 1 );
	}

	// Compute the test statistic:
	stat = testStatistic( diffs, means );

	// Compute the number of degrees of freedom:
	dof = ( M-1 ) * ( N-1 );

	// Compute the p-value:
	pval = 1 - chisqCDF( stat, dof );

	// Return test results:
	means = new ndarray( 'float64', means, [ M, N ], [ N, 1 ], 0, 'row-major', {
		'readonly': true
	});
	return new Results( pval, opts.alpha, stat, dof, means );
}
Performs a chi-square independence test.

@param {(MatrixLike|Collection<Collection>)} x - two-way table of observed frequencies
@param {Options} [options] - function options
@param {number} [options.alpha=0.05] - significance level
@param {boolean} [options.correct=true] - boolean indicating whether to use Yates' continuity correction when provided a 2x2 contingency table
@throws {TypeError} first argument must be an array of arrays or a two-dimensional ndarray-like object
@throws {TypeError} options argument must be an object
@throws {TypeError} must provide valid options
@throws {RangeError} significance level must be a number on the interval `[0,1]`
@returns {Object} test results

@example
var x = [ [ 20, 30 ], [ 30, 20 ] ];

var out = chi2test( x );

var o = out.toJSON();
// returns { 'rejected': false, 'alpha': 0.05, 'pValue': ~0.072, ... }