@@ -30,8 +30,8 @@ fn solve_arithmetic(prompt: &str) -> Option<i64> {
3030 while let Some ( op) = tokens. next ( ) {
3131 let n: i64 = tokens. next ( ) ?. parse ( ) . ok ( ) ?;
3232 match op {
33- "+" => acc += n ,
34- "-" => acc -= n ,
33+ "+" => acc = acc . checked_add ( n ) ? ,
34+ "-" => acc = acc . checked_sub ( n ) ? ,
3535 _ => return None ,
3636 }
3737 }
@@ -45,15 +45,15 @@ fn solve_algebra(prompt: &str) -> Option<i64> {
4545 let ( lhs, rhs) = eq. split_once ( '=' ) ?;
4646 let ( al, cl) = parse_linear ( lhs. trim ( ) ) ?;
4747 let ( ar, cr) = parse_linear ( rhs. trim ( ) ) ?;
48- let denom = al - ar ;
48+ let denom = al. checked_sub ( ar ) ? ;
4949 if denom == 0 {
5050 return None ;
5151 }
52- let num = cr - cl ;
53- if num % denom != 0 {
52+ let num = cr. checked_sub ( cl ) ? ;
53+ if num. checked_rem ( denom) ? != 0 {
5454 return None ;
5555 }
56- Some ( num / denom)
56+ num. checked_div ( denom)
5757}
5858
5959/// Parse a linear expression in `x` into `(coeff_of_x, constant)`.
@@ -76,12 +76,12 @@ fn parse_linear(s: &str) -> Option<(i64, i64)> {
7676 let m: i64 = it. next ( ) ?. parse ( ) . ok ( ) ?;
7777 match op {
7878 "+" => ( 1 , m) ,
79- "-" => ( 1 , -m ) ,
79+ "-" => ( 1 , m . checked_neg ( ) ? ) ,
8080 _ => return None ,
8181 }
8282 }
8383 } ;
84- return Some ( ( a * coeff_inner, a * const_inner) ) ;
84+ return Some ( ( a. checked_mul ( coeff_inner) ? , a. checked_mul ( const_inner) ? ) ) ;
8585 }
8686
8787 // Sum of `±`-separated terms.
@@ -99,10 +99,10 @@ fn parse_linear(s: &str) -> Option<(i64, i64)> {
9999 "-" => -1 ,
100100 _ => cpart. parse ( ) . ok ( ) ?,
101101 } ;
102- coeff += sign * c ;
102+ coeff = coeff . checked_add ( sign. checked_mul ( c ) ? ) ? ;
103103 } else {
104104 let n: i64 = t. parse ( ) . ok ( ) ?;
105- konst += sign * n ;
105+ konst = konst . checked_add ( sign. checked_mul ( n ) ? ) ? ;
106106 }
107107 sign = 1 ;
108108 }
@@ -127,31 +127,34 @@ fn solve_sequence(prompt: &str) -> Option<i64> {
127127fn next_in_sequence ( n : & [ i64 ] ) -> Option < i64 > {
128128 let last = * n. last ( ) ?;
129129
130- // Arithmetic: constant first difference.
131- let d = n[ 1 ] - n[ 0 ] ;
132- if n. windows ( 2 ) . all ( |w| w[ 1 ] - w[ 0 ] == d) {
133- return Some ( last + d) ;
130+ // Arithmetic: constant first difference. An overflowing difference means
131+ // the pattern doesn't fit, not that the answer wraps.
132+ if let Some ( d) = n[ 1 ] . checked_sub ( n[ 0 ] ) {
133+ if n. windows ( 2 ) . all ( |w| w[ 1 ] . checked_sub ( w[ 0 ] ) == Some ( d) ) {
134+ return last. checked_add ( d) ;
135+ }
134136 }
135137
136138 // Geometric: constant integer ratio.
137- if n. iter ( ) . all ( |& v| v != 0 ) && n[ 0 ] != 0 && n[ 1 ] % n[ 0 ] == 0 {
138- let r = n[ 1 ] / n[ 0 ] ;
139- if r != 0 && n. windows ( 2 ) . all ( |w| w[ 1 ] == w[ 0 ] * r) {
140- return Some ( last * r) ;
139+ if n. iter ( ) . all ( |& v| v != 0 ) && n[ 1 ] . checked_rem ( n[ 0 ] ) == Some ( 0 ) {
140+ if let Some ( r) = n[ 1 ] . checked_div ( n[ 0 ] ) {
141+ if r != 0 && n. windows ( 2 ) . all ( |w| w[ 0 ] . checked_mul ( r) == Some ( w[ 1 ] ) ) {
142+ return last. checked_mul ( r) ;
143+ }
141144 }
142145 }
143146
144147 // Fibonacci-like: each term is the sum of the two before it.
145- if n. len ( ) >= 3 && ( 2 ..n. len ( ) ) . all ( |i| n[ i] == n[ i - 1 ] + n[ i - 2 ] ) {
146- return Some ( n[ n. len ( ) - 1 ] + n[ n. len ( ) - 2 ] ) ;
148+ if n. len ( ) >= 3 && ( 2 ..n. len ( ) ) . all ( |i| n[ i - 1 ] . checked_add ( n[ i - 2 ] ) == Some ( n[ i] ) ) {
149+ return n[ n. len ( ) - 1 ] . checked_add ( n[ n. len ( ) - 2 ] ) ;
147150 }
148151
149152 // Squares: all perfect squares with consecutive roots.
150153 let roots: Option < Vec < i64 > > = n. iter ( ) . map ( |& v| isqrt_exact ( v) ) . collect ( ) ;
151154 if let Some ( roots) = roots {
152155 if roots. windows ( 2 ) . all ( |w| w[ 1 ] == w[ 0 ] + 1 ) {
153156 let nr = roots[ roots. len ( ) - 1 ] + 1 ;
154- return Some ( nr * nr) ;
157+ return nr . checked_mul ( nr) ;
155158 }
156159 }
157160
@@ -160,12 +163,16 @@ fn next_in_sequence(n: &[i64]) -> Option<i64> {
160163 . iter ( )
161164 . enumerate ( )
162165 . all ( |( i, & v) | if i % 2 == 0 { v >= 0 } else { v < 0 } ) ;
163- let mags: Vec < i64 > = n. iter ( ) . map ( |v| v. abs ( ) ) . collect ( ) ;
164- let md = mags[ 1 ] - mags[ 0 ] ;
165- if signs_alternate && mags. windows ( 2 ) . all ( |w| w[ 1 ] - w[ 0 ] == md) {
166- let next_mag = mags[ mags. len ( ) - 1 ] + md;
167- let next_sign = if last >= 0 { -1 } else { 1 } ;
168- return Some ( next_sign * next_mag) ;
166+ let mags: Option < Vec < i64 > > = n. iter ( ) . map ( |v| v. checked_abs ( ) ) . collect ( ) ;
167+ if signs_alternate {
168+ if let Some ( mags) = mags {
169+ if let Some ( md) = mags[ 1 ] . checked_sub ( mags[ 0 ] ) {
170+ if mags. windows ( 2 ) . all ( |w| w[ 1 ] . checked_sub ( w[ 0 ] ) == Some ( md) ) {
171+ let next_sign: i64 = if last >= 0 { -1 } else { 1 } ;
172+ return next_sign. checked_mul ( mags[ mags. len ( ) - 1 ] . checked_add ( md) ?) ;
173+ }
174+ }
175+ }
169176 }
170177
171178 None
@@ -179,7 +186,7 @@ fn isqrt_exact(v: i64) -> Option<i64> {
179186 let r = ( v as f64 ) . sqrt ( ) . round ( ) as i64 ;
180187 [ r - 1 , r, r + 1 ]
181188 . into_iter ( )
182- . find ( |& cand| cand >= 0 && cand * cand == v )
189+ . find ( |& cand| cand >= 0 && cand. checked_mul ( cand) == Some ( v ) )
183190}
184191
185192#[ cfg( test) ]
@@ -267,4 +274,80 @@ mod tests {
267274 assert_eq ! ( solve( "anagram" , "Unscramble: tca" ) , None ) ;
268275 assert_eq ! ( solve( "riddle" , "What has keys but no locks?" ) , None ) ;
269276 }
277+
278+ // #345: prompts are attacker/service-controlled and every literal can be a
279+ // valid i64 while the evaluation still overflows. Overflow must return
280+ // None (unsolvable), never a debug panic or a wrapped wrong answer.
281+
282+ #[ test]
283+ fn arithmetic_overflow_returns_none ( ) {
284+ assert_eq ! (
285+ solve( "arithmetic" , "What is 9223372036854775807 + 1?" ) ,
286+ None
287+ ) ;
288+ assert_eq ! (
289+ solve( "arithmetic" , "What is -9223372036854775808 - 1?" ) ,
290+ None
291+ ) ;
292+ // Boundary-adjacent values still solve.
293+ assert_eq ! (
294+ solve( "arithmetic" , "What is 9223372036854775806 + 1?" ) . as_deref( ) ,
295+ Some ( "9223372036854775807" )
296+ ) ;
297+ }
298+
299+ #[ test]
300+ fn algebra_overflow_returns_none ( ) {
301+ // num = i64::MIN, denom = -1: the quotient overflows.
302+ assert_eq ! (
303+ solve( "algebra" , "Solve for x: x + 1 = 2x + -9223372036854775807" ) ,
304+ None
305+ ) ;
306+ // sign * coefficient overflows at i64::MIN.
307+ assert_eq ! (
308+ solve( "algebra" , "Solve for x: x - -9223372036854775808x = 1" ) ,
309+ None
310+ ) ;
311+ // A large but solvable equation still solves.
312+ assert_eq ! (
313+ solve( "algebra" , "Solve for x: 4611686018427387904x + 0 = 0" ) . as_deref( ) ,
314+ Some ( "0" )
315+ ) ;
316+ }
317+
318+ #[ test]
319+ fn sequence_overflow_returns_none ( ) {
320+ // First difference overflows (1 - i64::MIN).
321+ assert_eq ! (
322+ solve(
323+ "sequence" ,
324+ "What is the next number in this sequence? -9223372036854775808, 1, 9223372036854775806, ?"
325+ ) ,
326+ None
327+ ) ;
328+ // i64::MIN in an alternating-sign candidate: abs() overflows.
329+ assert_eq ! (
330+ solve(
331+ "sequence" ,
332+ "What is the next number in this sequence? 1, -9223372036854775808, 3, ?"
333+ ) ,
334+ None
335+ ) ;
336+ // Perfect-square check near i64::MAX: candidate root squared overflows.
337+ assert_eq ! (
338+ solve(
339+ "sequence" ,
340+ "What is the next number in this sequence? 9223372036854775807, 9223372036854775800, 9223372036854775801, ?"
341+ ) ,
342+ None
343+ ) ;
344+ // Geometric next term overflows (r = 2, last * 2 > i64::MAX).
345+ assert_eq ! (
346+ solve(
347+ "sequence" ,
348+ "What is the next number in this sequence? 2305843009213693951, 4611686018427387902, 9223372036854775804, ?"
349+ ) ,
350+ None
351+ ) ;
352+ }
270353}
0 commit comments