Standard arithmetic operations on Nats:
Nat comparison is composed of 3 disjoint relations:
| Subtag | Alias | G | L | E | Meaning |
|---|---|---|---|---|---|
| gle | f | o | o | o | always false |
| glE | e | o | o | x | equal |
| gLe | l | o | x | o | less |
| gLE | le | o | x | x | less or equal |
| Gle | g | x | o | o | greater |
| GlE | ge | x | o | x | greater or equal |
| GLe | ne | x | x | o | not equal |
| GLE | t | x | x | x | always true |
Integer operations use Idx s as type. This is per se neither signed nor unsigned. Instead, the operations themselves exhibit signed or unsigned behavior.
For Idx s,
Concretely, a value \(x : \mathrm{Idx}\;s\) is considered non-negative iff \( x \le \lfloor (s-1)/2 \rfloor \), and negative otherwise. Hence the signed range is \( \big[ -\lfloor s/2 \rfloor,\; \lceil s/2 \rceil - 1 \big] \). For powers of two, i.e. \( s = 2^w \), this coincides exactly with the usual \(w\)-bit two's-complement interpretation.
A common problem when dealing with integer operations is overflow. All operations that may overflow take a leading parameter m - mode - of type Nat that determines the exact desired behavior in the case of an overflow. It comes first so that currying it - core.wrap.add 0 - still yields a value polymorphic in s.
Creates a literal of type Idx s from l while obeying mode m.
Creates a literal of type Idx ⊤.
These unary bitwise operations offer all 4 possible operations as summarized in the following table:
| Subtag | A | a | Comment |
|---|---|---|---|
| f | o | o | always false |
| neg | o | x | negate |
| id | x | o | identity |
| t | x | x | always true |
These binary bitwise operations offer all 16 possible operations as summarized in the following table:
| Subtag | AB | Ab | aB | ab | Comment |
|---|---|---|---|---|---|
| f | o | o | o | o | always false |
| nor | o | o | o | x | not or |
| nciff | o | o | x | o | not converse implication |
| nfst | o | o | x | x | not first argument |
| niff | o | x | o | o | not implication |
| nsnd | o | x | o | x | not second argument |
| xor_ | o | x | x | o | exclusive or |
| nand | o | x | x | x | not and |
| and_ | x | o | o | o | and |
| nxor | x | o | o | x | not exclusive or |
| snd | x | o | x | o | second argument |
| iff | x | o | x | x | implication (if and only if) |
| fst | x | x | o | o | first argument |
| ciff | x | x | o | x | converse implication |
| or_ | x | x | x | o | or |
| t | x | x | x | x | always true |
Shift right:
For Idx s, the shift count must be smaller than the minimal bit width needed to encode the carrier. For Idx ⊤, this width is 64; otherwise it is max(1, ceil(log2(s))). Larger counts are undefined behavior.
Integer operations that may overflow. You can specify the desired behavior in the case of an overflow with the leading curried argument by providing a mim::plug::core::Mode as Nat.
shl uses the same shift-count rule as core.shr. It multiplies the unsigned representative by 2^b and then wraps modulo s. nuw and nsw check the unwrapped unsigned and signed results, respectively.
Signed and unsigned integer division/remainder.
Integer comparison is composed of 5 disjoint relations:
Here is the complete picture for core.icmp.xygle x, y for 3 bit wide integers:
| x | y | |||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| binary | 000 | 001 | 010 | 011 | 100 | 101 | 110 | 111 | ||
| unsigned | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | ||
| signed | 0 | 1 | 2 | 3 | -4 | -3 | -2 | -1 | ||
| 000 | 0 | 0 | E | L | L | L | X | X | X | X |
| 001 | 1 | 1 | G | E | L | L | X | X | X | X |
| 010 | 2 | 2 | G | G | E | L | X | X | X | X |
| 011 | 3 | 3 | G | G | G | E | X | X | X | X |
| 100 | 4 | -4 | Y | Y | Y | Y | E | L | L | L |
| 101 | 5 | -3 | Y | Y | Y | Y | G | E | L | L |
| 110 | 6 | -2 | Y | Y | Y | Y | G | G | E | L |
| 111 | 7 | -1 | Y | Y | Y | Y | G | G | G | E |
And here is the overview of all possible combinations of relations. Note the aliases you can use for the common integer comparisons front-ends typically want to use:
| Subtag | Alias | X | Y | G | L | E | Meaning |
|---|---|---|---|---|---|---|---|
| xygle | f | o | o | o | o | o | always false |
| xyglE | e | o | o | o | o | x | equal |
| xygLe | o | o | o | x | o | less (same sign) | |
| xygLE | o | o | o | x | x | less or equal | |
| xyGle | o | o | x | o | o | greater (same sign) | |
| xyGlE | o | o | x | o | x | greater or equal | |
| xyGLe | o | o | x | x | o | greater or less | |
| xyGLE | o | o | x | x | x | greater or less or equal == same sign | |
| xYgle | o | x | o | o | o | minus plus | |
| xYglE | o | x | o | o | x | minus plus or equal | |
| xYgLe | sl | o | x | o | x | o | signed less |
| xYgLE | sle | o | x | o | x | x | signed less or equal |
| xYGle | ug | o | x | x | o | o | unsigned greater |
| xYGlE | uge | o | x | x | o | x | unsigned greater or equal |
| xYGLe | o | x | x | x | o | minus plus or greater or less | |
| xYGLE | o | x | x | x | x | not plus minus | |
| Xygle | x | o | o | o | o | plus minus | |
| XyglE | x | o | o | o | x | plus minus or equal | |
| XygLe | ul | x | o | o | x | o | unsigned less |
| XygLE | ule | x | o | o | x | x | unsigned less or equal |
| XyGle | sg | x | o | x | o | o | signed greater |
| XyGlE | sge | x | o | x | o | x | signed greater or equal |
| XyGLe | x | o | x | x | o | greater or less or plus minus | |
| XyGLE | x | o | x | x | x | not minus plus | |
| XYgle | x | x | o | o | o | different sign | |
| XYglE | x | x | o | o | x | different sign or equal | |
| XYgLe | x | x | o | x | o | signed or unsigned less | |
| XYgLE | x | x | o | x | x | signed or unsigned less or equal == not greater | |
| XYGle | x | x | x | o | o | signed or unsigned greater | |
| XYGlE | x | x | x | o | x | signed or unsigned greater or equal == not less | |
| XYGLe | ne | x | x | x | x | o | not equal |
| XYGLE | t | x | x | x | x | x | always true |
Minimum and maximum of two integers:
Absolute value of an integer.
Conversion between index types - both signed and unsigned - of different sizes.
Bitcast to reinterpret a value as another type. Can be used for pointer / integer conversions as well as integer / nat conversions.
Yields the size or align of a type.
Steers the partial evaluator.
More readable shorthand to create a select/if:
core.select one level up, to select between types.
Bindings for the infix operators, which are sugar for an application of the escaped name. use the module matching the domain at hand:
| Module | Domain |
|---|---|
| core.ops.n | Nat |
| core.ops.s.w | signed Idx s, wrap around |
| core.ops.s.nw | signed Idx s, core.mode.nsw |
| core.ops.u.w | unsigned Idx s, wrap around |
| core.ops.u.nw | unsigned Idx s, core.mode.nuw |
core.ops.s/core.ops.u offer only those operators that don't depend on the mode. Idx s has no / and %, since core.div expects a mem.M.