Ruby ZeroDivisionError - ruby

I am trying to create a encryption alghoritm in ruby. The encryption works fine, but the decryption part fails. The error is at line 67, and says the following:
decryption.rb:67:ln '/': divided by zero (ZeroDivisionError)
I have no idea why this is happening and i cannot find a answer to my question online.
This is the source code:
print ">"
ciphertext = gets.chomp
print ">"
symetricKey = gets.chomp
symetricKey.gsub! 'a', '1'
symetricKey.gsub! 'b', '2'
symetricKey.gsub! 'c', '3'
symetricKey.gsub! 'd', '4'
symetricKey.gsub! 'e', '5'
symetricKey.gsub! 'f', '6'
symetricKey.gsub! 'g', '7'
symetricKey.gsub! 'h', '8'
symetricKey.gsub! 'i', '9'
symetricKey.gsub! 'j', '10'
symetricKey.gsub! 'k', '11'
symetricKey.gsub! 'l', '12'
symetricKey.gsub! 'm', '13'
symetricKey.gsub! 'n', '14'
symetricKey.gsub! 'o', '15'
symetricKey.gsub! 'p', '16'
symetricKey.gsub! 'q', '17'
symetricKey.gsub! 'r', '18'
symetricKey.gsub! 's', '19'
symetricKey.gsub! 't', '20'
symetricKey.gsub! 'u', '21'
symetricKey.gsub! 'v', '22'
symetricKey.gsub! 'w', '23'
symetricKey.gsub! 'x', '24'
symetricKey.gsub! 'y', '25'
symetricKey.gsub! 'z', '26'
symetricKey=symetricKey.to_i
ciphertext.gsub! 'a', '1'
ciphertext.gsub! 'b', '2'
ciphertext.gsub! 'c', '3'
ciphertext.gsub! 'd', '4'
ciphertext.gsub! 'e', '5'
ciphertext.gsub! 'f', '6'
ciphertext.gsub! 'g', '7'
ciphertext.gsub! 'h', '8'
ciphertext.gsub! 'i', '9'
ciphertext.gsub! 'j', '10'
ciphertext.gsub! 'k', '11'
ciphertext.gsub! 'l', '12'
ciphertext.gsub! 'm', '13'
ciphertext.gsub! 'n', '14'
ciphertext.gsub! 'o', '15'
ciphertext.gsub! 'p', '16'
ciphertext.gsub! 'q', '17'
ciphertext.gsub! 'r', '18'
ciphertext.gsub! 's', '19'
ciphertext.gsub! 't', '20'
ciphertext.gsub! 'u', '21'
ciphertext.gsub! 'v', '22'
ciphertext.gsub! 'w', '23'
ciphertext.gsub! 'x', '24'
ciphertext.gsub! 'y', '25'
ciphertext.gsub! 'z', '26'
ciphertext = ciphertext.to_i
cleartext = ciphertext / (symetricKey / symetricKey / 100)
print"\n"
cleartext.to_s
cleartext.gsub! '1' ,'a'
cleartext.gsub! '2' ,'b'
cleartext.gsub! '3' ,'c'
cleartext.gsub! '4' ,'d'
cleartext.gsub! '5' ,'e'
cleartext.gsub! '6' ,'f'
cleartext.gsub! '7' ,'g'
cleartext.gsub! '8' ,'h'
cleartext.gsub! '9' ,'i'
cleartext.gsub! '10' ,'j'
cleartext.gsub! '11' ,'k'
cleartext.gsub! '12' ,'l'
cleartext.gsub! '13' ,'m'
cleartext.gsub! '14' ,'n'
cleartext.gsub! '15' ,'o'
cleartext.gsub! '16' ,'p'
cleartext.gsub! '17' ,'q'
cleartext.gsub! '18' ,'r'
cleartext.gsub! '19' ,'s'
cleartext.gsub! '20' ,'t'
cleartext.gsub! '21' ,'u'
cleartext.gsub! '22' ,'v'
cleartext.gsub! '23' ,'w'
cleartext.gsub! '24' ,'x'
cleartext.gsub! '25' ,'y'
cleartext.gsub! '26' ,'z'
puts cleartext

It doesn't work because the formula ciphertext / (symetricKey / symetricKey / 100) makes no sense at all.
symetricKey / symetricKey is always 1, divided by 100 that's always 0.01
symetricKey / symetricKey / 100 both operands are integers, so 0.01 is rounded down to 0
ciphertext / (symetricKey / symetricKey / 100) is equivalent to ciphertext / 0 (see 2.)
Your error is exactly what it says in the message: you're dividing by 0.
EDIT: As a small bonus, your code is broken anyway. 'k' for example is converted into 11, but when decrypting that will turn into 'aa'

Your question has been answered, but I'd like to suggest a more Ruby-like way to solve your problem.
If you look at the doc for Hash#gsub you will see that the second form of the method, written str.gsub(pattern, hash), where pattern is generally a regular expression and hash is, of course, a hash. Here's an example.
h = { 'c'=>'C', 'a'=>'A', 'n'=>'N', 'd'=>'D', 'o'=>'$', 'g'=>'G' }
'cat and dog.'.gsub(/./, h)
#=> "CAANDD$G"
/./ is a regular expression that simply matches any one character in str. It first matches 'c', then 'a', and so on. Consider the first letter, 'c'. Ruby checks to see if the hash h has a key 'c'. It does, so that character is replaced with the value of 'c' in the hash, 'C'. Next, 'a' is replaced with 'A'. h does not have a key 't', however, so Ruby converts 't' to an empty string. Similarly, the characters ' ' (a space) and '.' (a period) are not keys in h, so those characters are also converted to empty strings.
This form of gsub seems perfect for encrypting and decrypting strings.
Let's first construct the encoding hash.
arr = ('a'..'z').zip('01'..'26')
#=> [["a", "01"], ["b", "02"],..., ["z", "26"]]
encode = arr.to_h
#=> {"a"=>"01", "b"=>"02",..., "z"=>"26"}
encode[' '] = "27"
encode['.'] = "28"
So now
encode
#=> {"a"=>"01", "b"=>"02",..., "z"=>"26", " "=>"27", "."=>"." }
'a'..'z' and '01'..'2' are Ranges. See the docs for Enumerable#zip and Array#to_h. We would normally write those two lines as a single chained expression.
encode = ('a'..'z').zip('01'..'26').to_h
Notice that the values begin with "01", so that all are strings of the same length (2). If we started the values with "1" how could we decode the string "1226"? Would that be 'abbd' (1-2-3-4), 'abz' (1-2-26), 'lz' (12-26) or one of the other possibilities. That is not an issue if all the values of the hash encode are strings of length 2.
We could construct the hash for decoding in the same way( {'01'=>'a', '02'=>'b',...}), but it's easier to use the method Hash#invert:
decode = encode.invert
#=> {"01"=>"a", "02"=>"b",..., "26"=>"z", "27"=>" ", "28"=>"."}
If the string to be encrypted is
str = "how now, brown cow."
we can encode it with
encrypted = str.gsub(/./, encode)
#=> "081523271415232702181523142703152328"
which can then be decoded with
decoded = encrypted.gsub(/../, decode)
#=> "how now, brown cow."
Notice that the regular expression ("regex") for decoding matches any two characters at a time.
Two points. Firstly, we could use Hash#update (aka merge!) to write
encode = ('a'..'z').zip('01'..'26').to_h
encode.update( ' '=>"27", '.'=>"28" )
Ruby allows us to write encode.update({ ' '=>"27", '.'=>"28" }) without the braces (an example of syntactic sugar).
The better way is to use Hash#default_proc= to write
encode = ('a'..'z').zip('01'..'26').to_h
encode.default_proc = proc { |h,k| h[k] = k }
This is a bit complicated for a Ruby newbie, but the effect is to cause encode[k] to return k if encode does not have a key k. For example, encode['.'] #=> '.'.

Related

Changing SQL Server query to pure ANSI SQL query

I am working on a database system that uses SQL syntax. However I am unable to use the cross apply in the code below. Is there a way to rewrite this without applies?
declare #rsBuildDetails table(dt datetime, build varchar(255), val varchar(255));
insert into #rsBuildDetails (dt, build, val)
values ('20100101', '1', 'pass')
,('20100102', '2', 'fail')
,('20100103', '3', 'pass')
,('20100104', '4', 'fail')
,('20100105', '5', 'fail')
,('20100106', '6', 'fail')
,('20100107', '7', 'pass')
,('20100108', '8', 'pass')
,('20100109', '9', 'pass')
,('20100110', '10', 'fail');
with passed as
(
select *
from #rsBuildDetails
where val='pass'
)
select distinct
preFail.dt AS FailedDt,
postFail.dt AS SecondFailedDt
from
passed
cross apply
(select top 1
pre.*
from
#rsBuildDetails as pre
where
pre.dt < passed.dt
and pre.val = 'fail'
order by
pre.dt desc) as preFail
cross apply
(select top 1
post.*
from
#rsBuildDetails as post
where
post.dt > passed.dt
and post.val = 'fail'
order by
post.dt asc) as postFail
You might try to transfer the CTE and all applies to inlined sub-selects:
declare #rsBuildDetails table(dt datetime, build varchar(255), val varchar(255));
insert into #rsBuildDetails (dt, build, val) values
('20100101', '1', 'pass')
,('20100102', '2', 'fail')
,('20100103', '3', 'pass')
,('20100104', '4', 'fail')
,('20100105', '5', 'fail')
,('20100106', '6', 'fail')
,('20100107', '7', 'pass')
,('20100108', '8', 'pass')
,('20100109', '9', 'pass')
,('20100110', '10', 'fail');
select *
from
(
select distinct
(
select top 1 pre.Dt
from #rsBuildDetails as pre
where pre.dt<passed.dt
and pre.val='fail'
order by pre.dt desc
) as FailedDt
,(
select top 1 post.Dt
from #rsBuildDetails as post
where post.dt>passed.dt
and post.val='fail'
order by post.dt asc
) AS SecondFailedDt
from
(
select *
from #rsBuildDetails
where val='pass'
) AS passed
) AS tbl
where tbl.FailedDt IS NOT NULL
AND tbl.SecondFailedDt IS NOT NULL

Adding additional columns to my Oracle SQL result

I have a SQL query built that returns a singular column of figures. Sample is shown below. I need to enhance this in a singular query and retrieve a 2nd set of figures using similar filters/parameters (instead of status=E I'd look for category=E). This query has gotten fairly complex for my knowledge though and I'm a bit stumped on how to approach adding this additional SELECT statement to get a 2nd column. Appreciate if anyone can point me in the right direction.
SELECT Count(eqnbr) EQUIP,
imp_exp,
'TITLE' TITLE
FROM (SELECT cy.container EQNbr,
cy.location_position "POS",
Substr(cy.location_position, 1, 1) Row_Nbr,
CASE
WHEN Substr(cy.location_position, 1, 3) IN (
'M01', 'N03', 'N04', 'N05',
'N06', 'N07', 'N08', 'N09' ) THEN
'Rail'
WHEN Substr(cy.location_position, 1, 1) IN ( 'F', 'G', 'H', 'J',
'K', 'L', 'M', 'P',
'R', 'S' ) THEN
'Locals'
WHEN Substr(cy.location_position, 1, 1) IN ( 'B', 'D' ) THEN
'Export'
ELSE Concat ('Row ', Substr(cy.location_position, 1, 1))
END IMP_EXP
FROM current_yard cy
WHERE eq_class = 'CTR'
AND location_position NOT LIKE 'CT%'
AND using_line NOT IN ( 'TEST', 'UNK', 'TST', 'B125',
'BO77', 'BR75' )
AND using_line IS NOT NULL
AND ( container NOT LIKE 'TEST%'
OR container NOT LIKE 'KENT%' )
AND Length(container) >= 10
AND status = 'E'
AND Substr(cy.location_position, 1, 1) NOT IN
( '1', '2', '3', '4',
'5', '6', '7', '8',
'9', '0', 'A', 'C',
'E', 'U', 'V', 'W', 'X' )) a
GROUP BY imp_exp
ORDER BY 2 ASC

Many Replace after each other in SELECT statement

I've a column that have a varchar2 like this: ..x...y...z..
I want to replace x to 1, y to 2 and z to 3.
Is it possible to have multiple replace after each other in select statement to replace these characters?
(select)
replace(varchar2, 'x', '1')
replace(varchar2, 'y', '2')
replace(varchar2, 'z', '3')
Or use TRANSLATE function if that fits your needs http://docs.oracle.com/cd/E11882_01/server.112/e26088/functions216.htm#SQLRF06145
Do you mean something like this:
replace(replace(replace(varchar2, 'x', '1'), 'y', '2'), 'z', '3')

How to get the last letter of a string?

How can I get the last letter of a string and check if it's vowel or consonant? I am using oracle 10g.
Here is what I came up with already:
SELECT last_name,
Substr(last_name, -1, 1) "Last letter",
Substr(last_name, 1, 1) "First letter",
CASE
WHEN Substr(last_name, -1, 1) IN ( 'a', 'e', 'i', 'o', 'u' ) THEN
'ends with a vowel'
WHEN Substr(last_name, -1, 1) IN ( 'b', 'c', 'd', 'f',
'g', 'h', 'j', 'k',
'l', 'm', 'n', 'p',
'q', 'r', 's', 't',
'v', 'w', 'x', 'y', 'z' ) THEN
'ends with a consonant'
END "Last Letter Description",
CASE
WHEN Substr(last_name, 1, 1) IN ( 'a', 'e', 'i', 'o', 'u' ) THEN
'starts with a consonant'
WHEN Substr(last_name, 1, 1) IN ( 'b', 'c', 'd', 'f',
'g', 'h', 'j', 'k',
'l', 'm', 'n', 'p',
'q', 'r', 's', 't',
'v', 'w', 'x', 'y', 'z' ) THEN
'starts with a consonant'
END "First Letter Description"
FROM employees
GROUP BY first_name,
last_name
Now when you execute this on oracle 10g the "First Letter Description" is empty! What is wrong with my code?
Try this, not complete, but with easy adjustments you can make it work the way you want:
FUNCTION last_is_vowel (string_in VARCHAR2)
RETURN BOOLEAN
IS
BEGIN
RETURN CASE WHEN LOWER(SUBSTR(string_in, -1)) IN ('a', 'e', 'i', 'o', 'u')
THEN TRUE
ELSE FALSE
END;
END last_is_vowel;
Look at your data. Chances are the first character in employees.last_name is capitalized. Remember, Oracle is case sensitive. You can user UPPER() or LOWER() to help find your match.
Also it'd be more efficient to search just for vowels and use an else statement for to find exclusions as João suggests.
SELECT last_name,
Substr(last_name, -1, 1) "Last character",
Substr(last_name, 1, 1) "First character",
CASE
WHEN lower(Substr(last_name, -1, 1)) IN ( 'a', 'e', 'i', 'o', 'u' ) THEN
'ends with a vowel'
ELSE
'does not end with a vowel'
END "Last Letter Description",
CASE
WHEN lower(Substr(last_name, 1, 1)) IN ( 'a', 'e', 'i', 'o', 'u' ) THEN
'starts with a vowel'
ELSE
'does not start with a vowel'
END "First Letter Description"
FROM employees
GROUP BY first_name,
last_name

Encode (& decode) a URL as a string of letters

I want a function that can encode any URL into a string of all letters (upper and lowercase), and another function to decode it back into a URL. What's the best way to accomplish this?
Sample API:
> 'http://stackoverflow.com/questions/ask'.url_to_chars
=> 'mgzGBORuRcFSfNXDpDbVgzzvANHLqIEcgjCAXsKbNXGouOckToKkZRBnvE'
> 'mgzGBORuRcFSfNXDpDbVgzzvANHLqIEcgjCAXsKbNXGouOckToKkZRBnvE'.chars_to_url
=>'http://stackoverflow.com/questions/ask'
Base64 is a simple way to do this:
String encoded = Base64.encode("http://stackoverflow.com/questions/ask".getBytes());
System.out.println(encoded);
System.out.println(new String(Base64.decode(encoded)));
Prints:
aHR0cDovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy9hc2s=
http://stackoverflow.com/questions/ask
Update:
If you actually look at the RFC 1738 URLs are case-insensitve and only a range of characters are allowed. There's plenty of space to map it as long as your input strings are valid encoded URLs.
import string
l = string.ascii_letters + string.digits
t = string.ascii_lowercase + string.digits + ";/?:#=&$-_.+!*'(),"
d = dict(zip(l,t))
e = dict(zip(t,l))
d and e are the decoding and the reverse encoding mapping.
[('a', 'a'), ('b', 'b'), ('c', 'c'), ('d', 'd'), ('e', 'e'), ('f', 'f'), ('g', 'g'), ('h', 'h'), ('i', 'i'), ('j', 'j'), ('k', 'k'), ('l', 'l'), ('m', 'm'), ('n', 'n'), ('o', 'o'), ('p', 'p'), ('q', 'q'), ('r', 'r'), ('s', 's'), ('t', 't'), ('u', 'u'), ('v', 'v'), ('w', 'w'), ('x', 'x'), ('y', 'y'), ('z', 'z'), ('0', 'A'), ('1', 'B'), ('2', 'C'), ('3', 'D'), ('4', 'E'), ('5', 'F'), ('6', 'G'), ('7', 'H'), ('8', 'I'), ('9', 'J'), (';', 'K'), ('/', 'L'), ('?', 'M'), (':', 'N'), ('#', 'O'), ('=', 'P'), ('&', 'Q'), ('$', 'R'), ('-', 'S'), ('_', 'T'), ('.', 'U'), ('+', 'V'), ('!', 'W'), ('*', 'X'), ("'", 'Y'), ('(', 'Z'), (')', '0'), (',', '1')]
Decode and encode are only simple mappings:
def encode(s): return ''.join(e[c] for c in s)
def decode(s): return ''.join(d[c] for c in s)
The output is:
enc = encode("http://stackoverflow.com/questions/ask")
>>> decode(enc)
'http://stackoverflow.com/questions/ask'
>>> enc
'httpNLLstackoverflowUcomLquestionsLask'
You can make use of base64 encoding and decoding.
Depending on the data you can use some encryption and decryption algorithm to accomplish this. It will put the string into text with no special characters etc. With the added bonus that the data is encrypted.

Resources