← AT&T Interview Insights

AT&T·Data Scientist·Technical Phone Screen·Intermediate

IntermediatePrefer not to say
May 2026

Summary

Technical screen for a Data Scientist role that somehow went deep into low-level integer arithmetic. Wasn't expecting to be asked to reimplement multiplication from scratch, let alone handle overflow saturation without 64-bit types. Weird round but genuinely interesting.

Questions Asked (6)

Q1

Implement a multiply function for two integers without using the * or / operators. Must handle negatives, zero, INT_MIN * -1, and run in O(log n) time using a shift-and-add approach.

Algorithms & Data StructuresTechnical Trade-offs
Author's notes

I started with naive repeated addition and they immediately asked me what the complexity was.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Clarify the constraints and edge cases first, then explain the shift-and-add algorithm: iterate through bits of the multiplier, doubling the multiplicand and adding when the current bit is set. Emphasize handling negatives by converting to positive and applying the sign at the end, and address overflow for INT_MIN * -1 by using a wider integer type or detecting the overflow.

Pro tip: Mention that in languages like Python, integers have arbitrary precision, so overflow isn't an issue, but in fixed-width languages you must handle it explicitly. Also, note that the shift-and-add approach is essentially Russian peasant multiplication and runs in O(log n) where n is the absolute value of the multiplier.

1. Clarify requirements and edge cases

Ask about integer size, language constraints, and expected behavior for overflow (e.g., INT_MIN * -1). Confirm that the function should handle negatives, zero, and run in O(log n) time.

2. Outline the algorithm

Explain the shift-and-add method: while the multiplier is non-zero, if its least significant bit is 1, add the multiplicand to the result; then shift the multiplicand left and the multiplier right. Use absolute values and track the sign separately.

3. Handle negatives and zero

Determine the sign of the result by checking if exactly one operand is negative. Convert both operands to positive (careful with INT_MIN) and if either is zero, return zero immediately.

4. Address overflow for INT_MIN * -1

Explain that INT_MIN * -1 overflows in fixed-width integers because the positive counterpart is out of range. Suggest using a wider type (e.g., long long) or detecting and handling the overflow explicitly.

5. Analyze complexity and test

State that the loop runs O(log n) times where n is the absolute value of the multiplier. Walk through a few test cases including negatives, zero, and the overflow case.

Key Points to Mention

  • Shift-and-add algorithm (Russian peasant multiplication) and its O(log n) time complexity.
  • Handling negative numbers by converting to positive and applying sign at the end.
  • Zero handling: return 0 if either operand is zero.
  • Overflow issue with INT_MIN * -1 and solutions (wider type, explicit check).
  • Bitwise operations: left shift for doubling, right shift for halving, bitwise AND to check LSB.
  • Language-specific considerations (e.g., Python's arbitrary precision vs. C++/Java fixed-width).

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q2

After implementing the basic version, add overflow detection and saturate to INT_MAX or INT_MIN instead of wrapping, without using any 64-bit integer types.

Algorithms & Data StructuresTechnical Trade-offs
Author's notes

This is where I started sweating.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

First, clarify the problem constraints: integer addition with overflow detection and saturation, no 64-bit types. Then, explain the standard technique of checking signs and comparing against precomputed bounds (INT_MAX - b for positive b, INT_MIN - b for negative b) to detect overflow before it occurs. Finally, describe how to return the saturated value instead of the wrapped result, and discuss edge cases like adding zero or opposite signs.

Pro tip: Mention that you can use unsigned arithmetic to detect overflow portably, but since the problem forbids 64-bit types, stick to sign-based checks. Also, note that in languages like C, signed overflow is undefined behavior, so detection must happen before the operation.

1. Clarify constraints and edge cases

Confirm that only 32-bit signed integers are allowed and that saturation means clamping to INT_MAX or INT_MIN. Identify edge cases: adding zero, adding numbers of opposite signs, and the exact boundary values.

2. Explain overflow detection logic

For addition a + b, overflow occurs if a > 0 and b > 0 and a > INT_MAX - b, or if a < 0 and b < 0 and a < INT_MIN - b. For subtraction, similar logic applies with adjusted bounds.

3. Implement saturation

If overflow is detected, return INT_MAX for positive overflow or INT_MIN for negative overflow. Otherwise, return the normal sum.

4. Discuss trade-offs and alternatives

Compare sign-based checks with unsigned arithmetic or built-in overflow functions. Mention that sign-based checks are portable and avoid undefined behavior, but may be less efficient than hardware flags.

5. Test with boundary cases

Walk through examples like INT_MAX + 1, INT_MIN - 1, INT_MAX + INT_MIN, and zero additions to verify correctness.

Key Points to Mention

  • Overflow detection must occur before the arithmetic operation to avoid undefined behavior in languages like C/C++.
  • Use precomputed bounds: INT_MAX - b for positive b, INT_MIN - b for negative b.
  • Saturation returns INT_MAX or INT_MIN instead of wrapping.
  • Edge cases: adding zero, adding opposite signs, and exact boundary values.
  • Trade-offs: sign-based checks vs. unsigned arithmetic vs. compiler built-ins.
  • Portability and avoidance of 64-bit types as per constraints.

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q3

Walk through the time and space complexity of your implementation and argue why it's correct for all combinations of positive, negative, and zero inputs.

Algorithms & Data Structures
Author's notes

Felt more comfortable here.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by clearly stating the algorithm's time and space complexity in Big-O notation, then explain the reasoning behind each. Next, walk through how the algorithm handles positive, negative, and zero inputs, using examples or edge cases to demonstrate correctness. Conclude by summarizing why the complexity and correctness hold for all input combinations.

Pro tip: When discussing complexity, always relate it to the specific data structures and operations used, and mention any trade-offs. For correctness, explicitly address edge cases like empty input, single element, and extreme values to show thoroughness.

1. State the algorithm and its purpose

Briefly describe what the algorithm does and its intended use case, setting the stage for complexity analysis.

2. Analyze time complexity

Break down the algorithm into key operations, count their executions relative to input size, and derive the overall Big-O time complexity.

3. Analyze space complexity

Identify additional memory used (e.g., data structures, recursion stack) and express it in Big-O notation relative to input size.

4. Demonstrate correctness for all input types

Explain how the algorithm handles positive, negative, and zero values, using invariants or examples to prove it produces correct results.

5. Summarize and address edge cases

Recap the complexity and correctness, and mention any edge cases (e.g., empty input, overflow) and how they are handled.

Key Points to Mention

  • Big-O notation for time and space, with clear reasoning (e.g., O(n) time due to single loop, O(1) space if in-place).
  • How the algorithm's logic naturally accommodates positive, negative, and zero inputs without special cases.
  • Use of loop invariants or mathematical induction to argue correctness.
  • Consideration of worst-case, average-case, and best-case scenarios for complexity.
  • Edge cases such as empty input, single element, or extreme values (e.g., INT_MIN/INT_MAX).
  • Trade-offs between time and space, and why the chosen approach is optimal for the problem.

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q4

What unit tests would you write to validate this function? Think about edge cases specifically.

Algorithms & Data Structures
Author's notes

Rattled off the obvious ones: zero times anything, one times x, negative one times x, powers of two, INT_MAX times 2, INT_MIN times 2, INT_MIN times -1.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by clarifying the function's purpose, inputs, outputs, and expected behavior. Then systematically outline unit tests covering normal cases, edge cases (e.g., empty inputs, extreme values, invalid types), and error conditions. Emphasize that edge cases are critical for ensuring robustness in data science applications.

Pro tip: Mention that you prioritize edge cases based on their likelihood and impact in production, and that you use parameterized tests to efficiently cover multiple scenarios. This shows you think about test maintainability and real-world reliability.

1. Understand the function

Identify the function's signature, expected inputs, outputs, and any side effects. Clarify assumptions about data types, ranges, and error handling.

2. Identify normal cases

List typical inputs that represent common usage, ensuring the function behaves as expected under standard conditions.

3. Enumerate edge cases

Consider boundary values (e.g., min/max, zero, empty), special values (NaN, infinity), and invalid inputs (wrong type, null). Think about data-specific edge cases like missing values or outliers.

4. Design test cases

For each case, define the input, expected output, and assertion. Use parameterized tests to group similar cases and keep tests concise.

5. Prioritize and explain

Explain which edge cases are most critical and why, considering the function's role in the broader system and potential impact of failures.

Key Points to Mention

  • Boundary values: empty inputs, single element, maximum/minimum values
  • Invalid inputs: wrong data types, null/None, out-of-range values
  • Special floating-point values: NaN, infinity, negative zero
  • Data-specific edge cases: missing values, outliers, imbalanced classes
  • Error handling: expected exceptions or error messages
  • Test coverage and maintainability: using parameterized tests, mocking dependencies

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q5

How would you extend this approach to big integer multiplication, and how does it compare to Karatsuba's algorithm?

Algorithms & Data StructuresTechnical Trade-offs
Author's notes

I knew Karatsuba exists and roughly that it's O(n^1.585) versus O(n^2) for schoolbook, but I blanked on the actual recursive structure mid-explanation.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

First, clarify the 'approach' being extended (e.g., divide-and-conquer or FFT-based). Then explain how to adapt it to big integers by splitting numbers into limbs and handling carries. Finally, compare its complexity and practical trade-offs with Karatsuba's algorithm.

Pro tip: Mention that Karatsuba is often used for medium-sized numbers, while FFT-based methods dominate for very large numbers, and that hybrid approaches are common in practice.

1. Clarify the base approach

Briefly restate the approach you are extending (e.g., divide-and-conquer, FFT) and its key idea.

2. Adapt to big integers

Explain how to represent big integers as arrays of digits/limbs and apply the approach, handling carries and base conversion.

3. Analyze complexity

Derive the time complexity of the extended approach (e.g., O(n log n) for FFT) and compare it to Karatsuba's O(n^1.585).

4. Discuss trade-offs

Compare practical factors: constant factors, memory usage, implementation complexity, and threshold sizes where each algorithm is preferred.

5. Conclude with recommendations

Summarize when to use each method, possibly mentioning hybrid approaches used in libraries like GMP.

Key Points to Mention

  • Karatsuba's algorithm reduces multiplications from 4 to 3 in the divide-and-conquer step, giving O(n^1.585).
  • FFT-based multiplication achieves O(n log n) by using polynomial multiplication via FFT.
  • Big integers are represented in base 2^b (limbs) and require carry propagation.
  • FFT has higher constant factors and is beneficial only for very large numbers (e.g., >10^4 digits).
  • Karatsuba is simpler to implement and often faster for medium-sized numbers.
  • Hybrid algorithms (e.g., Toom-Cook, FFT) are used in practice, with thresholds tuned for performance.

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q6

What changes in your implementation if the platform doesn't guarantee arithmetic right shift behavior?

Algorithms & Data StructuresTechnical Trade-offs
Author's notes

Caught me completely off guard.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

First, clarify that arithmetic right shift preserves the sign bit, which is crucial for signed integer division by powers of two. Then, discuss how to detect the platform's behavior and implement a portable alternative, such as using division or explicit sign handling. Finally, emphasize the trade-offs between performance, readability, and correctness in a data science context.

Pro tip: Mention that in data science, bitwise operations often appear in optimized feature engineering or low-level data parsing, so ensuring portability without sacrificing performance is key. Also, note that Python's right shift on negative integers is arithmetic, but other languages like C can be implementation-defined.

1. Define arithmetic right shift and its importance

Explain that arithmetic right shift replicates the sign bit, effectively dividing signed integers by 2^n while preserving sign. This is essential for algorithms that rely on sign-preserving division.

2. Identify platform-dependent behavior

Discuss how some platforms (e.g., C on certain architectures) may perform logical right shift instead, filling with zeros. This can lead to incorrect results for negative numbers.

3. Implement a portable alternative

Propose using division by 2^n (with proper rounding toward negative infinity) or explicitly checking the sign and adjusting the shift. For example, in C: (x < 0) ? ~(~x >> n) : (x >> n).

4. Evaluate trade-offs

Compare performance of bitwise shift versus division, and consider readability and maintainability. In data science, clarity often outweighs micro-optimizations unless working with large-scale data.

5. Test and validate

Suggest writing unit tests with negative and positive integers to ensure correctness across platforms. Mention using static analysis or compiler flags to detect assumptions.

Key Points to Mention

  • Arithmetic vs. logical right shift: sign extension vs. zero filling
  • Platform-specific behavior in languages like C/C++ (implementation-defined)
  • Portable workarounds: division, conditional shifts, or using unsigned casts
  • Performance implications: bitwise operations are faster but may be unnecessary in high-level data science code
  • Python's behavior: right shift on negative integers is arithmetic (floor division by 2^n)
  • Use cases in data science: bit manipulation for feature hashing, compression, or low-level data parsing

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.