// Normalize inline/display LaTeX delimiters \( \) \[ \] -> $$ for ai-generated content. $map = ['\\(','\\)','\\[','\\]']; $qChanged=[]; $qn=0; DB::table('questions')->where('qa_status','ai_generated')->orderBy('id') ->chunkById(1000, function($rows) use (&$qChanged,&$qn,$map){ foreach ($rows as $r) { $newQ = str_replace($map, '$$', (string)$r->question); $newE = str_replace($map, '$$', (string)$r->explanation); $upd=[]; if ($newQ !== (string)$r->question) $upd['question']=$newQ; if ($newE !== (string)$r->explanation) $upd['explanation']=$newE; if ($upd) { $qChanged[]=['id'=>$r->id,'question'=>$r->question,'explanation'=>$r->explanation]; $upd['updated_at']=now(); DB::table('questions')->where('id',$r->id)->update($upd); $qn++; } } }); file_put_contents(storage_path('app/latexnorm_q_backup_'.date('Ymd_His').'.json'), json_encode($qChanged, JSON_UNESCAPED_UNICODE)); $sChanged=[]; $sn=0; DB::table('solutions')->where('source','ai')->orderBy('id') ->chunkById(1000, function($rows) use (&$sChanged,&$sn,$map){ foreach ($rows as $r) { $new = str_replace($map, '$$', (string)$r->solution); if ($new !== (string)$r->solution) { $sChanged[]=['id'=>$r->id,'solution'=>$r->solution]; DB::table('solutions')->where('id',$r->id)->update(['solution'=>$new,'updated_at'=>now()]); $sn++; } } }); file_put_contents(storage_path('app/latexnorm_s_backup_'.date('Ymd_His').'.json'), json_encode($sChanged, JSON_UNESCAPED_UNICODE)); echo "questions_updated=$qn solutions_updated=$sn\n"; $resQ = DB::table('questions')->where('qa_status','ai_generated')->where('question','like','%\\\\(%')->count(); $resS = DB::table('solutions')->where('source','ai')->where('solution','like','%\\\\(%')->count(); echo "residual_Q_with_backslash_paren=$resQ residual_SOL=$resS\n";