group-planner 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/constants.ts","../src/subsetSize.ts","../src/utils.ts","../src/common/options-validator.ts","../src/common/algorithm-base.ts","../src/algorithms/mip-solver.ts","../src/algorithms/mip-solver-lp.ts","../src/algorithms/clustering.ts"],"sourcesContent":["/**\n * Group Planner Library\n * \n * A TypeScript library for competitor partitioning algorithms.\n * Provides three different approaches: Mixed Integer Programming, \n * K-means Clustering, and Branch-and-bound.\n */\n\n// Core types\nexport type {\n Competitor,\n PartitionOptions,\n PartitionResult,\n AlgorithmResult,\n EquipmentClass,\n AgeGroup,\n Gender,\n Algorithm,\n SolutionSpace,\n} from './types';\n\n// Utility functions\nexport {\n validatePartition,\n calculateScore,\n createPartitionResult,\n getGuardianGroups,\n createGuardianFamilies,\n validateGuardianConstraints,\n isValidCompetitor,\n validateCompetitors,\n DEFAULT_OPTIONS,\n} from './utils';\n\nexport { MINIMUM_SUBSET_SIZE, retainLegalSubsets, smallestAllowedSubset } from './subsetSize';\n\n// Constants and validation\nexport {\n EQUIPMENT_CLASSES,\n AGE_GROUPS,\n GENDERS,\n EQUIPMENT_CLASS_DESCRIPTIONS,\n AGE_GROUP_DESCRIPTIONS,\n GENDER_DESCRIPTIONS,\n isValidEquipmentClass,\n isValidAgeGroup,\n isValidGender,\n getAgeGroupOrder,\n compareAgeGroups,\n} from './constants';\n\n// Algorithm implementations\nexport { solveMIP } from './algorithms/mip-solver';\nexport { solveMIPWithLP } from './algorithms/mip-solver-lp';\nexport { solveClustering } from './algorithms/clustering';\n\n// Import types and functions for internal use\nimport type { Competitor, PartitionOptions, AlgorithmResult } from './types';\nimport { solveMIP } from './algorithms/mip-solver';\nimport { solveMIPWithLP } from './algorithms/mip-solver-lp';\nimport { solveClustering } from './algorithms/clustering';\n\n/**\n * Main solver function - single API entry point for all algorithms\n * \n * @param competitors - Array of competitor objects to partition\n * @param options - Configuration options including algorithm selection\n * @returns Algorithm result with partitioned subsets\n * \n * @example\n * ```typescript\n * // Use specific algorithm\n * const result = solve(competitors, { algorithm: 'mip' });\n * \n * // Use auto-selection (runs all algorithms, returns best)\n * const result = solve(competitors, { algorithm: 'auto' });\n * \n * // Use default algorithm (auto)\n * const result = solve(competitors);\n * ```\n */\nexport async function solve(\n competitors: Competitor[],\n options: Partial<PartitionOptions> = {}\n): Promise<AlgorithmResult> {\n const algorithm = options.algorithm || 'auto';\n\n switch (algorithm) {\n case 'mip':\n return solveMIP(competitors, options);\n\n case 'mip-lp':\n return await solveMIPWithLP(competitors, options);\n\n case 'clustering':\n return solveClustering(competitors, options);\n\n case 'auto':\n default:\n // Auto mode: run all algorithms and return the best result\n const results: AlgorithmResult[] = [\n solveMIP(competitors, options),\n await solveMIPWithLP(competitors, options),\n solveClustering(competitors, options),\n ];\n\n const bestResult = results.reduce((best, current) =>\n current.result.score > best.result.score ? current : best\n );\n\n return {\n ...bestResult,\n algorithmUsed: `${bestResult.algorithmUsed} (auto-selected)`,\n };\n }\n}\n\n/**\n * Runs all algorithms and returns detailed results for comparison\n * \n * @param competitors - Array of competitor objects to partition\n * @param options - Configuration options (algorithm option is ignored)\n * @returns Object with best result and all algorithm results\n * \n * @example\n * ```typescript\n * const { bestResult, allResults } = compareAlgorithms(competitors);\n * console.log('Best:', bestResult.algorithmUsed);\n * allResults.forEach(r => console.log(`${r.algorithmUsed}: ${r.result.score}`));\n * ```\n */\nexport async function compareAlgorithms(\n competitors: Competitor[],\n options: Partial<PartitionOptions> = {}\n): Promise<{\n bestResult: AlgorithmResult;\n allResults: AlgorithmResult[];\n}> {\n // Remove algorithm option to avoid affecting individual algorithms\n const { algorithm, ...algorithmOptions } = options;\n\n const results: AlgorithmResult[] = [\n solveMIP(competitors, algorithmOptions),\n await solveMIPWithLP(competitors, algorithmOptions),\n solveClustering(competitors, algorithmOptions),\n ];\n\n const bestResult = results.reduce((best, current) =>\n current.result.score > best.result.score ? current : best\n );\n\n return {\n bestResult,\n allResults: results,\n };\n}\n\n// Version\nexport const version = '0.1.0';\n","/**\n * Constants for competitor categories and validation\n */\n\nimport type { EquipmentClass, AgeGroup, Gender } from './types';\n\n/**\n * Valid equipment classes\n */\nexport const EQUIPMENT_CLASSES: readonly EquipmentClass[] = [\n 'HB', // Heavy Bow\n 'LB', // Light Bow \n 'TR', // Traditional Recurve\n 'BHR', // Barebow Heavy Recurve\n 'BBR', // Barebow Basic Recurve\n 'FSR', // Freestyle Recurve\n 'BBC', // Barebow Compound\n 'BL', // Barebow Longbow\n 'BU', // Barebow Unlimited\n 'FSC', // Freestyle Compound\n 'FU', // Freestyle Unlimited\n] as const;\n\n/**\n * Valid age groups (ordered from youngest to oldest)\n */\nexport const AGE_GROUPS: readonly AgeGroup[] = [\n 'C', // Children\n 'J', // Juniors\n 'YA', // Young Adults\n 'A', // Adults\n 'V', // Veterans\n 'S', // Seniors\n] as const;\n\n/**\n * Valid genders\n */\nexport const GENDERS: readonly Gender[] = [\n 'F', // Female\n 'M', // Male\n] as const;\n\n/**\n * Equipment class descriptions for documentation\n */\nexport const EQUIPMENT_CLASS_DESCRIPTIONS: Record<EquipmentClass, string> = {\n 'HB': 'Heavy Bow',\n 'LB': 'Light Bow',\n 'TR': 'Traditional Recurve',\n 'BHR': 'Barebow Heavy Recurve',\n 'BBR': 'Barebow Basic Recurve',\n 'FSR': 'Freestyle Recurve',\n 'BBC': 'Barebow Compound',\n 'BL': 'Barebow Longbow',\n 'BU': 'Barebow Unlimited',\n 'FSC': 'Freestyle Compound',\n 'FU': 'Freestyle Unlimited',\n};\n\n/**\n * Age group descriptions for documentation\n */\nexport const AGE_GROUP_DESCRIPTIONS: Record<AgeGroup, string> = {\n 'C': 'Children',\n 'J': 'Juniors',\n 'YA': 'Young Adults',\n 'A': 'Adults',\n 'V': 'Veterans',\n 'S': 'Seniors',\n};\n\n/**\n * Gender descriptions for documentation\n */\nexport const GENDER_DESCRIPTIONS: Record<Gender, string> = {\n 'F': 'Female',\n 'M': 'Male',\n};\n\n/**\n * Checks if a value is a valid equipment class\n */\nexport function isValidEquipmentClass(value: string, allowed?: readonly string[]): boolean {\n return (allowed ?? EQUIPMENT_CLASSES).includes(value);\n}\n\n/**\n * Checks if a value is a valid age group\n */\nexport function isValidAgeGroup(value: string, allowed?: readonly string[]): boolean {\n return (allowed ?? AGE_GROUPS).includes(value);\n}\n\n/**\n * Checks if a value is a valid gender\n */\nexport function isValidGender(value: string, allowed?: readonly string[]): boolean {\n return (allowed ?? GENDERS).includes(value);\n}\n\n/**\n * Gets the age group order for comparison (lower number = younger)\n */\nexport function getAgeGroupOrder(ageGroup: AgeGroup): number {\n return AGE_GROUPS.indexOf(ageGroup);\n}\n\n/**\n * Compares two age groups (-1 if a is younger, 0 if same, 1 if a is older)\n */\nexport function compareAgeGroups(a: AgeGroup, b: AgeGroup): number {\n return getAgeGroupOrder(a) - getAgeGroupOrder(b);\n}\n","import type { Competitor } from './types';\n\n/** A target smaller than this is not a group. Size 1 and size 2 are never emitted. */\nexport const MINIMUM_SUBSET_SIZE = 3;\n\nexport function smallestAllowedSubset(requestedMin: number): number {\n return Math.max(MINIMUM_SUBSET_SIZE, requestedMin);\n}\n\nfunction isLinked(competitor: Competitor, group: Competitor[]): boolean {\n if (competitor.guardianId && group.some((member) => member.id === competitor.guardianId)) {\n return true;\n }\n return group.some((member) => member.guardianId === competitor.id);\n}\n\nfunction takeUnlinked(group: Competitor[]): Competitor | undefined {\n for (let index = group.length - 1; index >= 0; index--) {\n const competitor = group[index];\n if (!isLinked(competitor, group)) {\n group.splice(index, 1);\n return competitor;\n }\n }\n return undefined;\n}\n\nfunction undoMoves(short: Competitor[], moved: { person: Competitor; donor: Competitor[] }[]): void {\n for (const move of moved.reverse()) {\n const index = short.indexOf(move.person);\n if (index >= 0) {\n short.splice(index, 1);\n }\n move.donor.push(move.person);\n }\n}\n\nfunction fillFromDonors(groups: Competitor[][], shortIndex: number, min: number, max: number): boolean {\n const short = groups[shortIndex];\n const need = min - short.length;\n if (need <= 0 || short.length + need > max) {\n return false;\n }\n\n const moved: { person: Competitor; donor: Competitor[] }[] = [];\n let remaining = need;\n for (const group of groups) {\n remaining = takeFromDonor(group, short, remaining, min, moved);\n }\n\n if (remaining === 0) {\n return true;\n }\n undoMoves(short, moved);\n return false;\n}\n\nfunction takeFromDonor(\n group: Competitor[],\n short: Competitor[],\n remaining: number,\n min: number,\n moved: { person: Competitor; donor: Competitor[] }[]\n): number {\n let left = remaining;\n while (left > 0 && group !== short && group.length > min) {\n const person = takeUnlinked(group);\n if (!person) {\n return left;\n }\n short.push(person);\n moved.push({ person, donor: group });\n left -= 1;\n }\n return left;\n}\n\nfunction mergeIntoHost(groups: Competitor[][], shortIndex: number, min: number, max: number): boolean {\n const short = groups[shortIndex];\n const hostIndex = groups.findIndex((group, index) => {\n if (index === shortIndex) {\n return false;\n }\n const combined = group.length + short.length;\n return combined >= min && combined <= max;\n });\n if (hostIndex < 0) {\n return false;\n }\n groups[hostIndex].push(...short);\n groups.splice(shortIndex, 1);\n return true;\n}\n\nfunction absorbShortGroup(groups: Competitor[][], min: number, max: number): boolean {\n const shortIndex = groups.findIndex((group) => group.length < min);\n if (shortIndex < 0) {\n return false;\n }\n if (fillFromDonors(groups, shortIndex, min, max)) {\n return true;\n }\n if (mergeIntoHost(groups, shortIndex, min, max)) {\n return true;\n }\n groups.splice(shortIndex, 1);\n return true;\n}\n\n/**\n * Drops or repairs targets of size 1 or 2.\n * A short group is filled from donors, merged into another group, or removed.\n * Removed archers are not given a target.\n */\nexport function retainLegalSubsets(\n subsets: Competitor[][],\n min: number,\n max: number\n): Competitor[][] {\n const floor = smallestAllowedSubset(min);\n const groups = subsets.map((subset) => [...subset]).filter((subset) => subset.length > 0);\n let progressed = true;\n let steps = 0;\n const stepLimit = groups.length * groups.length + groups.length + 1;\n while (progressed && steps < stepLimit) {\n steps += 1;\n progressed = absorbShortGroup(groups, floor, max);\n }\n return groups.filter((group) => group.length >= floor);\n}\n","/**\n * Utility functions for competitor partitioning\n */\n\nimport type { Competitor, PartitionOptions, PartitionResult, SolutionSpace } from './types';\nimport { isValidEquipmentClass, isValidAgeGroup, isValidGender } from './constants';\nimport { smallestAllowedSubset } from './subsetSize';\n\nexport type ResolvedPartitionOptions = Omit<Required<PartitionOptions>, 'solutionSpace'> & {\n solutionSpace?: SolutionSpace;\n};\n\n/**\n * Default partition options\n */\nexport const DEFAULT_OPTIONS: ResolvedPartitionOptions = {\n algorithm: 'auto',\n groupByEquipmentClass: true,\n maxSubsets: 28,\n minSubsetSize: 3,\n maxSubsetSize: 6,\n preferredSubsetSize: 4,\n genderWeight: 1.0,\n ageCategoryWeight: 1.0,\n equipmentClassWeight: 1.0,\n closedGroupWeight: 0,\n};\n\n/**\n * Validates that a partition solution meets all constraints\n */\nexport function validatePartition(\n competitors: Competitor[],\n subsets: Competitor[][],\n options: Partial<PartitionOptions> = {}\n): { valid: boolean; errors: string[] } {\n const opts = { ...DEFAULT_OPTIONS, ...options };\n const errors: string[] = [];\n\n // Check all competitors are assigned exactly once\n const assignedCompetitors = new Set<string>();\n for (const subset of subsets) {\n for (const competitor of subset) {\n if (assignedCompetitors.has(competitor.id)) {\n errors.push(`Competitor ${competitor.id} assigned to multiple subsets`);\n }\n assignedCompetitors.add(competitor.id);\n }\n }\n\n if (assignedCompetitors.size !== competitors.length) {\n errors.push(`Not all competitors assigned: ${assignedCompetitors.size}/${competitors.length}`);\n }\n\n // Check subset size constraints\n for (let i = 0; i < subsets.length; i++) {\n const size = subsets[i].length;\n const minimum = smallestAllowedSubset(opts.minSubsetSize);\n if (size < minimum) {\n errors.push(`Subset ${i} too small: ${size} < ${minimum}`);\n }\n if (size > opts.maxSubsetSize) {\n errors.push(`Subset ${i} too large: ${size} > ${opts.maxSubsetSize}`);\n }\n }\n\n // Check maximum number of subsets\n if (subsets.length > opts.maxSubsets) {\n errors.push(`Too many subsets: ${subsets.length} > ${opts.maxSubsets}`);\n }\n\n // Check guardian constraints\n const guardianValidation = validateGuardianConstraints(competitors, subsets);\n errors.push(...guardianValidation.errors);\n\n return { valid: errors.length === 0, errors };\n}\n\nexport type SubsetScore = {\n size: number;\n closedGroup: number;\n gender: number;\n ageCategory: number;\n equipmentClass: number;\n total: number;\n};\n\nfunction mean(total: number, count: number): number {\n return count === 0 ? 0 : total / count;\n}\n\nfunction countLabels(subset: Competitor[], label: (competitor: Competitor) => string): Map<string, number> {\n const counts = new Map<string, number>();\n for (const competitor of subset) {\n const key = label(competitor);\n counts.set(key, (counts.get(key) ?? 0) + 1);\n }\n return counts;\n}\n\nfunction largestShare(counts: Map<string, number>, size: number): number {\n return Math.max(...counts.values()) / size;\n}\n\nfunction evenShare(counts: Map<string, number>, size: number): number {\n let even = 0;\n for (const count of counts.values()) {\n if (count % 2 === 0) {\n even += count;\n }\n }\n return even / size;\n}\n\nfunction equipmentPoints(subset: Competitor[], options: ResolvedPartitionOptions): number {\n const counts = countLabels(subset, (competitor) => competitor.equipmentClass);\n const size = subset.length;\n if (options.groupByEquipmentClass) {\n return largestShare(counts, size) * 10 * options.equipmentClassWeight;\n }\n return (counts.size / Math.min(size, 3)) * 10 * options.equipmentClassWeight;\n}\n\n/**\n * Score one subset. The greedy solver uses this while it fills groups.\n */\nexport function scoreSubset(\n subset: Competitor[],\n options: Partial<PartitionOptions> = {}\n): SubsetScore {\n if (subset.length === 0) {\n return { size: 0, closedGroup: 0, gender: 0, ageCategory: 0, equipmentClass: 0, total: 0 };\n }\n\n const opts = { ...DEFAULT_OPTIONS, ...options };\n const size = subset.length;\n const sizeScore = Math.max(0, 10 - Math.abs(size - opts.preferredSubsetSize) * 2);\n const closedGroup = size === opts.preferredSubsetSize ? 10 * opts.closedGroupWeight : 0;\n const gender = evenShare(countLabels(subset, (competitor) => competitor.gender), size) * 10 * opts.genderWeight;\n const ageCategory = largestShare(countLabels(subset, (competitor) => competitor.ageCategory), size) * 10 * opts.ageCategoryWeight;\n const equipmentClass = equipmentPoints(subset, opts);\n return {\n size: sizeScore,\n closedGroup,\n gender,\n ageCategory,\n equipmentClass,\n total: sizeScore + closedGroup + gender + ageCategory + equipmentClass,\n };\n}\n\n/**\n * Calculates the quality score for a partition solution\n */\nexport function calculateScore(\n subsets: Competitor[][],\n options: Partial<PartitionOptions> = {}\n): { score: number; breakdown: PartitionResult['metadata']['scoreBreakdown'] } {\n const totals = { size: 0, closedGroup: 0, gender: 0, ageCategory: 0, equipmentClass: 0 };\n\n for (const subset of subsets) {\n const parts = scoreSubset(subset, options);\n totals.size += parts.size;\n totals.closedGroup += parts.closedGroup;\n totals.gender += parts.gender;\n totals.ageCategory += parts.ageCategory;\n totals.equipmentClass += parts.equipmentClass;\n }\n\n const breakdown = {\n sizeScore: mean(totals.size, subsets.length),\n closedGroupScore: mean(totals.closedGroup, subsets.length),\n genderScore: mean(totals.gender, subsets.length),\n ageCategoryScore: mean(totals.ageCategory, subsets.length),\n equipmentClassScore: mean(totals.equipmentClass, subsets.length),\n };\n\n const score = breakdown.sizeScore\n + breakdown.closedGroupScore\n + breakdown.genderScore\n + breakdown.ageCategoryScore\n + breakdown.equipmentClassScore;\n\n return { score, breakdown };\n}\n\n/**\n * Creates a partition result with metadata\n */\nexport function createPartitionResult(\n subsets: Competitor[][],\n options: Partial<PartitionOptions> = {}\n): PartitionResult {\n const totalCompetitors = subsets.reduce((sum, subset) => sum + subset.length, 0);\n const { score, breakdown } = calculateScore(subsets, options);\n\n return {\n subsets,\n score,\n metadata: {\n totalCompetitors,\n totalSubsets: subsets.length,\n averageSubsetSize: subsets.length === 0 ? 0 : totalCompetitors / subsets.length,\n scoreBreakdown: breakdown,\n },\n };\n}\n\n/**\n * Helper to get competitors by guardian relationship\n */\nexport function getGuardianGroups(competitors: Competitor[]): Map<string, Competitor[]> {\n const guardianGroups = new Map<string, Competitor[]>();\n\n for (const competitor of competitors) {\n if (competitor.guardianId) {\n const key = competitor.guardianId;\n if (!guardianGroups.has(key)) {\n guardianGroups.set(key, []);\n }\n guardianGroups.get(key)!.push(competitor);\n\n // Also add the guardian if they exist in the competitors list\n const guardian = competitors.find(c => c.id === competitor.guardianId);\n if (guardian && !guardianGroups.get(key)!.includes(guardian)) {\n guardianGroups.get(key)!.push(guardian);\n }\n }\n }\n\n return guardianGroups;\n}\n\n/**\n * Groups competitors into guardian families (guardian + their dependents)\n * Returns an array where each element is either a single competitor or a guardian family\n */\nexport function createGuardianFamilies(competitors: Competitor[]): Competitor[][] {\n const families: Competitor[][] = [];\n const processed = new Set<string>();\n\n // Create a map of guardians to their dependents\n const guardianToDependents = new Map<string, Competitor[]>();\n const dependentToGuardian = new Map<string, string>();\n\n for (const competitor of competitors) {\n if (competitor.guardianId) {\n if (!guardianToDependents.has(competitor.guardianId)) {\n guardianToDependents.set(competitor.guardianId, []);\n }\n guardianToDependents.get(competitor.guardianId)!.push(competitor);\n dependentToGuardian.set(competitor.id, competitor.guardianId);\n }\n }\n\n for (const competitor of competitors) {\n if (processed.has(competitor.id)) {\n continue;\n }\n\n // If this competitor is a guardian, create a family\n if (guardianToDependents.has(competitor.id)) {\n const family = [competitor, ...guardianToDependents.get(competitor.id)!];\n families.push(family);\n\n // Mark all family members as processed\n for (const member of family) {\n processed.add(member.id);\n }\n }\n // If this competitor is a dependent, skip (already processed with guardian)\n else if (dependentToGuardian.has(competitor.id)) {\n // Will be processed when we encounter their guardian\n continue;\n }\n // If this competitor has no guardian relationship, they're a single unit\n else {\n families.push([competitor]);\n processed.add(competitor.id);\n }\n }\n\n return families;\n}\n\n/**\n * Validates that guardian constraints are satisfied in the partition\n */\nexport function validateGuardianConstraints(\n competitors: Competitor[],\n subsets: Competitor[][]\n): { valid: boolean; errors: string[] } {\n const errors: string[] = [];\n\n // Create a map of competitor ID to subset index\n const competitorToSubset = new Map<string, number>();\n for (let subsetIndex = 0; subsetIndex < subsets.length; subsetIndex++) {\n for (const competitor of subsets[subsetIndex]) {\n competitorToSubset.set(competitor.id, subsetIndex);\n }\n }\n\n // Check each competitor with a guardian\n for (const competitor of competitors) {\n if (competitor.guardianId) {\n const competitorSubset = competitorToSubset.get(competitor.id);\n const guardianSubset = competitorToSubset.get(competitor.guardianId);\n\n if (competitorSubset === undefined) {\n errors.push(`Competitor ${competitor.id} not assigned to any subset`);\n continue;\n }\n\n if (guardianSubset === undefined) {\n errors.push(`Guardian ${competitor.guardianId} for competitor ${competitor.id} not found in any subset`);\n continue;\n }\n\n if (competitorSubset !== guardianSubset) {\n errors.push(`Competitor ${competitor.id} and their guardian ${competitor.guardianId} are in different subsets (${competitorSubset} vs ${guardianSubset})`);\n }\n }\n }\n\n return { valid: errors.length === 0, errors };\n}\n\n/**\n * Shuffles an array using Fisher-Yates algorithm\n */\nexport function shuffle<T>(array: T[]): T[] {\n const result = [...array];\n for (let i = result.length - 1; i > 0; i--) {\n const j = Math.floor(Math.random() * (i + 1));\n [result[i], result[j]] = [result[j], result[i]];\n }\n return result;\n}\n\n/**\n * Validates a competitor object\n */\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\nexport function isValidCompetitor(competitor: unknown, space?: SolutionSpace): competitor is Competitor {\n if (!isRecord(competitor)) {\n return false;\n }\n\n const { id, equipmentClass, ageCategory, gender, guardianId } = competitor;\n\n if (typeof id !== 'string' || id.trim().length === 0) {\n return false;\n }\n\n if (typeof equipmentClass !== 'string' || !isValidEquipmentClass(equipmentClass, space?.equipmentClasses)) {\n return false;\n }\n\n if (typeof ageCategory !== 'string' || !isValidAgeGroup(ageCategory, space?.ageCategories)) {\n return false;\n }\n\n if (typeof gender !== 'string' || !isValidGender(gender, space?.genders)) {\n return false;\n }\n\n // Check optional guardian ID\n if (guardianId !== undefined && (typeof guardianId !== 'string' || guardianId.trim().length === 0)) {\n return false;\n }\n\n return true;\n}\n\n/**\n * Validates an array of competitors\n */\nexport function validateCompetitors(competitors: any[], space?: SolutionSpace): { valid: boolean; errors: string[] } {\n const errors: string[] = [];\n\n if (!Array.isArray(competitors)) {\n errors.push('Competitors must be an array');\n return { valid: false, errors };\n }\n\n if (competitors.length === 0) {\n errors.push('At least one competitor is required');\n return { valid: false, errors };\n }\n\n if (competitors.length > 168) {\n errors.push('Maximum 168 competitors allowed');\n }\n\n // Check each competitor\n const competitorIds = new Set<string>();\n competitors.forEach((competitor, index) => {\n if (!isValidCompetitor(competitor, space)) {\n errors.push(`Competitor at index ${index} is invalid`);\n return;\n }\n\n // Check for duplicate IDs\n if (competitorIds.has(competitor.id)) {\n errors.push(`Duplicate competitor ID: ${competitor.id}`);\n } else {\n competitorIds.add(competitor.id);\n }\n });\n\n // Check guardian references exist\n competitors.forEach(competitor => {\n if (competitor.guardianId && !competitors.some(c => c.id === competitor.guardianId)) {\n errors.push(`Guardian ${competitor.guardianId} for competitor ${competitor.id} not found`);\n }\n });\n\n // Check for circular guardian references\n const visited = new Set<string>();\n const recursionStack = new Set<string>();\n\n const hasCircularReference = (competitorId: string): boolean => {\n if (recursionStack.has(competitorId)) {\n return true; // Circular reference found\n }\n if (visited.has(competitorId)) {\n return false; // Already processed\n }\n\n visited.add(competitorId);\n recursionStack.add(competitorId);\n\n const competitor = competitors.find(c => c.id === competitorId);\n if (competitor?.guardianId) {\n if (hasCircularReference(competitor.guardianId)) {\n return true;\n }\n }\n\n recursionStack.delete(competitorId);\n return false;\n };\n\n for (const competitor of competitors) {\n if (hasCircularReference(competitor.id)) {\n errors.push(`Circular guardian reference detected involving competitor ${competitor.id}`);\n break;\n }\n }\n\n // Check family size constraints (max 3 members: 1 guardian + 2 dependents)\n const guardianFamilySizes = new Map<string, number>();\n\n // Count family sizes\n competitors.forEach(competitor => {\n if (competitor.guardianId) {\n // This is a dependent - increment the guardian's family size\n guardianFamilySizes.set(competitor.guardianId, (guardianFamilySizes.get(competitor.guardianId) || 1) + 1);\n } else {\n // This could be a guardian - ensure they're counted\n if (!guardianFamilySizes.has(competitor.id)) {\n guardianFamilySizes.set(competitor.id, 1);\n }\n }\n });\n\n // Check if any family exceeds the limit\n guardianFamilySizes.forEach((familySize, guardianId) => {\n if (familySize > 3) {\n errors.push(`Family with guardian ${guardianId} has ${familySize} members, maximum allowed is 3 (1 guardian + 2 dependents)`);\n }\n });\n\n return { valid: errors.length === 0, errors };\n}\n","/**\n * Common options validation and sanitization functionality\n * Used across all algorithm implementations\n */\n\nimport type { PartitionOptions } from '../types';\nimport { MINIMUM_SUBSET_SIZE } from '../subsetSize';\nimport { DEFAULT_OPTIONS, type ResolvedPartitionOptions } from '../utils';\n\nexport interface ValidatedPartitionOptions extends ResolvedPartitionOptions {\n // All properties are required and validated\n}\n\n/**\n * Validates and sanitizes partition options with common logic\n * @param options - Partial options to validate\n * @param competitorCount - Number of competitors (used for special constraints)\n * @returns Validated and sanitized options\n */\nexport function validateAndSanitizeOptions(\n options: Partial<PartitionOptions>,\n competitorCount: number\n): ValidatedPartitionOptions {\n const explicitMax = options.maxSubsetSize !== undefined && options.maxSubsetSize >= MINIMUM_SUBSET_SIZE;\n const sanitizedOptions = { ...DEFAULT_OPTIONS, ...options };\n swapInvertedSizes(sanitizedOptions);\n applySizeFloors(sanitizedOptions);\n applyDefaultFieldCap(sanitizedOptions, explicitMax, competitorCount);\n clampPreferredSize(sanitizedOptions);\n if (sanitizedOptions.closedGroupWeight < 0) {\n sanitizedOptions.closedGroupWeight = 0;\n }\n\n return sanitizedOptions;\n}\n\nfunction swapInvertedSizes(options: ResolvedPartitionOptions): void {\n if (options.minSubsetSize <= options.maxSubsetSize) {\n return;\n }\n const previousMin = options.minSubsetSize;\n options.minSubsetSize = options.maxSubsetSize;\n options.maxSubsetSize = previousMin;\n}\n\nfunction applySizeFloors(options: ResolvedPartitionOptions): void {\n if (options.minSubsetSize < MINIMUM_SUBSET_SIZE) {\n options.minSubsetSize = MINIMUM_SUBSET_SIZE;\n }\n if (options.maxSubsetSize < options.minSubsetSize) {\n options.maxSubsetSize = options.minSubsetSize;\n }\n}\n\nfunction applyDefaultFieldCap(\n options: ResolvedPartitionOptions,\n explicitMax: boolean,\n competitorCount: number\n): void {\n const useFieldCap = !explicitMax && competitorCount > 0 && competitorCount <= 112;\n if (!useFieldCap) {\n return;\n }\n options.maxSubsetSize = Math.min(options.maxSubsetSize, 4);\n if (options.maxSubsetSize < options.minSubsetSize) {\n options.maxSubsetSize = options.minSubsetSize;\n }\n}\n\nfunction clampPreferredSize(options: ResolvedPartitionOptions): void {\n if (options.preferredSubsetSize < options.minSubsetSize) {\n options.preferredSubsetSize = options.minSubsetSize;\n }\n if (options.preferredSubsetSize > options.maxSubsetSize) {\n options.preferredSubsetSize = options.maxSubsetSize;\n }\n}\n\n/**\n * Calculates the optimal number of subsets based on options and competitor count\n */\nexport function calculateOptimalSubsetCount(\n competitorCount: number,\n options: ValidatedPartitionOptions\n): {\n minSubsets: number;\n maxSubsets: number;\n preferredSubsets: number;\n recommendedSubsets: number;\n} {\n const minSubsets = Math.ceil(competitorCount / options.maxSubsetSize);\n const maxSubsets = Math.min(options.maxSubsets, Math.floor(competitorCount / options.minSubsetSize));\n const preferredSubsets = Math.round(competitorCount / options.preferredSubsetSize);\n\n // Recommend a subset count that balances constraints\n const recommendedSubsets = Math.max(\n minSubsets,\n Math.min(maxSubsets, preferredSubsets)\n );\n\n return {\n minSubsets,\n maxSubsets,\n preferredSubsets,\n recommendedSubsets\n };\n}\n","/**\n * Common algorithm functionality and base patterns\n */\n\nimport type { Competitor, PartitionOptions, AlgorithmResult } from '../types';\nimport { createPartitionResult } from '../utils';\nimport { retainLegalSubsets, smallestAllowedSubset } from '../subsetSize';\nimport { validateAndSanitizeOptions, ValidatedPartitionOptions } from './options-validator';\n\n/**\n * Common empty dataset handling for all algorithms\n */\nexport function handleEmptyDataset(\n options: Partial<PartitionOptions>,\n algorithmName: string\n): AlgorithmResult {\n const validatedOptions = validateAndSanitizeOptions(options, 0);\n\n return {\n result: createPartitionResult([], validatedOptions),\n executionTime: 0,\n algorithmUsed: algorithmName,\n };\n}\n\n/**\n * Common algorithm result wrapper that handles timing and validation\n */\nexport function createAlgorithmResult(\n competitors: Competitor[],\n subsets: Competitor[][],\n options: ValidatedPartitionOptions,\n algorithmName: string,\n startTime: number\n): AlgorithmResult {\n const executionTime = performance.now() - startTime;\n const legalSubsets = retainLegalSubsets(subsets, options.minSubsetSize, options.maxSubsetSize);\n\n return {\n result: createPartitionResult(legalSubsets, options),\n executionTime,\n algorithmUsed: algorithmName,\n };\n}\n\n/**\n * Common solution validation logic\n */\nexport function validateAlgorithmSolution(\n competitors: Competitor[],\n subsets: Competitor[][],\n options: ValidatedPartitionOptions\n): { valid: boolean; errors: string[] } {\n const errors: string[] = [];\n\n // Check all competitors are assigned exactly once\n const assignedIds = new Set<string>();\n for (const subset of subsets) {\n for (const competitor of subset) {\n if (assignedIds.has(competitor.id)) {\n errors.push(`Competitor ${competitor.id} assigned to multiple subsets`);\n }\n assignedIds.add(competitor.id);\n }\n }\n\n // Check all competitors are assigned\n if (assignedIds.size !== competitors.length) {\n errors.push(`Not all competitors assigned: ${assignedIds.size}/${competitors.length}`);\n }\n\n // Check subset size constraints\n for (let i = 0; i < subsets.length; i++) {\n const size = subsets[i].length;\n const minimum = smallestAllowedSubset(options.minSubsetSize);\n if (size < minimum) {\n errors.push(`Subset ${i} too small: ${size} < ${minimum}`);\n }\n if (size > options.maxSubsetSize) {\n errors.push(`Subset ${i} too large: ${size} > ${options.maxSubsetSize}`);\n }\n }\n\n // Check maximum number of subsets\n if (subsets.length > options.maxSubsets) {\n errors.push(`Too many subsets: ${subsets.length} > ${options.maxSubsets}`);\n }\n\n // Check guardian constraints\n for (const subset of subsets) {\n const subsetIds = new Set(subset.map(c => c.id));\n for (const competitor of subset) {\n if (competitor.guardianId && !subsetIds.has(competitor.guardianId)) {\n errors.push(`Competitor ${competitor.id} and their guardian ${competitor.guardianId} are in different subsets`);\n }\n }\n }\n\n return { valid: errors.length === 0, errors };\n}\n\n/**\n * Common interface for algorithm implementations\n */\nexport interface AlgorithmImplementation {\n name: string;\n solve(competitors: Competitor[], options: Partial<PartitionOptions>): AlgorithmResult | Promise<AlgorithmResult>;\n}\n\n/**\n * Abstract base class for algorithm implementations with common functionality\n */\nexport abstract class BaseAlgorithm implements AlgorithmImplementation {\n abstract name: string;\n\n protected handleEmptyInput(options: Partial<PartitionOptions>): AlgorithmResult {\n return handleEmptyDataset(options, this.name);\n }\n\n protected validateAndSanitizeOptions(options: Partial<PartitionOptions>, competitorCount: number): ValidatedPartitionOptions {\n return validateAndSanitizeOptions(options, competitorCount);\n }\n\n protected createResult(\n competitors: Competitor[],\n subsets: Competitor[][],\n options: ValidatedPartitionOptions,\n startTime: number\n ): AlgorithmResult {\n return createAlgorithmResult(competitors, subsets, options, this.name, startTime);\n }\n\n protected validateSolution(\n competitors: Competitor[],\n subsets: Competitor[][],\n options: ValidatedPartitionOptions\n ): { valid: boolean; errors: string[] } {\n return validateAlgorithmSolution(competitors, subsets, options);\n }\n\n abstract solve(competitors: Competitor[], options: Partial<PartitionOptions>): AlgorithmResult | Promise<AlgorithmResult>;\n}\n","/**\n * Mixed Integer Programming approach for competitor partitioning\n * \n * This algorithm formulates the partitioning problem as a MIP problem where:\n * - Binary variables x[i][j] indicate if competitor i is in subset j\n * - Objective function maximizes the quality score based on preferences\n * - Constraints ensure valid partitioning\n */\n\nimport type { Competitor, PartitionOptions, AlgorithmResult } from '../types';\nimport { createPartitionResult, validatePartition, createGuardianFamilies, scoreSubset, type ResolvedPartitionOptions } from '../utils';\nimport { validateAndSanitizeOptions, ValidatedPartitionOptions } from '../common/options-validator';\nimport { handleEmptyDataset, createAlgorithmResult } from '../common/algorithm-base';\n\ninterface MIPVariable {\n competitorIndex: number;\n subsetIndex: number;\n value: number; // 0 or 1\n}\n\ninterface MIPConstraint {\n variables: { competitorIndex: number; subsetIndex: number; coefficient: number }[];\n operator: '=' | '<=' | '>=';\n rhs: number;\n name: string;\n}\n\nfunction openSubsetFor(\n subsets: Competitor[][],\n familySize: number,\n currentSubset: number,\n options: { maxSubsetSize: number; preferredSubsetSize: number },\n maxSubsets: number\n): { index: number; nextSubset: number } | null {\n const started = Math.min(currentSubset, subsets.length);\n let bestIndex = -1;\n let bestScore = -1;\n\n for (let index = 0; index < started; index++) {\n const available = options.maxSubsetSize - subsets[index].length;\n if (available < familySize) {\n continue;\n }\n const newSize = subsets[index].length + familySize;\n const totalScore = 1000 - Math.abs(newSize - options.preferredSubsetSize) + available;\n if (totalScore > bestScore) {\n bestScore = totalScore;\n bestIndex = index;\n }\n }\n\n if (bestIndex >= 0) {\n return { index: bestIndex, nextSubset: currentSubset };\n }\n if (currentSubset >= maxSubsets) {\n return null;\n }\n return { index: currentSubset, nextSubset: currentSubset + 1 };\n}\n\n/**\n * Simple MIP solver using branch-and-bound with linear relaxation\n * Note: In production, you would use a proper MIP solver like CPLEX, Gurobi, or GLPK\n */\nclass SimpleMIPSolver {\n private competitors: Competitor[];\n private options: ResolvedPartitionOptions;\n private numCompetitors: number;\n private maxSubsets: number;\n private variables: MIPVariable[];\n private constraints: MIPConstraint[];\n private bestSolution: MIPVariable[] | null = null;\n private bestObjective = -Infinity;\n\n constructor(competitors: Competitor[], options: ResolvedPartitionOptions) {\n this.competitors = competitors;\n\n // Use common options validation\n this.options = validateAndSanitizeOptions(options, competitors.length);\n\n this.numCompetitors = competitors.length;\n this.maxSubsets = Math.min(this.options.maxSubsets, Math.ceil(competitors.length / this.options.minSubsetSize));\n this.variables = [];\n this.constraints = [];\n this.initializeVariables();\n this.initializeConstraints();\n }\n\n private initializeVariables(): void {\n // Create binary variables x[i][j] for competitor i in subset j\n for (let i = 0; i < this.numCompetitors; i++) {\n for (let j = 0; j < this.maxSubsets; j++) {\n this.variables.push({\n competitorIndex: i,\n subsetIndex: j,\n value: 0,\n });\n }\n }\n }\n\n private initializeConstraints(): void {\n // Constraint 1: Each competitor must be in exactly one subset\n for (let i = 0; i < this.numCompetitors; i++) {\n const constraint: MIPConstraint = {\n variables: [],\n operator: '=',\n rhs: 1,\n name: `competitor_${i}_assignment`,\n };\n\n for (let j = 0; j < this.maxSubsets; j++) {\n constraint.variables.push({\n competitorIndex: i,\n subsetIndex: j,\n coefficient: 1,\n });\n }\n\n this.constraints.push(constraint);\n }\n\n // Constraint 2: Subset size limits\n for (let j = 0; j < this.maxSubsets; j++) {\n // Minimum size constraint (if subset is used)\n const minConstraint: MIPConstraint = {\n variables: [],\n operator: '>=',\n rhs: 0, // Will be adjusted based on subset usage\n name: `subset_${j}_min_size`,\n };\n\n // Maximum size constraint\n const maxConstraint: MIPConstraint = {\n variables: [],\n operator: '<=',\n rhs: this.options.maxSubsetSize,\n name: `subset_${j}_max_size`,\n };\n\n for (let i = 0; i < this.numCompetitors; i++) {\n minConstraint.variables.push({\n competitorIndex: i,\n subsetIndex: j,\n coefficient: 1,\n });\n maxConstraint.variables.push({\n competitorIndex: i,\n subsetIndex: j,\n coefficient: 1,\n });\n }\n\n this.constraints.push(maxConstraint);\n // Note: Min constraint is handled differently in the objective\n }\n }\n\n private calculateObjectiveValue(solution: MIPVariable[]): number {\n // Convert solution to subsets\n const subsets: Competitor[][] = Array(this.maxSubsets).fill(null).map(() => []);\n\n for (const variable of solution) {\n if (variable.value === 1) {\n subsets[variable.subsetIndex].push(this.competitors[variable.competitorIndex]);\n }\n }\n\n // Remove empty subsets\n const nonEmptySubsets = subsets.filter(subset => subset.length > 0);\n\n // Check if solution is valid\n const validation = validatePartition(this.competitors, nonEmptySubsets, this.options);\n if (!validation.valid) {\n return -Infinity; // Invalid solution\n }\n\n // Calculate score\n const result = createPartitionResult(nonEmptySubsets, this.options);\n return result.score;\n }\n\n private isSolutionValid(solution: MIPVariable[]): boolean {\n // Check that each competitor is assigned exactly once\n const competitorAssignments = new Array(this.numCompetitors).fill(0);\n\n for (const variable of solution) {\n if (variable.value === 1) {\n competitorAssignments[variable.competitorIndex]++;\n }\n }\n\n return competitorAssignments.every(count => count === 1);\n }\n\n private solveGreedy(): MIPVariable[] {\n // Greedy heuristic with guardian constraints: assign guardian families as units\n const solution = this.variables.map(v => ({ ...v, value: 0 }));\n const subsets: Competitor[][] = Array(this.maxSubsets).fill(null).map(() => []);\n\n // Create guardian families (each family must be assigned together)\n const families = createGuardianFamilies(this.competitors);\n const unassignedFamilies = [...families];\n let currentSubset = 0;\n\n while (unassignedFamilies.length > 0 && currentSubset < this.maxSubsets) {\n const subset = subsets[currentSubset];\n\n // Try to fill current subset with families\n let canAddMore = true;\n while (canAddMore && unassignedFamilies.length > 0) {\n let bestFamilyIndex = -1;\n let bestScore = -Infinity;\n\n // Try each unassigned family and pick the one that fits and improves score most\n for (let i = 0; i < unassignedFamilies.length; i++) {\n const family = unassignedFamilies[i];\n\n // Check if family fits in current subset\n if (subset.length + family.length <= this.options.maxSubsetSize) {\n const testSubset = [...subset, ...family];\n const testScore = this.calculateSubsetScore(testSubset);\n\n if (testScore > bestScore) {\n bestScore = testScore;\n bestFamilyIndex = i;\n }\n }\n }\n\n // If we found a family that fits, assign it\n if (bestFamilyIndex >= 0) {\n const family = unassignedFamilies.splice(bestFamilyIndex, 1)[0];\n\n // Add all family members to current subset\n for (const competitor of family) {\n subset.push(competitor);\n\n const competitorIndex = this.competitors.findIndex(c => c.id === competitor.id);\n const variableIndex = competitorIndex * this.maxSubsets + currentSubset;\n solution[variableIndex].value = 1;\n }\n } else {\n // No more families fit in current subset\n canAddMore = false;\n }\n }\n\n // Move to next subset if current one meets minimum size or we can't add more\n if (subset.length >= this.options.minSubsetSize || !canAddMore) {\n currentSubset++;\n } else if (unassignedFamilies.length === 0) {\n break;\n }\n }\n\n // Place anyone still waiting into a group that is not already full.\n while (unassignedFamilies.length > 0) {\n const family = unassignedFamilies[0];\n const placement = openSubsetFor(subsets, family.length, currentSubset, this.options, this.maxSubsets);\n if (!placement) {\n break;\n }\n\n unassignedFamilies.shift();\n currentSubset = placement.nextSubset;\n for (const competitor of family) {\n subsets[placement.index].push(competitor);\n\n const competitorIndex = this.competitors.findIndex(c => c.id === competitor.id);\n const variableIndex = competitorIndex * this.maxSubsets + placement.index;\n solution[variableIndex].value = 1;\n }\n }\n\n return solution;\n }\n\n private calculateSubsetScore(subset: Competitor[]): number {\n return scoreSubset(subset, this.options).total;\n }\n\n solve(): MIPVariable[] {\n // For this simplified implementation, we use a greedy heuristic\n // In a real MIP solver, this would use branch-and-bound with linear relaxation\n const greedySolution = this.solveGreedy();\n\n if (this.isSolutionValid(greedySolution)) {\n const objective = this.calculateObjectiveValue(greedySolution);\n if (objective > this.bestObjective) {\n this.bestObjective = objective;\n this.bestSolution = greedySolution;\n }\n }\n\n return this.bestSolution || greedySolution;\n }\n\n getBestSolution(): { solution: MIPVariable[]; objective: number } {\n return {\n solution: this.bestSolution || [],\n objective: this.bestObjective,\n };\n }\n}\n\n/**\n * Solves the competitor partitioning problem using Mixed Integer Programming\n */\nexport function solveMIP(\n competitors: Competitor[],\n options: Partial<PartitionOptions> = {}\n): AlgorithmResult {\n const startTime = performance.now();\n\n // Handle empty dataset\n if (competitors.length === 0) {\n return handleEmptyDataset(options, 'Mixed Integer Programming');\n }\n\n // Use common options validation\n const opts = validateAndSanitizeOptions(options, competitors.length);\n const solver = new SimpleMIPSolver(competitors, opts);\n const solution = solver.solve();\n\n // Convert solution back to subsets\n const subsets: Competitor[][] = Array(opts.maxSubsets).fill(null).map(() => []);\n\n for (const variable of solution) {\n if (variable.value === 1) {\n subsets[variable.subsetIndex].push(competitors[variable.competitorIndex]);\n }\n }\n\n // Remove empty subsets\n const nonEmptySubsets = subsets.filter(subset => subset.length > 0);\n\n // Use common result creation utility\n return createAlgorithmResult(competitors, nonEmptySubsets, opts, 'Mixed Integer Programming', startTime);\n}\n","/**\n * Mixed Integer Programming approach using HiGHS solver\n * \n * This algorithm formulates the partitioning problem as a proper MIP problem where:\n * - Binary variables x[i][j] indicate if competitor i is in subset j\n * - Objective function maximizes the quality score based on preferences\n * - Constraints ensure valid partitioning and guardian relationships\n */\n\nimport type { Competitor, PartitionOptions, AlgorithmResult } from '../types';\nimport { createPartitionResult, createGuardianFamilies } from '../utils';\nimport { retainLegalSubsets } from '../subsetSize';\nimport { validateAndSanitizeOptions, type ValidatedPartitionOptions } from '../common/options-validator';\n\n// Import HiGHS solver\ntype HighsSolver = {\n solve: (model: string) => { Status: string; Columns: Record<string, { Primal: number }> };\n};\n\nfunction loadHighs(): Promise<HighsSolver> {\n const highs = require('highs') as () => Promise<HighsSolver>;\n return highs();\n}\n\ninterface HiGHSVariable {\n name: string;\n lower: number;\n upper: number;\n type: 'continuous' | 'integer' | 'binary';\n cost: number;\n}\n\ninterface HiGHSConstraint {\n name: string;\n lower: number;\n upper: number;\n entries: Array<{ variable: string; coefficient: number }>;\n}\n\ninterface HiGHSModel {\n sense: 'minimize' | 'maximize';\n variables: HiGHSVariable[];\n constraints: HiGHSConstraint[];\n}\n\n/**\n * Enhanced MIP solver using HiGHS for proper optimization\n */\nclass HiGHSMIPSolver {\n private competitors: Competitor[];\n private families: Competitor[][];\n private options: ValidatedPartitionOptions;\n private maxSubsets: number;\n\n constructor(competitors: Competitor[], options: ValidatedPartitionOptions) {\n this.competitors = competitors;\n this.families = createGuardianFamilies(competitors);\n this.options = options;\n this.maxSubsets = Math.min(this.options.maxSubsets, Math.ceil(competitors.length / this.options.minSubsetSize));\n }\n\n /**\n * Creates a MIP model for the HiGHS solver\n */\n private createMIPModel(): HiGHSModel {\n const model: HiGHSModel = {\n sense: 'maximize',\n variables: [],\n constraints: []\n };\n\n // Create binary variables: x[family_i][subset_j] = 1 if family i is assigned to subset j\n const variableMap = new Map<string, number>();\n let varIndex = 0;\n\n for (let i = 0; i < this.families.length; i++) {\n for (let j = 0; j < this.maxSubsets; j++) {\n const varName = `x_${i}_${j}`;\n variableMap.set(varName, varIndex++);\n\n // Calculate objective coefficient for this variable\n const cost = this.calculateObjectiveCoefficient(i, j);\n\n model.variables.push({\n name: varName,\n lower: 0,\n upper: 1,\n type: 'binary',\n cost: cost\n });\n }\n }\n\n // Constraint 1: Each family must be assigned to exactly one subset\n for (let i = 0; i < this.families.length; i++) {\n const entries: Array<{ variable: string; coefficient: number }> = [];\n\n for (let j = 0; j < this.maxSubsets; j++) {\n const varName = `x_${i}_${j}`;\n entries.push({ variable: varName, coefficient: 1 });\n }\n\n model.constraints.push({\n name: `assign_family_${i}`,\n lower: 1,\n upper: 1,\n entries: entries\n });\n }\n\n // Constraint 2: Subset maximum size limits\n for (let j = 0; j < this.maxSubsets; j++) {\n const entries: Array<{ variable: string; coefficient: number }> = [];\n\n for (let i = 0; i < this.families.length; i++) {\n const varName = `x_${i}_${j}`;\n const familySize = this.families[i].length;\n entries.push({ variable: varName, coefficient: familySize });\n }\n\n model.constraints.push({\n name: `max_size_subset_${j}`,\n lower: 0,\n upper: this.options.maxSubsetSize,\n entries: entries\n });\n }\n\n // Constraint 3: Minimum size constraints using indicator variables\n // For each subset j: if any family is assigned to j, then total size >= minSubsetSize\n for (let j = 0; j < this.maxSubsets; j++) {\n // Create indicator variable y[j] = 1 if subset j is used\n const indicatorVarName = `y_${j}`;\n variableMap.set(indicatorVarName, varIndex++);\n\n model.variables.push({\n name: indicatorVarName,\n lower: 0,\n upper: 1,\n type: 'binary',\n cost: -0.1 // Small penalty for using more subsets\n });\n\n // Link constraint: sum of assignments in subset j <= M * y[j]\n // where M is a large number (maxSubsetSize)\n const linkEntries: Array<{ variable: string; coefficient: number }> = [];\n linkEntries.push({ variable: indicatorVarName, coefficient: -this.options.maxSubsetSize });\n\n for (let i = 0; i < this.families.length; i++) {\n const varName = `x_${i}_${j}`;\n linkEntries.push({ variable: varName, coefficient: 1 });\n }\n\n model.constraints.push({\n name: `link_subset_${j}`,\n lower: -Infinity,\n upper: 0,\n entries: linkEntries\n });\n\n // Minimum size constraint: total size in subset j >= minSubsetSize * y[j]\n const minSizeEntries: Array<{ variable: string; coefficient: number }> = [];\n minSizeEntries.push({ variable: indicatorVarName, coefficient: -this.options.minSubsetSize });\n\n for (let i = 0; i < this.families.length; i++) {\n const varName = `x_${i}_${j}`;\n const familySize = this.families[i].length;\n minSizeEntries.push({ variable: varName, coefficient: familySize });\n }\n\n model.constraints.push({\n name: `min_size_subset_${j}`,\n lower: 0,\n upper: Infinity,\n entries: minSizeEntries\n });\n }\n\n return model;\n }\n\n /**\n * Calculates the objective coefficient for a family-subset assignment\n */\n private calculateObjectiveCoefficient(familyIndex: number, subsetIndex: number): number {\n const family = this.families[familyIndex];\n const familySize = family.length;\n\n // Base reward for assignment\n let coefficient = 10;\n\n // Size preference: prefer assignments that help achieve preferred subset size\n if (familySize === this.options.preferredSubsetSize) {\n coefficient += 5 + (10 * this.options.closedGroupWeight);\n } else if (familySize <= this.options.preferredSubsetSize) {\n coefficient += 3; // Smaller bonus for smaller families\n }\n\n // Gender balance bonus: simple check for mixed genders in family\n const hasMultipleGenders = new Set(family.map(c => c.gender)).size > 1;\n if (hasMultipleGenders) {\n coefficient += 2 * this.options.genderWeight;\n }\n\n // Age category bonus: bonus for families with same age category\n const uniqueAges = new Set(family.map(c => c.ageCategory)).size;\n if (uniqueAges === 1) {\n coefficient += 2 * this.options.ageCategoryWeight;\n }\n\n // Equipment class bonus\n const uniqueEquipment = new Set(family.map(c => c.equipmentClass)).size;\n if (this.options.groupByEquipmentClass && uniqueEquipment === 1) {\n coefficient += 2 * this.options.equipmentClassWeight;\n } else if (!this.options.groupByEquipmentClass && uniqueEquipment > 1) {\n coefficient += 1 * this.options.equipmentClassWeight;\n }\n\n return coefficient;\n }\n\n /**\n * Solves the MIP model using HiGHS and returns the solution\n */\n async solve(): Promise<Competitor[][]> {\n const model = this.createMIPModel();\n\n // Debug: log model size (only for small problems)\n if (this.families.length <= 5) {\n console.log(`HiGHS model: ${model.variables.length} variables, ${model.constraints.length} constraints`);\n }\n\n // Convert to HiGHS LP format\n const lpModel = this.convertToLPFormat(model);\n\n // Initialize HiGHS module and solve\n const solver = await loadHighs();\n const solution = solver.solve(lpModel);\n\n // Debug: log solution status\n if (this.families.length <= 5) {\n console.log('HiGHS solution status:', solution.Status);\n }\n\n if (!solution) {\n throw new Error('HiGHS solver returned no solution');\n }\n\n if (solution.Status !== 'Optimal') {\n throw new Error(`HiGHS solver failed with status: ${solution.Status}`);\n }\n\n // Extract solution from HiGHS format\n const subsets: Competitor[][] = Array(this.maxSubsets).fill(null).map(() => []);\n\n for (let i = 0; i < this.families.length; i++) {\n for (let j = 0; j < this.maxSubsets; j++) {\n const varName = `x_${i}_${j}`;\n const column = solution.Columns[varName];\n\n if (column && column.Primal > 0.5) { // Binary variable should be 1\n subsets[j].push(...this.families[i]);\n break; // Family can only be in one subset\n }\n }\n }\n\n // Remove empty subsets\n const nonEmptySubsets = subsets.filter(subset => subset.length > 0);\n\n // Validate solution\n if (!this.validateSolution(nonEmptySubsets)) {\n throw new Error('HiGHS solution validation failed - constraints not met');\n }\n\n return nonEmptySubsets;\n }\n\n /**\n * Converts our HiGHS model to LP format string that HiGHS can understand\n */\n private convertToLPFormat(model: HiGHSModel): string {\n let lp = '';\n\n // Objective function\n lp += model.sense === 'maximize' ? 'Maximize\\n' : 'Minimize\\n';\n lp += 'obj: ';\n\n const objTerms: string[] = [];\n for (const variable of model.variables) {\n if (variable.cost !== 0) {\n const sign = variable.cost >= 0 ? '+' : '';\n objTerms.push(`${sign}${variable.cost} ${variable.name}`);\n }\n }\n lp += objTerms.join(' ') + '\\n\\n';\n\n // Constraints\n lp += 'Subject To\\n';\n for (const constraint of model.constraints) {\n let constraintStr = `${constraint.name}: `;\n const terms: string[] = [];\n\n for (const entry of constraint.entries) {\n const sign = entry.coefficient >= 0 ? '+' : '';\n terms.push(`${sign}${entry.coefficient} ${entry.variable}`);\n }\n\n constraintStr += terms.join(' ');\n\n // Add bounds\n if (constraint.lower === constraint.upper) {\n constraintStr += ` = ${constraint.lower}`;\n } else {\n if (constraint.lower > -Infinity) {\n constraintStr += ` >= ${constraint.lower}`;\n }\n if (constraint.upper < Infinity) {\n if (constraint.lower > -Infinity) {\n // This is a range constraint, need to handle differently\n lp += constraintStr + '\\n';\n constraintStr = `${constraint.name}_upper: ${terms.join(' ')} <= ${constraint.upper}`;\n } else {\n constraintStr += ` <= ${constraint.upper}`;\n }\n }\n }\n\n lp += constraintStr + '\\n';\n }\n\n // Variable bounds and types\n lp += '\\nBounds\\n';\n const binaryVars: string[] = [];\n\n for (const variable of model.variables) {\n if (variable.type === 'binary') {\n binaryVars.push(variable.name);\n } else {\n if (variable.lower !== 0 || variable.upper !== Infinity) {\n lp += `${variable.lower} <= ${variable.name} <= ${variable.upper}\\n`;\n }\n }\n }\n\n if (binaryVars.length > 0) {\n lp += '\\nBinary\\n';\n lp += binaryVars.join(' ') + '\\n';\n }\n\n lp += '\\nEnd\\n';\n\n // Debug: log LP model for small problems\n if (this.families.length <= 3) {\n console.log('Generated LP model:');\n console.log(lp);\n }\n\n return lp;\n }\n\n\n /**\n * Validates that the solution meets all constraints\n */\n private validateSolution(subsets: Competitor[][]): boolean {\n // Check all competitors are assigned exactly once\n const assignedIds = new Set<string>();\n for (const subset of subsets) {\n for (const competitor of subset) {\n if (assignedIds.has(competitor.id)) {\n return false; // Duplicate assignment\n }\n assignedIds.add(competitor.id);\n }\n }\n\n // Check all competitors are assigned\n if (assignedIds.size !== this.competitors.length) {\n return false;\n }\n\n // Check subset size constraints\n for (const subset of subsets) {\n if (subset.length < this.options.minSubsetSize || subset.length > this.options.maxSubsetSize) {\n return false;\n }\n }\n\n // Check guardian constraints\n for (const subset of subsets) {\n const subsetIds = new Set(subset.map(c => c.id));\n for (const competitor of subset) {\n if (competitor.guardianId && !subsetIds.has(competitor.guardianId)) {\n return false; // Guardian not in same subset\n }\n }\n }\n\n return true;\n }\n\n}\n\n/**\n * Solves the competitor partitioning problem using HiGHS MIP solver\n */\nexport async function solveMIPWithLP(\n competitors: Competitor[],\n options: Partial<PartitionOptions> = {}\n): Promise<AlgorithmResult> {\n const startTime = performance.now();\n const opts = validateAndSanitizeOptions(options, competitors.length);\n\n if (competitors.length === 0) {\n return {\n result: createPartitionResult([], opts),\n executionTime: 0,\n algorithmUsed: 'Mixed Integer Programming (HiGHS)',\n };\n }\n\n // Use simplified solver to avoid memory issues\n const solver = new HiGHSMIPSolver(competitors, opts);\n const subsets = await solver.solve();\n\n const legalSubsets = retainLegalSubsets(subsets, opts.minSubsetSize, opts.maxSubsetSize);\n const result = createPartitionResult(legalSubsets, opts);\n const executionTime = performance.now() - startTime;\n\n return {\n result,\n executionTime,\n algorithmUsed: 'Mixed Integer Programming (HiGHS)',\n };\n}\n","/**\n * Clustering approach for competitor partitioning\n * \n * This algorithm uses a modified K-means clustering approach where:\n * - Competitors are represented as feature vectors\n * - Distance metric considers gender, age category, and equipment class\n * - Clusters are formed and then adjusted to meet size constraints\n * - Post-processing ensures all constraints are satisfied\n */\n\nimport type { Competitor, PartitionOptions, AlgorithmResult } from '../types';\nimport { createPartitionResult, shuffle, createGuardianFamilies } from '../utils';\nimport { validateAndSanitizeOptions, ValidatedPartitionOptions } from '../common/options-validator';\nimport { handleEmptyDataset, createAlgorithmResult } from '../common/algorithm-base';\n\ninterface CompetitorFeatures {\n competitor: Competitor;\n features: number[]; // Encoded feature vector\n clusterId: number;\n familyId: number; // Index of the guardian family this competitor belongs to\n}\n\ninterface FamilyFeatures {\n family: Competitor[]; // All competitors in this family (guardian + dependents)\n features: number[]; // Average feature vector for the family\n clusterId: number;\n}\n\ninterface ClusterCenter {\n id: number;\n features: number[];\n members: FamilyFeatures[]; // Now clusters contain families instead of individual competitors\n}\n\n/**\n * Encodes competitor attributes into numerical feature vector\n */\nfunction encodeCompetitor(competitor: Competitor, allCompetitors: Competitor[]): number[] {\n // Get unique values for categorical encoding\n const uniqueGenders = [...new Set(allCompetitors.map(c => c.gender))];\n const uniqueAgeCategories = [...new Set(allCompetitors.map(c => c.ageCategory))];\n const uniqueEquipmentClasses = [...new Set(allCompetitors.map(c => c.equipmentClass))];\n\n const features: number[] = [];\n\n // One-hot encode gender\n for (const gender of uniqueGenders) {\n features.push(competitor.gender === gender ? 1 : 0);\n }\n\n // One-hot encode age category\n for (const ageCategory of uniqueAgeCategories) {\n features.push(competitor.ageCategory === ageCategory ? 1 : 0);\n }\n\n // One-hot encode equipment class\n for (const equipmentClass of uniqueEquipmentClasses) {\n features.push(competitor.equipmentClass === equipmentClass ? 1 : 0);\n }\n\n // Add guardian relationship feature (binary)\n features.push(competitor.guardianId ? 1 : 0);\n\n return features;\n}\n\n\n/**\n * Calculates Euclidean distance between two feature vectors\n */\nfunction calculateDistance(features1: number[], features2: number[]): number {\n let sum = 0;\n for (let i = 0; i < features1.length; i++) {\n const diff = features1[i] - features2[i];\n sum += diff * diff;\n }\n return Math.sqrt(sum);\n}\n\n/**\n * Calculates weighted distance considering clustering preferences\n */\nfunction calculateWeightedDistance(\n features1: number[],\n features2: number[],\n options: ValidatedPartitionOptions,\n featureWeights: { genderStart: number; ageStart: number; equipmentStart: number }\n): number {\n let sum = 0;\n\n for (let i = 0; i < features1.length; i++) {\n const diff = features1[i] - features2[i];\n let weight = 1.0;\n\n // Apply different weights based on feature type\n if (i >= featureWeights.genderStart && i < featureWeights.ageStart) {\n weight = options.genderWeight;\n } else if (i >= featureWeights.ageStart && i < featureWeights.equipmentStart) {\n weight = options.ageCategoryWeight;\n } else if (i >= featureWeights.equipmentStart) {\n weight = options.equipmentClassWeight;\n }\n\n sum += weight * diff * diff;\n }\n\n return Math.sqrt(sum);\n}\n\n/**\n * Initializes cluster centers using K-means++ algorithm for families\n */\nfunction initializeClusterCenters(\n familyFeatures: FamilyFeatures[],\n numClusters: number,\n options: ValidatedPartitionOptions\n): ClusterCenter[] {\n const centers: ClusterCenter[] = [];\n const families = [...familyFeatures];\n\n // Choose first center randomly\n const firstCenter = families[Math.floor(Math.random() * families.length)];\n centers.push({\n id: 0,\n features: [...firstCenter.features],\n members: [],\n });\n\n // Choose remaining centers using K-means++ (probability proportional to squared distance)\n for (let i = 1; i < numClusters; i++) {\n const distances = families.map(family => {\n const minDist = Math.min(...centers.map(center =>\n calculateDistance(family.features, center.features)\n ));\n return minDist * minDist;\n });\n\n const totalDistance = distances.reduce((sum, dist) => sum + dist, 0);\n const randomValue = Math.random() * totalDistance;\n\n let cumulativeDistance = 0;\n let selectedIndex = 0;\n\n for (let j = 0; j < distances.length; j++) {\n cumulativeDistance += distances[j];\n if (cumulativeDistance >= randomValue) {\n selectedIndex = j;\n break;\n }\n }\n\n centers.push({\n id: i,\n features: [...families[selectedIndex].features],\n members: [],\n });\n }\n\n return centers;\n}\n\n/**\n * Creates family feature vectors by averaging individual competitor features\n */\nfunction createFamilyFeatures(families: Competitor[][], allCompetitors: Competitor[]): FamilyFeatures[] {\n return families.map((family, familyIndex) => {\n // Encode all family members\n const familyMemberFeatures = family.map(competitor =>\n encodeCompetitor(competitor, allCompetitors)\n );\n\n // Calculate average features for the family\n const avgFeatures = new Array(familyMemberFeatures[0].length).fill(0);\n for (const memberFeatures of familyMemberFeatures) {\n for (let i = 0; i < memberFeatures.length; i++) {\n avgFeatures[i] += memberFeatures[i];\n }\n }\n for (let i = 0; i < avgFeatures.length; i++) {\n avgFeatures[i] /= familyMemberFeatures.length;\n }\n\n return {\n family,\n features: avgFeatures,\n clusterId: -1,\n };\n });\n}\n\n/**\n * Assigns families to nearest cluster center\n */\nfunction assignToClusters(\n familyFeatures: FamilyFeatures[],\n centers: ClusterCenter[],\n options: ValidatedPartitionOptions,\n featureWeights: { genderStart: number; ageStart: number; equipmentStart: number }\n): void {\n // Clear previous assignments\n centers.forEach(center => center.members = []);\n\n for (const family of familyFeatures) {\n let minDistance = Infinity;\n let bestCluster = 0;\n\n for (let i = 0; i < centers.length; i++) {\n const distance = calculateWeightedDistance(\n family.features,\n centers[i].features,\n options,\n featureWeights\n );\n\n if (distance < minDistance) {\n minDistance = distance;\n bestCluster = i;\n }\n }\n\n family.clusterId = bestCluster;\n centers[bestCluster].members.push(family);\n }\n}\n\n/**\n * Updates cluster centers to centroid of assigned members\n */\nfunction updateClusterCenters(centers: ClusterCenter[]): boolean {\n let changed = false;\n\n for (const center of centers) {\n if (center.members.length === 0) continue;\n\n const newFeatures = new Array(center.features.length).fill(0);\n\n for (const member of center.members) {\n for (let i = 0; i < member.features.length; i++) {\n newFeatures[i] += member.features[i];\n }\n }\n\n for (let i = 0; i < newFeatures.length; i++) {\n newFeatures[i] /= center.members.length;\n }\n\n // Check if center moved\n for (let i = 0; i < center.features.length; i++) {\n if (Math.abs(center.features[i] - newFeatures[i]) > 1e-6) {\n changed = true;\n break;\n }\n }\n\n center.features = newFeatures;\n }\n\n return changed;\n}\n\n\n/**\n * Adjusts clusters to meet size constraints while keeping families together\n */\nfunction adjustClusterSizes(\n centers: ClusterCenter[],\n options: ValidatedPartitionOptions,\n maxAllowedClusters?: number\n): ClusterCenter[] {\n const effectiveMaxSubsets = maxAllowedClusters || options.maxSubsets;\n const validCenters: ClusterCenter[] = [];\n const redistributeFamilies: FamilyFeatures[] = [];\n\n // Calculate total competitors in each cluster (families can have multiple members)\n for (const center of centers) {\n if (center.members.length === 0) {\n continue; // Skip empty clusters\n }\n\n const totalCompetitors = center.members.reduce((sum, family) => sum + family.family.length, 0);\n\n if (totalCompetitors <= options.maxSubsetSize && totalCompetitors >= options.minSubsetSize) {\n // Cluster size is valid\n validCenters.push(center);\n } else if (totalCompetitors > options.maxSubsetSize) {\n // Cluster is too large, need to redistribute some families\n const shuffledFamilies = shuffle([...center.members]);\n let currentCluster: FamilyFeatures[] = [];\n let currentSize = 0;\n\n for (const family of shuffledFamilies) {\n const familySize = family.family.length;\n\n // If adding this family would exceed max size, start a new cluster\n if (currentSize + familySize > options.maxSubsetSize) {\n // Save current cluster if it meets minimum size\n if (currentSize >= options.minSubsetSize) {\n validCenters.push({\n id: validCenters.length,\n features: center.features,\n members: [...currentCluster],\n });\n } else {\n // Current cluster is too small, redistribute its families\n redistributeFamilies.push(...currentCluster);\n }\n\n currentCluster = [family];\n currentSize = familySize;\n } else {\n currentCluster.push(family);\n currentSize += familySize;\n }\n }\n\n // Handle remaining families\n if (currentCluster.length > 0) {\n if (currentSize >= options.minSubsetSize) {\n validCenters.push({\n id: validCenters.length,\n features: center.features,\n members: currentCluster,\n });\n } else {\n redistributeFamilies.push(...currentCluster);\n }\n }\n } else {\n // Cluster is too small, redistribute all families\n redistributeFamilies.push(...center.members);\n }\n }\n\n // Redistribute families that couldn't fit properly\n while (redistributeFamilies.length > 0) {\n let placed = false;\n\n // Try to add families to existing clusters that have room\n for (const center of validCenters) {\n if (redistributeFamilies.length === 0) break;\n\n const currentSize = center.members.reduce((sum, family) => sum + family.family.length, 0);\n\n // Find families that can fit in this cluster\n for (let i = redistributeFamilies.length - 1; i >= 0; i--) {\n const family = redistributeFamilies[i];\n if (currentSize + family.family.length <= options.maxSubsetSize) {\n center.members.push(family);\n redistributeFamilies.splice(i, 1);\n placed = true;\n break; // Only add one family per cluster per iteration to balance\n }\n }\n }\n\n // If no families could be placed in existing clusters, create new ones (if under max limit)\n if (!placed && redistributeFamilies.length > 0 && validCenters.length < effectiveMaxSubsets) {\n const newCluster: FamilyFeatures[] = [];\n let newClusterSize = 0;\n\n // Add families to new cluster until we reach preferred size or run out\n while (redistributeFamilies.length > 0 && newClusterSize < options.preferredSubsetSize) {\n const family = redistributeFamilies.shift()!;\n if (newClusterSize + family.family.length <= options.maxSubsetSize) {\n newCluster.push(family);\n newClusterSize += family.family.length;\n } else {\n // This family doesn't fit, put it back and stop\n redistributeFamilies.unshift(family);\n break;\n }\n }\n\n if (newCluster.length > 0) {\n // Calculate centroid for new cluster\n const newFeatures = new Array(newCluster[0].features.length).fill(0);\n for (const family of newCluster) {\n for (let i = 0; i < family.features.length; i++) {\n newFeatures[i] += family.features[i];\n }\n }\n for (let i = 0; i < newFeatures.length; i++) {\n newFeatures[i] /= newCluster.length;\n }\n\n validCenters.push({\n id: validCenters.length,\n features: newFeatures,\n members: newCluster,\n });\n }\n\n // If we still have families that can't fit anywhere, force them into existing clusters\n if (redistributeFamilies.length > 0 && (newCluster.length === 0 || validCenters.length >= effectiveMaxSubsets)) {\n // Force remaining families into existing clusters, prioritizing smallest clusters\n while (redistributeFamilies.length > 0) {\n let smallestCenter = validCenters[0];\n let smallestSize = smallestCenter.members.reduce((sum, family) => sum + family.family.length, 0);\n\n for (const center of validCenters) {\n const size = center.members.reduce((sum, family) => sum + family.family.length, 0);\n if (size < smallestSize) {\n smallestCenter = center;\n smallestSize = size;\n }\n }\n\n // Add one family to the smallest cluster (may exceed max size)\n const family = redistributeFamilies.shift()!;\n smallestCenter.members.push(family);\n }\n }\n }\n\n if (!placed && redistributeFamilies.length > 0) {\n const family = redistributeFamilies.shift()!;\n placeFamilyInSmallestCenter(validCenters, family);\n }\n }\n\n return validCenters;\n}\n\nfunction placeFamilyInSmallestCenter(\n validCenters: ClusterCenter[],\n family: FamilyFeatures\n): void {\n if (validCenters.length === 0) {\n validCenters.push({ id: 0, features: [...family.features], members: [family] });\n return;\n }\n\n let smallestCenter = validCenters[0];\n let smallestSize = centerSize(smallestCenter);\n for (const center of validCenters) {\n const size = centerSize(center);\n if (size < smallestSize) {\n smallestCenter = center;\n smallestSize = size;\n }\n }\n smallestCenter.members.push(family);\n}\n\nfunction centerSize(center: ClusterCenter): number {\n return center.members.reduce((sum, family) => sum + family.family.length, 0);\n}\n\n/**\n * Solves the competitor partitioning problem using clustering with guardian constraints\n */\nexport function solveClustering(\n competitors: Competitor[],\n options: Partial<PartitionOptions> = {}\n): AlgorithmResult {\n const startTime = performance.now();\n\n // Handle empty dataset\n if (competitors.length === 0) {\n return handleEmptyDataset(options, 'K-means Clustering');\n }\n\n // Validate and sanitize options using common utility\n const opts = validateAndSanitizeOptions(options, competitors.length);\n\n // Create guardian families (each family must stay together)\n const families = createGuardianFamilies(competitors);\n\n // Create family feature vectors\n const familyFeatures = createFamilyFeatures(families, competitors);\n\n // Calculate feature weight boundaries for weighted distance\n const uniqueGenders = [...new Set(competitors.map(c => c.gender))];\n const uniqueAgeCategories = [...new Set(competitors.map(c => c.ageCategory))];\n const featureWeights = {\n genderStart: 0,\n ageStart: uniqueGenders.length,\n equipmentStart: uniqueGenders.length + uniqueAgeCategories.length,\n };\n\n // Estimate optimal number of clusters based on families, not individual competitors\n const totalCompetitors = competitors.length;\n const minClusters = Math.ceil(totalCompetitors / opts.maxSubsetSize);\n const maxClusters = Math.min(opts.maxSubsets, Math.floor(totalCompetitors / opts.minSubsetSize));\n const preferredClusters = Math.round(totalCompetitors / opts.preferredSubsetSize);\n\n // Ensure we don't exceed max subsets constraint - this is the critical fix\n const numClusters = Math.min(\n maxClusters,\n Math.max(minClusters, Math.min(preferredClusters, families.length))\n );\n\n // Initialize cluster centers using families\n let centers = initializeClusterCenters(familyFeatures, numClusters, opts);\n\n // K-means iterations with families\n const maxIterations = 100;\n for (let iteration = 0; iteration < maxIterations; iteration++) {\n assignToClusters(familyFeatures, centers, opts, featureWeights);\n const centersChanged = updateClusterCenters(centers);\n\n if (!centersChanged) {\n break; // Converged\n }\n }\n\n // Adjust cluster sizes to meet constraints while keeping families together\n // Pass the initial cluster count to enforce max subsets constraint\n const adjustedCenters = adjustClusterSizes(centers, opts, numClusters);\n\n // Convert clusters to competitor subsets (flatten families back to individual competitors)\n let subsets: Competitor[][] = adjustedCenters.map(center => {\n const subset: Competitor[] = [];\n for (const family of center.members) {\n subset.push(...family.family);\n }\n return subset;\n });\n\n // CRITICAL FIX: Enforce max subsets constraint by merging excess subsets\n while (subsets.length > opts.maxSubsets) {\n let bestMerge = null;\n let bestScore = -1;\n\n // Find the best pair to merge (prioritize pairs that stay within max size)\n for (let i = 0; i < subsets.length - 1; i++) {\n for (let j = i + 1; j < subsets.length; j++) {\n const combinedSize = subsets[i].length + subsets[j].length;\n\n // Calculate merge score (prefer smaller combined sizes, and heavily prefer staying within limits)\n let score = 0;\n if (combinedSize <= opts.maxSubsetSize) {\n score = 1000 - combinedSize; // High score for valid merges\n } else if (combinedSize <= opts.maxSubsetSize + 1) {\n score = 50 - combinedSize; // Medium score for slightly oversized merges\n } else {\n score = 10 - combinedSize; // Low score for very oversized merges\n }\n\n if (score > bestScore) {\n bestScore = score;\n bestMerge = { i, j, combinedSize };\n }\n }\n }\n\n if (bestMerge) {\n // Merge the best pair\n subsets[bestMerge.i].push(...subsets[bestMerge.j]);\n subsets.splice(bestMerge.j, 1);\n } else {\n // Fallback: merge the two smallest if no good pairs found\n let smallestIdx1 = 0, smallestIdx2 = 1;\n for (let i = 0; i < subsets.length; i++) {\n if (subsets[i].length < subsets[smallestIdx1].length) {\n smallestIdx2 = smallestIdx1;\n smallestIdx1 = i;\n } else if (i !== smallestIdx1 && subsets[i].length < subsets[smallestIdx2].length) {\n smallestIdx2 = i;\n }\n }\n\n subsets[smallestIdx1].push(...subsets[smallestIdx2]);\n subsets.splice(smallestIdx2, 1);\n }\n }\n\n // FINAL FIX: Redistribute competitors to achieve optimal size distribution\n // BUT preserve guardian relationships\n if (subsets.length === opts.maxSubsets) {\n const totalCompetitors = subsets.reduce((sum, subset) => sum + subset.length, 0);\n const targetSize = Math.floor(totalCompetitors / opts.maxSubsets);\n const remainder = totalCompetitors % opts.maxSubsets;\n\n // Only redistribute if we have size violations, and do it carefully\n const oversized = subsets.filter(s => s.length > opts.maxSubsetSize);\n const undersized = subsets.filter(s => s.length < opts.minSubsetSize);\n\n if (oversized.length > 0 || undersized.length > 0) {\n // Rebuild families from current subsets to preserve guardian relationships\n const familiesInSubsets = families.map(family => {\n // Find which subset this family is in\n const familyIds = new Set(family.map(c => c.id));\n for (let i = 0; i < subsets.length; i++) {\n const subsetIds = new Set(subsets[i].map(c => c.id));\n if (family.every(c => subsetIds.has(c.id))) {\n return { family, subsetIndex: i };\n }\n }\n return { family, subsetIndex: -1 }; // Shouldn't happen\n });\n\n // Redistribute families to balance sizes while preserving guardian constraints\n subsets.length = 0; // Clear existing subsets\n\n // Sort families by size (largest first) for better packing\n const sortedFamilies = familiesInSubsets.sort((a, b) => b.family.length - a.family.length);\n\n // Greedy bin packing with families\n for (const { family } of sortedFamilies) {\n let bestSubsetIndex = -1;\n let bestScore = -1;\n\n // Find the best subset to place this family\n for (let i = 0; i < Math.min(subsets.length, opts.maxSubsets); i++) {\n const newSize = subsets[i].length + family.length;\n if (newSize <= opts.maxSubsetSize) {\n // Prefer subsets that are closer to target size\n const score = 1000 - Math.abs(newSize - targetSize);\n if (score > bestScore) {\n bestScore = score;\n bestSubsetIndex = i;\n }\n }\n }\n\n // If no existing subset can fit this family, create a new one (if under limit)\n if (bestSubsetIndex === -1 && subsets.length < opts.maxSubsets) {\n subsets.push([]);\n bestSubsetIndex = subsets.length - 1;\n }\n\n // Place the family\n if (bestSubsetIndex !== -1) {\n subsets[bestSubsetIndex].push(...family);\n } else {\n // Force into smallest subset if we must\n let smallestIndex = 0;\n for (let i = 1; i < subsets.length; i++) {\n if (subsets[i].length < subsets[smallestIndex].length) {\n smallestIndex = i;\n }\n }\n subsets[smallestIndex].push(...family);\n }\n }\n }\n }\n\n // FINAL CLEANUP: Ensure no subsets are smaller than minimum size\n let finalUndersized = subsets.filter(s => s.length < opts.minSubsetSize);\n let maxCleanupIterations = subsets.length; // Safety limit to prevent infinite loops\n let cleanupIterations = 0;\n\n while (finalUndersized.length > 0 && cleanupIterations < maxCleanupIterations) {\n cleanupIterations++;\n\n // Find the smallest undersized subset\n let smallestUndersized = finalUndersized[0];\n let smallestIdx = subsets.indexOf(smallestUndersized);\n\n for (const undersized of finalUndersized) {\n if (undersized.length < smallestUndersized.length) {\n smallestUndersized = undersized;\n smallestIdx = subsets.indexOf(undersized);\n }\n }\n\n // Find the best subset to merge it with (smallest that can accommodate)\n let bestMergeIdx = -1;\n let bestMergeSize = Infinity;\n\n for (let i = 0; i < subsets.length; i++) {\n if (i === smallestIdx) continue;\n\n const combinedSize = subsets[i].length + smallestUndersized.length;\n if (combinedSize <= opts.maxSubsetSize && subsets[i].length < bestMergeSize) {\n bestMergeSize = subsets[i].length;\n bestMergeIdx = i;\n }\n }\n\n // If no good merge found, merge with the smallest subset anyway (may exceed max size)\n if (bestMergeIdx === -1) {\n bestMergeIdx = 0;\n for (let i = 1; i < subsets.length; i++) {\n if (i !== smallestIdx && subsets[i].length < subsets[bestMergeIdx].length) {\n bestMergeIdx = i;\n }\n }\n }\n\n // Merge the undersized subset\n if (bestMergeIdx !== -1 && bestMergeIdx !== smallestIdx) {\n subsets[bestMergeIdx].push(...smallestUndersized);\n subsets.splice(smallestIdx, 1);\n\n // Update the list of undersized subsets\n finalUndersized = subsets.filter(s => s.length < opts.minSubsetSize);\n } else {\n // If we can't merge, break to avoid infinite loop\n break;\n }\n }\n\n // Use common result creation utility\n return createAlgorithmResult(competitors, subsets, opts, 'K-means Clustering', startTime);\n}\n"],"mappings":"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","names":["family","totalCompetitors"]}