Sometimes in chat, if someone says something you agree with, you'll send a message with an ^, which points at the message above:

Radvylf: Cats are far superior to JavaScript
You: ^

Sometimes you'll also add some text:

Radvylf: I sure do like integers
You: I mostly agree with ^ but floats are pretty neat too

You can also use multiple ^s to refer to messages farther back:

Timmy: Puppies are adorable.
Radvylf: I hate puppies.
You: ^^

In this challenge, given a chat log with ^s, you'll replace the ^s with the messages they refer to.


Given a multiline string of messages (without usernames), or an array where each message is a string (or some representation of one), resolve all of the ^ references in it. Note that messages can refer to messages that themselves have ^s, which need to be properly resolved.

For example:

Cats are funny sometimes
^^, but I've met serious cats too.
Opinions on static typing?
It's okay.
^ It depends on the implementation, though.
^ Big difference between good and bad static ty
Thanks ^_^

This would become:

Cats are funny sometimes
Cats are funny sometimes
Cats are funny sometimes, but I've met serious cats too.
Cats are funny sometimes
Opinions on static typing?
It's okay.
It's okay. It depends a lot on the implementation, though.
It's okay. It depends a lot on the implementation, though. Big difference between good and bad static ty
It's okay. It depends a lot on the implementation, though. Big difference between good and bad static typing
Thanks It's okay. It depends a lot on the implementation, though. Big difference between good and bad static typing_It's okay. It depends a lot on the implementation, though. Big difference between good and bad static typing

You'll never need to resolve a group of ^s that points up more times than there are messages to point at. E.g., ^ will never be the first line, and ^^ will never be the second.

Test cases:










^ ^^ ^^^


abc ab a


This is , so shortest answer in bytes per language wins.

  • 4
    \$\begingroup\$ I don't think ^ :) \$\endgroup\$
    – l4m2
    Mar 11, 2022 at 0:01
  • 1
    \$\begingroup\$ Oh wow this barely stayed on the sandbox at all \$\endgroup\$
    – Seggan
    Mar 11, 2022 at 0:14
  • \$\begingroup\$ ooh i like this, the next logical step of course is a chat program that implements the shortest answer to this :P \$\endgroup\$
    – des54321
    Mar 11, 2022 at 1:32
  • \$\begingroup\$ very interesting challenge! (and hard to come up with) brilliant radvylf! \$\endgroup\$
    – DialFrost
    Mar 11, 2022 at 5:57
  • \$\begingroup\$ as a cat I can confirm that we are much more interesting than JS \$\endgroup\$
    – Ginger
    Mar 19, 2022 at 14:23

13 Answers 13


JavaScript (ES6), 56 bytes

Expects and returns an array of strings.


Try it online!

  • 2
    \$\begingroup\$ Darn, had prepared this exact same solution (except using other letters for the variables). \$\endgroup\$
    – Adám
    Mar 12, 2022 at 21:15

brainfuck, 240 bytes


Try it online!

Now works with blank lines. This ended up saving bytes, and the slightly altered tape structure allowed the two other improvements I had tried earlier to save bytes as well.

The tape structure of this program is as follows:

1 firstline 0 0 0 1 secondline 0 0 0 ... 1 lastline 0 input

The 1 cell at the beginning of each line allows the program to cope with empty lines and lines beginning with ^ without additional effort. It also allowed me to shorten the gap between text lines, which saved a small number of bytes.

With formatting and comments:

Initialize tape with first line marker and input

While there is input:

  Duplicate input

  Test for newline

  If not newline:

    Test for ^ (94)

    If anything else:

      Output letter and take next input

      Move so rest of loop will end at next input cell


    If caret    

      Place 1 between text lines to track which line to copy

      Duplicate next input and check if caret
      (there's probably a shorter way to do this)

      If not another caret:

        Clear test cell

        Move to start of line to copy and delete the 1 from it

        For each letter in that line:


          Move next input cell to the right

          Populate cell to copy to; start backup copy too

          Copy letter to (next letter in current line) and (backup 3 left)


        Restore line and initial 1 cell

        Clear indicators between lines

        Move input to correct cell
        (that cell contains negative one from the previous loop)


      If was another caret:

        Delete input and set indicator to repeat caret loop



  If newline:

    Output newline

    Set up next line



Jelly,19 18 17 bytes


Try it online!

-1 thanks to Jonathan Allan

-1 thanks to Jonathan Allan again

Takes and returns lists of lines.

              ð ḟ    Starting with the input without any of its elements,
               ƒ     reduce by:
ɓ                    with reversed arguments: (line on left, lines on right)
 Œg                  Runs of consecutive equal elements.
        ʋ€           For each run:
       ṭ             append the run to
           }         the accumulated lines
          Ṛ          reversed
      ị              and get the item at the 1-index of
   ċ”^               the number of carets.
            F        Flatten the runs and substitutions back together,
             ṭ       and append to the list of accumulated lines.

Love when I shave two bytes off (from ɓŒg=”^Sị⁸¹?ɗ€Ṛ}Fṭµƒ“”) in the course of writing an explanation :P

  • 1
    \$\begingroup\$ Another one byte save can be made by indexing into the reverse of the lines so far with the current run appended (i.e. at index 0) TIO \$\endgroup\$ Mar 11, 2022 at 18:35
  • 2
    \$\begingroup\$ That is some powerful chaining. \$\endgroup\$
    – emanresu A
    May 19, 2022 at 5:06

Retina 0.8.2, 31 bytes


Try it online! Link includes test cases. Explanation:


Replace one match at a time, repeating until all matches have been replaced. (This allows a replaced line to be referenced in a later line.)


Match a number of ^s, then search back that many lines for the replacement text.


Replace the ^s with the found text.


Python 3, 87 bytes

import re
def f(a,*q):
	for x in a:q+=re.sub("\^+",lambda k:q[-len(k[0])],x),
	return q

Try it online!

-11 bytes thanks to Unrelated String


Ruby, 57 51 bytes


Try it online!

  • Saved 6 Bytes thanks to @G B

Lambda taking and returning an array of lines.

Instead of iterating the size of the array G B proposed to build the output by adding lines one by one, so that we take from that the previous (size of match) line.

We use gsub and the {|match| block form } which brings the last match every time.

  • \$\begingroup\$ 51 \$\endgroup\$
    – G B
    Mar 11, 2022 at 10:31
  • \$\begingroup\$ Really good improvement @G B , thanks! \$\endgroup\$
    Mar 11, 2022 at 17:59

05AB1E, 18 bytes


Input as a list of lines.

Try it online or verify all test cases.


ε           # Foreach of the (implicit) input-list:
 D          #  Duplicate the current line
  '^Ã      '#  Pop the copy, and only keep its "^"
     η      #  Pop and push the list of prefixes of this "^"-string
      R     #  Reverse this list of prefixes
  ā         #  Push a list in the range [1,length] (without popping)
   R        #  Reverse it to range [length,1]
    (       #  Negate each to range [-length,-1]
     N+     #  Add the 0-based loop-index to each: range [N-length,N-1]
       ¯    #  Push the global array
        s   #  Swap so the list of indices is at the top again
         è  #  Index each into the global array
  :         #  Replace all "^"-strings with these strings in the current line
   =        #  Print the modified line with trailing newlines (without popping)
    ˆ       #  Pop and add it to the global array

D, 100 bytes

Returns by modifying its input, exits with error.

import std;void f(string[]s){for(int i;;i++)s[i]=s[i].replaceAll!(x=>s[i-x[0].length])=`\^+`.regex;}

Try it online!

D, 97 bytes

This one returns a Range and doesn't error.

import std;
(string[]s,int i=0)=>s.map!(r=>s[i++]=r.replaceAll!(x=>s[i+~x[0].length])=`\^+`.regex)

Try it online!


APL (Dyalog Unicode), 43 bytes

Full program. Prompts for and prints list of strings.


Try it online!

 prompt for text from console

t← store in t

 tally of texts

indices 1 through that

{ apply the following anonymous lambda to each text; argument is :

i←⍵ store argument as i

t⊃⍨ pick that element from the input

'\^+'⎕R{}Replace each run of carets with:

  ⍵.Match the match

   the length of that

  i- subtract that from i

  t⊃⍨ pick that element from the input

(⍵⊃t)← in-place update the argument'th element of t with that (and implicitly return it)

 return (and implicitly print) that


SNOBOL4 (CSNOBOL4), 106 bytes

I	X =X + 1
R	I SPAN('^') . K =T[X - SIZE(K)]	:S(R)
	T[X] =OUTPUT =I	:(I)

Try it online!


Lua, 83 bytes

s=...;for i,l in ipairs(s)do print((l:gsub("%^+",function(m)return s[i-#m]end)))end

Must be loaded as a "chunk" (file or string). Expects input as table of strings. Prints output.


C++ (gcc), 191 186 181 bytes

int f(std::string*a,int n){for(int b=0,c,i;b<n;++b)for(i=-1;++i<a[b].size();c?a[b].replace(i-c,c,a[b-c]),i+=a[b-c].size()-c:0)for(c=0;a[b][i]==94;++c)++i;}

Try it online!

Dang, this is bad. Only wins the Brainfuck answer lol.

For anyone trying to help here's the ungolfed code:

#include <iostream>
#include <vector>
#include <string>

void chat_reference(std::vector<std::string> &chat)
    for(int list_message = 0; list_message < chat.size(); ++list_message)
        int reference_counter = 0;
        for(int i = 0; i < chat[list_message].size(); ++i)
            while(chat[list_message][i] == '^')
            if(reference_counter > 0)
                chat[list_message].replace(i - reference_counter, reference_counter, chat[list_message - reference_counter]);
                i += chat[list_message- reference_counter].size() - reference_counter;
                reference_counter = 0;

-6 bytes thanks to ceilingcat

-4 bytes thanks to ceilingcat

-11 bytes thanks to ceilingcat


Charcoal, 51 43 bytes


Try it online! Link is to verbose version of code. Takes input as a list of newline-terminated lines. Explanation:


Repeat for each line.


Add a new empty output line to the predefined empty list.


Start with no ^s.


Append a null character to the input line, split the result on ^s and loop over the pieces.


If this piece is not empty, then...


... remove the last line, concatenate a previous line if there were ^s accumulated, concatenate the split piece without any null characters, and add it back to the list of lines (the indexing for the previous line works because the last line isn't in the array during the concatenation); then...


... set the number of ^s to 1 assuming that there's a following piece that was separated from this piece with a ^.


Empty pieces correspond to consecutive ^s so simply increment the number of ^s.


Output the final list of lines.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.