# `gno.land/p/moul/xmath/v1` `Max`, `Min`, `Clamp`, `Abs` and `Sign` for every built-in numeric type, one concrete function per type. Gno has no generics, so the alternative is writing the same three-line helper again in each realm. ```go xmath.MaxInt(3, 7) // 7 xmath.ClampInt64(x, 0, 100) // x, bounded to [0,100] xmath.AbsFloat64(-2.5) // 2.5 xmath.SignInt(-9) // -1 ``` | type | functions | | --- | --- | | `int`, `int8`, `int16`, `int32`, `int64` | `Max`, `Min`, `Clamp`, `Abs`, `Sign` | | `uint`, `uint8`, `uint16`, `uint32`, `uint64` | `Max`, `Min`, `Clamp` | | `float32`, `float64` | `Max`, `Min`, `Clamp`, `Abs`, `Sign` | Unsigned types get no `Abs` or `Sign`: both are trivially themselves and 1, and offering them would only invite an overflow bug at the call site. The name is the type suffix: `MaxInt8`, `MinUint64`, `ClampFloat32`. `Sign` returns -1, 0 or 1. `Clamp` does not validate its bounds: calling it with `min > max` returns one bound or the other depending on the value, so check the bounds yourself if they are computed. ## Generated `xmath.gen.gno` and `xmath.gen_test.gno` are written by [`tools/xmathgen`](../../../tools/xmathgen) and carry a `DO NOT EDIT` header. Edit the generator, never the output: ```sh go -C tools run ./xmathgen # rewrite both files go -C tools run ./xmathgen -check # fail if they are stale ``` Adding a type is one row in the `types` table in `tools/xmathgen/main.go`; it expands to 5 functions plus their tests. The header used to name a `generator.go` that was never committed, so it was a rule nobody could follow. The generator above was reconstructed from the output it describes and reproduces both committed files byte for byte, which `go test ./tools/xmathgen` asserts on every run. ## `MulDiv`: the proportional share, through a 128-bit intermediate Hand-written rather than generated, in `muldiv.gno`, because it is one function and not a per-type family. ```go xmath.MulDiv(pot, stake, total) // this stake's cut, rounded toward zero xmath.MulDivUp(pot, stake, total) // the same ratio, rounded away from zero ``` `a*b/c` computes `a*b` first, and an int64 product that does not fit **wraps silently to a plausible number**. Measured with `a = 2^62`, `b = 4`, `c = 8`: the true share is `2305843009213693952` and the naive form answers **`0`**, because `2^62 * 4` is exactly `2^64` and wraps to zero. Nothing fails, nothing logs, and a payout split is simply wrong. **Ten realms deployed on gnoland-1 carry their own copy of this**, all reaching for `math/bits` the same way: `bubblerumble` (several versions) and `kourt`, whose `mulDiv128` is the same function with its own panic strings. It had no owner in `p/moul` or `p/nt` until now. `MulDiv` panics rather than returning a wrong number: on a negative operand, on a denominator at or below zero, and on a quotient that does not fit. **`MulDivUp` exists because rounding is a money question, not a style one.** Rounding a fee down and a payout up is how a pool pays out more than it holds. Use `MulDiv` for what someone receives and `MulDivUp` for what someone owes; `TestRoundingDirectionIsTheMoneyQuestion` pins that they never differ by more than one and never cross. ### Why `v1` even though adding a function is non-breaking It is non-breaking **for this repo**, and that is not the constraint that matters. `p/` packages are always public, and a live public path can never be redeployed, so functions added in place at `v0` reach `main` and never reach the chain. `v1` is the only way `MulDiv` becomes importable by anybody. The bump is free here: `xmath` has **zero importers**, in this repo and across every package deployed on `gnoland-1`. `v0` stays resolvable for nobody in particular. --- Part of **[moul/gno-contracts](https://github.com/moul/gno-contracts)** — moul's versioned gno.land contracts. See the repository for the full catalog, build/test tooling, and usage. > ⚠️ **Disclaimer:** provided as-is, without warranty; not security-audited. Full disclaimer: [DISCLAIMER](https://github.com/moul/gno-contracts/blob/main/DISCLAIMER.md).