Cubix, 238 234 217 151 110 100 bytes
Saved 14 bytes thanks to ETHProductions
u'^.:s+.;;;\-?W?rsos\(rrOIO:ur>'=o;^u.;;.>$.vUo^'rsu1;;@!\q?s*su;;IOu*+qU../;(*\(s.;<..r:''uq....qu\
Expanded:
u ' ^ . :
s + . ; ;
; \ - ? W
? r s o s
\ ( r r O
I O : u r > ' = o ; ^ u . ; ; . > $ . v
U o ^ ' r s u 1 ; ; @ ! \ q ? s * s u ;
; I O u * + q U . . / ; ( * \ ( s . ; <
. . r : ' ' u q . . . . q u \ . . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
Try it online!
Try it here
Explanation
The code consists of 8 steps, with two loops. I'll go over the code part by part.
Step 1 (A^B)
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
I O : u . . . . . . . . . . . . . . . .
U o ^ ' . . . . . . . . . . . . . . . .
; I O u . . . . . . / ; ( * \ . . . . .
? ? r : . . . . . . ? . . . \ ? ? ? ? ?
. . . . ? . . . . . ? . . . . . . . . .
? ? ? ? ?
. . . . .
. . . . .
. . . . .
. . . . .
This is the cube with the parts that are irrelevant to the first step removed. The question mark shows the no-ops the IP will visit, to make its path more clear.
IO:'^o;IO:r*(; # Explanation
I # Push the first input (A)
O # output that
: # duplicate it
'^ # Push the character "^"
o # output that
; # pop it from the stack
I # Push the second input (B)
O # output that
: # duplicate
r # rotate top 3 elements
* # Push the product of the top two elements
( # decrease it by one
; # pop it from the stack (making the last
# two operations useless, but doing it
# this way saves 10B)
Now, the stack looks like this: A, B, A, B
Step 2 (prepare for print loop)
The print loop takes 3 arguments (the top 3 elements on the stack): P
, Q
and R
. P
is the amount of repetitions, Q
is the separator (character code) and R
is the number to repeat. Luckily, the loop also takes care of the requirement that the resulting string should end in R
, not Q
.
We want to repeat A*
exactly B
times, so the separator is *
. Note that the stack starts as A, B, A, B
. Once again, I removed all irrelevant instructions. The IP start at the S
pointing north.
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . r . . . . . . . . . . . . . . .
. . . . r . . . . . . . . . . . . . . .
. . . . * . . . . . . . . . . . . . . .
. . . . ' . . . . . . . . . . . . . . .
. . . . S . . . . . . . . . . . . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
'*rr # Explanation
'* # Push * (Stack: A, B, A, B, *)
rr # Rotate top three elements twice
The stack is now A, B, B, *, A
.
Step 3/6/8 (the print loop)
Concept
E . . . . .
? r s o s u
\ ( r r O <
. . . . . S
The IP enters the loop through S
, pointing north, and exits the loop at E
, pointing north again. For this explanation, the stack is set to [..., A, B, C]
. The following instructions are executed. Note that the IP can't leave the loop before the question mark, so the first four instructions will always be executed.
Orr(?rsos # Explanation
O # Output `C`
rr # Rotate top three elements twice (Stack: [..., B, C, A])
( # Decrease A by one (Stack: [..., B, C, A-1])
? # If top of stack (A) > 0:
r # Rotate top of stack (Stack: [..., A-1, B, C])
s # Swap top elements (Stack: [..., A-1, C, B])
o # Output top of stack (B) as character code
s # Swap top elements (Stack: [..., A-1, B, C]
#
# ... and repeat ...
Implementation
Here's the cube again, with the irrelevant parts removed. The IP starts at S
, pointing east.
. . . . .
. . . . .
. . . . .
? r s o s
\ ( r r O
. . . . . S ' = o ; ^ u . . . . . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
As you can see, the IP comes across four instructions before it enters the loop. Since the character code is removed again, we reach the loop with the exact same stack as we entered this part.
'=o; # Explanation
'= # Push =
o # Output
; # Pop from stack
Inside the loop, the explanation above holds.
Step 4 (differentiating the IPs)
Since we use the above loop multiple times, and they all cause the IP to end up in the same spot, we have to differentiate between multiple runs. First, we can distinguish between the separator (first run has a *
, whereas runs two and three have a +
as separator). We can differentiate between runs 2 and 3 by checking the value of the number that is repeated. If that is one, the program should terminate.
First comparison
Here's what it looks like on the cube. The IP starts at S and points north. The stack contains [..., * or +, A or 1, 0]
. The number 1 shows where the IP will end up if this is the first loop (pointing north) and the number 2 shows where the IP will end up if this is the second (or third) loop (pointing east).
u ' . . .
s + . 1 .
; \ - ? 2
S . . . .
. . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
;s'+-? # Explanation
; # Delete top element (0)
s # Swap the top two elements (Stack: 1/A, */+)
'+ # Push the character code of +
- # Subtract the top two elements and push
# that to the stack (Stack: 1/A, */+, +, (*/+)-+)
? # Changes the direction based on the top
# item on the stack. If it's 0 (if (*/+) == +)
# the IP continues going right, otherwise, it
# turns and continues going north.
If the IP now is at 1
, the stack is [A, *, +, -1]
. Otherwise, the stack is [A or 1, +, +, 0]
. As you can see, there still is an unknown in the stack of the second case, so we have to do another comparison.
Second comparison
Because the IP has gone through step 5, the stack looks like this: [A^(B-1) or nothing, A or 1, +, +, 0]
. If the first element is nothing
, the second element is 1
, and the reverse holds too. The cube looks like this, with the IP starting at S and pointing east. If this is the second loop, the IP ends up at E
, pointing west. Otherwise, the program hits the @
and terminates.
. . . . .
. . . . ;
. . . S W
. . . . .
. . . . .
. . . . . . . . . . . . . ; . . . . . .
. . . . . . . . . E @ ! \ q . . . . . .
. . . . . . . . . . . . ( * . . . . . .
. . . . . . . . . . . . q u . . . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
The instructions executed that don't do anything to the control flow are listed below.
;;q*q(!@
;; # Delete top two elements (Stack [A^(B-1)/null, A/1, +])
q # Send top element to the bottom (Stack [+, A^(B-1)/0, A/1])
* # Push product of top two elements
# (Stack [+, A^(B-1)/0, A/1, A^B/0])
q # Send top element to the bottom
# (Stack [A^B/0, +, A^(B-1)/0, A/1])
( # Decrease the top element by 1
# (Stack [A^B/0, +, A^(B-1)/0, (A-1)/0])
! # If (top element == 0):
@ # Stop program
The stack is now [A^B, +, A^(B-1), A-1]
, provided the program didn't terminate.
Step 5 (preparing for "A+" (repeat A^(B-1)))
Sadly, Cubix doesn't have a power operator, so we need another loop. However, we need to clean up the stack first, which now contains [B, A, *, +, -1]
.
Cleaning up
Here's the cube again. As usual, the IP starts at S (pointing north), and ends at E, pointing west.
. . . ? .
. . . ; .
. . . S .
. . . . .
. . . . .
. . . . . . . . . . . . . . . . > $ . v
. . . . . . . . . . . . . . . . . . . ;
. . . . . . . . . . . . . . . . . . E <
. . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
;; # Explanation
;; # Remove top 2 elements (Stack: [B, A, *])
Calculating A^(B-1)
Another loop which works roughly the same as the print loop, but it's a bit more compact. The IP starts at S
, pointing west, with stack [B, A, *]
. The IP exits at E
pointing north.
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . . . . . . . . . . . E . . . . .
. . . . . . . . . . . . . . ? s * s u .
. . . . . . . . . . . . . . \ ( s . ; S
. . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
The loop body is the following.
;s(?s*s # Explanation
; # Pop top element.
s # Shift top elements.
( # Decrease top element by one
? # If not 0:
s # Shift top elements again
* # Multiply
s # Shift back
#
# ... and repeat ...
The resulting stack is [A, A^(B-1), 0]
.
Cleaning up the stack (again)
Now we need to get to the print loop again, with the top of the stack containing [..., A^(B-1), +, A]
. To do this, we execute the following. Here's the cube again,
. . ^ ? :
. . . . .
. . . . .
. . . . .
E . . . .
. . . . . s . . . . . . . . ; . . $ . .
. . . . . + q U . . . . . . S . . s . .
. . . . . ' u q . . . . . . . . . ? . .
. . . . . . . ? . . . . . . . . . ? . .
. . . . . . . ? . . . . . . . . . ? . .
. . ? . .
. . ? . .
. . ? . .
. . ? . .
. . ? . .
;:$sqq'+s # Explanation
; # Delete top element (Stack: [A, A^(B-1)])
: # Copy top element
$s # No-op
qq # Send top two elements to the bottom
# (Stack: [A^(B-1), A^(B-1), A])
'+ # Push +
# (Stack: [A^(B-1), A^(B-1), A, +])
s # Swap top two elements
# (Stack: [A^(B-1), A^(B-1), +, A])
Step 7 (preparing for last loop)
The stack is now [A^B, +, A^(B-1), A-1]
, the IP starts at S
, going west, and ends at E
, going right.
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . . E . . . . . . . . . . . . . .
. . . . . . u 1 ; ; S . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
The instructions executed:
;;1 # Explanation
;; # Delete top two elements
1 # Push 1
The stack now looks like [A^B, +, 1]
, and the IP is about to enter the print loop, so we're done.