@@ -241,6 +241,7 @@ export default function ProofSearchVisualization() {
241241 const runIdRef = useRef ( 0 ) ;
242242 const cancelRunRef = useRef ( null ) ;
243243 const szsDoneRef = useRef ( false ) ;
244+ const negatedRef = useRef ( new Set ( ) ) ;
244245
245246 useEffect ( ( ) => {
246247 outputRef . current = output ;
@@ -284,6 +285,7 @@ export default function ProofSearchVisualization() {
284285 const resetRunState = ( ) => {
285286 clauseMapRef . current . clear ( ) ;
286287 edgeMapRef . current . clear ( ) ;
288+ negatedRef . current . clear ( ) ;
287289 setClauses ( [ ] ) ;
288290 setEdges ( [ ] ) ;
289291 setSelectedId ( null ) ;
@@ -302,10 +304,21 @@ export default function ProofSearchVisualization() {
302304 text : text ?? existing ?. text ?? '' ,
303305 status : status ?? existing ?. status ?? 'new' ,
304306 subsumed : existing ?. subsumed ?? false ,
307+ negated : existing ?. negated ?? negatedRef . current . has ( key ) ,
305308 } ;
306309 map . set ( key , next ) ;
307310 } ;
308311
312+ const markNegated = ( id ) => {
313+ const key = String ( id ) ;
314+ negatedRef . current . add ( key ) ;
315+ const map = clauseMapRef . current ;
316+ const existing = map . get ( key ) ;
317+ if ( existing ) {
318+ map . set ( key , { ...existing , negated : true } ) ;
319+ }
320+ } ;
321+
309322 const markSubsumed = ( id ) => {
310323 const key = String ( id ) ;
311324 const map = clauseMapRef . current ;
@@ -328,10 +341,12 @@ export default function ProofSearchVisualization() {
328341 ids : [ ] ,
329342 statusById : new Map ( ) ,
330343 subsumedById : new Map ( ) ,
344+ negatedById : new Map ( ) ,
331345 } ;
332346 entry . ids . push ( String ( clause . id ) ) ;
333347 entry . statusById . set ( String ( clause . id ) , clause . status || 'new' ) ;
334348 entry . subsumedById . set ( String ( clause . id ) , Boolean ( clause . subsumed ) ) ;
349+ entry . negatedById . set ( String ( clause . id ) , Boolean ( clause . negated ) ) ;
335350 grouped . set ( textKey , entry ) ;
336351 } ) ;
337352
@@ -354,12 +369,14 @@ export default function ProofSearchVisualization() {
354369 ? 'passive'
355370 : ( activeIds . length ? 'active' : 'new' ) ;
356371 const subsumed = entry . subsumedById . get ( displayId ) || false ;
372+ const negated = entry . negatedById . get ( displayId ) || false ;
357373 entry . ids . forEach ( ( id ) => idToDisplay . set ( id , displayId ) ) ;
358374 displayClauses . push ( {
359375 id : displayId ,
360376 text : entry . text ,
361377 status,
362378 subsumed,
379+ negated,
363380 } ) ;
364381 } ) ;
365382
@@ -415,6 +432,12 @@ export default function ProofSearchVisualization() {
415432 } ;
416433
417434 const parseOutputLine = ( line ) => {
435+ if ( / n e g a t e d c o n j e c t u r e / i. test ( line ) ) {
436+ const idMatch = line . match ( / : \s * ( \d + ) \. \s / ) ;
437+ if ( idMatch ) {
438+ markNegated ( idMatch [ 1 ] ) ;
439+ }
440+ }
418441 const reduceMatch = line . match ( REDUCE_RE ) ;
419442 if ( reduceMatch ) {
420443 markSubsumed ( reduceMatch [ 2 ] ) ;
@@ -431,6 +454,16 @@ export default function ProofSearchVisualization() {
431454 bracket = bracketMatches [ bracketMatches . length - 1 ] [ 1 ] ;
432455 text = text . replace ( / \s * \[ [ ^ \] ] + \] \s * $ / , '' ) ;
433456 }
457+ if ( bracket ) {
458+ if ( / n e g a t e d c o n j e c t u r e / i. test ( bracket ) ) {
459+ markNegated ( id ) ;
460+ } else {
461+ const refs = Array . from ( bracket . matchAll ( / \b ( \d + ) \b / g) ) . map ( ( m ) => m [ 1 ] ) ;
462+ if ( refs . some ( ( ref ) => negatedRef . current . has ( String ( ref ) ) ) ) {
463+ markNegated ( id ) ;
464+ }
465+ }
466+ }
434467 if ( tag === 'SA' ) {
435468 const status = normalizePhaseStatus ( phase ) ;
436469 if ( status === 'new' ) {
@@ -577,25 +610,29 @@ export default function ProofSearchVisualization() {
577610 < div className = { styles . canvasFooter } >
578611 < div className = { styles . legend } >
579612 < span className = { styles . legendItem } >
580- < span className = { styles . dot } style = { { background : '#ff9fb2' } } />
613+ < span className = { ` ${ styles . dot } ${ styles . legendDotNew } ` } />
581614 New
582615 </ span >
583616 < span className = { styles . legendItem } >
584- < span className = { styles . dot } style = { { background : '#b62929' } } />
617+ < span className = { ` ${ styles . dot } ${ styles . legendDotActive } ` } />
585618 Active
586619 </ span >
587620 < span className = { styles . legendItem } >
588- < span className = { styles . dot } style = { { background : '#8a8a8a' } } />
621+ < span className = { ` ${ styles . dot } ${ styles . legendDotPassive } ` } />
589622 Passive
590623 </ span >
591624 < span className = { styles . legendItem } >
592- < span className = { styles . dot } style = { { background : '#f97316' } } />
625+ < span className = { ` ${ styles . dot } ${ styles . legendDotSelected } ` } />
593626 Selected
594627 </ span >
595628 < span className = { styles . legendItem } >
596629 < span className = { `${ styles . dot } ${ styles . subsumedDot } ` } />
597630 Subsumed
598631 </ span >
632+ < span className = { styles . legendItem } >
633+ < span className = { `${ styles . dot } ${ styles . negatedDot } ` } />
634+ Negated conjecture
635+ </ span >
599636 </ div >
600637 < div className = { styles . hint } >
601638 { awaitingInput ? 'Waiting for your clause choice…' : 'Awaiting run' }
0 commit comments