[Question]
  [
I recently learned from [a comment](https://mathoverflow.net/q/413087/104733#comment1058750_413087) by MathOverflow user [pregunton](https://mathoverflow.net/users/115044/pregunton) that it is possible to enumerate all rational numbers using iterated maps of the form \$f(x) = x+1\$ or \$\displaystyle g(x) = -\frac 1x\$, starting from \$0\$.
For example, $$0 \overset{f}{\mapsto} 1 \overset{f}{\mapsto} 2 \overset{g}{\mapsto} -\frac12 \overset{f}{\mapsto} \frac12 \overset{f}{\mapsto} \frac 32 \overset{g}{\mapsto} -\frac23 \overset{f}{\mapsto} \frac 13.$$
That is, $$ \frac13 = f(g(f(f(g(f(f(0))))))) = f\circ g\circ f\circ f\circ g\circ f\circ f(0).$$ This is an example of a *shortest path of iterated maps* to reach \$\frac13\$; every path from \$0\$ to \$\frac13\$ requires at least seven steps.
---
## Challenge
Your challenge is to take two integers, `n` and `d`, and return a string of `f`'s and `g`'s that represents a shortest path of iterated maps from \$0\$ to \$\displaystyle\frac nd\$.
This is a [code-golf](/questions/tagged/code-golf "show questions tagged 'code-golf'"), so shortest code wins.
#### Example
```
  n |  d | sequence of maps
----+----+-----------------
  1 |  3 | fgffgff
  3 |  1 | fff
  8 |  2 | ffff
  1 | -3 | gfff
  2 |  3 | fgfff
  0 |  9 | [empty string]
  1 |  1 | f
  2 |  1 | ff
  1 | -2 | gff
 -1 | -2 | fgff
  6 |  4 | ffgff
 -2 |  3 | gffgff
  8 |  9 | fgfffffffff
```
  
[Answer]
# [Python](https://www.python.org), 54 bytes (-1 @Jonathan Allan, -1 me)
```
f=lambda n,d:n and'gf'[p:=n*d>0]+f(p*n-d,[n,d][p])or''
```
[Attempt This Online!](https://ato.pxeger.com/run?1=VVBBboMwEFSv7if2ZjuBKKFVRJDoQ4o40JollojtglFUKT_phUv7p_Y1xTagdmXtetazMyt_fJl3e9ZqHD8Hi3H6fcS8rS4vogIViUxBpQRtkBYmy9VGPO3LLTKzUbGIiolQFqbkuqN0nn6WygyWcbLU61m2NRwy4uQinc_9XW9aaRm9UU7AdFJZ5t6R-RuPXBGc57ne9baThnEeHMafu3sABTcAMaW-fhtq9VqDRrhUpifxFNs1_QsCcHBzD1PCBt0hAfk-epg6mASI80TsOE3AyV8F19i7xmlKi7zXWphBeNFJgg6BeIXzFkdHfvTkQFh91kXTxcg7hyDhU34B)
#### Old [Python](https://www.python.org), 56 bytes (@tsh)
```
f=lambda n,d:n*d<0and'g'+f(-d,n)or n and'f'+f(n-d,d)or''
```
[Attempt This Online!](https://ato.pxeger.com/run?1=VVDBTsQgEI1X_Im5AW7Z7FZjuhv7I96qlC5JF9iWxpjsn3jpRf9Jv0YGSqMTMsN7vHkz4ePLvfuTNfP8OXklqu9K1X1zfpENmEIezZ182jVG0o5uFBOyMNwOYAAphZQJnAwcpUv_szZu8oyTXN9Oum9hfyRoWNh64bej67Vn9Eo5ATdo4xm-KxZvvMAiOa9rux39oB3jPE2Yf25uIaxwBZAhje1las1rC1bBuXEjESE2a_oXBGCPffchqU7hIQlFXkVYISwTVEuHQE2XcPnXAYkdEoeQsn30yspknH3K5ENArHDZ4hHFD1GcBOucddEqD4qTU5D0Kb8)
#### Old [Python](https://www.python.org), 57 bytes
```
f=lambda n,d:n//d<0and'g'+f(-d,n)or n and'f'+f(n-d,d)or''
```
[Attempt This Online!](https://ato.pxeger.com/run?1=VVDbTsQgEI2v-BPzBmTLXqox3Y39Ed-qlC5JF7ClMSb7J770Rf9Jv0YGSqMTMsM5nDkz4ePLvfuzNfP8OXklqu-jqvvm8iwbMIU8md1OPu4bI2lHN4oJWRhuBzCAlELKBE4GjtLF4EkbN3nGSa5vZ923cDgRdCxsvfDb0fXaM3qlnIAbtPEM3xWLN15gkZzXtd2OftCOcZ4mzD83txBWuALIkMb2dWrNSwtWwaVxIxEhNmv6FwTggH13IalO4SEJRV5FWCEsE1RLh0BNl3D51wGJPRLHkLJ99MrKZJx9yuRDQKxw2eIBxfdRnATrnHXRKg-Kk1OQ9Cm_)
Not 100% sure this is correct. Works on all test cases, though. It's kind of a Euclidean algorithm with overshoot.
#### Towards a proof of correctness
The group of transformations generated by f and g is called the [Modular group](https://en.wikipedia.org/wiki/Modular_group). There is quite a bit of interesting theory but the one thing that seems relevant to us is that gg and gfgfgf are a "complete set of relations" and that the modular group is the "free product of the cyclic groups generated by g and h:=gf". This means that any element of the modular group can be uniquely written as g's alternating with either h or hh. Transforming back to f's and g's gh becomes f and ghh becomes fgf, so except for the left end which can be h or hh, i.e. gf or gfgf all valid representations are exactly the words with g's separated by at least 2 f's. And we can check that the algorithm indeed produces such words.
So, are we done? I'm not sure. What's missing is that I don't know how exactly the rationals and the modular group are related. It may well be that two distinct group elements move 0 to the same rational which would send us back quite a bit if not all the way to square 1.
[Answer]
# [JavaScript (Node.js)](https://nodejs.org), 53 bytes
```
f=(n,d,p='',...e)=>n?f(...e,-d,n,p+'g',n-d,d,p+'f'):p
```
[Try it online!](https://tio.run/##bZBfC4IwFMXf@xR720bbTIuwwvog0YO4Pxi2DZWgT79mZRPd2MOF3@Gec@69fJZd1da2p9pw4ZwskCac2AJCwhgTuDjri0TDSCgnmtg1VJBoP/NhlhAfrauM7kwjWGMUkgikBIAtxieQJEAqOfzVTLL1knSULHHucRbwgnsHOjqoCM9mCRaCjRccfoKreNj@Bbq@rbW6RaxC0phPqBFLmYWUM0ynOHajvV@@@y@PLJi2jJ85n9T83OH73Bs "JavaScript (Node.js) – Try It Online")
Stackoverflow on last testcase.
[Answer]
# [Ruby](https://www.ruby-lang.org/), 45 bytes
```
f=->n,d{n==0?"":n/d<0??g+f[-d,n]:?f+f[n-d,d]}
```
[Try it online!](https://tio.run/##KypNqvz/P81W1y5PJ6U6z9bWwF5JySpPP8XGwN4@XTstWjdFJy/Wyj4NyMwDslNia/8XKKRFG@oYx/4HAA "Ruby – Try It Online")
Use as `f[n,d]`
basically a port of [loopy walt's python answer](https://codegolf.stackexchange.com/questions/240806/iterate-your-way-to-a-fraction/240808#240808)
[Answer]
# [Nibbles](http://golfscript.com/nibbles), 15.5 bytes
```
`.~$@?*@$~-@__*~@$$=`$*@$"fg "
```
That's 31 nibbles at half a byte each in the binary form.
This is based on the python answer although I found an iterative solution shorter. It felt kind of awkward still though.
```
`. ~$@      Iterate while unique, starting with the tuple $ @ (which is the input arguments)
  ? *@$     if product of values > 0
    ~-@_ _  then ($-@, @) since true clause has condition's value put in $ (unused here though)
    *~@ $   else (-1*@, $)
  $         the second value of the tuple from the if (tuple's aren't returned as first class things but splat into the context and must be reconstructed, normally very concise but here a bit awkward
```
The above code computes the sequences of numerators and denominators, now we just need to convert it to f and g. Note that it has a few extra terms since it didn't stop being unique until after the solution was found. Luckily n\*d is 0 in that case so we can use that to map it to a space. The below code is implicitly done via a map.
```
= `$ *@$   subscript using signum of n*d
  "fg "    into this list (0 is the last since 1 based).
```
Call it like `nibbles filename.nbl 1 3` (Nibbles isn't on TIO yet).
Note that this will also output 4 spaces after the answer, you could remove it by doing a filter, this would add two nibbles or 1 byte.
[Answer]
# [Haskell](https://www.haskell.org/), ~~44~~ 42 bytes
```
0#d=[]
n#d|n*d<0='g':(-d)#n|m<-n-d='f':m#d
```
adaption of the python solution, using guards.
Ungolfing barely changes it, just adding whitespace:
```
0#d          = []
n#d | n*d<0  = 'g':(-d)#n
    | m<-n-d = 'f':m#d
```
[Try it online!](https://tio.run/##y0gszk7Nyfn/30A5xTY6litPOaUmTyvFxsBWPV3dSkM3RVM5rybXRjdPN8VWPU3dKlc55X9uYmaegq1CQWlJcEmRgoqCpbL5/3/JaTmJ6cX/dZMLCgA "Haskell – Try It Online")
-2 Bytes thanks to xnor, TIO link also thanks to xnor
[Answer]
# JavaScript (ES6), 90 bytes
A naive algorithm trying all sequences of length \$k\$, starting with \$k=0\$ and incrementing \$k\$ until a solution is found.
Expects `(n)(d)`. Returns an empty array instead of an empty string.
```
n=>F=(d,k)=>(g=(k,o=[],N=n,D=d)=>k--?g(k,o+'f',N-D,D)||g(k,o+'g',-D,N):!N&&o)(k)||F(d,-~k)
```
[Try it online!](https://tio.run/##bZDLDoIwEEX3fkXdQCe2PsAYNKluiEt@wLgwQhsstkSIiYnx17EI4oM2s7pnemfunA7XQ3G8pHlJlY6TirNKsfWW4ZhIYGssGJZEs92eREyRkMVGlJRuRC2PXO6SiIYkhPu9VYRLjBDBahg5jgYsDdoaN/qQUB21KnSWjDMtMMdoBhj5AGgyQVzwugZ/HT7UXU1HnwaGeh3tYWNPW3thwd7v9B6fGr5s@C455@UNFeUlVWJvmdMtaRvSBbAt6HUL/lH6RW2nWRjn@dvZ8v0rnv22wSffK3/zqic "JavaScript (Node.js) – Try It Online")
---
# JavaScript (ES6), 44 bytes
Assuming [loopy walt's method](https://codegolf.stackexchange.com/a/240808/58563) is valid (and it most probably is).
Expects `(n,d)`.
```
f=(n,d)=>n*d<0?'g'+f(-d,n):n?'f'+f(n-d,d):''
```
[Try it online!](https://tio.run/##bZBNDoIwEIX3nmJ2bbWVP2OQiBzEuCCUNhhsCRATT48iWCJtM5vJN5333tzzZ94VbdX0TGleDoNIsaKcpBe15Wc/QxLtBGacKpKoDImxU5@WkwShodCq03W5r7XEAkNAISIEPA@EFGNtVgMRhWAesGFMITTQogFl827poOGfsoV9CqcJX8tH07@g69tKyZstYvw5FIx1h7fQeFtBtkDXRY4UDr@1js9LLvdBYxPsm3t6wxs "JavaScript (Node.js) – Try It Online")
[Answer]
# [C (gcc)](https://gcc.gnu.org/), 104 bytes
```
n=1;d=3;o;main(){if(d<0){n=-n;d=-d;}while(n){if(n<0){o=d;d=-n;n=o;printf("g");}else{n-=d;printf("f");}}}
```
[Try it online!](https://tio.run/##NclNCoMwEAbQu7hKFgMVl9M5jOSvgfRLqYKLkKs7mkK37zlKzqlCZvaycOX3mmFsy9H458M2COEe8tyPVy7B4HcYV8WPAkMqf74ZezRTmiz3ULbQQPf/OQ7uXfV0saxpUzou "C (gcc) – Try It Online")
[Answer]
# [Pari/GP](http://pari.math.u-bordeaux.fr/), 52 bytes
```
t(n,d)=if(n/d>0,Str(f,t(n-d,d)),n,Str(g,t(-d,n)),"")
```
[Try it online!](https://tio.run/##PctBCsMgEAXQq3xcKYw0JqWki@YSXYYspJIgiEhw09PbUUxX8@fN/GRPr49USpaRnHr5XcabWwZ651PuxKodu6LY5GBhiAxCqGJTCl9poRek08fMUdRF4GNDkJlg@RPragjTxmEimDpnwlgnu26HsT8MhGc/mO7memwN/U8Pwr3J1Z1rd1MoPw "Pari/GP – Try It Online")
A port of [@loopy walt's Python answer](https://codegolf.stackexchange.com/a/240808/9288).
Shorter (46 bytes) if we can take a rational number as input:
```
t(r)=if(r>0,Str(f,t(r-1)),r,Str(g,t(-1/r)),"")
```
[Try it online!](https://tio.run/##JYqxCsMwDER/RXiyQEJRUkoyND/RsXTwkmAoxQgv/XpHdqe79@5Kssxnaa1Gw0c@ou0TPavFg9ywIpINPp1ZxVyEgC2V8vnFBLxDsfytXkOHAF79Ay@VhWARJVhlJlBh57nLSbbO2lHH4jv/4y437@O2yvZGaBc "Pari/GP – Try It Online")
[Answer]
# [05AB1E](https://github.com/Adriandmen/05AB1E/wiki/Commands), 33 [bytes](https://github.com/Adriandmen/05AB1E/wiki/Codepage)
```
∞ε„fg©sã}˜õš.Δ®'>„z(‚‡0s.VI`/α₄z‹
```
Naive brute-force approach. Outputs in reversed order. If this is not allowed, a trailing `}R` will have to be added and it would be 35 [bytes](https://github.com/Adriandmen/05AB1E/wiki/Codepage) instead, making the approach below 1 byte shorter.
[Try it online](https://tio.run/##AUcAuP9vc2FiaWX//@KIns614oCeZmfCqXPDo33LnMO1xaEuzpTCric@4oCeeijigJrigKEwcy5WSWAvzrHigoR64oC5//9bMSwzXQ) or [verify all test cases](https://tio.run/##yy9OTMpM/V9Waa@k8KhtkoKSfWXo/0cd885tfdQwLy390Mriw4trT885vPXoQr1zUw6tU7cDildpPGqY9ahhoUGxXlhEgv65jY@aWqoeNez8X6vzPzraUMc4VifaWMcQSFroGAFJQx1dkJARWMJAxxIsZAgWMYRIg1TpQmkzHRMQD6LaAqg6FgA).
A port of [*@loopyWalt*'s second Python answer](https://codegolf.stackexchange.com/a/240808/52210) is **34 [bytes](https://github.com/Adriandmen/05AB1E/wiki/Codepage)**, but it outputs in the correct order:
```
"ÐPdi¬ĀiÆ0ǝ®.V'fìëõë`s(‚®.V'gì"©.V
```
[Try it online](https://tio.run/##yy9OTMpM/f9f6fCEgJTMQ2uONGQebjM4PvfQOr0w9bTDaw6vPrz18OqEYo1HDbPAYumH1ygdWqkX9v9/tKGOcSwA) or [verify all test cases](https://tio.run/##yy9OTMpM/V9Waa@k8KhtkoKSfeV/pcMTAlIyD6050pB5uM3g@NxD6/TC1NMOrzm8@vDWw6sTijUeNcwCi6UfXqN0aKVe2H@d/9HRhjrGsTrRxjqGQNJCxwhIGurogoSMwBIGOpZgIUOwiCFEGqRKF0qb6ZiAeBDVFkDVsQA).
**Explanation:**
```
∞                     # Push an infinite positive list: [1,2,3,...]
 ε                    # Map over each integer:
  „fg                 #  Push string "fg"
     ©                #  Store it in variable `®` (without popping)
      s               #  Swap so the current integer is at the top
       ã              #  Get the cartesian product
  }˜                  # After the map: flatten the list of lists
    õš                # Prepend an empty string
.Δ                    # Then find the first value which is truthy for:
  ®                   #  Push "fg" from variable `®`
   '>                '#  Push string ">"
     „z(              #  Push string "z("
        ‚             #  Pair them together: [">","z("]
         ‡            #  Transliterate all "f" to ">" and "g" to "z("
          0           #  Push 0
           s          #  Swap so the string is at the top again
            .V        #  Evaluate as 05AB1E code:
                      #   `>`: Increase the value by 1
                      #   `z(`: `z` will push 1/value, `(` will negate it
              I       #  Push the input-pair: [n,d]
               `      #  Pop and push `n` and `d` separated to the stack
                /     #  Divide `n` by `d`
                 α    #  Get the absolute difference between the two values
                    ‹ #  Check if this is smaller than
                  ₄z  #  1/1000
                      #  (`α₄z‹` can't be `Q` due to floating point inaccuracies)
                      # (after which the result is output implicitly)
```
```
"..."                 # Push the recursive string defined below
     ©                # Store this string in variable `®` (without popping)
      .V              # Evaluate it as 05AB1E code
Ð                     # Triplicate the current pair [n,d]
                      # (which will be the implicit input in the first call)
   i                  # If
 P                    # n*d (product of the pair)
  d                   # is >= 0:
      i               #  If
    ¬                 #  n (without popping the pair)
     Ā                #  is not 0:
       Æ              #   Push n-d (reduce by subtracting)
         ǝ            #   And insert this n-d back into the pair
        0             #   at index 0: [n-d,d]
          ®.V         #   Then do a recursive call
             'fì     '#   And prepend "f" to that
      ë               #  Else:
       õ              #   Push an empty string ""
   ë                  # Else:
    `                 #  Pop and push `n` and `d` separated to the stack
     s                #  Swap the two values on the stack
      (               #  Negate `n`
       ‚              #  Pair them back together: [d,-n]
        ®.V           #  Then do a recursive call
           'gì       '#  And prepend "g" to that
                      # (after which the result is output implicitly)
```
[Answer]
# [Charcoal](https://github.com/somebody1234/Charcoal), ~~65~~ ~~62~~ ~~52~~ 48 bytes
```
ＮθＮη¿θＷφ«≔θφ≔ηζ⊞υωＰ←⍘ＬυgfＦ⮌↨Ｌυ²¿κ≧⁻ζφ«≔ζε≔φζ≔±εφ
```
[Try it online!](https://tio.run/##TY7LigIxEEXX9lcUrqogLnR2M6txJ9gizhdEqTyYGNs8FBz89kyC3eDyHG5R52RkOF2kK2Xjh5x2@XzkgFf66t7ZVLYKqoe7sY4BFcFfN/uO0WqPVwGqLiY0Ah4N9zkazALuDfrskh2C9Qk/t6ySgLWM/JOq0bhlr1PdkoC5VnNqB@oSAA984xAZ2/Z9tSIiaEW/BL0cXo8PVpuEvfU51oKxiV3kljrFVc/NT6zG2Il3rGViZBrvn92zlCV8lMXN/QM "Charcoal – Try It Online") Link is to verbose version of code. Feels like it should be a bit shorter. Explanation:
```
ＮθＮη
```
Input `n` and `d`.
```
¿θＷφ«
```
Do nothing if `n=0`, otherwise loop until a reverse path to zero is found. (This loop uses the variable `f` which is predefined to `1000`, thus ensuring the loop happens at least once; it saves a byte over saving `n` and using `while (n)`.)
```
≔θφ≔ηζ
```
Make new copies of `n` and `d`.
```
⊞υω
```
Increment the length of the predefined empty list.
```
Ｐ←⍘Ｌυgf
```
Replace the canvas with the length interpreted as a base 2 string of `f`s and `g`s (with `f=1` and `g=0`). The string is reversed so that the last letter is always `f`.
```
Ｆ⮌↨Ｌυ²
```
Loop over the base 2 digits in reverse.
```
¿κ≧⁻ζφ
```
For `1`s undo the `f` step.
```
«≔ζε≔φζ≔±εφ
```
Otherwise undo the `g` step.
Switching to undoing steps saved me 10 bytes. It's possible that deciding whether to undo an `f` or `g` step based on the sign of the current fraction suffices to produce a minimal result. If that's true then the solution would only be 31 bytes:
```
ＮθＮηＷθ¿›×θη⁰«f≧⁻ηθ»«g≔ηζ≔θη≔±ζθ
```
[Try it online!](https://tio.run/##TY5BCsIwEEXX7SmGriZQQXHpypW4aBHxArFMk0Aa2yRVqHj2OFbBLh//v/nTaOmbm7QpHV0/xnrsruRxELt8yZr5oY0l4AhMC3jwJCMnF9NRwKEELUpYCwHPPDt54yIWbcFWVsl@H4JR7myUjlgZNwZul/DZeAHZQAtHzc5XQC5NC5xH/liT4g9wEr9TKW1gm1Z3@wY "Charcoal – Try It Online") Link is to verbose version of code. Explanation:
```
ＮθＮη
```
Input `n` and `d`.
```
Ｗθ
```
Repeat until `n=0`.
```
¿›×θη⁰«
```
If `n/d` is positive, then...
```
f≧⁻ηθ
```
... output an `f` and subtract `d` from `n`.
```
»«
```
Otherwise...
```
g≔ηζ≔θη≔±ζθ
```
... output a `g`, switch `n` and `d`, and negate `n`.
[Answer]
# [Stax](https://github.com/tomtheisen/stax), ~~18~~ 16 [bytes](https://github.com/tomtheisen/stax/blob/master/docs/packed.md#packed-stax)
```
ÿ☺╨K▼▓¡s'↓♫[╣♥╬╩
```
[Run and debug it](https://staxlang.xyz/#p=9801d04b1fb2ad7327190e5bb903ceca&i=+1%2F3%0A+3%2F1%0A+8%2F2%0A+1%2F-3%0A+2%2F3%0A+0%2F9%0A+1%2F1%0A+2%2F1%0A+1%2F-2%0A-1%2F-2%0A+6%2F4%0A-2%2F3%0A+8%2F9&a=1&m=2)
Pretty convenient with the rational type.
[Answer]
# Python3, 192 bytes:
```
lambda n,d:f([(0,n/float(d),'')])
f=lambda d:r[0][-1]if(r:=[i for i in d if round(i[0],4)==round(i[1],4)])else f([j for a,b,c in d for j in[(a+1,b,'f'+c)]+([(-1/(1.0*a),b,'g'+c)]if a else[])])
```
[Try it online!](https://tio.run/##XY9NbsMgEIX3OQU7D/W48cRV5VriJJQFCcEhcrFFnEVP70LaSNAd3@P9wPK9Xmbf9UvYRvG5TfrraDTzaAYLElr0ezvNegXDsaq44jsr/jxmCLJVsiHlLIRBSMfsHJhjzjPDnGVhvnsDLprwjQvxREqo@Hm6nVncuD5SGo94@k0mvMajBF1TlCtb1Seu6vichvZAr@2L5kkfH3oc0iyVSRVbtyU4v8IIhB3nuyd1SBn1eMiIsMmthyLY4kdhpcJJZU3e2vzj9/jp7LZc6dPK9gM)
[Answer]
# [APL (Dyalog Extended)](https://github.com/abrudz/dyalog-apl-extended), 48 bytes
Basic bruteforce approach, expects index origin 0.
```
f←-⍨\⍢⌽
g←-@0⌽
⌽o⊣1+⍣{0=⊃⍎⍕t,⍨↓⍪o∘←'gf'[⊤⍵]}⍱t←⎕
```
[Try it online!](https://tio.run/##SyzI0U2pTMzJT9dNrShJzUtJTfn/qG@qp/@jtgkGXI862jP@pwGZuo96V8Q86l30qGcvVzqI72AAYgJx/qOuxYbaj3oXVxvYPupqftTb96h3aokOUP2jtsmPelflP@qYAdSgnp6mHv2oa8mj3q2xtY96N5YAxYD2/Afa8D@DS0HBUEFBwZgLxDIGsgzBLAsgy4gLKntoPUTaCK7QAMiyhElDtRjBWWAtYN2H1iPYCmZABSYQUYRJIIssAQ "APL (Dyalog Extended) – Try It Online")
[Answer]
# [C (clang)](http://clang.llvm.org/), ~~59~~ \$\cdots\$ ~~53~~ 52 bytes
```
z;f(n,d){z=n*d>=0;n&&f(z*n-d,z?d:n,putchar(103-z));}
```
[Try it online!](https://tio.run/##dVHhaoMwEP7fpziElmgTZusYXTO7X3uKtQwxiRO6a6mOFYuvPnfRRCZjweTuvu/uy@XMRX7MsOi6RhqGXIW3JsVI7dJY4mJhWBOhULx5Vlvk5886f88ubBUnoglD2XYl1vCRlchCuM2AlgVqXdVv@HqAFG4rDgmHDQdy1hxi79ApaD@QoWjTymm1GqoTny6SXuexj1dDkd33A@zKbXMR6OtZ57V2EoEpjP0CDoEZTW8LH3unD4YMx3t6AIsR/0W5FbgezOkCzL6jRKWv1EEsnfsEVdnok2G@wfDOAdGISFgu@2w/Tz8VJCU3154/yAmtPK3@0OcLJRgWzNeKAx0gdrC3T0UOdOGYZ//@BHDTJOVxov9p74M9vrikLanPK4vQHdrptbO2@87NMSuqTnz9AA "C (clang) – Try It Online")
*Port of [loopy walt](https://codegolf.stackexchange.com/users/107561/loopy-walt)'s [Python answer](https://codegolf.stackexchange.com/a/240808/9481).*  
*Saved ~~5~~ 6 bytes thanks to [ceilingcat](https://codegolf.stackexchange.com/users/52904/ceilingcat)!!!*  
*Saved a byte thanks to [dingledooper](https://codegolf.stackexchange.com/users/88546/dingledooper)!!!*
Inputs integers \$n\$ and \$d\$.  
Outputs string of `f`'s and `g`'s that represents a shortest path of iterated maps from \$0\$ to \$\frac{n}{d}\$.
[Answer]
# [Wolfram Language (Mathematica)](https://www.wolfram.com/wolframscript/), 139 bytes
```
(m=1;f=If[#3<m,If[#1!=0,Do[#0[{#1-1,-1/#1}[[k]],Append[#2,k],#3+1],{k,2}],Throw@#2]]&;While[(r=Catch[f[#1/#2,{},1]])==Null&&#1!=0,m+=1];r)&
```
[Try it online!](https://tio.run/##RY3BCoJAFEX3/UUMDIpP9OmmsAGjNm2iRdDi8RaDKSM6KmK0EL/dlIhWl8Pl3Gv1YHKrhzLTs1GzYxUmhboUJOKDhTVxq0I4tyRCGgX6CD4GAieiihmOXZc3TxIRVAwi9pBhrCCaGO6mb9@piJhl8jBlnZPTq5MeMkPraLAo4wTI7Cp1fdW1lN8n6ynkpHflfOvLZkgNIcS8@cEO9n8Il2b@AA "Wolfram Language (Mathematica) – Try It Online")
Reverse recursive approach.  
We start with given fraction and recursively apply reverse operations until we reach `0`.  
Output is array with `1` for \$f\$ and `2` for \$g\$ or `Null` if `n==0`.
The code is not optimal (the same results are used several times), but optimization will increase the length of the code.  
I'll try golfing it more, sure it's possible.
Ungolfed version self-explained:
```
Clear[recurse, main];
recurse[x_, arr_, i_, max_] :=
  If[i < max,
   If[x != 0,
    Do[recurse[{x - 1, -1/x}[[k]], Append[arr, k], i + 1, max],
     {k, 2}],
    Throw@arr
    ]];
main[n_, d_] := Module[{max = 1, res = Null},
   While[res == Null && n != 0,
    res = Catch[recurse[n/d, {}, 1, max]];
    max += 1
    ]; res];
```
[Answer]
# [Scala](https://www.scala-lang.org/), ~~83~~ 78 bytes
Saved 5 bytes thanks to the comment of @ceilingcat
---
Golfed version. [Try it online!](https://tio.run/##LcsxCgIxEIXh3lMM22RGI2glLlpYWlh5gpiZLJEwG0wKQTx73IDNg5@PV7xLrs2Pp/gKNxcV5F1FucAlZ/g0lgAedbxqtdyXxnt9RZ3OMaCu@bQjM5mNxy1bJUlF4A97GobeJnTWxZkaQF7ONSl6PFo4EK2@7Qc)
```
def c(n:Int,d:Int):String=if(n*d<0)'g'+c(-d,n)else if(n*d<1)""else'f'+c(n-d,d)
```
Ungolfed version. [Try it online!](https://tio.run/##VY9Nb8IwDEDv@RVPXNqMInGbhlakHXfgxA9AoUmhqPWqNUKVoL@9pNnEx8W2nOfnuCtMbcbxZ39yhWdjKsH13ont@GpbLgqsKwlUkcqKb/EZNma9Yut/KzmQRwyqklTIc5Zcr9hYaWaz@Ojqzv1zTNgbVtMYXxzvXcKaAPWTqOczaPI1ySFh/rd/YTNEv8K7iXkI4GxqmvAlYYF96ifl3dOEEx6aQT3nKQ4qhDac5mtJ48RHxrvWalDjeAM)
```
object Main extends App {
  def calc(n: Int, d: Int): String = {
    if (n == 0 || d == 0) ""
    else {
      (n * d) match {
        case x if x < 0 => 'g' + calc(-d, n)
        case _ => {
          val m = n - d
          'f' + calc(m, d)
        }
      }
    }
  }
  println(calc(9, 7))
}
```
[Answer]
# SAS 101,
"*Generating Ratinal Numbers in SAS 4GL - 101*" :-D
Inspired by [loopy walt](https://codegolf.stackexchange.com/questions/240806/iterate-your-way-to-a-fraction/240808#240808) answer I decided to make SAS 4GL version.
The code:
```
data;set;do while(n);t=d;if(n*d<0)then do;put"g"@;d=n;n=-t;end;else do;put"f"@;n=n-d;end;end;put;run;
```
SAS is quite "talkative" by all those `if`s, `then`s, `do`s, `end`s and `else`s so, even tough I did my best to "boil" bytes down, I ended up on 101.
Explanation:
I don't like stack overflows so instead recursion I went with iterative approach.
It is SAS 4GL so the the standard input in form of a SAS dataset is expected (I added some extra test cases):
```
data i;
input n d;
cards;
1     3
3     1
16    4
8     2
4     1
1    -3
2     3
0     9
0     3
0     1
1     1
2     1
1    -2
-1    -2
6     4
-2     3
8     9
17    42 
17   -42 
17    303
42    303
99    100
990   1000
999   1002
999  -1002
1 11111
1 2
2 4
4 8
;
run;
```
Human readable code:
```
data _null_; /* 0) */
  set i; /* expect input data set 1) */
  put n= d= @; /* added just for printing */
  do while(n); /* while numerator is not 0 */
    x=d;
    if (n*d<0) 
      then /* for negative print "g", then negate and flip fraction  */
        do;
          put "g" @;
          d = n;
          n = -x;
        end;
      else /* for positive print "f", then substract 1 from fraction  */
        do;
          put "f" @;
          n = n-d;
        end;
  end;
  put; /* add new line character at the end of printout 2) */
run;
```
*Footnotes:*
*0) turn of production of output dataset*
*1) the `set` statement takes as input the last created data set, so code generating dataset `i` has to be executed just before.*
*2) the last `put;` can be skipped if the input dataset has only 1 observation what reduces length by 4 bytes to 97.*
Log output:
```
n=1 d=3 fgffgff
n=3 d=1 fff
n=16 d=4 ffff
n=8 d=2 ffff
n=4 d=1 ffff
n=1 d=-3 gfff
n=2 d=3 fgfff
n=0 d=9
n=0 d=3
n=0 d=1
n=1 d=1 f
n=2 d=1 ff
n=1 d=-2 gff
n=-1 d=-2 fgff
n=6 d=4 ffgff
n=-2 d=3 gffgff
n=8 d=9 fgfffffffff
n=17 d=42 fgffgffffgffgffgffgffgffgffgff
n=17 d=-42 gfffgffgfffffffff
n=17 d=303 fgffgffgffgffgffgffgffgffgffgffgffgffgffgffgffgffgfffgffgffgffgfffgff
n=42 d=303 fgffgffgffgffgffgffgffffffgfff
n=99 d=100 fgffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
n=990 d=1000 fgffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
n=999 d=1002 fgf[ 332 "f" hidden for readability ]f
n=999 d=-1002 gff[ 331 "gff" hidden for readability ]gff
n=1 d=11111 f[ 11109 "gff" hidden for readability ]gff
n=1 d=2 fgff
n=2 d=4 fgff
n=4 d=8 fgff
NOTE: There were 29 observations read from the data set WORK.I.
NOTE: DATA statement used (Total process time):
      real time           0.02 seconds
      cpu time            0.03 seconds
```
*A small comment at the end:*
We are in a group of functions with composition.
Function: \$ z(x) = g(f(g(f(g(x))))) = x{-}1 \$ is an inverse of \$f\$ (\$f(z(x))=x\$, \$z(f(x))=x\$) and \$g\$ is inverse of itself (\$g(g(x))=x\$)
So a path from \$0\$ to \$\frac{n}{d}\$ (assuming frac is reduced) of the form:
$$
\frac13 = f(g(f(f(g(f(f(0)))))))
$$
can be "walked back" using inverses of \$f\$ and \$g\$
$$
0 = z(z(g(z(z(g(z(\frac13)))))))
$$
Which is "base" for the algorithm:
"*1) take a fraction, 2) if the fraction is negative "flip it"(negate and inverse) else subtract 1 from it, 4) if fraction is 0 stop else got to 1)*"
]