autoCategories FFNet_Pattern_to_Categories (FFNet me, Pattern thee, int labeling) {
	try {
		if (! my outputCategories) {
			Melder_throw (U"The FFNet has no output categories.");
		}
		if (my nInputs != thy nx) {
			Melder_throw (U"The number of colums in the Pattern (", thy nx, U") should equal the number of inputs in the FFNet (", my nInputs, U").");
		}
		if (! _Pattern_checkElements (thee)) {
			Melder_throw (U"All Pattern elements must be in the interval [0, 1].\nYou could use \"Formula...\" to scale the Pattern values first.");
		}

		autoCategories him = Categories_create ();

		for (long k = 1; k <= thy ny; k++) {
			FFNet_propagate (me, thy z[k], nullptr);
			long index = FFNet_getWinningUnit (me, labeling);
			autoDaata item = Data_copy ((Daata) my outputCategories -> item[index]);
			Collection_addItem_move (him.peek(), item.move());
		}
		return him;
	} catch (MelderError) {
		Melder_throw (me, U": no Categories created.");
	}
}
Categories FFNet_Pattern_to_Categories (FFNet me, Pattern thee, int labeling) {
	try {
		if (my outputCategories == 0) {
			Melder_throw (U"The FFNet has no output categories.");
		}
		if (my nInputs != thy nx) Melder_throw (U"The number of colums in the Pattern (", thy nx,
			                                        U") should equal the number of inputs in the FFNet (", my nInputs, U").");
		if (! _Pattern_checkElements (thee)) Melder_throw
			(U"The elements in the Pattern are not all in the interval [0, 1].\n"
			 U"The input of the neural net can only process values that are between 0 and 1.\n"
			 U"You could use \"Formula...\" to scale the Pattern values first.");


		autoCategories him = Categories_create ();

		for (long k = 1; k <= thy ny; k++) {
			FFNet_propagate (me, thy z[k], 0);
			long index = FFNet_getWinningUnit (me, labeling);
			autoDaata item = Data_copy ( (Daata) my outputCategories -> item[index]);
			Collection_addItem (him.peek(), item.transfer());
		}
		return him.transfer();
	} catch (MelderError) {
		Melder_throw (me, U": no Categories created.");
	}
}
static void _FFNet_Pattern_Categories_checkDimensions (FFNet me, Pattern p, Categories c) {

	if (my nInputs != p -> nx) Melder_throw (U"The Pattern and the FFNet do not match.\n"
		        U"The number of colums in the Pattern must equal the number of inputs in the FFNet.");
	if (p -> ny != c -> size) Melder_throw (U"The Pattern and the categories do not match.\n"
		                                        U"The number of rows in the Pattern must equal the number of categories.");
	if (! _Pattern_checkElements (p)) Melder_throw (U"The elements in the Pattern are not all "
		        U"in the interval [0, 1].\nThe input of the neural net can only process values that are between 0 "
		        U"and 1.\nYou could use \"Formula...\" to scale the Pattern values first.");
}
static void _FFNet_Pattern_Categories_checkDimensions (FFNet me, Pattern p, Categories c) {

	if (my nInputs != p -> nx) {
		Melder_throw (U"The Pattern and the FFNet do not match.\nThe number of colums in the Pattern must equal the number of inputs in the FFNet.");
	}
	if (p -> ny != c -> size) {
		Melder_throw (U"The Pattern and the categories do not match.\nThe number of rows in the Pattern must equal the number of categories.");
	}
	if (! _Pattern_checkElements (p)) {
		Melder_throw (U"All Pattern elements must be in the interval [0, 1].\nYou could use \"Formula...\" to scale the Pattern values first.");
	}
}
static void _FFNet_Pattern_Activation_checkDimensions (FFNet me, Pattern p, Activation a) {
	if (my nInputs != p -> nx) Melder_throw (U"The Pattern and the FFNet do not match.\n"
		        "The number of colums in the Pattern must equal the number of inputs in the FFNet.");
	if (my nOutputs != a -> nx) Melder_throw (U"The Activation and the FFNet do not match.\n"
		        "The number of colums in the Activation must equal the number of outputs in the FFNet.");
	if (p -> ny != a -> ny) Melder_throw (U"The Pattern and the Activation do not match.\n"
		                                      "The number of rows in the Pattern must equal the number of rows in the Activation.");
	if (! _Pattern_checkElements (p)) Melder_throw (U"The elements in the Pattern are not all "
		        "in the interval [0, 1].\nThe input of the neural net can only process values that are between 0 "
		        "and 1.\nYou could use a \"Formula...\" to scale the Pattern values first.");
	if (! _Activation_checkElements (a)) Melder_throw (U"The elements in the Activation are not "
		        "all in the interval [0, 1].\nThe output of the neural net can only process values that are "
		        "between 0 and 1.\nYou could use \"Formula...\" to scale the Activation values first.");
}
Activation FFNet_Pattern_to_Activation (FFNet me, Pattern p, long layer) {
	try {
		if (layer < 1 || layer > my nLayers) {
			layer = my nLayers;
		}
		if (my nInputs != p -> nx) Melder_throw (U"The Pattern and the FFNet do not match. "
			        U"The number of colums in the Pattern (", p -> nx, U") should equal the number of inputs "
			        U"in the FFNet (", my nInputs, U").");
		if (! _Pattern_checkElements (p)) Melder_throw
			(U"The elements in the Activation are not all in the interval [0, 1].\n"
			 U"The output units of the neural net can only process values that are between 0 and 1.\n"
			 U"You could use \"Formula...\" to scale the Activation values first.");

		long nPatterns = p -> ny;
		autoActivation thee = Activation_create (nPatterns, my nUnitsInLayer[layer]);

		for (long i = 1; i <= nPatterns; i++) {
			FFNet_propagateToLayer (me, p -> z[i], thy z[i], layer);
		}
		return thee.transfer();
	} catch (MelderError) {
		Melder_throw (me, U": no Activation created.");
	}
}