| Bytes | Lang | Time | Link |
|---|---|---|---|
| 112 | 05AB1E legacy | 250627T033718Z | Lucenapo |
| 538 | R | 220613T213030Z | Dominic |
| 347 | Python | 220613T192930Z | loopy wa |
| 112 | Husk | 220613T153422Z | Dominic |
| 022 | 22 bytes total | 220613T162106Z | Cong Che |
| 9312 | Python | 220613T071647Z | pxeger |
| 6511 | JavaScript ES6 | 220613T084517Z | Arnauld |
| 011 | Retina | 220613T131325Z | m90 |
| 224 | x86 32bit machine code | 220613T124311Z | m90 |
| 014 | Octave | 220613T124249Z | flawr |
| 6612 | Zsh | 220613T070530Z | pxeger |
| 012 | Brachylog | 220613T090941Z | Fatalize |
| 167 | sh + coreutils | 220613T095142Z | matteo_c |
| 336 | Factor | 220613T094158Z | chunes |
| 034 | Haskell | 220613T092335Z | matteo_c |
| 336 | Charcoal | 220613T091111Z | Neil |
| 003 | BQN | 220613T080246Z | Bubbler |
| 112 | Vyxal | 220613T074836Z | emanresu |
| 8412 | Retina | 220613T073648Z | Neil |
| 003 | J | 220613T073244Z | Adá |
| 004 | APL Dyalog Unicode | 220613T073106Z | Adá |
| 224 | K ngn/k | 220613T072435Z | Bubbler |
| 007 | Wolfram Language Mathematica | 220613T070516Z | att |
| 007 | Pascal FPC | 220613T062216Z | tsh |
| 017 | Julia | 220613T051359Z | Czylabso |
| 112 | Jelly | 220613T045452Z | Anders K |
R, 5 + 3 = 8 bytes
nchar
abs
Commutative for positive values & zero, non-commutative for negative values (since the minus sign is counted as a character by nchar).
Python, 3+4 = 7 bytes
abs
hash
These two builtins commute for most integers.
But not all:
The following depends on an implementation detail of CPython; I didn't find an official spec, below I give my best guess:
hash maps integers onto themselves modulo 2**61-1 (which appears to be the 9th Mersenne prime; also, this may depend on the bitness (32/64 etc.) of the C long of the platform) while keeping the sign (so -4 does not map to 2**61-5 but to -4). Therefore hash(-n)==-hash(n) for all integers except those which equal +/-1 modulo 2**61-1 because in the underlying C implementation -1 is used as an error indicator. So, whenever the hash would be -1, -2 is returned instead.
Husk, 1+1 = 2 bytes
ṗ
¬
There are several 1+1 bytes pairs that work in Husk (including a port of the absolute-value/offset approach [a/→] that separates a/b by positive/negative values, and some other interesting approaches).
But I like this one best: ṗ and ¬ are commutative only for prime numbers, and non-commutative for all positive non-prime integers.
ṗ # index of x in the infinite list of primes, or zero if non-prime
D # logical NOT
So:
ṗ¬ # outputs zero for all nonzero inputs
¬ṗ # outputs 1 for non-primes, zero for prime numbers
22 bytes total
R, 15 bytes
\(x)round(x,-2)
R, 7 bytes
\(x)x^2
Commutes for x 0 mod 100, doesn't for x 0 mod 10 but not 0 mod 100.
Python, 9 + 3 = 12 bytes
\$ f(x) = \operatorname{bitwise\_or}(x, 1) = \begin{cases} x & x \text{ is odd} \\ x + 1 & x \text{ is even} \end{cases} \$
1 .__or__
\$ g(x) = \lvert x \rvert = \begin{cases} x & x \ge 0 \\ -x & x \le 0 \end{cases} \$
abs
Commutes for all \$ x \$ except negative even numbers.
If \$ x \$ is positive or odd, then at least one of \$ f \$ and \$ g \$ will act as the identity function \$ \operatorname{id} \$ and return \$ x \$ unchanged. In that case, clearly, they commute:
$$ h(\operatorname{id}(x)) = h(x) = \operatorname{id}(h(x)) $$
JavaScript (ES6), 6 + 5 = 11 bytes
These functions commute for odd negative values and for even non-negative values. They do not commute for the other cases.
f=
x=>x%2 // 6 bytes
g=
x=>~x // 5 bytes
Explanation
The sign of the modulo in JS is the sign of the numerator. So the functions are more formally defined as:
$$\begin{align}&f(x)=\cases{0&if $x$ is even\\-1&if $x<0$ and $x$ is odd\\1&if $x>0$ and $x$ is odd}\\ &g(x)=-x-1\end{align}$$
For odd negative values, we have: \$\cases{g(f(x))=g(-1)=0\\f(g(x))=f(-x-1)=0}\$
For even negative values, we have: \$\cases{g(f(x))=g(0)=-1\\f(g(x))=f(-x-1)=1}\$
For odd non-negative values, we have: \$\cases{g(f(x))=g(1)=-2\\f(g(x))=f(-x-1)=0}\$
For even non-negative values, we have: \$\cases{g(f(x))=g(0)=-1\\f(g(x))=f(-x-1)=-1}\$
Generalization
We can actually use any modulo \$m>1\$ for \$f\$, making the non-commutative cases more and more sparse as \$m\$ increases (but obviously, we'd still get infinitely many of them).
Try it online! (example with \$m=5\$)
Retina, 0 + 1 = 1 byte
This is a Count stage that matches an empty string at the start, between every two consecutive digits, and at the end. This produces the number of digits plus 1.
.
This is a Count stage that matches every digit, producing the number of digits.
Applying these in the two possible orders gives \$\mathrm{ndigits}(\mathrm{ndigits}(n))+1\$ and \$\mathrm{ndigits}(\mathrm{ndigits}(n)+1)\$, which are equal if and only if \$\mathrm{ndigits}(n)\$ is 1 less than a power of 10.
x86 32-bit machine code, 2 + 2 = 4 bytes
40 C3
98 C3
Uses the regparm(1) calling convention – argument in EAX, result in EAX.
In assembly:
inc eax; ret – \$f\$ simply adds 1 to a number.
cwde; ret – \$g\$ sign-extends the low 16 bits of EAX, duplicating bit 15 into all the higher bits.
If the low 15 bits are not all 1, these two functions do not interact, thus they commute.
If the low 16 bits are 1111 1111 1111 1111, either order of functions results in 0.
If the low 16 bits are 0111 1111 1111 1111, applying \$f\$ then \$g\$ results in -32768, while applying \$g\$ then \$f\$ results in 32768.
Octave, 14 bytes
I haven't seen anyone use this one yet, it might work better in another language. (And it is definitely possible to do better in Octave!)
@(x)x<0
@(x)x>0
The key here is that true == 1 and false == 0.
Try it online!
Zsh, 6 + 6 = 12 bytes
tr 1 2
tr 2 3
The first program replaces 1s with 2s; the second replaces 2s with 3s. These commute only for numbers not containing the digit 1.
Old answer:
Zsh, 10 + 10 = 20 bytes
\$ f(x) = \lfloor \frac x 2 \rfloor \$
<<<$[$1/2]
\$ g(x) = 2x \$
<<<$[$1*2]
For even \$ x \$, \$ f(x) = \frac x 2 \$, so \$ g(f(x)) = x \$.
But for odd \$ x \$, /2 rounds downwards, so \$ f(x) = f(x-1) \$, so \$ g(f(x)) = x - 1 \ne x \$
\$ g(x) \$ is always even, so for all \$ x \$, \$ f(g(x)) = x \$.
Therefore, \$ f(g(x)) = g(f(x)) \$ if and only if \$ x \$ is even.
Brachylog, 1 byte (or 2 bytes if including call modifier)
b
I don’t know exactly if that’s bending the rules or not, let me know.
In this single program, f(x) is b - behead, with a given input and an unknown output, and g(x) is also b - behead, but with a given output and an unknown input.
You can run f(g(x) with ↰₁~↰₁ in the header, and g(f(x)) with ~↰₁↰₁. This is why I’m ok with this being scored as 2 bytes, as you need to use ~ to "reverse" the program’s calling order.
Explanation
b asserts that its output is the input without the first element.
For all positive integers that begin with a leading
1(except1itself),f(g(x))will remove this leading 1, and then add an unknown leading digit back. Since0cannot be a leading digit, this leading digit will unify with1as the next possibility, and sof(g(x)) = x. Similarily,g(f(x))will append an unknown leading digit first, and then remove it, so once againg(f(x)) = x = f(g(x)).For all positive integers that don’t begin with a leading
1(and1),f(g(x))will remove the leading digit, and then append an unknown leading digit, which will unify with1as it is the first possibility. Since the initial leading digit wasn’t1, we havef(g(x)) ≠ x. On the other hand,g(f(x))will append an unknown leading digit, and then remove it, leavingxunchanged. So we haveg(f(x)) = x ≠ f(g(x)).
Obviously, there are infinitely many positive integers that start with a leading 1, and infinitely many positive integers that don’t.
sh + coreutils, 16 + 7 bytes
(cat;echo +1)|bc
tr -d -
The first function adds one to the number given in input; the second function calculates the absolute value. The composition here is intended as piping the output of one into the other, that is:
echo -n "-12" | (cat;echo +1)|bc | tr -d -
echo -n "-12" | tr -d - | (cat;echo +1)|bc
Factor, 3 + 3 = 6 bytes
abs
?1+
Absolute value and increment. (?1+ is a single word/function that always increments integers by one or changes f [Factor's canonical false value] to 0.) Factor does not come with a 'normal' increment word, such as 1+ in Forth.
Haskell, 3 + 4 bytes
abs
(+1)
If x is non-negative the functions commute, otherwise they do not.
Haskell, 4 + 8 bytes
(*2)
(`div`2)
If x is even, the functions commute.
If x is odd, (``div`` 2) $ (*2) x is equl to x, while (*2) $ (``div`` 2) x is equal to x-1.
Charcoal, 3 + 3 = 6 bytes
I↔N
Try it online! Link is to verbose version of code.
I⊕N
Try it online! Link is to verbose version of code.
Another port of @att's Mathematica answer.
BQN, 3 bytes (SBCS)
|¬
|
abs(1-x) and abs(x) respectively. f(g(x)) = abs(1-abs(x)) and g(f(x)) = abs(abs(1-x)) = abs(1-x). The results differ if and only if x is negative.
F←|¬
G←|
x←¯10+↕20
(F G x)≍(G F x)
┌─
╵ 9 8 7 6 5 4 3 2 1 0 1 0 1 2 3 4 5 6 7 8
11 10 9 8 7 6 5 4 3 2 1 0 1 2 3 4 5 6 7 8
┘
Vyxal, 1 + 1 = 2 bytes
ȧ is absolute value and ‹ is decrement, so \$f(g(x))\$ = \$\left|x-1\right|\$ and \$g(f(x))\$ = \$ \left|x\right| - 1\$, which are the same only on positive integers.
Retina, 8 + 4 = 12 bytes
V`[1-9]+
Try it online! Link includes test cases. Explanation: Reverses all runs of non-zero digits.
L`.$
Try it online! Link includes test cases. Explanation: Outputs the last digit, without a sign.
The functions commute in a lot of cases such as the trivial ones of the input being a multiple of 10 or a repdigit.
APL (Dyalog Unicode), 4 bytes
| absolute value
1∘+ increment (lit. 1 curried to plus)
See att's answer for explanation.
K (ngn/k), 2 + 2 = 4 bytes
0=
1>
- f:
0=, returns 1 if the input is 0, 0 otherwise. - g:
1>, returns 1 if the input is 0 or negative, 0 otherwise.
g is boolean negation when the domain is limited to booleans (0 and 1). f(x) and g(x) are the same for x>=0 and different otherwise, and the same can be said for g(f(x)) (negation of f(x)) and f(g(x)) (negation of g(x)).
4 bytes is optimal in ngn/k since any monadic function is at least two bytes long.
Czylabson Asa's mod-2 and mod-3 are also the same length in K: 2! and 3!.
Wolfram Language (Mathematica), 7 bytes
Abs
#+1&
\$(f\circ g)(x)=|x+1|\$, the distance of \$x\$ from \$-1\$. \$(g\circ f)(x)=|x|+1\$, one more than the distance of \$x\$ from \$0\$. These are equal when \$x\ge0\$, and unequal when \$x<0\$.
I suspect this is minimal. Trig functions such as Sin unfortunately are not \$\mathbb Z\to\mathbb Z\$.
Pascal (FPC), 7 bytes
succ
odd
succ acted as \$x \rightarrow x+1\$; while odd acted as \$x \rightarrow x \bmod 2\$. So succ(odd(x)) = odd(succ(x)) iff x is even.
Jelly, 1 + 1 = 2 bytes
Ḃ
\$f(x) = x \bmod 2\$ (Try it online!).
Ḥ
\$g(x) = 2x\$ (Try it online!). These functions commute only for even input.
