7 * Remove common variations of "+ C" at the end of an indefinite integral.
8 */
9function stripPlusC(str) {
10 return str.replace(/\+\s*c\s*$/i, "").trim();
11}
15 * Remove or replace common LaTeX constructs.
16 */
17function latexToExpression(latex) {
18 // Remove display math delimiters: $$...$$ or $...$
19 let expr = latex.replace(/\${1,2}/g, "");
22 expr = expr.replace(/\\frac\s*\{([^}]+)\}\s*\{([^}]+)\}/g, "($1)/($2)");
23
24 // Some common function replacements for Algebrite
25 expr = expr.replace(/\\sin/g, "sin");
26 expr = expr.replace(/\\cos/g, "cos");
46 * Return the simplified string or null on error.
47 */
48function parseAndSimplify(expr) {
49 try {
50 // If it's an empty string, treat that as invalid
59 * Check if two expressions (already simplified) differ by at most a constant.
60 */
61function differByConstant(simpl1, simpl2) {
62 try {
63 const diff = Algebrite.simplify(`(${simpl1}) - (${simpl2})`).toString();
74 * Symbolic check: compare simplified forms, then see if they differ by a constant.
75 */
76function symbolicCheck(expr1, expr2) {
77 const s1 = parseAndSimplify(expr1);
78 const s2 = parseAndSimplify(expr2);
85 * Evaluate an expression numerically at x, returning a float or null if invalid.
86 */
87function safeEval(expr, x) {
88 try {
89 const val = parseFloat(Algebrite.eval(expr, { x }).toString());
100 * If the difference changes by more than a small tolerance, we reject it.
101 */
102function numericCheck(expr1, expr2, samples = [-2, 0, 2]) {
103 const diffs = [];
104
125 * Loose equivalence: first do a symbolic check, if that fails, do numeric check.
126 */
127function expressionsAreLooselyEquivalent(expr1, expr2) {
128 // 1) Symbolic check
129 if (symbolicCheck(expr1, expr2)) {
137 * GPT-4 call to generate a new math problem.
138 */
139async function handleProblemGeneration() {
140 const openai = new OpenAI();
141 const completion = await openai.chat.completions.create({
153 },
154 ],
155 functions: [
156 {
157 name: "generate_math_problem",
177 },
178 ],
179 function_call: { name: "generate_math_problem" },
180 });
181
182 const functionCall = completion.choices[0].message.function_call;
183 if (!functionCall || functionCall.name !== "generate_math_problem") {
184 return new Response("Failed to generate problem", { status: 500 });
185 }
187 let problem;
188 try {
189 problem = JSON.parse(functionCall.arguments);
190 } catch (error) {
191 console.error("Failed to parse function call arguments:", error);
192 console.log("Raw arguments:", functionCall.arguments);
193 return new Response("Failed to generate problem", { status: 500 });
194 }
213 * - Return result
214 */
215async function handleAnswerCheck(request) {
216 const { answer, problemId } = await request.json();
217 const problem = await blob.getJSON(`problem_${problemId}`);
257 * Main request handler.
258 */
259export default async function(req: Request): Promise<Response> {
260 const url = new URL(req.url);
261