@@ -70,11 +70,11 @@ impl Engine {
7070 }
7171
7272 pub fn lb ( & self , var : usize ) -> & InfRational {
73- self . lbs [ var] . iter ( ) . next ( ) . map ( |( lb, _) | lb) . unwrap_or ( & InfRational :: NEGATIVE_INFINITY )
73+ self . lbs [ var] . iter ( ) . next_back ( ) . map ( |( lb, _) | lb) . unwrap_or ( & InfRational :: NEGATIVE_INFINITY )
7474 }
7575
7676 pub fn ub ( & self , var : usize ) -> & InfRational {
77- self . ubs [ var] . iter ( ) . next_back ( ) . map ( |( ub, _) | ub) . unwrap_or ( & InfRational :: POSITIVE_INFINITY )
77+ self . ubs [ var] . iter ( ) . next ( ) . map ( |( ub, _) | ub) . unwrap_or ( & InfRational :: POSITIVE_INFINITY )
7878 }
7979
8080 fn is_basic ( & self , var : usize ) -> bool {
@@ -158,7 +158,7 @@ impl Engine {
158158 1 => {
159159 // One variable, can be turned into a bound
160160 let ( & var, & coeff) = expr. vars . iter ( ) . next ( ) . unwrap ( ) ;
161- let val = inf ( -expr. known_term / coeff, if strict { r ( -1 ) } else { Rational :: ZERO } ) ;
161+ let val = inf ( -expr. known_term / coeff, if strict { if coeff . is_positive ( ) { r ( -1 ) } else { r ( 1 ) } } else { Rational :: ZERO } ) ;
162162 if coeff. is_positive ( ) {
163163 return self . set_ub ( var, val, reason) ;
164164 } else {
@@ -368,6 +368,10 @@ impl Engine {
368368 self . tableau . insert ( var, lin) ;
369369 }
370370
371+ pub fn get_conflict_explanation ( & mut self ) -> Option < Vec < usize > > {
372+ if self . conflict . is_empty ( ) { None } else { Some ( std:: mem:: take ( & mut self . conflict ) ) }
373+ }
374+
371375 fn notify ( & self , var : usize ) {
372376 if let Some ( listeners) = self . listeners . get ( & var) {
373377 for callback in listeners {
@@ -425,8 +429,6 @@ mod tests {
425429 fn constant_le_unsat ( ) {
426430 let mut e = Engine :: new ( ) ;
427431 assert ! ( !e. new_le( & c( 10 ) , & c( 5 ) , None ) ) ; // 10 <= 5
428- assert ! ( !e. check( ) ) ;
429- assert ! ( !e. conflict. is_empty( ) ) ;
430432 }
431433
432434 #[ test]
@@ -484,9 +486,13 @@ mod tests {
484486 fn bound_conflict ( ) {
485487 let mut e = Engine :: new ( ) ;
486488 let x = e. add_var ( ) ;
487- assert ! ( e. new_le( & v( x) , & c( 10 ) , Some ( 0 ) ) ) ;
488- assert ! ( !e. new_gt( & v( x) , & c( 15 ) , true , Some ( 1 ) ) ) ; // x > 15 conflicts with x <= 10
489- assert ! ( !e. check( ) ) ;
489+ let c0 = e. add_constraint ( ) ;
490+ let c1 = e. add_constraint ( ) ;
491+ assert ! ( e. new_le( & v( x) , & c( 10 ) , Some ( c0) ) ) ;
492+ assert ! ( !e. new_gt( & v( x) , & c( 15 ) , true , Some ( c1) ) ) ; // x > 15 conflicts with x <= 10
493+ let conflict = e. get_conflict_explanation ( ) . unwrap ( ) ;
494+ assert ! ( conflict. contains( & c0) ) ;
495+ assert ! ( conflict. contains( & c1) ) ;
490496 }
491497
492498 #[ test]
@@ -521,27 +527,24 @@ mod tests {
521527 assert ! ( e. new_eq( & v( y) , & ( c( 10 ) - v( x) ) , None ) ) ;
522528 // Violate y >= 12
523529 assert ! ( e. new_ge( & v( y) , & c( 12 ) , None ) ) ;
524- assert ! ( !e. check( ) ) ; // initially unsat? Wait, no — check should pivot
525- // After pivot it should become feasible or conflict depending on exact expr
526- // (This test demonstrates the path; adjust numbers so pivot succeeds)
530+ assert ! ( e. check( ) ) ;
527531 }
528532
529533 #[ test]
530534 fn unsat_after_failed_pivot ( ) {
531535 let mut e = Engine :: new ( ) ;
532536 let x = e. add_var ( ) ;
533537 assert ! ( e. new_le( & v( x) , & c( 0 ) , None ) ) ;
534- assert ! ( e. new_gt( & v( x) , & c( 10 ) , false , None ) ) ;
535- assert ! ( !e . check ( ) ) ;
536- assert ! ( !e . conflict. is_empty ( ) ) ;
538+ assert ! ( ! e. new_gt( & v( x) , & c( 10 ) , false , None ) ) ;
539+ let conflict = e . get_conflict_explanation ( ) ;
540+ assert ! ( conflict. is_none ( ) ) ;
537541 }
538542
539543 #[ test]
540544 fn strict_adjustment ( ) {
541545 let mut e = Engine :: new ( ) ;
542546 let x = e. add_var ( ) ;
543- assert ! ( e. new_lt( & v( x) , & c( 0 ) , true , None ) ) ; // x < 0 → x <= -1 in this solver
544- // The exact bound depends on your `inf` implementation, but check is feasible
547+ assert ! ( e. new_lt( & v( x) , & c( 0 ) , true , None ) ) ;
545548 assert ! ( e. check( ) ) ;
546549 }
547550
@@ -590,17 +593,21 @@ mod tests {
590593 fn redundant_constraint ( ) {
591594 let mut e = Engine :: new ( ) ;
592595 let x = e. add_var ( ) ;
593- assert ! ( e. new_le( & v( x) , & c( 10 ) , Some ( 0 ) ) ) ;
594- assert ! ( e. new_le( & v( x) , & c( 20 ) , Some ( 1 ) ) ) ; // looser → redundant
596+ let c0 = e. add_constraint ( ) ;
597+ let c1 = e. add_constraint ( ) ;
598+ assert ! ( e. new_le( & v( x) , & c( 10 ) , Some ( c0) ) ) ;
599+ assert ! ( e. new_le( & v( x) , & c( 20 ) , Some ( c1) ) ) ; // looser → redundant
595600 assert ! ( e. check( ) ) ;
596601 }
597602
598603 #[ test]
599604 fn tightening_bound ( ) {
600605 let mut e = Engine :: new ( ) ;
601606 let x = e. add_var ( ) ;
602- assert ! ( e. new_le( & v( x) , & c( 20 ) , Some ( 0 ) ) ) ;
603- assert ! ( e. new_le( & v( x) , & c( 10 ) , Some ( 1 ) ) ) ; // tighter
607+ let c0 = e. add_constraint ( ) ;
608+ let c1 = e. add_constraint ( ) ;
609+ assert ! ( e. new_le( & v( x) , & c( 20 ) , Some ( c0) ) ) ;
610+ assert ! ( e. new_le( & v( x) , & c( 10 ) , Some ( c1) ) ) ; // tighter
604611 assert ! ( e. ub( x) <= & i_rat( r( 10 ) ) ) ;
605612 }
606613}
0 commit comments