import { t as invariant } from "./invariant-BWiDH1ss.js";

//#region src/assertions/meteor.ts
let PorterStemmer;
let WordNet;
async function ensureNaturalPackage() {
	if (PorterStemmer && WordNet) return;
	try {
		const natural = await import("natural");
		PorterStemmer = natural.PorterStemmer;
		WordNet = natural.WordNet;
	} catch (_err) {
		throw new Error("The \"natural\" package is required for METEOR assertions. Install it with: npm install natural@^8.1.0");
	}
}
function preprocessWord(word) {
	return word.toLowerCase();
}
function generateEnums(candidate, reference, preprocess = preprocessWord) {
	if (typeof candidate === "string") throw new TypeError(`"candidate" expects pre-tokenized candidate (string[]): ${candidate}`);
	if (typeof reference === "string") throw new TypeError(`"reference" expects pre-tokenized reference (string[]): ${reference}`);
	return [candidate.map((word, idx) => [idx, preprocess(word)]), reference.map((word, idx) => [idx, preprocess(word)])];
}
function matchExactEnums(enumCandidateList, enumReferenceList) {
	const wordMatch = [];
	const candidateCopy = [...enumCandidateList];
	const referenceCopy = [...enumReferenceList];
	for (let i = candidateCopy.length - 1; i >= 0; i--) for (let j = referenceCopy.length - 1; j >= 0; j--) if (candidateCopy[i][1] === referenceCopy[j][1]) {
		wordMatch.push([candidateCopy[i][0], referenceCopy[j][0]]);
		candidateCopy.splice(i, 1);
		referenceCopy.splice(j, 1);
		break;
	}
	return [
		wordMatch,
		candidateCopy,
		referenceCopy
	];
}
async function matchStemEnums(enumCandidateList, enumReferenceList, stemmer) {
	await ensureNaturalPackage();
	invariant(PorterStemmer, "PorterStemmer should be loaded");
	const actualStemmer = stemmer || PorterStemmer;
	const candidateCopy = [...enumCandidateList];
	const referenceCopy = [...enumReferenceList];
	const candidateStems = candidateCopy.map(([idx, word]) => [idx, actualStemmer.stem(word)]);
	const referenceStems = referenceCopy.map(([idx, word]) => [idx, actualStemmer.stem(word)]);
	return matchExactEnums(candidateStems.map(([idx, stem]) => [idx, stem]), referenceStems.map(([idx, stem]) => [idx, stem]));
}
async function matchSynonymEnums(enumCandidateList, enumReferenceList, wordnet) {
	await ensureNaturalPackage();
	invariant(WordNet, "WordNet should be loaded");
	const actualWordNet = wordnet || new WordNet();
	const wordMatch = [];
	const candidateCopy = [...enumCandidateList];
	const referenceCopy = [...enumReferenceList];
	for (let i = candidateCopy.length - 1; i >= 0; i--) {
		const candidateWord = candidateCopy[i][1];
		const candidateSynsets = await new Promise((resolve) => {
			actualWordNet.lookup(candidateWord, (results) => resolve(results));
		});
		const candidateSynonymSet = new Set([candidateWord, ...candidateSynsets.flatMap((synset) => synset.synonyms.filter((syn) => !syn.includes("_")))]);
		for (let j = referenceCopy.length - 1; j >= 0; j--) {
			const referenceWord = referenceCopy[j][1];
			if (candidateSynonymSet.has(referenceWord)) {
				wordMatch.push([candidateCopy[i][0], referenceCopy[j][0]]);
				candidateCopy.splice(i, 1);
				referenceCopy.splice(j, 1);
				break;
			}
		}
	}
	return [
		wordMatch,
		candidateCopy,
		referenceCopy
	];
}
function countChunks(matches) {
	if (matches.length === 0) return 0;
	let chunks = 1;
	for (let i = 0; i < matches.length - 1; i++) if (matches[i + 1][0] !== matches[i][0] + 1 || matches[i + 1][1] !== matches[i][1] + 1) chunks++;
	return chunks;
}
async function calculateSingleMeteorScore(reference, candidate, alpha = .9, beta = 3, gamma = .5) {
	const [enumCandidate, enumReference] = generateEnums(candidate, reference);
	const translationLength = enumCandidate.length;
	const referenceLength = enumReference.length;
	const [exactMatches, remainingCandidate, remainingReference] = matchExactEnums(enumCandidate, enumReference);
	const [stemMatches, remainingCandidateAfterStem, remainingReferenceAfterStem] = await matchStemEnums(remainingCandidate, remainingReference);
	const [synonymMatches, ,] = await matchSynonymEnums(remainingCandidateAfterStem, remainingReferenceAfterStem);
	const allMatches = [
		...exactMatches,
		...stemMatches,
		...synonymMatches
	].sort((a, b) => a[0] - b[0]);
	const matchesCount = allMatches.length;
	if (matchesCount === 0) return 0;
	let fragFrac = 0;
	let fmean = 0;
	if (translationLength === 0 || referenceLength === 0 || matchesCount === 0) return 0;
	const precision = matchesCount / translationLength;
	const recall = matchesCount / referenceLength;
	const denominator = alpha * precision + (1 - alpha) * recall;
	if (denominator === 0) return 0;
	fmean = precision * recall / denominator;
	fragFrac = countChunks(allMatches) / matchesCount;
	return (1 - gamma * Math.pow(fragFrac, beta)) * fmean;
}
async function calculateMeteorScore(candidate, references, alpha = .9, beta = 3, gamma = .5) {
	if (!candidate || references.length === 0) throw new Error("Invalid inputs");
	const scores = await Promise.all(references.map((reference) => calculateSingleMeteorScore(reference.split(/\s+/).map((word) => word.replace(/\.+$/, "")), candidate.split(/\s+/).map((word) => word.replace(/\.+$/, "")), alpha, beta, gamma)));
	return Math.max(...scores);
}
async function handleMeteorAssertion({ assertion, inverse, outputString, renderedValue }) {
	invariant(typeof renderedValue === "string" || Array.isArray(renderedValue) && renderedValue.every((v) => typeof v === "string"), "\"meteor\" assertion must have a string or array of strings value");
	const references = Array.isArray(renderedValue) ? renderedValue : [renderedValue];
	const meteorAssertion = assertion;
	const alpha = meteorAssertion.alpha ?? .9;
	const beta = meteorAssertion.beta ?? 3;
	const gamma = meteorAssertion.gamma ?? .5;
	const threshold = meteorAssertion.threshold ?? .5;
	const score = await calculateMeteorScore(outputString, references, alpha, beta, gamma);
	const pass = inverse ? score < threshold : score >= threshold;
	return {
		pass,
		score: inverse ? 1 - score : score,
		reason: pass ? "METEOR assertion passed" : `METEOR score ${score.toFixed(4)} did not meet threshold ${threshold}`,
		assertion
	};
}

//#endregion
export { handleMeteorAssertion };
//# sourceMappingURL=meteor-CiwayH8v.js.map