Use "XOR swap"-style swaps to move values around
One of the biggest issues with Mini-Flak's command set is that, having only "two" stacks (the active stack, and the stack of temporary values implied by the structure of the program), you can't move two values past each other with simple copying; if you have value a above b on the primary stack, and want b above a, at some point you'll have to have a stack element that's storing parts of b and a simultaneously.
There's a well known trick in languages like C to swap two values without using a temporary:
a ^= b; /* now a = a0^b0, b = b0 */
b ^= a; /* now a = a0^b0, b = a0 */
a ^= b; /* now a = b0, b = a0 */
The trick is that you temporarily use a
to store a combination of a
and b
, so that you can use the combined value to transform b
into a
, and then use the value of a
you just obtained to recover b
from the combined value.
Less well known is that you can do the same trick with addition and subtraction:
a -= b; /* now a = a0-b0, b = b0 */
b += a; /* now a = a0-b0, b = a0 */
a = b-a; /* now a = b0, b = a0 */
This version of the trick uses only operations that we have available in Brain-Flak, so we can actually write it.
When I wrote it by hand, I eventually golfed it into the following program (which uses a different sequence of additions and subtractions from the example above):
(({}({}))[({}[{}])])
Try it online!
I've also been writing a computer search program to try to optimise Mini-Flak fragments, and tested it on this problem; it confirmed that 20 bytes is the shortest possible swap of the top two stack elements, but found a different way to write the solution, which is probably a bit easier to follow (in addition to matching the algorithm given more closely):
([({}[({})])]({}{}))
Try it online!
Here's how it documented its own version of the program (stacks are written with the active end at the right, and "Working" is the Mini-Flak equivalent of the third stack):
[$b, $a] Working: $W
( [$b, $a] Working: $W, 0
([ [$b, $a] Working: $W, 0, 0
([( [$b, $a] Working: $W, 0, 0, 0
([({} [$b] Working: $W, 0, 0, $a
([({}[ [$b] Working: $W, 0, 0, $a, 0
([({}[( [$b] Working: $W, 0, 0, $a, 0, 0
([({}[({} [] Working: $W, 0, 0, $a, 0, $b
([({}[({}) [$b] Working: $W, 0, 0, $a, $b
([({}[({})] [$b] Working: $W, 0, 0, $a-$b
([({}[({})]) [$b, $a-$b] Working: $W, 0, $a-$b
([({}[({})])] [$b, $a-$b] Working: $W, -$a+$b
([({}[({})])]( [$b, $a-$b] Working: $W, -$a+$b, 0
([({}[({})])]({} [$b] Working: $W, -$a+$b, $a-$b
([({}[({})])]({}{} [] Working: $W, -$a+$b, $a
([({}[({})])]({}{}) [$a] Working: $W, $b
([({}[({})])]({}{})) [$a, $b] Working: $W+$b
We start by calculating the difference $a-$b
, removing $a
from the stack in the process but leaving $b
there. Then we make two copies of the difference on our working stack, with opposite signs ($a-$b
and $b-$a
); we can add the $a-$b
copy to $b
to transform it into $a
, and the result of the push $a
ends up adding to the $b-$a
copy to produce $b
, which we can push to the stack on top of the new $a
.
The same general idea can be used to do arbitrary stack operations (e.g. copying the fourth stack element into the second position); although the programs are fairly long by the standards of most programming languages, they're not really that long by Mini-Flak standards, and the only other way such a program could be written would be to handle each possible value for the intervening stack elements individually, which would be less general and much more verbose.