// Convert standalone display math $$...$$ to inline \(...\) (with \frac -> \dfrac) // so in-sentence fractions render inline, not as oversized display blocks. $conv = function($t) { if ($t === null || $t === '') return $t; if (strpos($t, '$$') === false) return $t; return preg_replace_callback('/\$\$(.*?)\$\$/s', function($m) { $inner = trim($m[1]); $inner = str_replace('\\frac', '\\dfrac', $inner); return '\\(' . $inner . '\\)'; }, $t); }; $qChanged=[]; $qn=0; DB::table('questions')->where('qa_status','ai_generated')->orderBy('id') ->chunkById(1000, function($rows) use (&$qChanged,&$qn,$conv){ foreach ($rows as $r) { $upd=[]; foreach (['question','explanation','answer0','answer1','answer2','answer3'] as $c) { $new=$conv((string)$r->$c); if ($new !== (string)$r->$c) $upd[$c]=$new; } if ($upd) { $qChanged[]=['id'=>$r->id]+(array)$r; $upd['updated_at']=now(); DB::table('questions')->where('id',$r->id)->update($upd); $qn++; } } }); file_put_contents(storage_path('app/fracinline_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,$conv){ foreach ($rows as $r) { $new=$conv((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/fracinline_s_backup_'.date('Ymd_His').'.json'), json_encode($sChanged, JSON_UNESCAPED_UNICODE)); echo "questions_updated=$qn solutions_updated=$sn\n"; $resQ=0; DB::table('questions')->where('qa_status','ai_generated')->orderBy('id')->chunkById(2000,function($rows)use(&$resQ){ foreach($rows as $r){ if(strpos((string)$r->question,'$$')!==false) $resQ++; } }); echo "questions still containing \$\$ : $resQ\n";