# Min and Max: Change the Operator

*Task 5 of 6 · [Reductions](https://gpu.rocks/learn/reductions-3dadc130.md) · GPU.js Learn*

Here's the secret hiding inside the ladder: nothing about it is really about
*addition*. Any operation that combines two values and doesn't care about order
or grouping — associative and commutative — can ride the same ladder. Swap
`+` for `Math.min` and the scalar at the bottom is the smallest
value in the array. `Math.max` gives the largest.

Two kernels, one driver. The structure doesn't change at all — only the fold
rule.

## Goal

**Goal:** find both the minimum and the maximum of `data`
with two halving-ladder kernels, and log both.

## Requirements

- `minStep` folds with `Math.min`, `maxStep` with `Math.max`
- Both kernels use `dynamicOutput: true` and `dynamicArguments: true`
- Ride each ladder down to a scalar and `console.log` both results

## Hint 1 — Math inside kernels

`Math.min(a, b)` and `Math.max(a, b)` both work inside
kernel functions. The fold becomes

```js
Math.min(data[this.thread.x], data[this.thread.x + this.output.x])
```

## Hint 2 — one driver, two ladders

Wrap last task's while-loop in a plain JS function that takes the kernel as
a parameter — `await reduce(minStep, data)`, `await reduce(maxStep, data)`
— instead of writing it twice.

## Same idea elsewhere

Pluggable operators are why every library ships reduce as a higher-order
function: `thrust::reduce` and ROCm's rocPRIM accept any binary op plus an
identity value, Metal Performance Shaders sells min/max reductions pre-built, and
WGSL's `subgroupMin`/`subgroupMax` are this exact ladder burned
into silicon.

## Starter code

```js
// Same ladder, new fold rule. Only the operator changes.
const gpu = new GPU({ mode });

const minStep = gpu.createKernel(function (data) {
  // TODO: keep the SMALLER of the pair, not the sum
  return data[this.thread.x] + data[this.thread.x + this.output.x];
}, { dynamicOutput: true, dynamicArguments: true });

const maxStep = gpu.createKernel(function (data) {
  // TODO: keep the LARGER of the pair
  return data[this.thread.x] + data[this.thread.x + this.output.x];
}, { dynamicOutput: true, dynamicArguments: true });

async function reduce(step, values) {
  // Float32Array from the start — an argument's type is locked on first call.
  let v = Float32Array.from(values);
  let n = v.length;
  while (n > 1) {
    n = n / 2;
    step.setOutput([n]);
    v = await step(v);
  }
  return v[0];
}

console.log('min:', await reduce(minStep, data));
console.log('max:', await reduce(maxStep, data));
```

---

Interactive version: https://gpu.rocks/learn/reductions-3dadc130/5

[Previous task](https://gpu.rocks/learn/reductions-3dadc130/4.md) · [Next task](https://gpu.rocks/learn/reductions-3dadc130/6.md)
