Skip to content

Commit

Permalink
Merge pull request #10 from isaacphysics/hotfix/linear-comparison
Browse files Browse the repository at this point in the history
Add linear comparison for non-permuting arrays
  • Loading branch information
jacbn authored Dec 6, 2024
2 parents 3c0a211 + 20ea6a8 commit a9d414f
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 20 deletions.
40 changes: 22 additions & 18 deletions src/models/Chemistry.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { AddFrac, CheckerResponse, ChemicalSymbol, ChemistryOptions, Fraction, listComparison, mergeResponses, MultFrac, removeAggregates } from './common'
import { AddFrac, CheckerResponse, ChemicalSymbol, ChemistryOptions, Fraction, linearComparison, listComparison, mergeResponses, MultFrac, removeAggregates } from './common'
import isEqual from "lodash/isEqual";

export type Type = 'error'|'element'|'bracket'|'compound'|'ion'|'term'|'expr'|'statement'|'electron';
Expand Down Expand Up @@ -385,15 +385,17 @@ function checkNodesEqual(test: ASTNode, target: ASTNode, response: CheckerRespon
response.isEqual = false;
}
}

return linearComparison(test.elements, target.elements, response, checkNodesEqual);
}

// If permutations are allowed, we instead compare the atomCounts of the elements
if (response.options?.allowPermutations && !response.checkingPermutations) {
const permutationResponse = structuredClone(response);
permutationResponse.checkingPermutations = true;

const testResponse = listComparison(test.elements, test.elements, permutationResponse, checkNodesEqual);
const targetResponse = listComparison(target.elements, target.elements, permutationResponse, checkNodesEqual);
const testResponse = linearComparison(test.elements, test.elements, permutationResponse, checkNodesEqual);
const targetResponse = linearComparison(target.elements, target.elements, permutationResponse, checkNodesEqual);

response.isEqual = response.isEqual && isEqual(testResponse.atomCount, targetResponse.atomCount) && isEqual(testResponse.termAtomCount, targetResponse.termAtomCount);
return response
Expand All @@ -410,20 +412,6 @@ function checkNodesEqual(test: ASTNode, target: ASTNode, response: CheckerRespon
}
else if (isIon(test) && isIon(target)) {
if (test.molecules && target.molecules) {
// If permutations are disallowed, we can attempt to directly compare the elements at this level
if (!response.options?.allowPermutations) {
if (!isEqual(test, target)) {
if (test.molecules.length !== target.molecules.length) {
// TODO: Implement special cases for certain permutations e.g. reverse of an ion chain
response.sameElements = false;
response.isEqual = false;
}
else {
response.isEqual = false;
}
}
}

const comparator = (test: [Molecule, number], target: [Molecule, number], response: CheckerResponse): CheckerResponse => {
const newResponse = checkNodesEqual(test[0], target[0], response);
newResponse.sameCharge = newResponse.sameCharge && test[1] === target[1];
Expand All @@ -449,6 +437,22 @@ function checkNodesEqual(test: ASTNode, target: ASTNode, response: CheckerRespon
return newResponse;
}

// If permutations are disallowed, we can attempt to directly compare the elements at this level
if (!response.options?.allowPermutations) {
if (!isEqual(test, target)) {
if (test.molecules.length !== target.molecules.length) {
// TODO: Implement special cases for certain permutations e.g. reverse of an ion chain
response.sameElements = false;
response.isEqual = false;
}
else {
response.isEqual = false;
}
}

return linearComparison(test.molecules, target.molecules, response, comparator);
}

// Check all permutations of the ion chain until we get a match
return listComparison(test.molecules, target.molecules, response, comparator);
} else {
Expand Down Expand Up @@ -562,7 +566,7 @@ function checkNodesEqual(test: ASTNode, target: ASTNode, response: CheckerRespon
return finalResponse;
} else {
// There was a type mismatch
response.sameElements = false;
response.typeMismatch = true;
response.isEqual = false;
// We must still check the children of the node to get complete aggregate counts
if (test.type == "error") {
Expand Down
23 changes: 21 additions & 2 deletions src/models/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,18 +84,37 @@ export function removeAggregates(response: CheckerResponse): CheckerResponse {
return response;
}

export function linearComparison<T>(
testList: T[],
targetList: T[],
response: CheckerResponse,
comparator: (test: T, target: T, response: CheckerResponse) => CheckerResponse
): CheckerResponse {
let possibleResponse = structuredClone(response);

if (testList.length !== targetList.length) {
possibleResponse.sameElements = false;
possibleResponse.isEqual = false;
return possibleResponse;
}

for (let i = 0; i < testList.length; i++) {
possibleResponse = comparator(testList[i], targetList[i], structuredClone(possibleResponse));
}

return possibleResponse;
}

export function listComparison<T>(
testList: T[],
targetList: T[],
response: CheckerResponse,
comparator: (test: T, target: T, response: CheckerResponse) => CheckerResponse
): CheckerResponse {
// TODO: look at a more efficient method of comparison
const indices: number[] = []; // the indices on which a match was made
let possibleResponse = structuredClone(response);

// Get aggregates
// TODO: discuss and see if a better solution exists
let aggregatesResponse = structuredClone(response);
for (let item of testList) {
// This will always pass, this is to get the accurate aggregate bookkeeping values
Expand Down

0 comments on commit a9d414f

Please sign in to comment.