1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
15 # Mix of all things cryptography-related
21 # NOTE: works on letters only
23 # assert 'x'.rot(6) == 'd'
24 # assert 'T'.rot(15) == 'I'
25 # assert '1'.rot(10) == '1'
26 # assert '$'.rot(10) == '$'
27 # assert 'z'.rot(-2) == 'x'
28 fun rot
(x
: Int): Char do
29 if not is_letter
then return self
39 if val
> 122 then val
-= 26
46 # Performs a Rotation of `x` on each letter of self
48 # Works by replacing every character in `self` by its
51 # Say we have a rotation of `3` (Caesar rotation, for
52 # culture) for a string : "aybabtu"
54 # a, rotated by 3 becomes d
55 # y, rotated by 3 becomes b
56 # b, rotated by 3 becomes e
57 # t, rotated by 3 becomes w
58 # u, rotated by 3 becomes x
60 # We then replace every letter in our original string by
61 # their rotated representations, therefore yielding : "dbedewx"
63 # assert "All your base are belong to us".rot(13) == "Nyy lbhe onfr ner orybat gb hf"
64 # assert "This is no moon.".rot(4).rot(22) == "This is no moon."
66 # NOTE : Works on letters only
67 # NOTE : This cipher is symmetrically decrypted with an `x` of 26-`x`
68 fun rot
(x
: Int): Text do
70 if rot
< 0 then rot
+= 26
71 var d
= new FlatBuffer.with_capacity
(length
)
78 # Returns a rail-fence cipher from `self` with `depth` rails
80 # Rail works by drawing a zig-zag pattern on `depth` rails.
82 # Say we have "fuckingbehemoth".railfence(4)
84 # This happens in-memory:
93 # Therefore, yielding the ciphertext : "fgounbmtcieehkh"
95 # assert "fuckingbehemoth".railfence(4) == "fgounbmtcieehkh"
96 fun railfence
(depth
: Int): Text do
97 var lines
= new Array[FlatBuffer].with_capacity
(depth
)
99 for i
in [0..depth
[ do
100 lines
[i
] = new FlatBuffer.with_capacity
(length
)
104 for j
in [0..depth
[ do
105 if j
== curr_depth
then
116 if curr_depth
== 0 then
119 if curr_depth
== (depth
- 1) then
123 var r
= new FlatBuffer.with_capacity
(length
)
125 r
.append i
.to_s
.replace
(".", "")
130 # Transforms a rail-fence-encrypted Text to its original
132 # assert "fgounbmtcieehkh".unrail(4) == "fuckingbehemoth"
133 fun unrail
(depth
: Int): Text do
134 var dots
= "." * length
135 var arr
= new FlatBuffer.from
(dots
)
137 var paces
= depth
.unrail_paces
138 for i
in [0..depth
[ do
139 var lp
= paces
[i
].first
140 var rp
= paces
[i
].second
143 while pos
< length
do
144 arr
[pos
] = chars
[start
]
158 # Returns `self` xored with `key`
160 # The shortest of the two is cycled through until the longest has been
163 # assert "goodmorning".xor(" ".to_bytes) == "GOODMORNING"
164 fun xor
(key
: SequenceRead[Byte]): Text do
165 var xored
= new Bytes.with_capacity
(bytelen
.max
(key
.length
))
167 var shortest
: SequenceRead[Byte]
168 var longest
: SequenceRead[Byte]
170 if key
.length
> self.length
then
171 shortest
= self.to_bytes
175 longest
= self.to_bytes
178 for i
in longest
.length
.times
do
179 xored
.add
(longest
[i
] ^ shortest
[i
% shortest
.length
])
187 # Generates the paces for each depth.
189 # Each entry of the returned array is a couple of the first pace
190 # and the second one, they are alternated when deciphering a rail-encrypted string.
192 # Say we have the encrypted string "fgounbmtcieehkh" on 4 rails
194 # To find the distance between each character on the original railed
195 # string, we need to compute the extremes.
197 # The extremes always have a distance of `depth - 1`, multiplied by 2, no pairing.
199 # In the example, that would be : [(4 - 1) * 2, (4 - 1) * 2] => [6,6]
201 # For every rail in-between, the first distance is the largest absolute value
202 # of the difference between the current depth and the extremes, multiplied by 2.
204 # Its pair is the distance of maximum and the distance yielded by the previous
207 # In our example, that would be :
209 # Maximums : (4 - 1) * 2 = 3 * 2 => [6,6]
210 # In between : Distance for depth 2 : max(2 - 1, 4 - 2) => 2
211 # The calculation yields the couple [(2 * 2), 6 - 4] => [4, 2]
212 # The symmetric couple is reversed : [2, 4]
214 # In fine, the example yields the array : [[6,6], [4,2], [2,4], [6,6]]
216 # In the end, our string is read using the generated array
218 # SEE: `Text::unrail` for how the array is used
219 private fun unrail_paces
: Array[Couple[Int, Int]] do
220 var ret
= new Array[Couple[Int,Int]].with_capacity
(self)
221 var extremes
= new Couple[Int, Int]((self - 1) * 2, (self - 1) * 2)
222 for i
in [0..self[ do
225 var mid
= ((self.to_f
)/2.0).floor
.to_i
226 for i
in [1 .. mid
[ do
228 var lodepth
= self - rd
229 var hidepth
= (rd
- self).abs
231 if hidepth
> lodepth
then
236 var cp
= new Couple[Int, Int](dd
, extremes
.first-dd
)
237 var ccp
= new Couple[Int, Int](extremes
.first
- dd
, dd
)
242 if not self.is_even
then
243 ret
[mid
] = new Couple[Int, Int](extremes
.first
/2, extremes
.first
/2)