Math behind obtaining a charaters Ascii code - javascript

So, basically I am doing some JS exercises and I understand this code, just cant figure out whats the math behind obtaining characters Ascii code in String.fromCharCode function?
why do we decrement by 18?
Have the function LetterChanges(str) take the str parameter being passed and modify it using the following algorithm. Replace every letter in the string with the letter following it in the alphabet (ie. c becomes d, z becomes a). Then capitalize every vowel in this new string (a, e, i, o, u) and finally return this modified string.
function LetterChanges(str) {
str = str.trim().toLowerCase();
var len = str.length;
var newStr = '';
for (var i = 0; i < len; i++) {
if (/[a-ce-gi-mo-su-y]/.test(str[i])) {
newStr += String.fromCharCode(((str[i].charCodeAt(0) - 18) % 26) +
97)
}
else if (/[zdhnt]/.test(str[i])) {
newStr += String.fromCharCode(((str[i].charCodeAt(0) - 18) % 26) +
65);
}
else {
newStr += str[i];
}
}
return newStr;
}
LetterChanges(readline());

Fact: charCode for 'a' is 97, and (97 - 18) % 26 == 1
Take 'b' for example, (98 - 18) % 26 == 2, so after change, its charCode equals 97 + 2 == 99, becomes 'c'. This rule applies to all 'a-z'.
Now you should see the pattern.
Quiz: what if you want to change uppercase letters, what would you pick as the "magic number" for this case?

About ASCII
Well, as you know, computers don't store characters as 'a', 'b', 'z' or '.', they only accept binary bits (0 or 1). These binary bits can be used to form integers and numbers, but not strings and characters.
So how does the computer store characters? That's where ASCII (American Standard Code for Information Interchange) comes in.
So the idea is that each character needs to be stored as a number, and ASCII code is then the standardization for what number corresponds to what character and vice versa.
So to answer your first question: String.fromCharCode(x) is just returning you the character in the standardized ASCII table corresponding to the number x. For eg, String.fromCharCode(65) will return you 'A' because the ASCII code for 'A' is standardized by the standard as 65.
Why -18
It's a clever (but confusing) hack to force all the lowercase letters to the character following it.
Because str has been made to be lowercase by the line str = str.trim().toLowerCase();, str[i] will now only contain values from 97 ('a') to 97+25 = 122 ('z').
Now 18 = 122 (mod 26) or 122%26.
This is necessary to accommodate for the fact that 'z' needs to go to 'a', and since we only have 26 characters from 'a' to 'z' that we need to "wrap around" (modulus/%)
Note that:
(97('a')-18)%26 = 1
(98('b')-18)%26 = 2
...
(122('z')-18)%26 = 0
So when you add 97 ('a') to each of the numbers you get a function mapping of 'a'->'b', 'b'->'c', ... , 'z'->'a'

Related

Seeking an explanation of Caesar Cipher code [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
After trying (and failing) to code a Caesar Cipher assignment for the Odin Project exercise, I finally caved in and looked up the answer. However, I do not quite understand it.
I am seeking an explanation of each line does and why it works. The code I copied has some brief descriptions of what each line does, but I still don't understand how it all works.
const caesar = function (str, amount) {
// Wrap the amount
if (amount < 0) {
return caesar(str, amount + 26);
}
// Make an output variable
var output = "";
// Go through each character
for (var i = 0; i < str.length; i++) {
// Get the character we'll be appending
var c = str[i];
// If it's a letter...
if (c.match(/[a-z]/i)) {
// Get its code
var code = str.charCodeAt(i);
// Uppercase letters
if (code >= 65 && code <= 90) {
c = String.fromCharCode(((code - 65 + amount) % 26) + 65);
}
// Lowercase letters
else if (code >= 97 && code <= 122) {
c = String.fromCharCode(((code - 97 + amount) % 26) + 97);
}
}
// Append
output += c;
}
// All done!
return output;
};
The first if-statement:
if (amount < 0) {
return caesar(str, amount + 26)
}
Makes sure that the shifting amount is 0 and above by calling itself until it is. Then the following line loops through all the characters in the entire string.
for (var i = 0; i < str.length; i++) {
For every character it checks if its a letter using something called a regex (Google for more information)
if (c.match(/[a-z]/i)) {
The line
var code = str.charCodeAt(i);
Gets the number representing the character at position "i" in the string. The number is the way the computer represents letters and other characters. Upper- and lowercase characters have two completely different numbers associated with them. That is what the two following if-statements is for. I will explain the one for lowercase letters, you should be able to see how the uppercase one works as well.
c = String.fromCharCode(((code - 65 + amount) % 26) + 65);
It starts by subtracting 65 from the number. This is because the first lowercase letter "a" has a value of 65. After that it shifts the result by "amount". The %-sign might seem weird. But all it does is divide the two sides and returning the "rest", the residual number. For example if we write:
5 % 2
It is equal to 1. This is done to "loop" the number and keep it between 0 and 26. After that it adds back the 65 and turns the number back to a character. The last line:
output += c;
Adds the character to the resulting string. Hope this helped you out!

how to find lexicographic string based on specified ranking

Consider all the strings of length 6 composed of capital Latin letters (A - Z), sorted in lexicographic order. The string AAAAAA is the first. The string AAAAAZ is the 26th . The 27th is AAAABA. The hint to this problem is the Nth string where N is the number of primes less than 2^ 30 − M . M is a permutation of 123456789, and we won’t tell you which one it is, but we will give you the following constraints to reduce the space of possibilities:
M is divisible by 567.
M starts with 2, ends with 4, and the middle digit is 8.
I managed to find M and the Nth, but I am not able to find a solution on how to find the right string based on the ranking. Please note that I found 9 possibilities on the ranking (Nth) which are:
43973488
43929860
41992802
41914646
41831591
41232030
41066565
40861259
40167328
Thank you for your help.
you need to convert the numbers to base 26 , and match each digit to the related chars
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
function convertToBase26Strings(number) {
number = number - 1 ;
var res = ""
while(number >= chars.length ) {
var i = number % chars.length ;
number = Math.floor(number / chars.length)
res += chars[i];
}
res += chars[number];
return res.split("").reverse().join("").padStart(6 , "A");
}
console.log(convertToBase26Strings(1));
console.log(convertToBase26Strings(26));
console.log(convertToBase26Strings(27));

Modulous for a non-0 based range of numbers

I'm trying to manipulate the ASCII code for a given string of letters (all caps). In the example below, I increment the ASCII code by one and output it to #newText.
<h2 id="text">ABCXYZ</h2>
<h2 id="newText">placeholder</h2>
var text = document.getElementById("text").firstChild.nodeValue;
var newText = "";
for (i=0; i < text.length; i++){
newText = newText + String.fromCharCode(text.charCodeAt(i)+1);
}
document.getElementById("newText").firstChild.nodeValue = newText;
JSFiddle: http://jsfiddle.net/KatieK/gg9Hx/19/
The problem is that Z is changed into a [. (This makes sense, since the ASCII range for A-Z is 65-90, and [ has an ASCII value of 91.) Instead, I want Z to transform into A.
I've tried modulus to get the ASCII values to wrap around, but I'm having difficulty with the non-0 range of ASCII values.
If x = 91;, then console.log(((x-65)%26)+65); results in 65, which is fine. But if x = 64; then the same formula results in 64, when I want it to to be 90.
How can I account for the non-zero range of the capital letters' ASCII values when incrementing and modulating them? In this example, I'd like to transform 91 into 65, 92 into 66, 93 into 67, 64 into 90, 63 into 89, and 62 into 88 as examples.
You could just pad x with another 26 to handle values within 26 below 65.
console.log(((x+26-65)%26)+65);
If you need more padding, just add some other multiple of 26.
Well, A-Z is 26 characters so do your arithmetic modulo 26. Then encode the result (0...25) as the desired character range, which in this case is as simple as adding 65.
Similarly to decode, first convert from the character range to numbers and then apply arithmetic.
You'll need a condition to see if the number is lower than 65 or higher than 90, then just do the opposite of what you're currently doing for numbers over 90
var x = text.charCodeAt(i)+1;
x = x > 90 ? ((x-65)%26)+65 : x < 65 ? (((x+65)%26)+65) : x;
newText = newText + String.fromCharCode(x);
FIDDLE
To make it work with any number, use
x = x > 90 ? x-(Math.ceil((x-90)/26)*26) : x < 65 ? x+(Math.ceil((65-x)/26)*26) : x;
You have the first part of the solution, console.log(((x-65)%26)+65);. As you noticed, it works in some cases but not others. So use it in the cases where it works and use something else in the other cases. Just write code that does what you want.

Decode base64 data as array in Python

I'm using this handy Javascript function to decode a base64 string and get an array in return.
This is the string:
base64_decode_array('6gAAAOsAAADsAAAACAEAAAkBAAAKAQAAJgEAACcBAAAoAQAA')
This is what's returned:
234,0,0,0,235,0,0,0,236,0,0,0,8,1,0,0,9,1,0,0,10,1,0,0,38,1,0,0,39,1,0,0,40,1,0,0
The problem is I don't really understand the javascript function:
var base64chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'.split("");
var base64inv = {};
for (var i = 0; i < base64chars.length; i++) {
base64inv[base64chars[i]] = i;
}
function base64_decode_array (s)
{
// remove/ignore any characters not in the base64 characters list
// or the pad character -- particularly newlines
s = s.replace(new RegExp('[^'+base64chars.join("")+'=]', 'g'), "");
// replace any incoming padding with a zero pad (the 'A' character is zero)
var p = (s.charAt(s.length-1) == '=' ?
(s.charAt(s.length-2) == '=' ? 'AA' : 'A') : "");
var r = [];
s = s.substr(0, s.length - p.length) + p;
// increment over the length of this encrypted string, four characters at a time
for (var c = 0; c < s.length; c += 4) {
// each of these four characters represents a 6-bit index in the base64 characters list
// which, when concatenated, will give the 24-bit number for the original 3 characters
var n = (base64inv[s.charAt(c)] << 18) + (base64inv[s.charAt(c+1)] << 12) +
(base64inv[s.charAt(c+2)] << 6) + base64inv[s.charAt(c+3)];
// split the 24-bit number into the original three 8-bit (ASCII) characters
r.push((n >>> 16) & 255);
r.push((n >>> 8) & 255);
r.push(n & 255);
}
// remove any zero pad that was added to make this a multiple of 24 bits
return r;
}
What's the function of those "<<<" and ">>>" characters.
Or is there a function like this for Python?
Who cares. Python has easier ways of doing the same.
[ord(c) for c in '6gAAAOsAAADsAAAACAEAAAkBAAAKAQAAJgEAACcBAAAoAQAA'.decode('base64')]
In Python I expected you'd just use the base64 module...
... but in response to your question about << and >>>:
<< is the left-shift operator; the result is the first operand shifted left by the second operand number of bits; for example 5 << 2 is 20, as 5 is 101 in binary, and 20 is 10100.
>>> is the non-sign-extended right-shift operator; the result is the first operand shifted
right by the second operand number of bits... with the leftmost bit always being filled with a 0.
Why not just:
from binascii import a2b_base64, b2a_base64
encoded_data = b2a_base64(some_string)
decoded_string = a2b_base64(encoded_data)
def base64_decode_array(string):
return [ord(c) for c in a2b_base64(string)]
Just for fun/completeness, I'll translate the javascript more literally: :)
# No particular reason to make a list of chars here instead of a string.
base64chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
lookup = dict((c, i) for (i, c) in enumerate(base64chars))
def base64_decode_array(s):
# Filter out meaningless chars, especially newlines. No need for a regex.
s = ''.join(c for c in s if c in base64chars + '=')
# replace any incoming padding with a zero pad (the 'A' character is zero)
# Their way:
# p = ('AA' if s[-2] == '=' else 'A') if s[-1] == '=' else ''
# s = s[:len(s) - len(p)] + p
# My way (allows for more padding than that;
# '=' will only appear at the end anyway
s = s.replace('=', 'A')
r = []
# Iterate over the string in blocks of 4 chars - an ugly hack
# though we are preserving the original code's assumption that the text length
# is a multiple of 4 (that's what the '=' padding is for) ;)
for a, b, c, d in zip(*([iter(s)] * 4)):
# Translate each letter in the quad into a 6-bit value and bit-shift them
# together into a 24-bit value
n = (lookup[a] << 18) + (lookup[b] << 12) + (lookup[c] << 6) + lookup[d]
# split the 24-bit number into the original three 8-bit (ASCII) characters
r += [(n >> 16) & 0xFF), (n >> 8) & 0xFF), (n & 0xFF)]
return r

Where is my implementation of rot13 in JavaScript going wrong?

Code in question with syntax highlighting here: via Friendpaste
rot13.js:
ERRONEOUS
<script>
String.prototype.rot13 = rot13 = function(s)
{
return (s = (s) ? s : this).split('').map(function(_)
{
if (!_.match(/[A-Za-z]/)) return _;
c = Math.floor(_.charCodeAt(0) / 97);
k = (_.toLowerCase().charCodeAt(0) - 96) % 26 + 13;
return String.fromCharCode(k + ((c == 0) ? 64 : 96));
}).join('');
};
</script>
As you can see, using quite literally a single line to attach a method to the String object a la prototyping, I'm having a map() method that I previously set up (I know for sure that that code works perfectly; it's simply iterating over each element in the array and applying the function specified in the parameter) go over each character in a string and do what I thought were the proper calculations to transform the string into it's rot13'd counterpart. I was sadly mistaken. Can anybody spot where I went wrong?
You could use the super-short:
s.replace(/[a-zA-Z]/g,function(c){return String.fromCharCode((c<="Z"?90:122)>=(c=c.charCodeAt(0)+13)?c:c-26);});
Here's a solution using replace and indexOf functions:
function rot13(s) {
return s.replace(/[A-Z]/gi, c =>
"NOPQRSTUVWXYZABCDEFGHIJKLMnopqrstuvwxyzabcdefghijklm"[
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".indexOf(c) ] )
}
This is made up of:
/[A-Z]/gi regular expression for matching only characters
replace is used to substitute those characters
an a replacer function written as an arrow function
indexOf is to convert the character into a numeric lookup index
we lookup the index in the substitution array and we're done
This gives correct results.
function rot13(s)
{
return (s ? s : this).split('').map(function(_)
{
if (!_.match(/[A-Za-z]/)) return _;
c = Math.floor(_.charCodeAt(0) / 97);
k = (_.toLowerCase().charCodeAt(0) - 83) % 26 || 26;
return String.fromCharCode(k + ((c == 0) ? 64 : 96));
}).join('');
}
alert(rot13(rot13("Mark this as accepted answer :)")));
Just because it's even shorter and also more understandable/logical:
function rot13(s) {
return s.replace( /[A-Za-z]/g , function(c) {
return String.fromCharCode( c.charCodeAt(0) + ( c.toUpperCase() <= "M" ? 13 : -13 ) );
} );
}
Kevin M's solution is compact and elegant. It's got one tiny error, though: the regular expression used with the replace function doesn't limit substitution to alphabetic characters. The [A-z] character range includes punctuation characters ([\] ^ _ `), which will be swapped for letters when they should be left alone.
The fixed version looks like this:
function r(a,b){return++b?String.fromCharCode((a<"["?91:123)>(a=a.charCodeAt()+13)?a:a-26):a.replace(/[a-zA-Z]/g,r)}
It's still only 116 bytes. Remarkably small and quite clever.
(Sorry for the full answer posting; I'm still lacking the 50 rep required to post this as a comment to Kevin's excellent answer.)
var rot13 = String.prototype.rot13 = function(s)
{
return (s = (s) ? s : this).split('').map(function(_)
{
if (!_.match(/[A-Za-z]/)) return _;
c = _.charCodeAt(0)>=96;
k = (_.toLowerCase().charCodeAt(0) - 96 + 12) % 26 + 1;
return String.fromCharCode(k + (c ? 96 : 64));
}
).join('');
};
alert('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'.rot13());
yields nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM
Mixing zero-based and one-based indices for the lose. I blame Netscape.
Here's a version that's 80-columns, doesn't update string.prototype, well indented and reasonably short.
function rot13(str) {
return str.replace(/[a-zA-Z]/g, function(chr) {
var start = chr <= 'Z' ? 65 : 97;
return String.fromCharCode(start + (chr.charCodeAt(0) - start + 13) % 26);
});
}
And an example showing it is working:
rot13('[abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ]')
"[nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM]"
rot13(rot13('[abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ]'))
"[abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ]"
One-liner that weighs in at 116 bytes:
function r(a,b){return++b?String.fromCharCode((a<"["?91:123)>(a=a.charCodeAt()+13)?a:a-26):a.replace(/[a-zA-Z]/g,r)}
Usage:
r('The Quick Brown Fox Jumps Over The Lazy Dog.');
There is still room for enhancement, checking (c<="Z") is actually a check against the codepoint (that we need later on), following that idea gives us a win !
//versus Kevin M's style : 115 chars (vs 116)
//102 chars with nodejs Buffer (see below)
function r(a,b){return++b?String.fromCharCode(((a=a.charCodeAt())<91?78:110)>a?a+13:a-13):a.replace(/[a-zA-Z]/g,r)}
//nodejs style
function r(a,b){return++b?Buffer([((a=Buffer(a)[0])<91?78:110)>a?a+13:a-13]):a.replace(/[a-zA-Z]/g,r)}
//versus Ben Alpert style : 107 chars (vs 112)
//93 chars with nodejs Buffer (see below)
s.replace(/[a-zA-Z]/g,function(a){return String.fromCharCode(((a=a.charCodeAt())<91?78:110)>a?a+13:a-13)});
//nodejs style
s.replace(/[a-zA-Z]/g,function(a){return Buffer([((a=Buffer(a)[0])<91?78:110)>a?a+13:a-13])})
// Same code, formated for production
String.prototype.rot13 = function() {
return this.replace(/[a-zA-Z]/g, function(a){
return String.fromCharCode(((a=a.charCodeAt())<91?78:110)>a?a+13:a-13);
});
}
In nodejs, you can use Buffer to cast/serialize codepoints, e.g. :
var a=65;
""+Buffer([a]) == "A" // note that the cast is done automatically if needed
String.fromCharCode(a) == "A"
var b="A";
Buffer(a)[0] == 65
a.charCodeAt() == 65
My golfed version is 82 bytes long (vs. Ben Albert, which is 35% heavier, but inspired mine):
S.replace(/[a-z]/gi,c=>String.fromCharCode((c=c.charCodeAt())+((c&95)>77?-13:13)))
Differences:
case-insensitive to catch only English alphabet.
arrow functions without return and braces.
delete parameters from charCodeAt.
test against code insted of string.
doing +13-26=-13.
test uppercased (&95) against 77 (78+13=91, overflow).
Extra: If you want to perform ROT5 on digits, add:
.replace(/\d/gi,c=>(c>4?-5:5)+c*1)
Combining various techniques here, I came up with this 78 character JavaScript ES6 function, which works on Node:
rot13=s=>s.replace(/[a-z]/ig,c=>Buffer([((d=Buffer(c)[0])&95)<78?d+13:d-13]));
Here’s a JavaScript library that performs ROT-n letter substitution: https://github.com/mathiasbynens/rot
rot is a JavaScript library that performs rotational letter substitution. It can be used to shift any ASCII letters in the input string by a given number of positions in the alphabet. To ROT-13 the string 'abc', for example:
// ROT-13 is the default
rot('abc');
// → 'nop'
// Or, specify `13` explicitly:
rot('abc', 13);
// → 'nop'
This is in no way trying to compete with the excellent stuff here, as you see I can't comment but I have my own novice attempt to write this in JS and getting it to work before I read more elegant solutions here - I'm going to share it here.
I tried to write it with indexOf, a switch, by adding 13, by String.fromCharCode() and CharCodeAt(). They were getting too long - the helper function in this one is unnecessary but this was my shortest : )
function rot13(string) {
var result = '',
store,
str = string.toLowerCase();
//helper function
function strgBreak(a){
var result = [];
return result = a.split('');
}
//rot13 arrays
var alphArr = strgBreak('abcdefghijklmnopqrstuvwxyz');
var inverseArr = strgBreak('nopqrstuvwxyzabcdefghijklm');
for ( var i = 0; i < str.length; i++ ) {
if (alphArr.indexOf( str[i] ) !== -1) {
result += inverseArr[ alphArr.indexOf( str[i] ) ];
} else result += str[i];
}
return result.toUpperCase();
}
Here's a modern approach to the ROT13 substitution cipher:
const ROT13 = s =>
s.replace(/[a-z]/gi, c =>
String.fromCharCode(c.charCodeAt() + 13 - 26 * /[n-z]/i.test(c)));
console.log(ROT13('The quick brown fox jumps over 13 lazy dogs.'));
The result of the test case above is:
Gur dhvpx oebja sbk whzcf bire 13 ynml qbtf.
While I really like the RegEx solution, I primarily undertook the project to see if I could get it done. Glad to report that I finally did manage to do so:
String.prototype.rot13 = rot13 = function(s)
{
return (s ? s : this).split('').map(function(_)
{
if (!_.match(/[A-za-z]/)) return _;
c = Math.floor(_.charCodeAt(0) / 97);
k = (_.toLowerCase().charCodeAt(0) - 83) % 26 || 26;
return String.fromCharCode(k + ((c == 0) ? 64 : 96));
}).join('');
}
CoffeeScript version of #ben-alpert 's answer:
string.replace /[a-zA-Z]/g, (c) -> String.fromCharCode if (if c <= 'Z' then 90 else 122) >= (c = c.charCodeAt(0) + 13) then c else c - 26
Or as function:
ROT13 = (string) -> string.replace /[a-zA-Z]/g, (c) -> String.fromCharCode if (if c <= 'Z' then 90 else 122) >= (c = c.charCodeAt(0) + 13) then c else c - 26
ROT13('asd') # Returns: 'nfq'
My favourite and simple to understand version of decision ROT13
function rot13(message) {
var a = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
var b = "nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM"
return message.replace(/[a-z]/gi, c => b[a.indexOf(c)])
}
a - classic alphabet, b - prepered dictionary with 13th replacement, return result with simple RegEx replacement

Categories

Resources