# Bilibili AV/BV Code Conversion

### Background

Bilibili is a China-based video sharing platform, similar to Nico Nico Douga in Japan, and Youtube around the world.

Since the establishment, Bilibili had been using the AV code to reference a video. An AV code is a string of 3 or more characters, starting with av and followed by a natural number, like av82054919.

However, since 23 March 2020, Bilibili introduced a new referencing system called the BV code. This time, the code is a string of exactly 12 characters, starting with BV and followed by 10 alphanumerical characters, like BV1XJ41157tQ. Still, users can use both codes to reference a video, and there is a conversion algorithm between the two.

(For your information, the two examples reference the same video)

### The algorithm

To convert from AV code to BV code:

1. Remove the av part of the AV code.
2. Do a bitwise XOR between the result in step 1 (as a number) and 177451812.
3. Add 8728348608 to the result in step 2.
4. Convert the result in step 3 to a 6-digit base-58 number with digits fZodR9XQDSUm21yCkr6zBqiveYah8bt4xsWpHnJE7jL5VG3guMTKNPAwcF in place of 0, 1, ..., 57 respectively. Here, 0, I, O, l are omitted to eliminate ambiguity. Assume the result is abcdef.
5. The BV code is then BV1db4a1c7ef.

To convert from BV code to AV code:

1. Remove the 1st to 3rd, the 6th, the 8th and the 10th characters. Assume the result is abcdef.
2. Rearrange the result in step 1 to cbdaef.
3. Treat the result in step 2 as a base-58 number with digits fZodR9XQDSUm21yCkr6zBqiveYah8bt4xsWpHnJE7jL5VG3guMTKNPAwcF in place of 0, 1, ..., 57 respectively. Convert this base-58 number into decimal.
4. Subtract 8728348608 from the result in step 3.
5. Do a bitwise XOR between the result in step 4 and 177451812.
6. The AV code is then av followed by the result in step 5.

### Example

To convert av82054919 to BV code:

1. Removing av from the code results in 82054919.
2. $$\82054919\text{ XOR }177451812=242727971\$$
3. $$\242727971+8728348608=8971076579\$$
4. $$\8971076579_{10} = (13,38,43,6,30,7)_{58}\$$1J5XtQ
5. Substituting: BV1[X][J]4[1]1[5]7[t][Q]BV1XJ41157tQ

To convert BV1XJ41157tQ back to AV code:

1. Remove the bracketed numbers: [BV1]XJ[4]1[1]5[7]tQXJ15tQ
2. Rearranging the characters results in 1J5XtQ.
3. 1J5XtQ$$\(13,38,43,6,30,7)_{58} = 8971076579_{10}\$$
4. $$\8971076579-8728348608=242727971\$$
5. $$\242727971\text{ XOR }177451812=82054919\$$
6. Prepending av to the result, we have av82054919.

### Challenge

Write two independent programs or functions, one receiving the AV code as input and outputting the corresponding BV code, and one receiving the BV code as input and outputting the corresponding AV code. Both the prefixes av and BV are case insensitive, you may choose either upper or lower case to accept or output. Each code should work even without the code from the other.

You may assume that the input for both programs must have a valid format, and the number in its corresponding AV code is between 1 and 2147483647 inclusive.

### Test cases

AV code <-> BV code
av82054919 <-> BV1XJ41157tQ
av123456789 <-> BV1yn411L7tG
av1 <-> BV1xx411c7mQ
av2147483647 <-> BV1Fr4k1q7G1


You may check your results through this tool (not developed by me). Click the first button after entering the AV/BV code into the input, then the textbox will show the result.

### Winning condition

The total length of the codes of the two programs (measured in bytes) will be your code length, and since this is a code-golf challenge, the submission with shortest code length for each language wins. No standard loopholes.

• A useless note: av numbers are not only used to reference videos. There may also be some non-video contents.These includes 3 announcements (1, 1113, 8219) and all posts under Flash game section. Flash game section contains Flash and also Applet, and is no longer available now (due to security reason). – tsh May 18 '20 at 10:17
• Can we share data between 2 functions, e.g a global string storing the value "fZodR9XQDSU..."? – Surculose Sputum May 19 '20 at 4:58
• @SurculoseSputum Since basically I'm asking for two independent programs or functions, so I'm not expecting any shared parts. Indeed this exact question was also asked by another user in the sandbox, and that's why I added the word "independent" into the requirements. – Shieru Asakoto May 19 '20 at 7:30
• I've VTCed this as unclear because it is very unclear how we get from step 4 to 5 in the AV/BV conversion. It took me several readthroughs and some reverse engineering to figure it out. – Shaggy May 19 '20 at 21:11
• 2081*2**22+1984==8728348608 and 2081-1984==97==ord('a') for what it's worth. – David May 24 '20 at 11:52

# Python 2, 170 + 150 = 320 bytes

### AV to BV, 180 179 173 170 bytes:

lambda s:"BV1{2}{4}4{5}1{3}7{1}{0}".format(*["fZodR9XQDSUm21yCkr6zBqiveYah8bt4xsWpHnJE7jL5VG3guMTKNPAwcF"[((int(s[2:])^177451812)+8728348608)/58**i%58]for i in range(6)])


Try it online!

### BV to AV, 165 158 150 bytes:

s=input()
n=0
for i in 6,4,8,3,10,11:n=n*58+"fZodR9XQDSUm21yCkr6zBqiveYah8bt4xsWpHnJE7jL5VG3guMTKNPAwcF".find(s[i])
print"av"+n-8728348608^177451812


Try it online!

# Python 3, 518465439422420412406402 401 bytes

-6 bytes thanks to Surculose Sputum. (I didn't know Python could be made that unreadable!)
-4 bytes thanks to Gavin S. Yancey.
-1 yet another byte gone thanks to Surculose Sputum

Encode:

def s(x):
b,a=(int(x[2:])^177451812)+8728348608,''
while b:a+='fZodR9XQDSUm21yCkr6zBqiveYah8bt4xsWpHnJE7jL5VG3guMTKNPAwcF'[b%58];b//=58
return f'BV1{a[2::2]}4{a[5]}1{a[3]}7{a[1::-1]}'


Decode:

def z(i):
i=i[3:5]+i[6:9:2]+i[10:];a,q,m=i[2]+i[1]+i[3]+i[0]+i[4:],0,1
for c in a[::-1]:q,m=q+m*'fZodR9XQDSUm21yCkr6zBqiveYah8bt4xsWpHnJE7jL5VG3guMTKNPAwcF'.index(c),m*58
return'av'+str((q-8728348608)^177451812)


A fairly simple solution comprised of parts of this and this. Can probably be golfed down a lot, when I get the time. I think it is golfed down to a good-enough level now. And now it works

• The first function doesn't seem to work? Maybe you introduced a bug or two while golfing it. For example, I think the 5th line needs to be b//=58. Even with that, it doesn't output the values I expect from the test cases. Tip: Put some print calls in the Footer to demonstrate the code. – Neil May 18 '20 at 19:20
• @SurculoseSputum (and Neil) Many thanks – nope May 18 '20 at 21:23
• You could save 5 bytes on your encode by using an f-string: return f'BV1{a[2]+a[4]}4{a[5]}1{a[3]}7{a[1::-1]}' – Gavin S. Yancey May 19 '20 at 5:22
• And another 1 by replacing a[2]+a[4] with a[2:5:2] – Gavin S. Yancey May 19 '20 at 5:25
• a[2:5:2] can be just a[2::2], saving 1 byte. – Surculose Sputum May 19 '20 at 19:52

# 05AB1E, 192164 163 (81+82) bytes

AV to BV - 96 82 81 bytes:

þ•A³ú[•^•2G&©I•+žL¨…lIOм{œ•F
mʒØà\ç×н˜=ˆ§kð€³ä‘λ°Ð‘ç«(вÍè\pÌ•èÅвA"BV1db4a1c7ef"r‡


BV to AV - 96 82 bytes:

•1ïÓî•S£ιθSƵª.IžL¨…lIOм{œ•F


NOTE: The TIO's use S with .I (convert to character-list and $$\n^{th}\$$ permutation builtin) instead of œ with è (get all permutations and index into the list), since generating all permutations is of course way too slow.

### Explanation:

AV to BV:

þ                     # Only leave digits of the (implicit) input (to remove "av")
•A³ú[•               # Push compressed integer 177451812
^              # Bitwise-XOR the two integers together
•2G&©I•       # Push compressed integer 8728348608
+      # Add the two integers together
žL                    # Push builtin string "zyxwvutsrqponmlkjihgfedcbaZYXWVUTSRQPONMLKJIHGFEDCBA9876543210"
¨                   # Remove the last character (the "0")
…lIO               # Push string "lIO"
м              # Remove those three as well
{             # Sort the remaining characters in the string
œ            # Get a list of all permutations of this string
•F\nmʒØà\ç×н˜=ˆ§kð€³ä‘λ°Ð‘ç«(вÍè\pÌ•
# Push compressed integer 1563341720558629316384554749509959660779122984425616460522557465911652079492533
è                    # Index it into the list of permutations:
#  "fZodR9XQDSUm21yCkr6zBqiveYah8bt4xsWpHnJE7jL5VG3guMTKNPAwcF"
Åв                  # Convert the integer to this custom base
A                 # Push the lowercase alphabet
"BV1db4a1c7ef"   # Push string "BV1db4a1c7ef"
r  # Reverse the three values on the stack
‡ # Transliterate the lowercase alphabet to the base-converted
# characters in "BV1db4a1c7ef"
# (after which the result is output implicitly)


BV to AV:

•1ïÓî•                # Push compressed integer 32111112
S               # Convert it to a list of digits: [3,2,1,1,1,1,1,2]
£              # Split the (implicit) input-string into parts of that size
ι             # Uninterleave it
θ            # Only keep the last result
S           # And convert it to a flattened list of characters
Ƶª         # Push compressed integer 270
.I       # And take the 270th 0-based permutation
žL¨…lIOм{œ•F\nmʒØà\ç×н˜=ˆ§kð€³ä‘λ°Ð‘ç«(вÍè\pÌ•è
# Same as in the AB to BV conversion above,
Åβ                   # but the other way around (convert from the custom base to integer)
•A³ú[•^       # Bitwise-XOR it with 177451812
„avì   # Prepend "av"
# (after which the result is output implicitly)


See this 05AB1E tip of mine (section How to compress large integers?) to understand why •A³ú[• is 177451812; •2G&©I• is 8728348608; •F\nmʒØà\ç×н˜=ˆ§kð€³ä‘λ°Ð‘ç«(вÍè\pÌ• is 1563341720558629316384554749509959660779122984425616460522557465911652079492533; •1ïÓî• is 32111112; and Ƶª is 270.

The 1563341720558629316384554749509959660779122984425616460522557465911652079492533 is generated by this Jelly builtin (minus 1, since Jelly uses 1-based indexing and 05AB1E uses 0-based indexing).

• This is the closest answer to my bounty request, since it at least uses the fact that the string is a permutation of a subset of the alphanumerics. – Solomon Ucko May 26 '20 at 19:05

# C (gcc), 191 + 181 = 372 bytes

Saved 4 5 8 15 bytes thanks to ceilingcat!!!

# C (gcc), 224 $$\\cdots\$$ 198 191 bytes

r[]=L"BV1__4_1_7";e(a,w)long a,w;{w=(strtol(a+2,0,10)^177451812)+8728348608;for(a=6;a--;w/=58)r["6483:;"[a]-48]="fZodR9XQDSUm21yCkr6zBqiveYah8bt4xsWpHnJE7jL5VG3guMTKNPAwcF"[w%58];wprintf(r);}


Try it online!

# C (gcc), 224 $$\\cdots\$$ 186 181 bytes

char*d="fZodR9XQDSUm21yCkr6zBqiveYah8bt4xsWpHnJE7jL5VG3guMTKNPAwcF",b;long w;c(char*s){for(w=b=0;b<6;w+=index(d,s["6483:;"[b++]-48])-d)w*=58;printf("av%ld",w-8728348608^177451812);}


Try it online!

• @ceilingcat Oh, very nice you cracked it - thanks! :D – Noodle9 May 25 '20 at 9:45

# Charcoal, 208 bytes

## AV code to BV code, 117 bytes:

≔⍘"0|7mγη≔Ｉ✂θ²χ¹θ≔⍘⁺⁻｜θη＆θη⍘!,/>#iγ”\>⊟∨↖κ2¹W«ε➙⊗MιΦ/⁷Aêμ⁵Ｓ⁶Ｄt÷r≔l⪪≦｜⭆±⊗υ↓1↖Þ»χＺ⁹～”θ≔⁺×f⁻⁶Ｌθθθ⭆BV1db4a1c7ef⎇№βι§θ⌕βιι


Try it online! Link is to verbose version of code. Explanation:

≔⍘"0|7mγη


Get the constant 177451812 into a variable as we need it twice because we don't have a bitwise XOR builtin.

≔Ｉ✂θ²χ¹θ


Extract the value in the AV code as a number.

≔⍘⁺⁻｜θη＆θη⍘!,/>#iγ”\>⊟∨↖κ2¹W«ε➙⊗MιΦ/⁷Aêμ⁵Ｓ⁶Ｄt÷r≔l⪪≦｜⭆±⊗υ↓1↖Þ»χＺ⁹～”θ


Subtract the bitwise AND from the bitwise OR giving the bitwise XOR, add the offset, then perform custom base conversion using a compressed version of the given string.

≔⁺×f⁻⁶Ｌθθθ


Pad the string to 6 characters.

⭆BV1db4a1c7ef⎇№βι§θ⌕βιι


Substitute the characters in the appropriate positions in the BV code.

## BV code to AV code: 91 bytes:

≔⍘"0|7mγη≔⁻⍘⭆684921§θ±Ｉι”\>⊟∨↖κ2¹W«ε➙⊗MιΦ/⁷Aêμ⁵Ｓ⁶Ｄt÷r≔l⪪≦｜⭆±⊗υ↓1↖Þ»χＺ⁹～”⍘!,/>#iγθavＩ⁻｜θη＆θη


Try it online! Link is to verbose version of code. Explanation:

≔⍘"0|7mγη


Get the constant 177451812 into a variable again.

≔⁻⍘⭆684921§θ±Ｉι”\>⊟∨↖κ2¹W«ε➙⊗MιΦ/⁷Aêμ⁵Ｓ⁶Ｄt÷r≔l⪪≦｜⭆±⊗υ↓1↖Þ»χＺ⁹～”⍘!,/>#iγθ


Extract the characters from the appropriate positions of the BV code, perform custom base conversion, and subtract the offset.

avＩ⁻｜θη＆θη


Print the bitwise XOR prefixed by av.

# JavaScript (Node.js), 322 bytes

f=(s,[a,b,c,d,e,f]=(g=n=>n?g(n/58|0)+'fZodR9XQDSUm21yCkr6zBqiveYah8bt4xsWpHnJE7jL5VG3guMTKNPAwcF'[n%58]:'')((s.slice(2)^177451812)+8728348608))=>'BV1'+d+b+4+a+1+c+7+e+f
F=s=>'av'+([...'315078'].map(n=>'fZodR9XQDSUm21yCkr6zBqiveYah8bt4xsWpHnJE7jL5VG3guMTKNPAwcF'.search(s[2-~n])).reduce((x,y)=>x*58+y)-8728348608^177451812)


Try it online!

A simple and stupid solution. Not so creative. But it at least works.

• I don't know how easy it is to get into TIO, but can you use btoa\x7d\x9a\x1dG\xd5\xd0\x0d\x25\x26\xdb\x5c\x82\x92\xbe\xb3\x06\xa8\xafy\x86\xa1\xf1\xbbx\xc6\xc5\xa9\x1erD\xee2\xf9Tm\xe0\xb8\xc4\xca4\xf00p  in any useful way? – Dom Hastings May 18 '20 at 11:36
• @DomHastings fwiw i think most things are fine to get into TIO, the only exception being nul bytes because textareas don't support that – ASCII-only May 19 '20 at 2:05

# JavaScript (Node.js), 295 bytes

F=t=>'bv1BD4E1C7A9'.replace(/[9-F]/g,c=>'fZodR9XQDSUm21yCkr6zBqiveYah8bt4xsWpHnJE7jL5VG3guMTKNPAwcF'[((t.slice(2)^177451812)+8728348608)/58**('0x'+c-9)%58|0])
G=s=>'av'+([7,5,9,4,11,12].map(n=>x=x*58+'fZodR9XQDSUm21yCkr6zBqiveYah8bt4xsWpHnJE7jL5VG3guMTKNPAwcF'.search(s[n])|0,x=16717449)|x^177451812)


Try it online!

Thank Arnauld for -7 bytes

# perl -pl (+ bc), 186 + 189 = 375 182 + 187 = 369 bytes

## AV to BV

s/^..//;@_=(split//,fZodR9XQDSUm21yCkr6zBqiveYah8bt4xsWpHnJE7jL5VG3guMTKNPAwcF)[echo "obase=58;@{[($_^177451812)+8728348608]}"|bc=~/\d+/g];$_="BV1$_[3]$_[1]4$_[0]1$_[2]7$_[4]$_[5]"


Try it online!

This uses bc to do the decimal to base-58 conversion.

## BV to AV

$n=$x=0;%_=map{$_=>$x++}split//,fZodR9XQDSUm21yCkr6zBqiveYah8bt4xsWpHnJE7jL5VG3guMTKNPAwcF;@y=@_{/1(.)(.)4(.)1(.)7(.)(.)/};$n=58*$n+$_ for@y[2,1,3,0,4,5];$_=av.((\$n-8728348608)^177451812)


Try it online!

If you have only 1 line of input, the first 8 bytes can be removed.

Update: Removed 4 + 2 = 6 bytes, because you don't need to quote the strings.

# Prolog, 725 bytes

:-use_module(library(clpz)).
:-use_module(library(lists)).
v(A,B):-var(A),var(B).
d(N,D):-v(N,D),!.
d(N,D):-nth0(N,"0123456789",D).
e(N,D):-v(N,D),!.
e(N,D):-nth0(N,"fZodR9XQDSUm21yCkr6zBqiveYah8bt4xsWpHnJE7jL5VG3guMTKNPAwcF",D).
b(0,_,[0]).
b(1,_,[1]).
b(A,B,C):-A#>1,E#>=0,L#=E+1,(nonvar(C),length(C,L);nonvar(A)),A#<B^(E+1),A#>=B^E,labeling([down],[E]),length(C,L),reverse(C,F),c(A,B,F).
c(0,_,[]).
c(A,B,[C|D]):-A#>0,C#>=0,C#=<B,E#>=0,C#=A mod B,E#=A//B,c(E,B,D).
a([a,v|A],['B','V','1',E,C,'4',B,'1',D,'7',F,G]):-H=[B,C,D,E,F,G],I=177451812,J=8728348608,K#>=0,L#>=0,K#=L xor I+J,L#=(K-J)xor I,O in 1..9,indomain(O),length(A,O),maplist(d,M,A),maplist(e,N,H),b(L,10,M),b(K,58,N),label([L,K]),maplist(d,M,A),maplist(e,N,H).


Clear version:

:- use_module(library(clpz)).
:- use_module(library(lists)).

v(A, B) :- var(A), var(B).

% Base 10.
d(N, D) :- v(N, D), !. % Keep variable.
d(N, D) :-
nth0(N, "0123456789", D).

% Base 58.
e(N, D) :- v(N, D), !. % Keep variable.
e(N, D) :-
nth0(N, "fZodR9XQDSUm21yCkr6zBqiveYah8bt4xsWpHnJE7jL5VG3guMTKNPAwcF", D).

% Base conversion. Base B, Number A, Array C.
b(0, _, [0]).
b(1, _, [1]).
b(A, B, C) :-
A #> 1,
nonvar(B),
E #>= 0,
L #= E + 1,
(   nonvar(C), length(C, L)
;   nonvar(A)
),
A #< B ^ (E + 1),
A #>= B ^ E,
labeling([down], [E]),
length(C, L),
reverse(C, F),
c(A, B, F).

% Required for b.
c(0, _, []).
c(A, B, [C|D]) :-
A #> 0,
C #>= 0,
C #=< B,
E #>= 0,
C #= A mod B,
E #= A // B,
c(E, B, D).

% Bidirectional decoder.
a([a, v|A], ['B', 'V', '1', E, C, '4', B, '1', D, '7', F, G]) :-
H = [B, C, D, E, F, G],
I = 177451812,
J = 8728348608,
K #>= 0,
L #>= 0,
K #= L xor I + J,
L #= (K - J) xor I,
O in 1..9,
indomain(O),
length(A, O),
maplist(d, M, A),
maplist(e, N, H),
b(L, 10, M),
b(K, 58, N),
label([L, K]),
maplist(d, M, A),
maplist(e, N, H).