/* OTGrammar_ex_metrics.c */
/* Paul Boersma, November 25, 2001 */

#include "OTGrammar.h"

#define WSP  1
#define FtNonfinal  2
#define Iambic  3
#define Parse  4
#define FootBin  5
#define WFL  6
#define WFR  7
#define Main_L  8
#define Main_R  9
#define AFL  10
#define AFR  11
#define Nonfinal  12

static void path (OTGrammar me, OTGrammarTableau tableau, long numberOfSyllables, int stress [],
	int startingSyllable, int footedToTheLeft_in [], int footedToTheRight_in [], unsigned long weightPattern, int parsing)
{
	static char *syllable [] = { "L", "L1", "L2", "H", "H1", "H2" };
	int isyll, footedToTheLeft [10], footedToTheRight [10];
	/* Localize all arguments. */
	for (isyll = 1; isyll <= startingSyllable; isyll ++) {
		footedToTheLeft [isyll] = footedToTheLeft_in [isyll];
		footedToTheRight [isyll] = footedToTheRight_in [isyll];
	}
	for (isyll = startingSyllable + 1; isyll <= numberOfSyllables; isyll ++)
		footedToTheLeft [isyll] = footedToTheRight [isyll] = 0;
	if (startingSyllable > numberOfSyllables) {
		char output [100];
		if (parsing) {
			output [0] = '\0';
		} else {
			strcpy (output, "[");
			for (isyll = 1; isyll <= numberOfSyllables; isyll ++) {
				if (isyll > 1) strcat (output, " ");
				strcat (output, syllable [stress [isyll] + ( (weightPattern & (1 << (numberOfSyllables - isyll))) != 0 ? 3 : 0 )]);
			}
			strcat (output, "] \\-> ");
		}
		strcat (output, "/");
		for (isyll = 1; isyll <= numberOfSyllables; isyll ++) {
			if (isyll > 1) strcat (output, " ");
			if (footedToTheRight [isyll] || ! footedToTheLeft [isyll] && stress [isyll] != 0) strcat (output, "(");
			strcat (output, syllable [stress [isyll] + ( (weightPattern & (1 << (numberOfSyllables - isyll))) != 0 ? 3 : 0 )]);
			if (footedToTheLeft [isyll] || ! footedToTheRight [isyll] && stress [isyll] != 0) strcat (output, ")");
		}
		strcat (output, "/");
		tableau -> candidates [++ tableau -> numberOfCandidates]. output = Melder_strdup (output); cherror
	} else {
		path (me, tableau, numberOfSyllables, stress, startingSyllable + 1,
			footedToTheLeft, footedToTheRight, weightPattern, parsing); cherror
		if (stress [startingSyllable] == 0 && startingSyllable < numberOfSyllables && stress [startingSyllable + 1] != 0) {
			footedToTheLeft [startingSyllable + 1] = TRUE;
			footedToTheRight [startingSyllable] = TRUE;
			path (me, tableau, numberOfSyllables, stress, startingSyllable + 1,
				footedToTheLeft, footedToTheRight, weightPattern, parsing); cherror
			footedToTheLeft [startingSyllable + 1] = FALSE;
			footedToTheRight [startingSyllable] = FALSE;
		}
		if (stress [startingSyllable] == 0 && startingSyllable > 1 && stress [startingSyllable - 1] != 0
		    && ! footedToTheLeft [startingSyllable - 1])
		{
			footedToTheRight [startingSyllable - 1] = TRUE;
			footedToTheLeft [startingSyllable] = TRUE;
			path (me, tableau, numberOfSyllables, stress, startingSyllable + 1,
				footedToTheLeft, footedToTheRight, weightPattern, parsing); cherror
		}
	}
end:
	return;
}

static void createOvertPatternTableau (OTGrammar me, long numberOfSyllables, int stress [], unsigned long weightPattern, int parsing) {
	OTGrammarTableau tableau;
	char input [100];
	int isyll, footedToTheLeft [10], footedToTheRight [10];
	if (parsing) {
		tableau = & my tableaus [++ my numberOfTableaus];
		strcpy (input, "[");
		for (isyll = 1; isyll <= numberOfSyllables; isyll ++) {
			static char *syllable [] = { "L", "L1", "L2", "H", "H1", "H2" };
			if (isyll > 1) strcat (input, " ");
			strcat (input, syllable [stress [isyll] + ( (weightPattern & (1 << (numberOfSyllables - isyll))) != 0 ? 3 : 0 )]);
		}
		strcat (input, "]");
		tableau -> input = Melder_strdup (input);
		tableau -> candidates = NUMstructvector (OTGrammarCandidate, 1, 21); cherror
	} else {
		if (stress [1] == 1 && stress [2] == 0 && (numberOfSyllables < 3 || stress [3] == 0)
		    && (numberOfSyllables < 4 || stress [4] == 0) && (numberOfSyllables < 5 || stress [5] == 0)
		    && (numberOfSyllables < 6 || stress [6] == 0) && (numberOfSyllables < 7 || stress [7] == 0))
		{
			tableau = & my tableaus [++ my numberOfTableaus];
			strcpy (input, "|");
			for (isyll = 1; isyll <= numberOfSyllables; isyll ++) {
				static char *syllable [] = { "L", "H" };
				if (isyll > 1) strcat (input, " ");
				strcat (input, syllable [(weightPattern & (1 << (numberOfSyllables - isyll))) != 0 ? 1 : 0]);
			}
			strcat (input, "|");
			tableau -> input = Melder_strdup (input);
			tableau -> candidates = NUMstructvector (OTGrammarCandidate, 1, 3136); cherror
		} else {
			tableau = & my tableaus [my numberOfTableaus];
		}
	}
	for (isyll = 1; isyll <= numberOfSyllables; isyll ++)
		footedToTheLeft [isyll] = footedToTheRight [isyll] = 0;
	path (me, tableau, numberOfSyllables, stress, 1, footedToTheLeft, footedToTheRight, weightPattern, parsing); cherror
end:
	return;
}

OTGrammar OTGrammar_create_metrics (int parsing, int equal_footForm_wsp) {
	long icons, itab, icand;
	int numberOfSyllables, mainStressed, secondary1, secondary2, secondary3, secondary4, secondary5, secondary6;
	static char *constraintNames [1+12] = { 0,
		"WSP", "F\\s{T}N\\s{ONFINAL}", "I\\s{AMBIC}", "P\\s{ARSE}",
		"F\\s{OOT}B\\s{IN}", "WFL", "WFR", "M\\s{AIN}-L",
		"M\\s{AIN}-R", "AFL", "AFR", "N\\s{ONFINAL}" };
	OTGrammar me = new (OTGrammar); cherror
	my constraints = NUMstructvector (OTGrammarConstraint, 1, my numberOfConstraints = 12); cherror
	for (icons = 1; icons <= 12; icons ++) {
		OTGrammarConstraint constraint = & my constraints [icons];
		constraint -> name = Melder_strdup (constraintNames [icons]); cherror
		constraint -> ranking = 100.0;
	}
	if (equal_footForm_wsp >= 2) {
		/* Foot form constraints high. */
		my constraints [FtNonfinal]. ranking = 101.0;
		my constraints [Iambic]. ranking = 101.0;
	}
	if (equal_footForm_wsp == 3) {
		/* Quantity sensitivity high, foot form constraints in the second stratum. */
		my constraints [WSP]. ranking = 102.0;
	}
	my tableaus = NUMstructvector (OTGrammarTableau, 1, parsing ? 3824 : 62); cherror
	for (numberOfSyllables = 2; numberOfSyllables <= 7; numberOfSyllables ++) {
		unsigned long weightPattern, maximumWeightPattern = numberOfSyllables <= 5 ? (1 << numberOfSyllables) - 1 : 0;
		for (weightPattern = 0; weightPattern <= maximumWeightPattern; weightPattern ++) {
			for (mainStressed = 1; mainStressed <= numberOfSyllables; mainStressed ++) {
				int stress [10];
				stress [mainStressed] = 1;
				for (secondary1 = FALSE; secondary1 <= TRUE; secondary1 ++) {
					stress [mainStressed <= 1 ? 2 : 1] = secondary1 ? 2 : 0;
					if (numberOfSyllables == 2) {
						createOvertPatternTableau (me, 2, stress, weightPattern, parsing); cherror
					} else for (secondary2 = FALSE; secondary2 <= TRUE; secondary2 ++) {
						stress [mainStressed <= 2 ? 3 : 2] = secondary2 ? 2 : 0;
						if (numberOfSyllables == 3) {
							createOvertPatternTableau (me, 3, stress, weightPattern, parsing); cherror
						} else for (secondary3 = FALSE; secondary3 <= TRUE; secondary3 ++) {
							stress [mainStressed <= 3 ? 4 : 3] = secondary3 ? 2 : 0;
							if (numberOfSyllables == 4) {
								createOvertPatternTableau (me, 4, stress, weightPattern, parsing); cherror
							} else for (secondary4 = FALSE; secondary4 <= TRUE; secondary4 ++) {
								stress [mainStressed <= 4 ? 5 : 4] = secondary4 ? 2 : 0;
								if (numberOfSyllables == 5) {
									createOvertPatternTableau (me, 5, stress, weightPattern, parsing); cherror
								} else for (secondary5 = FALSE; secondary5 <= TRUE; secondary5 ++) {
									stress [mainStressed <= 5 ? 6 : 5] = secondary5 ? 2 : 0;
									if (numberOfSyllables == 6) {
										createOvertPatternTableau (me, 6, stress, weightPattern, parsing); cherror
									} else for (secondary6 = FALSE; secondary6 <= TRUE; secondary6 ++) {
										stress [mainStressed <= 6 ? 7 : 6] = secondary6 ? 2 : 0;
										createOvertPatternTableau (me, 7, stress, weightPattern, parsing); cherror
									}
								}
							}
						}
					}
				}
			}
		}
	}
	/* Compute violation marks. */
	for (itab = 1; itab <= my numberOfTableaus; itab ++) {
		OTGrammarTableau tableau = & my tableaus [itab];
		for (icand = 1; icand <= tableau -> numberOfCandidates; icand ++) {
			OTGrammarCandidate candidate = & tableau -> candidates [icand];
			int depth;
			char *firstSlash = strchr (candidate -> output, '/');
			char *lastSlash = & candidate -> output [strlen (candidate -> output) - 1];
			char *p, *q;
			candidate -> marks = NUMivector (1, candidate -> numberOfConstraints = 12); cherror
			/* Violations of WSP: count all H not followed by 1 or 2. */
			for (p = firstSlash + 1; p != lastSlash; p ++) {
				if (p [0] == 'H' && ! (p [1] == '1' || p [1] == '2'))
					candidate -> marks [WSP] ++;
			}
			/* Violations of FtNonfinal: count all heads followed by ). */
			for (p = firstSlash + 1; p != lastSlash; p ++) {
				if ((p [0] == '1' || p [0] == '2') && p [1] == ')')
					candidate -> marks [FtNonfinal] ++;
			}
			/* Violations of Iambic: count all heads not followed by ). */
			for (p = firstSlash + 1; p != lastSlash; p ++) {
				if ((p [0] == '1' || p [0] == '2') && p [1] != ')')
					candidate -> marks [Iambic] ++;
			}
			/* Violations of Parse: count all syllables not between (). */
			depth = 0;
			for (p = firstSlash + 1; p != lastSlash; p ++) {
				if (p [0] == '(') depth ++;
				else if (p [0] == ')') depth --;
				else if ((p [0] == 'H' || p [0] == 'L') && depth != 1)
					candidate -> marks [Parse] ++;
			}
			/* Violations of FootBin: count all (L1) and (L2). */
			for (p = firstSlash + 1; p != lastSlash; p ++) {
				if (p [0] == 'L' && p [-1] == '(' && (p [1] == '1' || p [1] == '2') && p [2] == ')')
					candidate -> marks [FootBin] ++;
			}
			/* Violations of WFL: count all initial / not followed by (. */
			if (firstSlash [1] != '(')
				candidate -> marks [WFL] = 1;
			/* Violations of WFR: count all final / not preceded by ). */
			if (lastSlash [-1] != ')')
				candidate -> marks [WFR] = 1;
			/* Violations of Main_L: count syllables from foot containing X1 to left edge. */
			for (p = strchr (firstSlash, '1'); *p != '('; p --) { }
			for (; p != firstSlash; p --) {
				if (p [0] == 'H' || p [0] == 'L')
					candidate -> marks [Main_L] ++;
			}
			/* Violations of Main_R: count syllables from foot containing X1 to right edge. */
			for (p = strchr (firstSlash, '1'); *p != ')'; p ++) { }
			for (; p != lastSlash; p ++) {
				if (p [0] == 'H' || p [0] == 'L')
					candidate -> marks [Main_R] ++;
			}
			/* Violations of AFL: count syllables from every foot to left edge. */
			for (p = firstSlash + 1; p != lastSlash; p ++) {
				if (p [0] == '(') {
					for (q = p; q != firstSlash; q --) {
						if (q [0] == 'H' || q [0] == 'L')
							candidate -> marks [AFL] ++;
					}
				}
			}
			/* Violations of AFR: count syllables from every foot to right edge. */
			for (p = firstSlash + 1; p != lastSlash; p ++) {
				if (p [0] == ')') {
					for (q = p; q != lastSlash; q ++) {
						if (q [0] == 'H' || q [0] == 'L')
							candidate -> marks [AFR] ++;
					}
				}
			}
			/* Violations of Nonfinal: count all final / preceded by ). */
			if (lastSlash [-1] == ')')
				candidate -> marks [Nonfinal] = 1;
		}
	}
	OTGrammar_checkIndex (me);
	OTGrammar_newDisharmonies (me, 0.0);
end:	iferror forget (me);
	return me;
}

/* End of file OTGrammar_ex_metrics.c */
