Problem Set

OPTIVER SWE OA 26/27

PowerCellBank

Language: C++20

You are writing the dispatch logic for the battery management system of a tiered power-cell bank that powers a heavy-duty industrial load. Cells are installed into the bank via LoadCell, and the load draws batches of cells via Discharge. Unused cells slowly self-discharge from charged through depleted to spent. To keep output voltage stable across discharge bursts, the system equalises charge between adjacent racks before each dispatch batch begins.

Scoring

The list below shows the test-case categories used to evaluate your submission, roughly ordered from most to least impactful. Use this as guidance for where to invest effort; categories are intentionally broad and can overlap, so don't over-optimize for labels.

  1. State Management
  2. Core Logic
  3. Edge Cases
  4. Scalability/Performance

Overview

Implement the PowerCellBank class with the following operations:

The bank has num_racks racks arranged front-to-back, where rack 1 is the front rack (electrically closest to the load) and each successive rack sits further behind. The capacity of rack i is 2i. Each cell carries a rated_duration that drives the three-state lifecycle described in "Temporal States". Each Discharge call first runs a "Charge Equalisation" cycle and then dispatches cells one at a time via the "Bus Shift" rule.

Functions

__init__(num_racks: integer)

LoadCell(timestamp: float, cell_id: string, rated_duration: float) -> bool

Discharge(timestamp: float, max_dispatch: integer) -> list[string]

Temporal States

Each cell in the bank is in one of three temporal states, determined by the elapsed time t = timestamp - load_timestamp since its LoadCell call:

StateElapsed TimeDispatchable?Occupies a Slot?Status Label
Chargedt < rated_durationyesyes"charged"
Depletedrated_duration ≤ t < 2×rated_durationyesyes"depleted"
Spentt ≥ 2×rated_durationnono—

State transitions happen automatically as time passes; no bookkeeping is required at the moment of transition. Spent cells are treated as absent: they do not appear in any output, they do not count against rack capacity, and they are excluded from every count and selection in Charge Equalisation and Bus Shift. Throughout the rest of this specification, every reference to "a cell" or "cells" means a non-spent cell unless stated otherwise.

The charge window of a cell at the call's timestamp is load_timestamp + rated_duration - timestamp. It is positive for charged cells and negative for depleted ones. Cells are ordered by their charge window at the call's timestamp, with ties broken by smallest cell_id under ordinary lexicographic (ASCII) comparison — for example, a precedes b.

Under this ordering, the most-charged cell in any set has the largest charge window, while the most-depleted cell has the smallest (smallest cell_id on ties for both). These two are duals: the most-charged cell is also the least-depleted, and the most-depleted is also the least-charged.

Charge Equalisation

At the start of each Discharge call, the system equalises charge between adjacent racks so the load sees a stable voltage as it begins drawing.

A rack is below-sag when it holds at least one cell and its charged fraction — the count of charged cells divided by the count of cells — is strictly below 50%.

The cycle visits racks in order from front to back. At each rack k that has a rear neighbour, while rack k is below-sag and rack k+1 holds at least one charged cell, the system performs a balance swap: rack k's most-depleted cell is swapped with rack k+1's most-charged cell. The cycle then moves on to rack k+1.

A rack with no rear neighbour, and a rack whose rear neighbour has run out of charged cells, take no further action in the cycle.

Bus Shift

Each pull dispatches one cell in two actions:

  1. Dispatch: from the active rack — the front-most rack that holds at least one cell — dispatch the most-charged cell. If no such rack exists, the call stops dispatching and returns what it has so far.
  2. Inward shift: the active rack's freed capacity is filled by advancing the most-charged cell forward from the next rack back. The inward shift frees one slot of capacity in that rack, which is filled the same way from the rack behind it, and so on. The shift halts as soon as the next rack back is empty or there is no further rack back.

A cell that arrives at a rack via the inward shift is immediately eligible for the next pull.

Constraints

Input Format For Custom Testing

Input to the program is specified using a simple text format. The format and details of parsing are not relevant to answering the question but custom input can be used to help with development and debugging.

The first line must always be in the format below:

Init <num_racks>

Each subsequent input line may contain one of the instructions as in the format below:

LoadCell <timestamp> <cell_id> <rated_duration>
Discharge <timestamp> <max_dispatch>

An example input and its expected output is described below.

Sample Case

Sample input for custom testing:

Init 3
LoadCell 100.000 a 130.000
LoadCell 100.000 b 130.000
LoadCell 100.000 c 1000.000
LoadCell 100.000 d 1000.000
LoadCell 100.000 e 80.000
LoadCell 100.000 f 80.000
LoadCell 100.000 g 1000.000
LoadCell 100.000 h 1000.000
Discharge 250.000 3
Discharge 600.000 5

Expected output:

LoadCell=True
LoadCell=True
LoadCell=True
LoadCell=True
LoadCell=True
LoadCell=True
LoadCell=True
LoadCell=True
Discharge=['c:charged', 'd:charged', 'g:charged']
Discharge=['h:charged']

Explanation

The sample has two Discharge calls, at t = 250 and t = 600. The cell deadlines below are written as absolute timestamps for direct comparison against those two call times.

Init 3 creates a bank with rack capacities [2, 4, 8] (14 cells of capacity in total). Front-most-first loading lands a-b on rack 1, c-f on rack 2, and g-h on rack 3, all at t=100. Cell deadlines:

CellsRatedDepleted tsSpent ts
a, b130230360
c, d100011002100
e, f80180260
g, h100011002100

Bank state across the two Discharge calls:

StepRack 1Rack 2Rack 3
After 8 LoadCells[a, b][c, d, e, f][g, h]
Discharge 250 post-equalisation[b, c][a, d, f, g][e, h]
Discharge 250 post-3-pulls[b, h][a, f, e][]
Discharge 600 spent excluded[h][][]
Discharge 600 post-1-pull[][][]

Per-Discharge walkthrough:

Discharge 250.000 3 — at the discharge timestamp t = 250, cells a, b, e, f are depleted (windows -20, -20, -70, -70) and c, d, g, h are charged (window +850).

Discharge 600.000 5 — at the discharge timestamp t = 600, cells a, b, e, f are all spent (spent thresholds ≤ 360) and excluded from every count and selection. Rack 1 therefore holds only h (charged, window +500), and racks 2-3 are empty. Equalisation is a no-op (fraction 1/1 = 100%). The first pull dispatches h:charged; the second pull finds the bank empty and dispatching stops after one cell.

The cascade in the first call — rack 1's swap dropping rack 2 below 50%, which then prompts rack 2 to draw from rack 3 — is the equalisation rule's signature behaviour: charge propagates forward through the rack chain in a single front-to-back pass.

Code Stub

class PowerCellBank {
public:
    PowerCellBank(int numRacks) {
        // Your code here...
    }

    bool loadCell(
        double timestamp,
        const std::string& cellId,
        double ratedDuration
    ) {
        // Your code here...
        return false;
    }

    std::vector<std::string> discharge(
        double timestamp,
        int maxDispatch
    ) {
        // Your code here...
        return {};
    }
};

OPTIVER SWE OA 26/27

OverheatPreventionController

Language: C++20

You are implementing the firmware controller that prevents a multi-core microprocessor from overheating. Every core runs a workload that produces heat proportional to its configured load, and all cores share a fixed cooling capacity. The operating system (OS) is in charge of assigning workloads via SetCoreLoad, and drives the controller forward via Tick on a cadence of its choosing to learn how each core's status has changed.

Scoring

The list below shows the test-case categories used to evaluate your submission, roughly ordered from most to least impactful. Use this as guidance for where to invest effort; categories are intentionally broad and can overlap, so don't over-optimize for labels.

  1. State Management
  2. Core Logic
  3. Edge Cases
  4. Scalability/Performance

Overview

Implement the OverheatPreventionController class with the following operations:

The controller doesn't have temperature sensors; instead, it must calculate the temperature of each core based on the configured loads and the cooling layout, as defined in Thermal Dynamics. Loads are given in watts (W).

Functions

__init__(passive_cooling_capacity: float, active_cooling_capacity_per_core: float, core_ids: list[string])

SetCoreLoad(timestamp: float, core_id: string, load_watts: float)

Tick(timestamp: float) -> list[string]

It does, in order:

  1. Advances the controller to timestamp in a single step. See Thermal Dynamics for how temperatures evolves.
  2. Shuts down any core whose temperature has reached 80 degrees C (its load is cleared to 0).
  3. Processes any pending load changes, including restart attempts on shut down cores.
  4. Decides which cores should have active cooling engaged for the upcoming interval. See Active Cooling for details.

Returns the cores whose status has changed since the previous Tick (or diverged from the initial state), where a core's status is one of:

Each entry of the returned list has the format "core_id=status". The list is sorted alphabetically by core_id. An empty list means nothing has changed.

Thermal Dynamics

At every Tick, the controller first computes each core's temperature at the given timestamp. A core's temperature is determined as follows:

Active Cooling

At the end of each Tick, the controller decides which cores should have active cooling engaged until the next Tick, when the decision will be reassessed from scratch. Active cooling works as follows:

Constraints

Input Format For Custom Testing

Input to the program is specified using a simple text format. The format and details of parsing are not relevant to answering the question but custom input can be used to help with development and debugging.

The first line must always be in the format below:

Init <passive_cooling_capacity> <active_cooling_capacity_per_core> <core1> <core2> ...

Each subsequent input line may contain one of the instructions as in the format below:

SetCoreLoad <timestamp> <core_id> <load_watts>
Tick <timestamp>

An example input and its expected output is described below.

Sample Case

Sample input for custom testing:

Init 545 60 coreA coreB coreC
SetCoreLoad 100.000 coreA 350.0
SetCoreLoad 100.000 coreB 200.0
SetCoreLoad 100.000 coreC 50.0
Tick 100.000
Tick 250.000
SetCoreLoad 1099.000 coreA 350.0
Tick 1100.000

Expected output:

Tick=['coreA=cooling', 'coreB=cooling']
Tick=['coreA=shutdown', 'coreB=idle']
Tick=['coreA=cooling', 'coreB=cooling']

Explanation

The controller manages a three-core processor with 545 W of shared passive cooling and 60 W of active cooling per core. All cores start at 20.0 degrees C, running, with load 0 (status idle).

TimestampEventTemp AStatus ATemp BStatus BTemp CStatus C
—Init20.000idle20.000idle20.000idle
100.000SetCoreLoad x3 (loads pending)20.000idle20.000idle20.000idle
100.000Tick20.000cooling20.000cooling20.000idle
250.000Tick82.75shutdown20.000+idle50.75idle
1099.000SetCoreLoad coreA 350 (pending restart)82.75shutdown20.000+idle50.75idle
1100.000Tick (row cut off in photos; reconstructed from walkthrough)48.75cooling20.000+cooling20.000+idle

+ = clamped at the ambient floor of 20.0 degrees C.
(Note: the final Tick 1100.000 row was cut off at the bottom of the screenshots; its values are reconstructed from the per-Tick walkthrough below.)

Per-Tick walkthrough

Starter Code (C++20)

#include <map>

class OverheatPreventionController {
public:
    OverheatPreventionController(double passiveCoolingCapacity, double activeCoolingCapacityPerCore,
        const std::vector<std::string>& coreIds) {
        // Your code here...
    }

    void setCoreLoad(double timestamp, const std::string& coreId, double loadWatts) {
        // Your code here...
    }

    std::vector<std::string> tick(double timestamp) {
        // Your code here...
        return {};
    }
};

int main() { ... }