CoolFace
Apppublic

sanket3280/code-execution

sourceHugging Faceupdated 10mo agoView on Hugging Face
0likes
codeEvaluation.service.js1373 linesDownload Raw Back to services
1const axios = require('axios');2const { applyWrappers } = require('./execution');3const heClient = require('./execution/hackerEarthClient');4const { LanguageExecutorFactory } = require('./execution/languageExecutors');5const AnswerVerifier = require('../utils/answerVerifiers');6 7// NEW: Import modular test harness and validators8const { createTestHarness, supportsOptimizedExecution } = require('./testHarness');9const { validateMultipleOutputs, compareOutputs } = require('./validators/outputValidator');10 11// DOCKER: Import Docker executor12const dockerExecutor = require('./execution/dockerExecutor');13const USE_DOCKER = process.env.USE_DOCKER_EXECUTOR === 'true';14 15// Silent initialization16 17// HackerEarth V4 API Configuration18const CODE_EVAL_URL = process.env.CODE_EVAL_URL || 'https://api.hackerearth.com/v4/partner/code-evaluation/submissions/';19const CLIENT_SECRET = process.env.CLIENT_SECRET;20 21// Status codes mapping22const STATUS_CODES = {23  'AC': 'Accepted',24  'TLE': 'Time Limit Exceeded',25  'MLE': 'Memory Limit Exceeded',26  'RE': 'Runtime Error',27  'CE': 'Compilation Error',28  'REQUEST_FAILED': 'Request Failed'29};30 31/**32 * Parse test input from various formats33 */34const parseTestInput = (raw) => {35  try {36    return JSON.parse(raw);37  } catch (e) {38    const result = {};39    const lines = String(raw || '').split(/\r?\n/).map(l => l.trim()).filter(Boolean);40    for (const line of lines) {41      const m = line.match(/^(\w+)\s*=\s*(.+)$/);42      if (m) {43        const key = m[1];44        let val = m[2].trim();45        const normalized = val.replace(/'/g, '"');46        try {47          result[key] = JSON.parse(normalized);48        } catch (e) {49          const num = Number(val);50          if (!Number.isNaN(num)) {51            result[key] = num;52          } else {53            result[key] = val.replace(/^\"|\"$/g, '');54          }55        }56      } else if (!result.x && /^-?\d+(?:\.\d+)?$/.test(line)) {57        result.x = Number(line);58      }59    }60    return result;61  }62};63 64 65/**66 * Submit code - Docker with HackerEarth fallback67 */68const submitCode = async (sourceCode, language, testInput, memoryLimit = 131072, timeLimit = 3, options = {}) => {69  // Debug: Show executor status70  // Try Docker first if enabled71  if (USE_DOCKER) {72    try {73      console.log('\n๐Ÿณ DOCKER EXECUTION');74      const dockerResult = await submitCodeDocker(sourceCode, language, testInput, memoryLimit, timeLimit, options);75      console.log('โœ… Docker: SUCCESS\n');76      return dockerResult;77    } catch (dockerError) {78      console.log('โŒ Docker: FAILED -', dockerError.message);79      console.log('๐Ÿ”„ Fallback: HackerEarth API (using original code)\n');80      // Fall through to HackerEarth with ORIGINAL code (not test harness)81    }82  }83 84  // Use HackerEarth (default or fallback)85  // Note: HackerEarth gets ORIGINAL sourceCode, not test harness86  console.log('\n๐ŸŒ HACKEREARTH API EXECUTION');87  return await submitCodeHackerEarth(sourceCode, language, testInput, memoryLimit, timeLimit, options);88};89 90/**91 * Docker-based execution92 */93const submitCodeDocker = async (sourceCode, language, testInput, memoryLimit, timeLimit, options) => {94  let processedSourceCode = sourceCode;95  96  // Apply wrappers97  try {98    const wrapped = applyWrappers({ sourceCode, language, topic: options.topic });99    if (wrapped && wrapped.sourceCode) {100      processedSourceCode = wrapped.sourceCode;101    }102  } catch (e) { /* noop */ }103 104  // Execute in Docker105  const result = await dockerExecutor.executeCode({106    sourceCode: processedSourceCode,107    language,108    input: testInput,109    timeLimit,110    memoryLimit: Math.floor(memoryLimit / 1024)111  });112 113  // Docker should ALWAYS return a result - don't fallback unless Docker itself crashes114  // Handle all error cases: TLE, CE, RE, empty output, etc.115  116  // Convert to HackerEarth-compatible format117  const dockerResult = {118    he_id: `docker_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,119    result: {120      run_status: {121        status: result.status, // AC, TLE, CE, RE, ERROR122        output: result.output || '',123        stderr: result.error || '',124        time_used: result.executionTime || 0,125        memory_used: result.memoryUsed || 0126      }127    },128    request_status: {129      code: 'REQUEST_COMPLETED' // Always completed, even if code failed130    }131  };132  133  // Store result for polling134  heClient.storeDockerResult(dockerResult.he_id, dockerResult);135  136  return dockerResult;137};138/**139 * HackerEarth execution (original implementation)140 */141const submitCodeHackerEarth = async (sourceCode, language, testInput, memoryLimit, timeLimit, options) => {142  try {143    let processedSourceCode = sourceCode;144    let usedRunner = false;145 146    // Apply topic-specific wrappers147    try {148      const wrapped = applyWrappers({ sourceCode, language, topic: options.topic });149      if (wrapped && wrapped.sourceCode) {150        processedSourceCode = wrapped.sourceCode;151        usedRunner = wrapped.usedRunner || usedRunner;152      }153    } catch (e) { /* noop */ }154 155    const inputObj = parseTestInput(testInput);156 157    // Process based on language using optimized executors158    if (testInput !== 'all_test_cases') {159      const result = LanguageExecutorFactory.processCode(processedSourceCode, language, inputObj);160      processedSourceCode = result.processedSourceCode;161      usedRunner = result.usedRunner || usedRunner;162    }163 164    const response = await heClient.submitToHackerEarth({165      source: processedSourceCode,166      language,167      memoryLimit,168      timeLimit,169      input: testInput,170      usedRunner171    });172 173    return response;174  } catch (error) {175    console.error('โŒ Error submitting code:', error.message);176    console.error('โŒ Error details:', error.response?.data);177    throw error;178  }179};180 181/**182 * Get status of code execution from HackerEarth API183 */184const getStatus = async (heId) => heClient.getStatus(heId);185 186/**187 * Poll for completion of code execution188 */189const pollForCompletion = async (heId, maxAttempts = 10, interval = 1000) => heClient.pollForCompletion(heId, maxAttempts, interval);190 191/**192 * Get output content from HackerEarth URL193 */194const getOutputContent = async (outputUrl) => heClient.extractOutput({ output: outputUrl });195 196/**197 * Normalize output for easier comparison and UI display198 */199const normalizeOutputForCompare = heClient.normalizeOutputForCompare;200 201/**202 * Extract output text from run_status regardless of field name203 */204const extractOutput = async (runStatus) => heClient.extractOutput(runStatus);205 206/**207 * Execute code against multiple test cases - OPTIMIZED VERSION208 */209const executeTestCases = async (sourceCode, language, testCases, options = {}) => {210  // Silent execution211 212  // Auto-enable optimized batch execution for compiled languages213  // BUT respect explicit runAllTestCases: false to prevent infinite loops214  const shouldUseOptimized = options.runAllTestCases === true ||215    (options.runAllTestCases !== false && (216      language.startsWith('JAVA') ||217      language.startsWith('CPP') ||218      language === 'C' ||219      language.startsWith('PYTHON') ||220      language === 'JAVASCRIPT_NODE'221    ));222 223  if (shouldUseOptimized) {224    return await executeAllTestCasesOptimized(sourceCode, language, testCases, options);225  }226 227  const results = {228    summary: {229      total: testCases.length,230      passed: 0,231      failed: 0,232      errors: 0233    },234    cases: []235  };236 237  const concurrency = Math.min(options.concurrency || 3, 5);238  const queue = [];239 240  const pushTask = async (index) => {241    const testCase = testCases[index];242    const caseResult = {243      caseNumber: index + 1,244      input: testCase.input,245      expected: testCase.expected,246      expected_output: testCase.expected, // Add for consistency247      output: '',248      status: 'Error',249      error: null,250      executionTime: null,251      memoryUsed: null,252      isHidden: testCase.isHidden || false,253      pass: false // Add pass field254    };255 256    try {257      const submission = await submitCode(sourceCode, language, testCase.input, undefined, undefined, { topic: options.topic });258      const heId = submission.he_id;259      260      // Optimized polling for HackerEarth261      const maxAttempts = 20;262      const interval = 200;263      264      const finalResult = await pollForCompletion(heId, maxAttempts, interval);265      const runStatus = finalResult.result?.run_status;266 267      if (runStatus) {268        caseResult.executionTime = runStatus.time_used;269        caseResult.memoryUsed = runStatus.memory_used;270 271        if (runStatus.status === 'AC' || runStatus.status === 'CODE_COMPILED') {272          caseResult.output = await extractOutput(runStatus);273          if (typeof caseResult.output === 'string') {274            caseResult.output = caseResult.output.replace(/\[\s+/g, '[').replace(/\s+\]/g, ']').replace(/\s*,\s*/g, ',').trim();275            if (testCase.expected && testCase.expected.startsWith('"') && testCase.expected.endsWith('"')) {276              if (!caseResult.output.startsWith('"')) caseResult.output = `"${caseResult.output}"`;277            } else if (testCase.expected && !testCase.expected.startsWith('"') && caseResult.output.startsWith('"') && caseResult.output.endsWith('"')) {278              caseResult.output = caseResult.output.slice(1, -1);279            }280          }281 282          const normalizedOutput = normalizeOutputForCompare(caseResult.output);283          const normalizedExpected = normalizeOutputForCompare(testCase.expected);284 285 286 287          let isValid = false;288          if (options.verifierType) {289            const verifyResult = AnswerVerifier.verify(290              options.verifierType,291              testCase.input,292              caseResult.output,293              testCase.expected,294              options.verifierMetadata || {}295            );296            isValid = verifyResult.valid;297          } else if (normalizedOutput === normalizedExpected) {298            isValid = true;299          } else {300            // Fallback comparisons - handle quotes and type mismatches301            const outputStr = String(caseResult.output || '');302            const expectedStr = String(testCase.expected || '');303            304            // Remove quotes for comparison305            const cleanOutput = outputStr.replace(/^["']|["']$/g, '');306            const cleanExpected = expectedStr.replace(/^["']|["']$/g, '');307            308            if (cleanOutput === cleanExpected || outputStr === expectedStr) {309              isValid = true;310            } else {311              const outputNum = Number(cleanOutput);312              const expectedNum = Number(cleanExpected);313              if (!isNaN(outputNum) && !isNaN(expectedNum) && outputNum === expectedNum) {314                isValid = true;315              } else {316                const normalizeArrayStr = (str) => str.replace(/\s+/g, '');317                if (normalizeArrayStr(outputStr) === normalizeArrayStr(expectedStr)) {318                  isValid = true;319                }320              }321            }322          }323 324          if (isValid) {325            caseResult.status = 'Pass';326            caseResult.pass = true;327          } else {328            caseResult.status = 'Fail';329            caseResult.pass = false;330          }331        } else {332          caseResult.status = STATUS_CODES[runStatus.status] || 'Error';333          caseResult.error = runStatus.stderr || runStatus.compile_status;334          caseResult.output = await extractOutput(runStatus);335        }336      } else {337        caseResult.status = 'Error';338        caseResult.error = 'No run status received';339      }340    } catch (error) {341      caseResult.status = 'Error';342      caseResult.error = error.message;343    }344 345    return caseResult;346  };347 348  const indices = Array.from({ length: testCases.length }, (_, i) => i);349  const active = new Set();350  const resultsArr = new Array(testCases.length);351 352  async function runNext() {353    if (indices.length === 0) return;354    const idx = indices.shift();355    const p = pushTask(idx).then((r) => { resultsArr[idx] = r; active.delete(p); });356    active.add(p);357    if (active.size >= concurrency) await Promise.race(Array.from(active));358    return runNext();359  }360 361  await runNext();362  await Promise.all(Array.from(active));363 364  for (const cr of resultsArr) {365    results.cases.push(cr);366    if (cr.status === 'Pass') results.summary.passed++;367    else if (cr.status === 'Fail') results.summary.failed++;368    else results.summary.errors++;369  }370 371  // โŒ DON'T filter hidden cases - we need them for submission results372  // results.cases = results.cases.filter(testCase => !testCase.isHidden);373  return results;374};375 376/**377 * OPTIMIZED: Execute all test cases in a single submission378 */379const executeAllTestCasesOptimized = async (sourceCode, language, testCases, options = {}, retryCount = 0) => {380  // Silent execution - logs only in routes381 382  const results = {383    summary: {384      total: testCases.length,385      passed: 0,386      failed: 0,387      errors: 0388    },389    cases: []390  };391 392  try {393    let testHarness = '';394    let testInput = '';395 396    // NEW: Use modular test harness factory397    if (supportsOptimizedExecution(language)) {398      testHarness = createTestHarness(sourceCode, language, testCases);399      testInput = 'all_test_cases';400    } else {401      console.log('โš ๏ธ Language not optimized, falling back to individual submissions');402      return await executeTestCases(sourceCode, language, testCases, { ...options, runAllTestCases: false });403    }404 405    // Test harness created silently406 407    // Smart batching: Check if test harness is too large408    const MAX_HARNESS_SIZE = 950 * 1024; // 950KB limit409 410    if (testHarness.length > MAX_HARNESS_SIZE) {411      console.log(`โš ๏ธ Large harness (${(testHarness.length / 1024).toFixed(2)}KB) - using smart batching...`);412 413      // Split into batches414      const batches = [];415      let currentBatch = [];416 417      for (const tc of testCases) {418        currentBatch.push(tc);419        // NEW: Use modular harness420        const batchHarness = createTestHarness(sourceCode, language, currentBatch);421 422        if (batchHarness.length > MAX_HARNESS_SIZE) {423          currentBatch.pop();424          if (currentBatch.length > 0) {425            batches.push([...currentBatch]);426          }427          currentBatch = [tc];428        }429      }430 431      if (currentBatch.length > 0) {432        batches.push(currentBatch);433      }434 435      console.log(`๐Ÿ“ฆ Split into ${batches.length} batches`);436 437      // Execute each batch438      const allResults = {439        summary: { total: testCases.length, passed: 0, failed: 0, errors: 0 },440        cases: []441      };442 443      for (let i = 0; i < batches.length; i++) {444        console.log(`๐Ÿš€ Batch ${i + 1}/${batches.length}: ${batches[i].length} cases`);445        const batchResults = await executeAllTestCasesOptimized(sourceCode, language, batches[i], options, 0);446 447        allResults.cases.push(...batchResults.cases);448        allResults.summary.passed += batchResults.summary.passed;449        allResults.summary.failed += batchResults.summary.failed;450        allResults.summary.errors += batchResults.summary.errors;451      }452 453      console.log(`โœ… All batches complete: ${allResults.summary.passed}/${allResults.summary.total} passed`);454      return allResults;455    }456 457    const submission = await submitCode(testHarness, language, testInput, undefined, undefined, { topic: options.topic });458    const heId = submission.he_id;459 460    // Optimized polling for HackerEarth461    const maxAttempts = 20;462    const interval = 200;463 464    const finalResult = await pollForCompletion(heId, maxAttempts, interval);465 466    const runStatus = finalResult.result?.run_status;467    468    // Check for Docker infrastructure errors (not user code errors or compilation errors)469    const output = await extractOutput(runStatus);470    471    // Compilation errors should be returned to user, not trigger fallback472    // Only fallback for actual Docker infrastructure issues473    const isDockerInfraError = output && (474      output.includes('Docker daemon') ||475      output.includes('docker: Error') ||476      output.includes('Cannot connect to Docker') ||477      output.includes('container failed to start')478    );479    480    // Only fallback if Docker infrastructure error481    if (isDockerInfraError && heId.startsWith('docker_')) {482      console.log('โŒ Docker infrastructure error detected');483      console.log('๐Ÿ”„ Falling back to HackerEarth API...');484      throw new Error('Docker execution failed: ' + (output?.substring(0, 100) || runStatus?.status));485    }486    487    if (runStatus && runStatus.status === 'AC') {488      const parsedResults = parseOptimizedOutput(output, testCases, language, options);489      results.cases = parsedResults.cases;490      results.summary = parsedResults.summary;491    } else if (runStatus && runStatus.status === 'CODE_COMPILED') {492      // CODE_COMPILED without output - try to parse or fallback493      if (output && output.includes('TEST_RESULTS_START')) {494        const parsedResults = parseOptimizedOutput(output, testCases, language, options);495        results.cases = parsedResults.cases;496        results.summary = parsedResults.summary;497      } else {498        // No output, fallback to individual execution499        console.log('โš ๏ธ CODE_COMPILED but no output, falling back...');500        return await executeTestCases(sourceCode, language, testCases, { ...options, runAllTestCases: false });501      }502    } else {503      console.log('โš ๏ธ Optimized execution status:', runStatus?.status);504      505      // Check for compilation errors - return immediately without retry506      if (output && (output.includes('error:') || output.includes('cannot be converted') || 507          output.includes('cannot find symbol') || output.includes('incompatible types'))) {508        console.log('โš ๏ธ Compilation error detected - returning error to user');509        return {510          summary: { total: testCases.length, passed: 0, failed: 0, errors: testCases.length },511          cases: testCases.map((tc, i) => ({512            caseId: i + 1,513            visibility: tc.visibility || 'open',514            status: 'CE',515            pass: false,516            input: tc.input,517            expected: tc.expected,518            expected_output: tc.expected,519            output: output.substring(0, 500),520            timeMs: 0,521            memoryKB: 0,522            errorMsg: 'Compilation Error',523            isHidden: tc.isHidden || false524          }))525        };526      }527      528      console.log('โš ๏ธ Attempting to parse any available output...');529      try {530        if (output && output.trim()) {531          console.log('๐Ÿ“Š PARTIAL OUTPUT:', output);532          const parsedResults = parseOptimizedOutput(output, testCases, language, options);533          console.log(`โœ… PARTIAL: ${parsedResults.summary.passed}/${parsedResults.summary.total} test cases passed`);534          return parsedResults;535        }536      } catch (error) {537        console.log('โŒ Failed to parse partial output:', error.message);538      }539 540      // Reduced retries - only retry once if not compilation error541      if (retryCount < 1) {542        return await executeAllTestCasesOptimized(sourceCode, language, testCases, options, retryCount + 1);543      } else {544        console.log('โš ๏ธ Falling back to individual submissions...');545        return await executeTestCases(sourceCode, language, testCases, { ...options, runAllTestCases: false });546      }547    }548 549  } catch (error) {550    console.error('โŒ Optimized execution error:', error.message);551 552    // If it's a compilation error, return it immediately - don't retry553    if (error.message.includes('error:') || error.message.includes('cannot be converted') || 554        error.message.includes('cannot find symbol') || error.message.includes('incompatible types')) {555      console.log('โš ๏ธ Compilation error detected - returning error to user');556      return {557        summary: { total: testCases.length, passed: 0, failed: 0, errors: testCases.length },558        cases: testCases.map((tc, i) => ({559          caseId: i + 1,560          visibility: tc.visibility || 'open',561          status: 'CE',562          pass: false,563          input: tc.input,564          expected: tc.expected,565          expected_output: tc.expected,566          output: error.message.substring(0, 500),567          timeMs: 0,568          memoryKB: 0,569          errorMsg: 'Compilation Error',570          isHidden: tc.isHidden || false571        }))572      };573    }574 575    // Don't retry if HackerEarth is stuck - waste of time576    if (error.message.includes('timed out') || error.message.includes('stuck')) {577      console.log('โš ๏ธ HackerEarth API is overloaded/stuck');578      console.log('โš ๏ธ Skipping retry, falling back immediately...');579      return await executeTestCases(sourceCode, language, testCases, { ...options, runAllTestCases: false });580    }581 582    console.log('โŒ Optimized execution failed completely');583    console.log('โš ๏ธ Falling back to individual test case execution...');584    return await executeTestCases(sourceCode, language, testCases, { ...options, runAllTestCases: false });585  }586 587  return results;588};589 590/**591 * Create JavaScript test harness for optimized execution592 */593const createJavaScriptTestHarness = (sourceCode, testCases) => {594  // Add helper function if not present and code uses it595  let harness = '';596  if (sourceCode.includes('isAlphaNumeric') && !sourceCode.includes('function isAlphaNumeric')) {597    harness += '// Helper function for alphanumeric check\n';598    harness += 'function isAlphaNumeric(char) { return /[a-z0-9]/i.test(char); }\n\n';599  }600 601  // Declare common global variables used in memoization602  harness += '// Global variables for memoization (reset between test cases)\n';603  harness += 'let memo, cache, dp;\n\n';604 605  harness += sourceCode + '\n\n';606  harness += '// Test harness for all test cases\n';607  harness += 'const testCases = [\n';608 609  testCases.forEach((testCase, index) => {610    const input = JSON.parse(testCase.input);611    const expected = testCase.expected;612    harness += `  { input: ${JSON.stringify(input)}, expected: ${JSON.stringify(expected)}, index: ${index} },\n`;613  });614 615  harness += `];616 617// Helper to check if string is palindrome618function isPalindromeStr(s) {619  return s === s.split('').reverse().join('');620}621 622// TreeNode helper for binary tree problems623function TreeNode(val, left, right) {624  this.val = (val===undefined ? 0 : val);625  this.left = (left===undefined ? null : left);626  this.right = (right===undefined ? null : right);627}628 629function buildBinaryTree(arr) {630  if (!arr || arr.length === 0) return null;631  const root = new TreeNode(arr[0]);632  const queue = [root];633  let i = 1;634  while (queue.length > 0 && i < arr.length) {635    const node = queue.shift();636    if (i < arr.length && arr[i] !== null) {637      node.left = new TreeNode(arr[i]);638      queue.push(node.left);639    }640    i++;641    if (i < arr.length && arr[i] !== null) {642      node.right = new TreeNode(arr[i]);643      queue.push(node.right);644    }645    i++;646  }647  return root;648}649 650console.log('TEST_RESULTS_START');651for (let i = 0; i < testCases.length; i++) {652  const testCase = testCases[i];653  try {654    // Reset global memoization variables between test cases655    // Initialize as objects/arrays to support different memoization patterns656    memo = {};657    cache = {};658    dp = [];659    660    // Convert tree inputs if needed661    const processedInput = {};662    for (const key in testCase.input) {663      if (key === 'root' && Array.isArray(testCase.input[key])) {664        processedInput[key] = buildBinaryTree(testCase.input[key]);665      } else {666        processedInput[key] = testCase.input[key];667      }668    }669    const result = ${extractFunctionName(sourceCode)}(...Object.values(processedInput));670    let output;671    let expected;672    673    // Format output based on type674    if (Array.isArray(result)) {675      output = '[' + result.join(',') + ']';676    } else if (typeof result === 'string') {677      output = JSON.stringify(result);678    } else if (typeof result === 'boolean') {679      output = String(result);680    } else {681      output = String(result);682    }683    684    // Format expected based on type - ensure it matches output format685    if (typeof testCase.expected === 'boolean') {686      expected = String(testCase.expected);687    } else if (typeof testCase.expected === 'number') {688      expected = String(testCase.expected);689    } else if (typeof testCase.expected === 'string') {690      // Check if it's already a quoted string or a plain string691      if (testCase.expected.startsWith('"') && testCase.expected.endsWith('"')) {692        expected = testCase.expected;693      } else {694        // It's a plain string value like "3" - keep it as is for comparison695        expected = testCase.expected;696      }697    } else {698      expected = String(testCase.expected);699    }700    701    // Special validation for palindrome problems - accept any valid palindrome of same length702    let passed = output === expected;703    if (!passed && typeof result === 'string' && typeof testCase.expected === 'string') {704      const expectedStr = testCase.expected.replace(/^"|"$/g, '');705      if (result.length === expectedStr.length && isPalindromeStr(result) && isPalindromeStr(expectedStr)) {706        passed = true;707      }708    }709    710    console.log(\`TEST_\${i+1}:\${passed ? 'PASS' : 'FAIL'}:\${output}:\${expected}\`);711  } catch (error) {712    console.log(\`TEST_\${i+1}:ERROR:\${error.message}\`);713  }714}715console.log('TEST_RESULTS_END');`;716 717  return harness;718};719 720/**721 * Create Python test harness for optimized execution722 */723const createPythonTestHarness = (sourceCode, testCases) => {724  // Add helper function if not present and code uses it725  let harness = '';726  if ((sourceCode.includes('is_alpha_numeric') || sourceCode.includes('isAlphaNumeric')) &&727    !sourceCode.includes('def is_alpha_numeric')) {728    harness += '# Helper function for alphanumeric check\n';729    harness += 'def is_alpha_numeric(char):\n';730    harness += '    return char.isalnum()\n\n';731  }732 733  harness += sourceCode + '\n\n';734  harness += '# Test harness for all test cases\n';735  harness += 'test_cases = [\n';736 737  testCases.forEach((testCase, index) => {738    const input = JSON.parse(testCase.input);739    const expected = testCase.expected;740    harness += `    {"input": ${JSON.stringify(input)}, "expected": ${JSON.stringify(expected)}, "index": ${index}},\n`;741  });742 743  harness += `]744 745import json746 747def is_palindrome_str(s):748    return s == s[::-1]749 750class TreeNode:751    def __init__(self, val=0, left=None, right=None):752        self.val = val753        self.left = left754        self.right = right755 756def build_binary_tree(arr):757    if not arr or len(arr) == 0:758        return None759    root = TreeNode(arr[0])760    queue = [root]761    i = 1762    while queue and i < len(arr):763        node = queue.pop(0)764        if i < len(arr) and arr[i] is not None:765            node.left = TreeNode(arr[i])766            queue.append(node.left)767        i += 1768        if i < len(arr) and arr[i] is not None:769            node.right = TreeNode(arr[i])770            queue.append(node.right)771        i += 1772    return root773 774print('TEST_RESULTS_START')775for i, test_case in enumerate(test_cases):776    try:777        # Convert tree inputs if needed778        processed_input = {}779        for key, value in test_case['input'].items():780            if key == 'root' and isinstance(value, list):781                processed_input[key] = build_binary_tree(value)782            else:783                processed_input[key] = value784        result = ${extractPythonFunctionName(sourceCode)}(**processed_input)785        if isinstance(result, list):786            output = '[' + ','.join(map(str, result)) + ']'787        elif isinstance(result, str):788            output = json.dumps(result)789        else:790            output = str(result)791        expected = test_case['expected'] if isinstance(test_case['expected'], str) and test_case['expected'].startswith('"') else json.dumps(test_case['expected'])792        793        # Special validation for palindrome problems794        passed = output == expected795        if not passed and isinstance(result, str) and isinstance(test_case['expected'], str):796            expected_str = test_case['expected'].strip('"')797            if len(result) == len(expected_str) and is_palindrome_str(result) and is_palindrome_str(expected_str):798                passed = True799        800        print(f'TEST_{i+1}:{"PASS" if passed else "FAIL"}:{output}:{expected}')801    except Exception as e:802        print(f'TEST_{i+1}:ERROR:{str(e)}')803print('TEST_RESULTS_END')`;804 805  return harness;806};807 808/**809 * Create Java test harness for optimized execution810 */811const createJavaTestHarness = (sourceCode, testCases) => {812  const functionName = extractJavaFunctionName(sourceCode);813 814  let cleanSourceCode = sourceCode815    .replace(/\bpublic\s+class\s+Main\s*\{[\s\S]*?\}/g, '')816    .replace(/\bpublic\s+static\s+void\s+main\s*\([^)]*\)\s*\{[\s\S]*?\}/g, '')817    .replace(/\bclass\s+Main\s*\{[\s\S]*?\}/g, '')818    .replace(/\bpublic\s+class\s+TestHarness\s*\{[\s\S]*?\}/g, '')819    .replace(/\bclass\s+TestHarness\s*\{[\s\S]*?\}/g, '');820 821  // Add helper method if needed822  if (cleanSourceCode.includes('isAlphaNumeric') && !cleanSourceCode.includes('isAlphaNumeric(')) {823    // Helper will be added inside Solution class824  }825 826  let harness = cleanSourceCode + '\n\n';827  harness += 'public class TestHarness {\n';828  harness += '    public static void main(String[] args) {\n';829  harness += '        Solution solution = new Solution();\n';830  harness += '        System.out.println("TEST_RESULTS_START");\n';831 832  testCases.forEach((testCase, index) => {833    const input = JSON.parse(testCase.input);834    const expected = testCase.expected;835    harness += `        // Test case ${index + 1}\n`;836    harness += `        try {\n`;837 838    let methodCall = `solution.${functionName}(`;839    const inputKeys = Object.keys(input);840 841    // Helper to safely convert value to Java literal842    const toJavaValue = (value) => {843      if (Array.isArray(value)) {844        return `new int[]{${value.join(',')}}`;845      } else if (typeof value === 'string') {846        return `"${value}"`;847      } else if (typeof value === 'number') {848        // Check if number is within int range849        if (value > 2147483647 || value < -2147483648) {850          return `(int)${value}L`; // Cast from long to int (will overflow as expected)851        }852        return value;853      } else {854        return value;855      }856    };857 858    if (inputKeys.length === 1) {859      const value = input[inputKeys[0]];860      methodCall += toJavaValue(value);861    } else {862      const args = inputKeys.map(key => toJavaValue(input[key])).join(', ');863      methodCall += args;864    }865    methodCall += ')';866 867    harness += `            Object result = ${methodCall};\n`;868    harness += `            String output;\n`;869    harness += `            if (result instanceof int[]) {\n`;870    harness += `                output = java.util.Arrays.toString((int[])result);\n`;871    harness += `            } else if (result instanceof String) {\n`;872    harness += `                output = "\\"" + result + "\\"";\n`;873    harness += `            } else {\n`;874    harness += `                output = String.valueOf(result);\n`;875    harness += `            }\n`;876    const expectedIsString = typeof expected === 'string' && expected.startsWith('"');877    const expectedStr = expectedIsString ? expected : `"${expected}"`;878    harness += `            String expectedStr = ${expectedStr};\n`;879    harness += `            boolean passed = output.equals(expectedStr);\n`;880    harness += `            // Special validation for palindrome problems\n`;881    harness += `            if (!passed && result instanceof String) {\n`;882    harness += `                String resultStr = (String)result;\n`;883    harness += `                String expStr = expectedStr.replace("\\"", "");\n`;884    harness += `                if (resultStr.length() == expStr.length()) {\n`;885    harness += `                    String revResult = new StringBuilder(resultStr).reverse().toString();\n`;886    harness += `                    String revExp = new StringBuilder(expStr).reverse().toString();\n`;887    harness += `                    if (resultStr.equals(revResult) && expStr.equals(revExp)) {\n`;888    harness += `                        passed = true;\n`;889    harness += `                    }\n`;890    harness += `                }\n`;891    harness += `            }\n`;892    harness += `            System.out.println("TEST_${index + 1}:" + (passed ? "PASS" : "FAIL") + ":" + output + ":${expected}");\n`;893    harness += `        } catch (Exception e) {\n`;894    harness += `            System.out.println("TEST_${index + 1}:ERROR:" + e.getMessage());\n`;895    harness += `        }\n`;896  });897 898  harness += '        System.out.println("TEST_RESULTS_END");\n';899  harness += '    }\n';900  harness += '}';901 902  return harness;903};904 905/**906 * Create C++ test harness for optimized execution907 */908const createCppTestHarness = (sourceCode, testCases) => {909  const functionName = extractCppFunctionName(sourceCode);910 911  // Simple approach: Split by "int main" and take only the first part912  let cleanSourceCode = sourceCode;913  const mainIndex = cleanSourceCode.indexOf('int main');914  if (mainIndex !== -1) {915    cleanSourceCode = cleanSourceCode.substring(0, mainIndex).trim();916  }917 918  // Ensure all required includes are present919  let includes = '';920  if (!cleanSourceCode.includes('#include <climits>')) includes += '#include <climits>\n';921  if (!cleanSourceCode.includes('#include <cctype>')) includes += '#include <cctype>\n';922  if (!cleanSourceCode.includes('#include <string>')) includes += '#include <string>\n';923  if (!cleanSourceCode.includes('#include <iostream>')) includes += '#include <iostream>\n';924  if (!cleanSourceCode.includes('#include <vector>')) includes += '#include <vector>\n';925  if (!cleanSourceCode.includes('#include <algorithm>')) includes += '#include <algorithm>\n';926  if (!cleanSourceCode.includes('using namespace std')) includes += 'using namespace std;\n';927 928  // Add helper function if needed929  let helpers = '';930  if (cleanSourceCode.includes('isAlphaNumeric') && !cleanSourceCode.includes('bool isAlphaNumeric')) {931    helpers += '\n// Helper function for alphanumeric check\n';932    helpers += 'bool isAlphaNumeric(char c) { return isalnum(c); }\n\n';933  }934 935  let harness = includes + helpers + cleanSourceCode + '\n\n';936  harness += 'int main() {\n';937  harness += '    std::cout << "TEST_RESULTS_START" << std::endl;\n';938 939  testCases.forEach((testCase, index) => {940    const input = JSON.parse(testCase.input);941    const expected = testCase.expected;942    harness += `    // Test case ${index + 1}\n`;943    harness += `    try {\n`;944 945    // Helper to safely convert value to C++ literal946    const toCppValue = (value) => {947      if (Array.isArray(value)) {948        return `{${value.join(',')}}`;949      } else if (typeof value === 'string') {950        return `"${value}"`;951      } else if (typeof value === 'number') {952        // Check if number is within int range953        if (value > 2147483647 || value < -2147483648) {954          return `static_cast<int>(${value}LL)`; // Cast from long long to int955        }956        return value;957      } else {958        return value;959      }960    };961 962    let functionCall = `${functionName}(`;963    const inputKeys = Object.keys(input);964    if (inputKeys.length === 1) {965      const value = input[inputKeys[0]];966      functionCall += toCppValue(value);967    } else {968      const args = inputKeys.map(key => toCppValue(input[key])).join(', ');969      functionCall += args;970    }971    functionCall += ')';972 973    harness += `        auto result = ${functionCall};\n`;974    harness += `        std::string resultStr;\n`;975    harness += `        if constexpr (std::is_same_v<decltype(result), std::string>) {\n`;976    harness += `            resultStr = "\\"" + result + "\\"";\n`;977    harness += `        } else {\n`;978    harness += `            resultStr = std::to_string(result);\n`;979    harness += `        }\n`;980    const expectedIsString = typeof expected === 'string' && expected.startsWith('"');981    const expectedStr = expectedIsString ? expected : `"${expected}"`;982    harness += `        std::string expectedStr = ${expectedStr};\n`;983    harness += `        bool passed = resultStr == expectedStr;\n`;984    harness += `        // Special validation for palindrome problems\n`;985    harness += `        if (!passed && std::is_same_v<decltype(result), std::string>) {\n`;986    harness += `            std::string expStr = expectedStr;\n`;987    harness += `            expStr.erase(std::remove(expStr.begin(), expStr.end(), '"'), expStr.end());\n`;988    harness += `            if (result.length() == expStr.length()) {\n`;989    harness += `                std::string revResult = result;\n`;990    harness += `                std::reverse(revResult.begin(), revResult.end());\n`;991    harness += `                std::string revExp = expStr;\n`;992    harness += `                std::reverse(revExp.begin(), revExp.end());\n`;993    harness += `                if (result == revResult && expStr == revExp) {\n`;994    harness += `                    passed = true;\n`;995    harness += `                }\n`;996    harness += `            }\n`;997    harness += `        }\n`;998    harness += `        std::cout << "TEST_${index + 1}:" << (passed ? "PASS" : "FAIL") << ":" << resultStr << ":" << expectedStr << std::endl;\n`;999    harness += `    } catch (...) {\n`;1000    harness += `        std::cout << "TEST_${index + 1}:ERROR:Exception" << std::endl;\n`;1001    harness += `    }\n`;1002  });1003 1004  harness += '    std::cout << "TEST_RESULTS_END" << std::endl;\n';1005  harness += '    return 0;\n';1006  harness += '}';1007 1008  return harness;1009};1010 1011/**1012 * NOTE: validateMultipleOutputs is now imported from validators/outputValidator.js1013 * The old implementation has been removed to avoid duplication1014 */1015 1016/**1017 1018/**1019 * Parse optimized output to extract individual test results1020 * Extracts results between TEST_RESULTS_START and TEST_RESULTS_END1021 */1022const parseOptimizedOutput = (output, testCases, language, options = {}) => {1023  const results = {1024    summary: {1025      total: testCases.length,1026      passed: 0,1027      failed: 0,1028      errors: 01029    },1030    cases: []1031  };1032 1033  console.log(`๐Ÿ“Š Parsing test results from output (${output.length} chars)`);1034  console.log(`๐Ÿ“ Raw output:\n${output}`);1035 1036  // Extract content between TEST_RESULTS_START and TEST_RESULTS_END1037  const startMarker = 'TEST_RESULTS_START';1038  const endMarker = 'TEST_RESULTS_END';1039  const startIdx = output.indexOf(startMarker);1040  const endIdx = output.indexOf(endMarker);1041 1042  if (startIdx === -1 || endIdx === -1) {1043    console.log('โš ๏ธ Could not find TEST_RESULTS markers in output');1044    // Fallback: try to parse all lines1045    const lines = output.split('\n');1046    const testResults = lines.filter(line => line.trim().startsWith('TEST_') && line.includes(':'));1047    console.log(`   Found ${testResults.length} test result lines without markers`);1048    return parseTestResultLines(testResults, testCases, options);1049  }1050 1051  const testResultsSection = output.substring(startIdx + startMarker.length, endIdx).trim();1052  const testResultLines = testResultsSection.split('\n').filter(line => line.trim().startsWith('TEST_'));1053 1054  return parseTestResultLines(testResultLines, testCases, options);1055};1056 1057/**1058 * Parse individual test result lines1059 * Format: TEST_1:PASS:321:321 or TEST_1:FAIL:123:321 or TEST_1:ERROR:message1060 */1061const parseTestResultLines = (testResultLines, testCases, options = {}) => {1062  const results = {1063    summary: {1064      total: testCases.length,1065      passed: 0,1066      failed: 0,1067      errors: 01068    },1069    cases: []1070  };1071 1072  testResultLines.forEach((resultLine, index) => {1073    const trimmedLine = resultLine.trim();1074 1075    // Parse format: TEST_N:STATUS:OUTPUT:EXPECTED1076    const match = trimmedLine.match(/^TEST_(\d+):([^:]+):(.*)$/);1077 1078    if (!match) {1079      return;1080    }1081 1082    const testNumber = parseInt(match[1], 10);1083    const status = match[2];1084    const remainder = match[3];1085 1086    // Split remainder by last colon to separate output and expected1087    const lastColonIdx = remainder.lastIndexOf(':');1088    let output = remainder;1089    let expected = '';1090 1091    if (lastColonIdx !== -1 && status !== 'ERROR') {1092      output = remainder.substring(0, lastColonIdx);1093      expected = remainder.substring(lastColonIdx + 1);1094    }1095 1096    const testCaseIndex = testNumber - 1;1097    if (testCaseIndex < 0 || testCaseIndex >= testCases.length) {1098      return;1099    }1100 1101    const testCase = testCases[testCaseIndex];1102    let isPassed = status === 'PASS';1103 1104    // Re-validate if status is FAIL but output matches expected1105    if (!isPassed && status === 'FAIL' && output && expected) {1106      // Normalize both for comparison - remove quotes and compare values1107      const normalizedOutput = output.trim().replace(/^["']|["']$/g, '');1108      const normalizedExpected = expected.trim().replace(/^["']|["']$/g, '');1109 1110      // Try direct string comparison first1111      if (normalizedOutput === normalizedExpected) {1112        isPassed = true;1113      }1114      // Try with original values (handles quote differences)1115      else if (output.trim() === expected.trim()) {1116        isPassed = true;1117      }1118      // Try numeric comparison if both can be parsed as numbers1119      else if (!isNaN(normalizedOutput) && !isNaN(normalizedExpected)) {1120        if (Number(normalizedOutput) === Number(normalizedExpected)) {1121          isPassed = true;1122        }1123      }1124      // Check if output is array and expected is array length (count)1125      else if (normalizedOutput.startsWith('[') && !isNaN(normalizedExpected)) {1126        try {1127          const outputArray = JSON.parse(normalizedOutput);1128          if (Array.isArray(outputArray) && outputArray.length === Number(normalizedExpected)) {1129            isPassed = true;1130          }1131        } catch (e) {}1132      }1133      // Check for in-place modification problems (expected = "merged")1134      else if (normalizedExpected === 'merged' && normalizedOutput.startsWith('[')) {1135        try {1136          const outputArr = JSON.parse(normalizedOutput);1137          const inputObj = JSON.parse(testCase.input);1138          1139          // For merge sorted array: check if output is sorted1140          if (inputObj.nums1 && inputObj.nums2 && inputObj.m !== undefined && inputObj.n !== undefined) {1141            const isSorted = outputArr.every((val, i, arr) => i === 0 || arr[i - 1] <= val);1142            const expectedLength = inputObj.m + inputObj.n;1143            1144            if (isSorted && outputArr.length === expectedLength) {1145              isPassed = true;1146              console.log(`   ๐Ÿ”„ In-place modification: Array is sorted with correct length (${outputArr.length})`);1147            }1148          }1149        } catch (e) {}1150      }1151      // Check for palindrome problems - any palindrome of same length is valid1152      else if (typeof normalizedOutput === 'string' && typeof normalizedExpected === 'string') {1153        const isPalindrome = (s) => s === s.split('').reverse().join('');1154        1155        if (normalizedOutput.length === normalizedExpected.length && 1156            isPalindrome(normalizedOutput) && 1157            isPalindrome(normalizedExpected)) {1158          isPassed = true;1159          console.log(`   ๐Ÿ”„ Palindrome validation: "${normalizedOutput}" is a valid palindrome (same length as "${normalizedExpected}")`);1160        }1161      }1162      // Dynamic validation for problems with multiple valid outputs1163      if (!isPassed && normalizedOutput.startsWith('[') && normalizedExpected.startsWith('[')) {1164        try {1165          const outputArr = JSON.parse(normalizedOutput);1166          const expectedArr = JSON.parse(normalizedExpected);1167 1168          if (Array.isArray(outputArr) && Array.isArray(expectedArr)) {1169            const inputObj = JSON.parse(testCase.input);1170 1171            // Validate using dynamic validator1172            const validationResult = validateMultipleOutputs(outputArr, expectedArr, inputObj);1173            if (validationResult.isValid) {1174              isPassed = true;1175              console.log(`   ๐Ÿ”„ Dynamic validation: ${validationResult.message}`);1176            }1177          }1178        } catch (e) {1179          // Not valid JSON arrays, skip this validation1180        }1181      }1182      // Finally, try custom verifier if available and nothing else matched1183      if (!isPassed && options.verifierType) {1184        console.log(`   ๐Ÿ”ง Using custom verifier: ${options.verifierType}`);1185        const verifyResult = AnswerVerifier.verify(1186          options.verifierType,1187          testCase.input,1188          normalizedOutput,1189          normalizedExpected,1190          options.verifierMetadata || {}1191        );1192        isPassed = verifyResult.valid;1193        console.log(`   ๐Ÿ” Verifier result: ${verifyResult.valid ? 'โœ… VALID' : 'โŒ INVALID'} - ${verifyResult.reason}`);1194        1195        // Store verification info for frontend1196        if (isPassed && verifyResult.allValidOutputs) {1197          testCase.verificationReason = verifyResult.reason;1198          testCase.validOutputs = verifyResult.allValidOutputs;1199        }1200      }

Showing the first 1,200 of 1373 lines. Download the file for the rest.