{"version":3,"sources":["assignmentProblem.ts"],"names":[],"mappings":"AACA,qBAAa,iBAAiB;IAC5B,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;IAYnC,OAAO,CAAC,MAAM,CAAC,IAAI;CAKpB;AAED,8BAAsB,QAAQ;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBAEV,OAAO,EAAE,MAAM,EAAE,EAAE;IASxB,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,SAAS;aAanD,KAAK,IAAI,QAAQ;CAClC;AAED,qBAAa,QAAQ;IACnB,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,CAAC;IAC9B,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;gBAEhB,QAAQ,EAAE,QAAQ,EAAE,UAAU,CAAC,EAAE,MAAM,EAAE;IAMrD,UAAU,IAAI,OAAO;IAIrB,OAAO,IAAI,OAAO;IAIlB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ;IAO5C,oBAAoB,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO;CAY/C;AAYD,qBAAa,sBAAuB,SAAQ,QAAQ;IAClD,OAAO,CAAC,mBAAmB,CAAC,CAAW;gBAE3B,OAAO,EAAE,MAAM,EAAE,EAAE;IAI/B,qBAAqB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO;IAapD,4BAA4B,CAAC,SAAS,EAAE,QAAQ;IAShD,KAAK,IAAI,QAAQ;IAQjB,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ;IAwBvC,OAAO,CAAC,SAAS;IAiBjB,OAAO,CAAC,GAAG;IAIX,OAAO,CAAC,kBAAkB;CAa3B","file":"assignmentProblem.d.ts","sourcesContent":["import { arrayContains } from \"./utils.js\";\nexport class AssignmentProblem {\n  static instance(weights: number[][]) {\n    const rows = weights.length;\n    if (!rows) {\n      throw \"Cannot create instance with 0x0 weights matrix\";\n    }\n    const cols = weights[0].length;\n    if (rows > cols) {\n      throw \"The weights matrix may not have more rows than columns\";\n    }\n    return new NaiveAlgorithmInstance(AssignmentProblem.copy(weights));\n  }\n\n  private static copy(weights: number[][]): number[][] {\n    const ret: number[][] = [];\n    weights.forEach((row) => ret.push(row));\n    return ret;\n  }\n}\n\nexport abstract class Instance {\n  readonly weights: number[][];\n  readonly rows: number;\n  readonly cols: number;\n\n  constructor(weights: number[][]) {\n    if (!weights?.length || !weights[0]?.length) {\n      throw \"Not a valid weights matrix: \" + weights;\n    }\n    this.weights = weights;\n    this.rows = weights.length;\n    this.cols = weights[0].length;\n  }\n\n  public weightOfAssignment(assignment: number[]): number | undefined {\n    if (!assignment) {\n      throw \"Not a valid assignment: \" + assignment;\n    }\n    if (assignment?.length === 0) {\n      return undefined;\n    }\n    const sum = assignment\n      .map((col, row) => this.weights[row][col])\n      .reduce((a, b) => a + b);\n    return sum;\n  }\n\n  public abstract solve(): Solution;\n}\n\nexport class Solution {\n  readonly assignment: number[];\n  readonly weight: number | undefined;\n  readonly instance: Instance;\n\n  constructor(instance: Instance, assignment?: number[]) {\n    this.instance = instance;\n    this.assignment = assignment || [];\n    this.weight = instance.weightOfAssignment(this.assignment);\n  }\n\n  isComplete(): boolean {\n    return this.assignment.length >= this.instance.rows;\n  }\n\n  isEmpty(): boolean {\n    return this.assignment.length === 0;\n  }\n\n  assignColumnInNextRow(col: number): Solution {\n    if (this.isComplete()) {\n      throw \"Solution is already complete\";\n    }\n    return new Solution(this.instance, [...this.assignment, col]);\n  }\n\n  isBetterSolutionThan(other: Solution): boolean {\n    if (!(this.isComplete() && other.isComplete())) {\n      throw \"Cannot compare incomplete solutions\";\n    }\n    if (\n      typeof this.weight === \"undefined\" ||\n      typeof other.weight === \"undefined\"\n    ) {\n      throw \"Cannot compare empty solutions\";\n    }\n    return this.weight < other.weight;\n  }\n}\n\nclass ValueWithIndex {\n  readonly value: number;\n  readonly index: number;\n\n  constructor(value: number, index: number) {\n    this.value = value;\n    this.index = index;\n  }\n}\n\nexport class NaiveAlgorithmInstance extends Instance {\n  private currentBestSolution?: Solution;\n\n  constructor(weights: number[][]) {\n    super(weights);\n  }\n\n  isLowerThanBestWeight(weightToTest: number): boolean {\n    if (!this.currentBestSolution) {\n      return true;\n    }\n    if (!this.currentBestSolution.isComplete()) {\n      return true;\n    }\n    if (!this.currentBestSolution.weight) {\n      return true;\n    }\n    return this.currentBestSolution.weight > weightToTest;\n  }\n\n  updateBestSolutionIfPossible(candidate: Solution) {\n    if (\n      !this.currentBestSolution ||\n      candidate.isBetterSolutionThan(this.currentBestSolution)\n    ) {\n      this.currentBestSolution = candidate;\n    }\n  }\n\n  solve(): Solution {\n    this.doSolve(0, new Solution(this));\n    if (!this.currentBestSolution) {\n      return new Solution(this);\n    }\n    return this.currentBestSolution;\n  }\n\n  doSolve(row: number, solution: Solution) {\n    if (row >= this.rows) {\n      this.updateBestSolutionIfPossible(solution);\n      return;\n    }\n    if (solution.weight) {\n      const bestAttainableScore = this.sum(\n        this.minPerRow(row, solution.assignment)\n      );\n      if (!this.isLowerThanBestWeight(solution.weight + bestAttainableScore)) {\n        return;\n      }\n    }\n    const nMin = this.rowSortedAscending(row, solution.assignment);\n    for (let i = 0; i < nMin.length; i++) {\n      if (\n        !solution.weight ||\n        this.isLowerThanBestWeight(solution.weight + nMin[i].value)\n      ) {\n        this.doSolve(row + 1, solution.assignColumnInNextRow(nMin[i].index));\n      }\n    }\n  }\n\n  private minPerRow(startRow: number, skipCols: number[]): number[] {\n    const ret = [];\n    for (let r = startRow; r < this.rows; r++) {\n      let min = Number.MAX_VALUE;\n      for (let c = 0; c < this.cols; c++) {\n        if (!arrayContains(skipCols, c)) {\n          const val = this.weights[r][c];\n          if (min > val) {\n            min = val;\n          }\n        }\n      }\n      ret.push(min);\n    }\n    return ret;\n  }\n\n  private sum(arr: number[]) {\n    return arr.reduce((a, b) => a + b);\n  }\n\n  private rowSortedAscending(\n    row: number,\n    skipCols: number[]\n  ): ValueWithIndex[] {\n    const sorted: ValueWithIndex[] = [];\n    for (let i = 0; i < this.cols; i++) {\n      if (!arrayContains(skipCols, i)) {\n        sorted.push(new ValueWithIndex(this.weights[row][i], i));\n      }\n    }\n    sorted.sort((l, r) => l.value - r.value);\n    return sorted;\n  }\n}\n"]}