Shorten a Number Code by HALF

How do you split a number code in half?

For anyone who’s not familiar with how binary code works, I recommend looking it up, should take a minute or two to learn. Otherwise, you might get lost while reading this.

So, a while ago, during a lecture in one of my courses, we were learning about BCD (Binary Coded Decimal). In BCD, you convert every digit of a decimal number into 4 bits binary…

Ex:

0 = 0000
1 = 0001
2 = 0010
3 = 0011
...
18 = 0001 1000
19 = 0001 1001
20 = 0010 0000
etc.

Well, I’ve been working on a project where the save code is… like… very long (depending on the actual data being saved). Trying to think up of solutions, I figured out a way to shorten a long code by HALF while also being able to convert it back.

However, this method only works with number codes, and is great when it comes to working with files.

First, let’s use this number as an example:

2477123892

We’re going to convert every digit into 4 bits (a “nibble”).
Now, the highest decimal value we can reach with a nibble is 15 (1111), so rather than converting every number from 0 to 9 into 4 bits, we can go from 0 to 15.
Note: In BCD, 4 bits are used to represent a number from 0 to 9, not 0 to 15, so the method I’m using here is different from BCD.

For this, though, we’ll go from 0 to 14 to keep 15 (“1111”) for another purpose.

Another issue is that if you replace, let’s say, every 12 with 1100, when you go to replace every 1 with 0001 or 0 with 0000, you might replace the new 0’s and 1’s from the binary code, and if you were to start with replacing the 0’s and 1’s first, it’ll affect the numbers between 10 and 14. So, to keep things less complicated, first convert every number from 14 to 0 with a letter, then that letter to a nibble.

14 = o = 1110
13 = n = 1101
12 = m = 1100
11 = l = 1011
10 = k = 1010
 9 = j = 1001
 8 = i = 1000
 7 = h = 0111
 6 = g = 0110
 5 = f = 0101
 4 = e = 0100
 3 = d = 0011
 2 = c = 0010
 1 = b = 0001
 0 = a = 0000

^ Note that you’ll need to start with 14, 13, 12, 11, & 10 before the other numbers

Now every single number is equal to 4 bits of 0’s and 1’s. Every 8 bits can be converted to a single ASCII value… so in other words, if the number we wanted to save was, let’s say, 51, we can convert it to BCD: 0101 0001, then combine these two nibbles into one byte… It’d be equal to a decimal value of 81, & using an ASCII table, we can see that’s equal to Q. We can reverse this process to turn Q back into 51.

As you can tell, “Q” is a shorter code than “51.”

Let’s go back to that number example I gave earlier and nearly forgot about:

2477123892

Convert every digit to a letter to avoid complicating things

cehhmdijc

Then every letter to 4 bits (I added spaces to help show this)

0010 0100 0111 0111 1100 0011 1000 1001 0010

And group every 2 groups of 4 bits together

0010 0100 
0111 0111 
1100 0011 
1000 1001 
0010

For the last group, you’ll need 4 more bits to complete it… This is where you add 1111 to complete the last byte:

0010 0100 
0111 0111 
1100 0011 
1000 1001 
0010 1111

^ Every line is one byte, I only added spaces to show how I grouped things

Lastly, by converting this binary code to ASCII, you’ll get this:

$wÉ/

^ Length went from 9 to 5, reduced by 44.44%

Code for everything I just explained so far
// converts string to 8bit binary code
window.stringToBinary=function(str) {
  let binary = "";
  for (let i = 0; i < str.length; i++) {
    const charCode = str.charCodeAt(i).toString(2);
    binary += "0".repeat(8 - charCode.length) + charCode;
  }
  return binary;
};

// converts 8bit binary code to string
window.binaryToString=function(binary) {
  let str = "";
  for (let i = 0; i < binary.length; i += 8) {
    const byte = binary.substr(i, 8);
    str += String.fromCharCode(parseInt(byte, 2));
  }
  return str;
};

// replaces all characters from arr1 with arr2 in string str
window.replaceA=function(str,arr1,arr2){
    var a=str.toString(); // convert number to a string
    arr1.forEach(function(value,index){
        a=a.replaceAll(value,arr2[index]);
    });
    return a;
};

// converts number to code
window.numToCode = function(data){
    var a=replaceA(
        replaceA(data,['14','13','12','11','10','9','8','7','6','5','4','3','2','1','0'],
                      [ 'o', 'n', 'm', 'l', 'k','j','i','h','g','f','e','d','c','b','a']),
        [  'a' ,  'b' ,  'c' ,  'd' ,  'e' ,  'f' ,  'g' ,  'h' ,  'i' ,  'j' ,  'k' ,  'l' ,  'm' ,  'n' ,  'o' ],
        ['0000','0001','0010','0011','0100','0101','0110','0111','1000','1001','1010','1011','1100','1101','1110']
    );
    return binaryToString(a+((a.length)%8>0?'1111':''));
};

// converts code back to number
window.codeToNum = function(code){
    var num=['0','1','2','3','4','5','6','7','8','9','10','11','12','13','14'];
    var bi=['0000','0001','0010','0011','0100','0101','0110','0111','1000','1001','1010','1011','1100','1101','1110'];
    r='';
    var n=stringToBinary(code);
    for (let i = 0; i < n.length; i += 4) {
        const substr = n.substr(i, 4);
        if(substr!=='1111'){
            r=r+num[bi.indexOf(substr)];
        }
    }
    return r;
};
function copyToClipboard(str) {
  navigator.clipboard.writeText(str)
    .then(() => {
      console.log(`Copied "${str}" to clipboard.`);
    })
    .catch((error) => {
      console.error(`Error copying "${str}" to clipboard: ${error}`);
    });
}
var c=prompt("Here's the number you just entered as a code",numToCode(prompt('enter number')));
window.location="#"+c;
copyToClipboard(window.location.toString());


Here’s another example, a longer number:

57889712000399347987857929304985920398902230894853417782390102498948936793929891247862783647283871982000109231223120091021893019829

In ASCII, you can expect some unrecognizable characters:

Wˆ—À9“G˜xW’“˜Y 9‰#”…4x#¢I‰H“g“’˜œG†'ƒdrƒ‡‚
’<#À	¢“˜

Honestly, I’d be lying if I was to say that I have done enough testing with this because I haven’t.
If the max byte is 11111111, which is… let’s see… (2^8)-1= 255… there are 255 different characters! Second thought, in our situation the highest byte would be 11101111, since we only add 1111 at the end to complete the bytes, which is 255-(2^4)=239 different characters!

Click here then look at this page’s url, you should see the ASCII code added after the #
Now click here and see the difference
Notice here some characters have been replaced with a percentage sign followed by a number.
This is known as percent-encoding. The number after the percentage sign is the hexadecimal value for that character.

If you plan to use the code in the link, then you should expect that to happen. Though if you plan to save the code into the local storage (which has a 5MB limit) or inside a file, then I’d recommend this method.

However, I should also note that the first 32 ASCII characters (0 to 31) are “control characters” that cannot be printed, and the 177th ASCII character can’t be printed either. We’ll have to find every one of these characters and replace them with something else.

You might be wondering (or maybe not), how in JavaScript are we supposed to have our code find characters that we can’t even type?

Well, you can add “\” with the octal value for any character to refer to it.
For example, if you were to run this code:

alert("\33")

You’d see, in very small font, the letters: “ESC” (which stands for “escape”)
… don’t ask why I used that as an example :sweat_smile:

You \30 type any character with this!
Btw, the 33 from the example above is the octal value for ESC.
Below is a table of all unprintable characters that we want to replace along with their octal value:

Octal Value ASCII Control Character
000 NUL (null)
001 SOH (start of heading)
002 STX (start of text)
003 ETX (end of text)
004 EOT (end of transmission)
005 ENQ (enquiry)
006 ACK (acknowledge)
007 BEL (bell)
010 BS (backspace)
011 HT (horizontal tab)
012 LF (line feed)
013 VT (vertical tab)
014 FF (form feed)
015 CR (carriage return)
016 SO (shift out)
017 SI (shift in)
020 DLE (data link escape)
021 DC1 (device control 1)
022 DC2 (device control 2)
023 DC3 (device control 3)
024 DC4 (device control 4)
025 NAK (negative acknowledge)
026 SYN (synchronous idle)
027 ETB (end of transmission block)
030 CAN (cancel)
031 EM (end of medium)
032 SUB (substitute)
033 ESC (escape)
034 FS (file separator)
035 GS (group separator)
036 RS (record separator)
037 US (unit separator)
177 DEL (delete)

Next step is finding a replacement for these characters… it’s possible that we’re using all 239 characters depending on how long the data code is. So for this case, I’ll pick characters from another language… here’s an array of the control characters and their replacements:

var cc=[
    {cc: "\0" , r:"١"} , {cc: "\1" , r:"٥"},
    {cc: "\2" , r:"٢"} , {cc: "\3" , r:"٦"},
    {cc: "\4" , r:"٣"} , {cc: "\5" , r:"٧"},
    {cc: "\6" , r:"٤"} , {cc: "\7" , r:"٨"},
    
    {cc:"\10" , r:"٩"} , {cc:"\11" , r:"ث"},
    {cc:"\12" , r:"ا"} , {cc:"\13" , r:"ج"},
    {cc:"\14" , r:"ب"} , {cc:"\15" , r:"ح"},
    {cc:"\16" , r:"ت"} , {cc:"\17" , r:"خ"},
    
    {cc:"\20" , r:"ض"} , {cc:"\21" , r:"ع"},
    {cc:"\22" , r:"ص"} , {cc:"\23" , r:"غ"},
    {cc:"\24" , r:"ق"} , {cc:"\25" , r:"ه"},
    {cc:"\26" , r:"ف"} , {cc:"\27" , r:"ن"},
    
    {cc:"\30" , r:"و"} , {cc:"\31" , r:"ز"},
    {cc:"\32" , r:"ى"} , {cc:"\33" , r:"ط"},
    {cc:"\34" , r:"ر"} , {cc:"\35" , r:"ك"},
    {cc:"\36" , r:"ء"} , {cc:"\37" , r:"ظ"},
    
    {cc:"\177", r:"م"}
];

When converting from code back to a number, you’ll just need to replace the Arabic characters back with the unprintable characters before converting to binary.

And I believe that’s… everything :exploding_head:
Now… for the final test…

Final Test
0123456789 = ب4VxŸ 

^ 10 to 5 characters, reduced by 50%

16151413121101010977712799022 = فهíËا©w|y"

^ 29 to 11 characters, reduced by 62.069%

83741934103894823819 = ƒtز4£‰H#Ÿ

^ 20 to 10 characters, reduced by 50%

10101010679247120375511129715982 = ªªg’GÀ7U¼—ه˜/

^ 32 to 13 characters, reduced by 59.375%

1145789266479065547822677744637899923411 = ءW‰&dy٤UG‚&wtF7‰™#K

^ 40 to 19 characters, reduced by 52.5%

012 = ب

^ 3 to 1 characters, reduced by 66.66%


Now… after short a headache, I hereby present to you the code for this:

stop();
// converts string to 8bit binary code
window.stringToBinary=function(str) {
  let binary = "";
  for (let i = 0; i < str.length; i++) {
    const charCode = str.charCodeAt(i).toString(2);
    binary += "0".repeat(8 - charCode.length) + charCode;
  }
  return binary;
};

// converts 8bit binary code to string
window.binaryToString=function(binary) {
  let str = "";
  for (let i = 0; i < binary.length; i += 8) {
    const byte = binary.substr(i, 8);
    str += String.fromCharCode(parseInt(byte, 2));
  }
  return str;
};

// replaces all characters from arr1 with arr2 in string str
window.replaceA=function(str,arr1,arr2){
    var a=str.toString(); // convert number to a string
    arr1.forEach(function(value,index){
        a=a.replaceAll(value,arr2[index]);
    });
    return a;
};

// Dealing with nonprintable characters
var cc=[
    {cc: "\0" , r:"١"} , {cc: "\1" , r:"٥"},
    {cc: "\2" , r:"٢"} , {cc: "\3" , r:"٦"},
    {cc: "\4" , r:"٣"} , {cc: "\5" , r:"٧"},
    {cc: "\6" , r:"٤"} , {cc: "\7" , r:"٨"},
    
    {cc:"\10" , r:"٩"} , {cc:"\11" , r:"ث"},
    {cc:"\12" , r:"ا"} , {cc:"\13" , r:"ج"},
    {cc:"\14" , r:"ب"} , {cc:"\15" , r:"ح"},
    {cc:"\16" , r:"ت"} , {cc:"\17" , r:"خ"},
    
    {cc:"\20" , r:"ض"} , {cc:"\21" , r:"ع"},
    {cc:"\22" , r:"ص"} , {cc:"\23" , r:"غ"},
    {cc:"\24" , r:"ق"} , {cc:"\25" , r:"ه"},
    {cc:"\26" , r:"ف"} , {cc:"\27" , r:"ن"},
    
    {cc:"\30" , r:"و"} , {cc:"\31" , r:"ز"},
    {cc:"\32" , r:"ى"} , {cc:"\33" , r:"ط"},
    {cc:"\34" , r:"ر"} , {cc:"\35" , r:"ك"},
    {cc:"\36" , r:"ء"} , {cc:"\37" , r:"ظ"},
    
    {cc:"\177", r:"م"}
];

// converts number to code
window.numToCode = function(data){
    var a=replaceA(
        replaceA(data,['14','13','12','11','10','9','8','7','6','5','4','3','2','1','0'],
                      [ 'o', 'n', 'm', 'l', 'k','j','i','h','g','f','e','d','c','b','a']),
        [  'a' ,  'b' ,  'c' ,  'd' ,  'e' ,  'f' ,  'g' ,  'h' ,  'i' ,  'j' ,  'k' ,  'l' ,  'm' ,  'n' ,  'o' ],
        ['0000','0001','0010','0011','0100','0101','0110','0111','1000','1001','1010','1011','1100','1101','1110']
    );
    // BCD to ASCII String
    var bs=binaryToString(a+((a.length)%8>0?'1111':''));
    // Replacing non-printable characters
    cc.forEach(function(v){
        bs=bs.replaceAll(v.cc,v.r);
    });
    return bs;
};

// converts code back to number
window.codeToNum = function(code){
    var num=['0','1','2','3','4','5','6','7','8','9','10','11','12','13','14'];
    var bi=['0000','0001','0010','0011','0100','0101','0110','0111','1000','1001','1010','1011','1100','1101','1110'];
    r='';
    var c=code;
    cc.forEach(function(v){
        c=c.replaceAll(v.r,v.cc);
    });
    var n=stringToBinary(c);
    for (let i = 0; i < n.length; i += 4) {
        const substr = n.substr(i, 4);
        if(substr!=='1111'){
            r=r+num[bi.indexOf(substr)];
        }
    }
    return r;
};

function copyToClipboard(str) {
  navigator.clipboard.writeText(str)
    .then(() => {
      console.log(`Copied "${str}" to clipboard.`);
    })
    .catch((error) => {
      console.error(`Error copying "${str}" to clipboard: ${error}`);
    });
}

var test=prompt("Here's the number you just entered as a code",numToCode(prompt('enter number')));
window.location="#"+test;
copyToClipboard(window.location.toString());
alert(codeToNum(prompt("Write code to convert it to number",test)));

^ 105 lines total, first 100 are all the functions you need, last 5 are for testing purposes only.
^^ I feel like there’s a way to shorten my code as well, but I’m too lazy to try that :yawning_face:

numToCode(number) converts number to code
codeToNum(code) converts the code back to a number
stringToBinary(str) converts a string to binary code
binaryToString(binary) converts binary code to a string
replaceA(str, arr1, arr2) replaces elements from arr1 with those from arr2 inside string
copyToClipboard(str) copies text to the clipboard, works with unidentifiable characters
Last five lines are only for testing purposes, you won’t need em’


I know that this works… but it feels incomplete… or maybe I did something wrong…? When working with binary and such I usually make mistakes. Was there an easier way of doing this…? In other words, I’m not really sure if this is complete, in that case, any thoughts or feedback are welcomed and I’ll turn this into a wiki to allow others to make edits.

I’ll have to go now and touch grass.
I don’t expect a lot of people to need this, only time you’d need it is when you have a save code that’s a number. Let me know your thoughts on this idea though :+1:

3 Likes

First, It is good that you are taking these computer courses. Binary numbers / ascii codes, and that stuff is fun for only a few of us. This is data compression. I think that our current data compression software (like 7zip, winzip, etc…) use similar principles. [1] Data -> [2] Software to encode/compress data, [3] encoded data, [4] Software to decode/uncompressed the data. Another way is to define data patterns as ascii symbols. Example, you can have a list of predefined most common large patters… lets say that these 4 bytes pattern 0xFFEACB0A is well known as a repeated pattern… so we could have an special ASCII symbol for that one, and so on.

This is a good territory to explore… specially in data transfer, because, now days, we transfer huge chunks of data… for streaming videos, to play online games, etc…

1 Like

That’s very cool.

I felt inspired and thought of another integer encoding algorithm. It encodes an integer of any size to base 256. 256 was chosen because that’s the maximum number that can be stored by one byte (plus one). Here it is:

let num = BigInt(INPUT_STRING);
let out = [];

while (num > 0) {
	out.push(Number(num % 256n));
	num = num / 256n;
}

// you can return a string
return String.fromCharCode(...out);

// or return a Uint8Array
return new Uint8Array(out);
2 Likes

That’s the same method I used for Pixel Lide’s share link code

     2015 characters (short url):
https://pixel.lide.repl.co/?3//243,77=243,89=243)02=243)15=243)28=243)41=256)02=268)02=268,89=268,77=268)15=268)28=268)41=307)41=307)28=307)15=307)02=307,89=307,77=32$77=332,77=32$102=32$141=332)41=358,77=358,89=358)02=358)15=358)28=358)41=37%141=383)41=409)41=409)28=409)15=409)02=409,89=409,77=422)41=435)41=46$141=46$128=46$115=46$102=46$89=46$77=473,77=486,77=486,89=486)02=486)15=486)28=486)41=473)41=383,217_2,^383,230_2,^383,243_2,^396,256_2,^409,256_2,^422,256_2,^435,256_2,^447,243_2,^447,230_2,^447,217_2,^396)79_2,^396)66_2,^435)79_2,^435)66_2,^396,217_2,^409,217_2,^422,217_2,^435,217_2,^28%192_3,^28%179_3,^28%166_3,^28%153_3,^28%141_3,^294)28_3,^307)28_3,^32$141_3,^32$153_3,^32$166_3,^32$179_3,^32$192_3,^307,205_3,^294,205_3,^358,205_3,^383)41_3,^396)28_3,^409)28_3,^409)41_3,^396)41_3,^409)53_3,^409)66_3,^409)79_3,^409)92_3,^409,205_3,^396,205_3,^383,205_3,^422,205_3,^435,205_3,^28%128_3,^32$128_3,^332)28_3,^345)28_3,^358)28_3,^37%128_3,^383)15_3,^383)28_3,^37%115_3,^358)15_3,^345)15_3,^332)15_3,^32$115_3,^307)15_3,^294)15_3,^28%115_3,^268)15_3,^256)15_3,^256)28_3,^256)41_3,^256)53_3,^243)53_3,^23$153_3,^23$166_3,^23$179_3,^243)79_3,^256)79_3,^268)79_3,^307,217_3,^307,230_3,^307,243_3,^307,256_3,^307,268_3,^307,281_3,^307,294_3,^307,307_3,^32$307_3,^332,307_3,^345,307_3,^358,307_3,^435,217_3,^422,230_3,^409,230_3,^396,230_3,^383,230_3,^383,243_3,^383,256_3,^383,268_3,^383,281_3,^383,294_3,^383,307_3,^37%307_3,^294,307_3,^28%307_3,^268,307_3,^256,307_3,^243,307_3,^23$307_3,^217,307_3,^205,307_3,^396,307_3,^409,307_3,^422,307_3,^435,307_3,^447,307_3,^46$307_3,^473,307_3,^486,307_3,^498,307_3,^51%307_3,^524,307_3,^537,307_3,^55$307_3,^562,307_3,^575,307_3,^588,307_3,^60%307_3,^614,307_3,^626,307_3,^639,320_3,^652,320_3,^665,320_3,^677,320_3,^69$320_3,^703,307_3,^69$307_3,^677,307_3,^665,307_3,^652,307_3,^639,307_3,^716,307_3,^729,307_3,^486)15_3,^486)02_3,^498)02_3,^51%102_3,^51%115_3,^51%128_3,^51%141_3,^498)41_3,^498)53_3,^498)79_3,^//{red:255,green:255,blue:255,alpha:1}V2url

     3706 characters (long url):
https://pixel.lide.repl.co/?3//243,77,0,0,0,1,1;243,89,0,0,0,1,1;243,102,0,0,0,1,1;243,115,0,0,0,1,1;243,128,0,0,0,1,1;243,141,0,0,0,1,1;256,102,0,0,0,1,1;268,102,0,0,0,1,1;268,89,0,0,0,1,1;268,77,0,0,0,1,1;268,115,0,0,0,1,1;268,128,0,0,0,1,1;268,141,0,0,0,1,1;307,141,0,0,0,1,1;307,128,0,0,0,1,1;307,115,0,0,0,1,1;307,102,0,0,0,1,1;307,89,0,0,0,1,1;307,77,0,0,0,1,1;320,77,0,0,0,1,1;332,77,0,0,0,1,1;320,102,0,0,0,1,1;320,141,0,0,0,1,1;332,141,0,0,0,1,1;358,77,0,0,0,1,1;358,89,0,0,0,1,1;358,102,0,0,0,1,1;358,115,0,0,0,1,1;358,128,0,0,0,1,1;358,141,0,0,0,1,1;371,141,0,0,0,1,1;383,141,0,0,0,1,1;409,141,0,0,0,1,1;409,128,0,0,0,1,1;409,115,0,0,0,1,1;409,102,0,0,0,1,1;409,89,0,0,0,1,1;409,77,0,0,0,1,1;422,141,0,0,0,1,1;435,141,0,0,0,1,1;460,141,0,0,0,1,1;460,128,0,0,0,1,1;460,115,0,0,0,1,1;460,102,0,0,0,1,1;460,89,0,0,0,1,1;460,77,0,0,0,1,1;473,77,0,0,0,1,1;486,77,0,0,0,1,1;486,89,0,0,0,1,1;486,102,0,0,0,1,1;486,115,0,0,0,1,1;486,128,0,0,0,1,1;486,141,0,0,0,1,1;473,141,0,0,0,1,1;383,217,0,0,0,2,1;383,230,0,0,0,2,1;383,243,0,0,0,2,1;396,256,0,0,0,2,1;409,256,0,0,0,2,1;422,256,0,0,0,2,1;435,256,0,0,0,2,1;447,243,0,0,0,2,1;447,230,0,0,0,2,1;447,217,0,0,0,2,1;396,179,0,0,0,2,1;396,166,0,0,0,2,1;435,179,0,0,0,2,1;435,166,0,0,0,2,1;396,217,0,0,0,2,1;409,217,0,0,0,2,1;422,217,0,0,0,2,1;435,217,0,0,0,2,1;281,192,0,0,0,3,1;281,179,0,0,0,3,1;281,166,0,0,0,3,1;281,153,0,0,0,3,1;281,141,0,0,0,3,1;294,128,0,0,0,3,1;307,128,0,0,0,3,1;320,141,0,0,0,3,1;320,153,0,0,0,3,1;320,166,0,0,0,3,1;320,179,0,0,0,3,1;320,192,0,0,0,3,1;307,205,0,0,0,3,1;294,205,0,0,0,3,1;358,205,0,0,0,3,1;383,141,0,0,0,3,1;396,128,0,0,0,3,1;409,128,0,0,0,3,1;409,141,0,0,0,3,1;396,141,0,0,0,3,1;409,153,0,0,0,3,1;409,166,0,0,0,3,1;409,179,0,0,0,3,1;409,192,0,0,0,3,1;409,205,0,0,0,3,1;396,205,0,0,0,3,1;383,205,0,0,0,3,1;422,205,0,0,0,3,1;435,205,0,0,0,3,1;281,128,0,0,0,3,1;320,128,0,0,0,3,1;332,128,0,0,0,3,1;345,128,0,0,0,3,1;358,128,0,0,0,3,1;371,128,0,0,0,3,1;383,115,0,0,0,3,1;383,128,0,0,0,3,1;371,115,0,0,0,3,1;358,115,0,0,0,3,1;345,115,0,0,0,3,1;332,115,0,0,0,3,1;320,115,0,0,0,3,1;307,115,0,0,0,3,1;294,115,0,0,0,3,1;281,115,0,0,0,3,1;268,115,0,0,0,3,1;256,115,0,0,0,3,1;256,128,0,0,0,3,1;256,141,0,0,0,3,1;256,153,0,0,0,3,1;243,153,0,0,0,3,1;230,153,0,0,0,3,1;230,166,0,0,0,3,1;230,179,0,0,0,3,1;243,179,0,0,0,3,1;256,179,0,0,0,3,1;268,179,0,0,0,3,1;307,217,0,0,0,3,1;307,230,0,0,0,3,1;307,243,0,0,0,3,1;307,256,0,0,0,3,1;307,268,0,0,0,3,1;307,281,0,0,0,3,1;307,294,0,0,0,3,1;307,307,0,0,0,3,1;320,307,0,0,0,3,1;332,307,0,0,0,3,1;345,307,0,0,0,3,1;358,307,0,0,0,3,1;435,217,0,0,0,3,1;422,230,0,0,0,3,1;409,230,0,0,0,3,1;396,230,0,0,0,3,1;383,230,0,0,0,3,1;383,243,0,0,0,3,1;383,256,0,0,0,3,1;383,268,0,0,0,3,1;383,281,0,0,0,3,1;383,294,0,0,0,3,1;383,307,0,0,0,3,1;371,307,0,0,0,3,1;294,307,0,0,0,3,1;281,307,0,0,0,3,1;268,307,0,0,0,3,1;256,307,0,0,0,3,1;243,307,0,0,0,3,1;230,307,0,0,0,3,1;217,307,0,0,0,3,1;205,307,0,0,0,3,1;396,307,0,0,0,3,1;409,307,0,0,0,3,1;422,307,0,0,0,3,1;435,307,0,0,0,3,1;447,307,0,0,0,3,1;460,307,0,0,0,3,1;473,307,0,0,0,3,1;486,307,0,0,0,3,1;498,307,0,0,0,3,1;511,307,0,0,0,3,1;524,307,0,0,0,3,1;537,307,0,0,0,3,1;550,307,0,0,0,3,1;562,307,0,0,0,3,1;575,307,0,0,0,3,1;588,307,0,0,0,3,1;601,307,0,0,0,3,1;614,307,0,0,0,3,1;626,307,0,0,0,3,1;639,320,0,0,0,3,1;652,320,0,0,0,3,1;665,320,0,0,0,3,1;677,320,0,0,0,3,1;690,320,0,0,0,3,1;703,307,0,0,0,3,1;690,307,0,0,0,3,1;677,307,0,0,0,3,1;665,307,0,0,0,3,1;652,307,0,0,0,3,1;639,307,0,0,0,3,1;716,307,0,0,0,3,1;729,307,0,0,0,3,1;486,115,0,0,0,3,1;486,102,0,0,0,3,1;498,102,0,0,0,3,1;511,102,0,0,0,3,1;511,115,0,0,0,3,1;511,128,0,0,0,3,1;511,141,0,0,0,3,1;498,141,0,0,0,3,1;498,153,0,0,0,3,1;498,179,0,0,0,3,1;//{red:255,green:255,blue:255,alpha:1}

It’s a bit of a messy process, but definitely effective!
Come to think of it, I just realized that I should be able to define these patterns and give them an ASCII replacement inside of the actual code string… This sounds like a great idea, I’ll give it a try!

I just realized what my brain tried to tell me at that moment.

Rather than turning every single number into 4 bits, I could’ve just turned the entire number into one large binary code :man_facepalming:
For example, rather than turning 51 to 0101 0001, we could turn it into 110011 (6 bits).

For a larger number like:

763141863787231948319378762434619837284619874635198379

We can turn it into:

// 179 digits
11111110111101100101101101000111001001011110101001100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

Rather than this:

// 212 digits
01110110001111100001100001100011011110000111001000110001100101001000001100011001001101111000011101100010010000110100011000011001100000110111001010000100011000011001100001110100011000110101000110011000001101111001

What I like about the first code is how it has 128 zeros at the end, because as Jovanny mentioned, repeated patterns can be replaced with ASCII symbols. After making adjustments to my code, I’ve been able to reduce the 54 digits number example above (76314…379) to 9 characters.

٨÷²Ú9/S٠٠

Impressive, right?


How to use successive division to turn a number into binary

The process is quite simple. Let’s take a number like 1781 for example.
Divide 1781 by 2, we get 890 with a remainder of 1. Keep track of the remainder and repeat the process until you reach zero with a remainder of 1. Then take a look at all the remainders, they should be in order from the least significant to the most significant bit. It’s hard to explain in words, so here’s an image of me turning 1781 into binary with this method:

If you think about this deeply enough, then it’ll make sense.


Code for doing this in JS:

window.sd=function(num){
    var bin="";
    var n=Number(num);
    while(n>0){
        bin=(n%2)+bin;
        n=Math.floor(n/2);
    }
    return bin;
};

New Code (56 lines)
stop();
window.bta=function(binary) {
  let str = "";
  for (let i = 0; i < binary.length; i += 8) {
    const byte = binary.substr(i, 8);
    str += String.fromCharCode(parseInt(byte, 2));
  }
  return str;
};
window.atb=function(str) {
  let binary = "";
  for (let i = 0; i < str.length; i++) {
    const charCode = str.charCodeAt(i).toString(2);
    binary += "0".repeat(8 - charCode.length) + charCode;
  }
  return binary;
};
window.ntb=function(num){
    var bin="";
    var n=Number(num);
    while(n>0){
        bin=(n%2)+bin;
        n=Math.floor(n/2);
    }
    var i=8-(bin.length%8);
    bin="0".repeat((i===8)?0:i)+bin;
    return bin;
};
window.btn=function(bin){
    var n=0;
    for(var i=0;i<bin.length;i++){
        n+=bin[i]*(Math.pow(2,((bin.length-1)-i)));
    }
    return n;
};
var cc=[{cc:"\0\0\0\0\0\0\0\0",r:"٠"},{cc:"\0\0\0\0",r:"،"},{cc:"\0\0",r:"؟"},{cc:"\0",r:"١"},{cc:"\1",r:"٥"},{cc:"\2",r:"٢"},{cc:"\3",r:"٦"},{cc:"\4",r:"٣"},{cc:"\5",r:"٧"},{cc:"\6",r:"٤"},{cc:"\7",r:"٨"},{cc:"\10",r:"٩"},{cc:"\11",r:"ث"},{cc:"\12",r:"ا"},{cc:"\13",r:"ج"},{cc:"\14",r:"ب"},{cc:"\15",r:"ح"},{cc:"\16",r:"ت"},{cc:"\17",r:"خ"},{cc:"\20",r:"ض"},{cc:"\21",r:"ع"},{cc:"\22",r:"ص"},{cc:"\23",r:"غ"},{cc:"\24",r:"ق"},{cc:"\25",r:"ه"},{cc:"\26",r:"ف"},{cc:"\27",r:"ن"},{cc:"\30",r:"و"},{cc:"\31",r:"ز"},{cc:"\32",r:"ى"},{cc:"\33",r:"ط"},{cc:"\34",r:"ر"},{cc:"\35",r:"ك"},{cc:"\36",r:"ء"},{cc:"\37",r:"ظ"},{cc:"\177", r:"م"}];
window.fixASCII=function(ASCII,fix){
    var r=ASCII;
    cc.forEach(function(v){
        r=r.replaceAll(fix?v.cc:v.r,fix?v.r:v.cc);
    });
    prompt("The ASCII below has been "+(fix?"":"un")+"fixed.\n\n"+ASCII,r);
    return r;
};
window.ntc = function(num){
    var binary = ntb(num);
    var ascii = bta(binary);
    var fixed = fixASCII(ascii,true);
    return fixed;
};
window.ctn=function(code){
    var ascii = fixASCII(code,false);
    var binary = atb(ascii);
    var number = btn(binary);
    return number;
};
New Code with alerts & comments to test & guide you through the process (80 lines)
stop();

// binary to ASCII
window.bta=function(binary) {
  let str = "";
  for (let i = 0; i < binary.length; i += 8) {
    const byte = binary.substr(i, 8);
    str += String.fromCharCode(parseInt(byte, 2));
  }
  prompt("The binary code below has been successfully converted to ASCII\n\n"+binary,str);
  
  return str;
};

// ASCII to binary
window.atb=function(str) {
  let binary = "";
  for (let i = 0; i < str.length; i++) {
    const charCode = str.charCodeAt(i).toString(2);
    binary += "0".repeat(8 - charCode.length) + charCode;
  }
  prompt("The code below has been turned into binary.\n\n"+str,binary);
  return binary;
};

// number to binary
window.ntb=function(num){
    var bin="";
    var n=Number(num);
    while(n>0){
        bin=(n%2)+bin;
        n=Math.floor(n/2);
    }
    var i=8-(bin.length%8);
    bin="0".repeat((i===8)?0:i)+bin;
    
    prompt(num+"\n"+bin+"\n\n"+"#".repeat(Math.ceil(bin.length/8))+"\n"+num.length+" => "+(Math.ceil(bin.length/8))+
    "\nReduced by: "+(100*((num.length-(Math.ceil(bin.length/8)))/num.length)).toFixed(2)+"%"+"\n\n"+bin.length+
    " vs "+(num.length*4),bin);
    
    return bin;
};

// binary to number
window.btn=function(bin){
    var n=0;
    for(var i=0;i<bin.length;i++){
        n+=bin[i]*(Math.pow(2,((bin.length-1)-i)));
    }
    return n;
};

// Dealing with nonprintable characters
var cc=[{cc:"\0\0\0\0\0\0\0\0",r:"٠"},{cc:"\0\0\0\0",r:"،"},{cc:"\0\0",r:"؟"},{cc:"\0",r:"١"},{cc:"\1",r:"٥"},{cc:"\2",r:"٢"},{cc:"\3",r:"٦"},{cc:"\4",r:"٣"},{cc:"\5",r:"٧"},{cc:"\6",r:"٤"},{cc:"\7",r:"٨"},{cc:"\10",r:"٩"},{cc:"\11",r:"ث"},{cc:"\12",r:"ا"},{cc:"\13",r:"ج"},{cc:"\14",r:"ب"},{cc:"\15",r:"ح"},{cc:"\16",r:"ت"},{cc:"\17",r:"خ"},{cc:"\20",r:"ض"},{cc:"\21",r:"ع"},{cc:"\22",r:"ص"},{cc:"\23",r:"غ"},{cc:"\24",r:"ق"},{cc:"\25",r:"ه"},{cc:"\26",r:"ف"},{cc:"\27",r:"ن"},{cc:"\30",r:"و"},{cc:"\31",r:"ز"},{cc:"\32",r:"ى"},{cc:"\33",r:"ط"},{cc:"\34",r:"ر"},{cc:"\35",r:"ك"},{cc:"\36",r:"ء"},{cc:"\37",r:"ظ"},{cc:"\177", r:"م"}];
window.fixASCII=function(ASCII,fix){
    var r=ASCII;
    cc.forEach(function(v){
        r=r.replaceAll(fix?v.cc:v.r,fix?v.r:v.cc);
    });
    prompt("The ASCII below has been "+(fix?"":"un")+"fixed.\n\n"+ASCII,r);
    return r;
};

// Number to Code
window.ntc = function(num){
    var binary = ntb(num);
    var ascii = bta(binary);
    var fixed = fixASCII(ascii,true);
    return fixed;
};

// Code to Number
window.ctn=function(code){
    var ascii = fixASCII(code,false);
    var binary = atb(ascii);
    var number = btn(binary);
    return number;
};

prompt("The code you've inputted has been successfully converted into a number: ",ctn(prompt("Here's the code.\n\nClick \"OK\" to turn back into a number.",ntc(prompt("Enter number below","2000")))));

It seems like this method here is very effective on larger numbers.
Note: Since the code has to always start with a 1, there are 127 ASCII characters that the code would never start with (31 in which are unprintable). You can use one of these characters in the beginning of the code to define how many repeated zeroes are there at the end, resulting in an even smaller code than this for larger numbers.

Check out these Examples

99 digits to 17 characters
575758605277426294238817682495882536425754741288854027819859279119868537619212235539005949035547763498621101082830317183875025812310773802161775805311874378697767867722781485114986890

ŠÀÒ_xمx٠٠٠٠٠٠٠٠،١

50 digits to 9 characters
48122778735713291502003605236658373445463768584896

íL٨¶A4٠،؟

9 digits to 4 characters
926385401

77„ù

150 digits to 17 characters
495674392157643783292756428995362228221264978247936352846555741146584238015323940132475314008523773328186482907307178979064022913158275779760178384156398305440684359936839112785

٨ÔزءE¬È€٠٠٠٠٠٠٠٠؟

Of course, there seems to be a limit to the size of numbers that wick can deal with. When I tried a 200 digit long number, I got an error. At 300 digits, Wick crashed. However, a 150 digit number was easily turned to 17 characters (reduced by 88.7%) which is better than the first method I’ve used.

1 Like