Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/monoids/string_monoid.py
4045 views
1
r"""
2
Free String Monoids
3
4
AUTHORS:
5
6
- David Kohel <[email protected]>, 2007-01
7
8
Sage supports a wide range of specific free string monoids.
9
"""
10
#*****************************************************************************
11
# Copyright (C) 2007 David Kohel <[email protected]>
12
#
13
# Distributed under the terms of the GNU General Public License (GPL)
14
#
15
# http://www.gnu.org/licenses/
16
#*****************************************************************************
17
18
# from sage.rings.integer import Integer
19
# from sage.structure.parent_gens import ParentWithGens, normalize_names
20
from free_monoid import FreeMonoid_class
21
from string_monoid_element import StringMonoidElement
22
from string_ops import strip_encoding
23
24
import weakref
25
26
_cache = {}
27
28
def BinaryStrings():
29
r"""
30
Returns the free binary string monoid on generators `\{ 0, 1 \}`.
31
32
OUTPUT:
33
34
- Free binary string monoid.
35
36
EXAMPLES::
37
38
sage: S = BinaryStrings(); S
39
Free binary string monoid
40
sage: u = S('')
41
sage: u
42
43
sage: x = S('0')
44
sage: x
45
0
46
sage: y = S('1')
47
sage: y
48
1
49
sage: z = S('01110')
50
sage: z
51
01110
52
sage: x*y^3*x == z
53
True
54
sage: u*x == x*u
55
True
56
"""
57
# Here we cache the binary strings to make them unique
58
# if _cache.has_key(2):
59
# The method .has_key() has been deprecated since Python 2.2. Use
60
# "k in Dict" instead of "Dict.has_key(k)".
61
if 2 in _cache:
62
S = _cache[2]()
63
if not S is None:
64
return S
65
S = BinaryStringMonoid()
66
_cache[2] = weakref.ref(S)
67
return S
68
69
def OctalStrings():
70
r"""
71
Returns the free octal string monoid on generators `\{ 0, 1, \dots, 7 \}`.
72
73
OUTPUT:
74
75
- Free octal string monoid.
76
77
EXAMPLES::
78
79
sage: S = OctalStrings(); S
80
Free octal string monoid
81
sage: x = S.gens()
82
sage: x[0]
83
0
84
sage: x[7]
85
7
86
sage: x[0] * x[3]^3 * x[5]^4 * x[6]
87
033355556
88
"""
89
# Here we cache the octal strings to make them unique
90
# if _cache.has_key(8):
91
# The method .has_key() has been deprecated since Python 2.2. Use
92
# "k in Dict" instead of "Dict.has_key(k)".
93
if 8 in _cache:
94
S = _cache[8]()
95
if not S is None:
96
return S
97
S = OctalStringMonoid()
98
_cache[8] = weakref.ref(S)
99
return S
100
101
def HexadecimalStrings():
102
r"""
103
Returns the free hexadecimal string monoid on generators
104
`\{ 0, 1, \dots , 9, a, b, c, d, e, f \}`.
105
106
OUTPUT:
107
108
- Free hexadecimal string monoid.
109
110
EXAMPLES::
111
112
sage: S = HexadecimalStrings(); S
113
Free hexadecimal string monoid
114
sage: x = S.gen(0)
115
sage: y = S.gen(10)
116
sage: z = S.gen(15)
117
sage: z
118
f
119
sage: x*y^3*z
120
0aaaf
121
"""
122
# Here we cache the hexadecimal strings to make them unique
123
# if _cache.has_key(16):
124
# The method .has_key() has been deprecated since Python 2.2. Use
125
# "k in Dict" instead of "Dict.has_key(k)".
126
if 16 in _cache:
127
S = _cache[16]()
128
if not S is None:
129
return S
130
S = HexadecimalStringMonoid()
131
_cache[16] = weakref.ref(S)
132
return S
133
134
def Radix64Strings():
135
r"""
136
Returns the free radix 64 string monoid on 64 generators
137
138
::
139
140
A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,
141
a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,
142
0,1,2,3,4,5,6,7,8,9,+,/
143
144
OUTPUT:
145
146
- Free radix 64 string monoid.
147
148
EXAMPLES::
149
150
sage: S = Radix64Strings(); S
151
Free radix 64 string monoid
152
sage: x = S.gens()
153
sage: x[0]
154
A
155
sage: x[62]
156
+
157
sage: x[63]
158
/
159
"""
160
# Here we cache the radix-64 strings to make them unique
161
# if _cache.has_key(64):
162
# The method .has_key() has been deprecated since Python 2.2. Use
163
# "k in Dict" instead of "Dict.has_key(k)".
164
if 64 in _cache:
165
S = _cache[64]()
166
if not S is None:
167
return S
168
S = Radix64StringMonoid()
169
_cache[64] = weakref.ref(S)
170
return S
171
172
def AlphabeticStrings():
173
r"""
174
Returns the string monoid on generators A-Z:
175
`\{ A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z \}`.
176
177
OUTPUT:
178
179
- Free alphabetic string monoid on A-Z.
180
181
EXAMPLES::
182
183
sage: S = AlphabeticStrings(); S
184
Free alphabetic string monoid on A-Z
185
sage: x = S.gens()
186
sage: x[0]
187
A
188
sage: x[25]
189
Z
190
"""
191
# Here we cache the alphabetic strings to make them unique
192
# if _cache.has_key(26):
193
# The method .has_key() has been deprecated since Python 2.2. Use
194
# "k in Dict" instead of "Dict.has_key(k)".
195
if 26 in _cache:
196
S = _cache[26]()
197
if not S is None:
198
return S
199
S = AlphabeticStringMonoid()
200
_cache[26] = weakref.ref(S)
201
return S
202
203
#*****************************************************************************
204
205
class StringMonoid_class(FreeMonoid_class):
206
r"""
207
A free string monoid on `n` generators.
208
"""
209
210
def __init__(self, n, alphabet=()):
211
r"""
212
Create free binary string monoid on `n` generators.
213
214
INPUT:
215
216
- ``n`` -- Integer
217
218
- ``alphabet`` -- String or tuple whose characters or elements denote
219
the generators.
220
221
EXAMPLES::
222
223
sage: S = BinaryStrings(); S
224
Free binary string monoid
225
sage: x = S.gens()
226
sage: x[0]*x[1]**5 * (x[0]*x[1])
227
01111101
228
"""
229
# Names must be alphabetical -- omitted since printing is
230
# defined locally.
231
# FreeMonoid_class.__init__(self, n, names = alphabet)
232
FreeMonoid_class.__init__(self, n)
233
self._alphabet = alphabet
234
235
def __contains__(self, x):
236
return isinstance(x, StringMonoidElement) and x.parent() == self
237
238
def alphabet(self):
239
return tuple(self._alphabet)
240
241
def gen(self, i=0):
242
r"""
243
The `i`-th generator of the monoid.
244
245
INPUT:
246
247
- ``i`` -- integer (default: 0)
248
249
EXAMPLES::
250
251
sage: S = BinaryStrings()
252
sage: S.gen(0)
253
0
254
sage: S.gen(1)
255
1
256
sage: S.gen(2)
257
Traceback (most recent call last):
258
...
259
IndexError: Argument i (= 2) must be between 0 and 1.
260
sage: S = HexadecimalStrings()
261
sage: S.gen(0)
262
0
263
sage: S.gen(12)
264
c
265
sage: S.gen(16)
266
Traceback (most recent call last):
267
...
268
IndexError: Argument i (= 16) must be between 0 and 15.
269
"""
270
n = self.ngens()
271
if i < 0 or not i < n:
272
raise IndexError(
273
"Argument i (= %s) must be between 0 and %s." % (i, n-1))
274
return StringMonoidElement(self, [int(i)])
275
276
#*****************************************************************************
277
# Specific global string monoids
278
#*****************************************************************************
279
280
class BinaryStringMonoid(StringMonoid_class):
281
r"""
282
The free binary string monoid on generators `\{ 0, 1 \}`.
283
"""
284
285
def __init__(self):
286
r"""
287
Create free binary string monoid on generators `\{ 0, 1 \}`.
288
289
EXAMPLES::
290
291
sage: S = BinaryStrings(); S
292
Free binary string monoid
293
sage: x = S.gens()
294
sage: x[0]*x[1]**5 * (x[0]*x[1])
295
01111101
296
"""
297
StringMonoid_class.__init__(self, 2, ['0', '1'])
298
299
def __cmp__(self, other):
300
if not isinstance(other, BinaryStringMonoid):
301
return -1
302
return 0
303
304
def __repr__(self):
305
return "Free binary string monoid"
306
307
def __call__(self, x, check=True):
308
r"""
309
Return ``x`` coerced into this free monoid.
310
311
One can create a free binary string monoid element from a
312
Python string of 0's and 1's or list of integers.
313
314
NOTE: Due to the ambiguity of the second generator '1' with
315
the identity element '' of the monoid, the syntax S(1) is not
316
permissible.
317
318
EXAMPLES::
319
320
sage: S = BinaryStrings()
321
sage: S('101')
322
101
323
sage: S.gen(0)
324
0
325
sage: S.gen(1)
326
1
327
"""
328
## There should really some careful type checking here...
329
if isinstance(x, StringMonoidElement) and x.parent() == self:
330
return x
331
elif isinstance(x, list):
332
return StringMonoidElement(self, x, check)
333
elif isinstance(x, str):
334
return StringMonoidElement(self, x, check)
335
else:
336
raise TypeError("Argument x (= %s) is of the wrong type." % x)
337
338
def encoding(self, S, padic=False):
339
r"""
340
The binary encoding of the string ``S``, as a binary string element.
341
342
The default is to keep the standard ASCII byte encoding, e.g.
343
344
::
345
346
A = 65 -> 01000001
347
B = 66 -> 01000010
348
.
349
.
350
.
351
Z = 90 -> 01001110
352
353
rather than a 2-adic representation 65 -> 10000010.
354
355
Set ``padic=True`` to reverse the bit string.
356
357
EXAMPLES::
358
359
sage: S = BinaryStrings()
360
sage: S.encoding('A')
361
01000001
362
sage: S.encoding('A',padic=True)
363
10000010
364
sage: S.encoding(' ',padic=True)
365
00000100
366
"""
367
from Crypto.Util.number import bytes_to_long
368
bit_string = []
369
for i in range(len(S)):
370
n = int(bytes_to_long(S[i]))
371
bits = []
372
for i in range(8):
373
bits.append(n%2)
374
n = n >> 1
375
if not padic:
376
bits.reverse()
377
bit_string.extend(bits)
378
return self(bit_string)
379
380
# def ngens(self):
381
# r"""
382
# Return the number of generators of this free binary string monoid.
383
# There are only 2 elements in the binary number system. Hence, this
384
# is the number of generators.
385
386
# EXAMPLES::
387
388
# sage: S = BinaryStrings()
389
# sage: S.ngens()
390
# 2
391
# """
392
# return 2
393
394
class OctalStringMonoid(StringMonoid_class):
395
r"""
396
The free octal string monoid on generators `\{ 0, 1, \dots, 7 \}`.
397
"""
398
399
def __init__(self):
400
r"""
401
Create free octal string monoid on generators `\{ 0, 1, \dots, 7 \}`.
402
403
EXAMPLES::
404
405
sage: S = OctalStrings(); S
406
Free octal string monoid
407
sage: x = S.gens()
408
sage: (x[0]*x[7])**3 * (x[0]*x[1]*x[6]*x[5])**2
409
07070701650165
410
sage: S([ i for i in range(8) ])
411
01234567
412
"""
413
StringMonoid_class.__init__(self, 8, [ str(i) for i in range(8) ])
414
415
def __cmp__(self, other):
416
if not isinstance(other, OctalStringMonoid):
417
return -1
418
return 0
419
420
def __repr__(self):
421
return "Free octal string monoid"
422
423
def __call__(self, x, check=True):
424
r"""
425
Return ``x`` coerced into this free monoid.
426
427
One can create a free octal string monoid element from a
428
Python string of 0's to 7's or list of integers.
429
430
EXAMPLES::
431
432
sage: S = OctalStrings()
433
sage: S('07070701650165')
434
07070701650165
435
sage: S.gen(0)
436
0
437
sage: S.gen(1)
438
1
439
sage: S([ i for i in range(8) ])
440
01234567
441
"""
442
## There should really some careful type checking here...
443
if isinstance(x, StringMonoidElement) and x.parent() == self:
444
return x
445
elif isinstance(x, list):
446
return StringMonoidElement(self, x, check)
447
elif isinstance(x, str):
448
return StringMonoidElement(self, x, check)
449
else:
450
raise TypeError("Argument x (= %s) is of the wrong type." % x)
451
452
class HexadecimalStringMonoid(StringMonoid_class):
453
r"""
454
The free hexadecimal string monoid on generators
455
`\{ 0, 1, \dots, 9, a, b, c, d, e, f \}`.
456
"""
457
458
def __init__(self):
459
r"""
460
Create free hexadecimal string monoid on generators
461
`\{ 0, 1, \dots, 9, a, b, c, d, e, f \}`.
462
463
EXAMPLES::
464
465
sage: S = HexadecimalStrings(); S
466
Free hexadecimal string monoid
467
sage: x = S.gens()
468
sage: (x[0]*x[10])**3 * (x[0]*x[1]*x[9]*x[15])**2
469
0a0a0a019f019f
470
sage: S([ i for i in range(16) ])
471
0123456789abcdef
472
"""
473
alph = '0123456789abcdef'
474
StringMonoid_class.__init__(self, 16, [ alph[i] for i in range(16) ])
475
476
def __cmp__(self, other):
477
if not isinstance(other, HexadecimalStringMonoid):
478
return -1
479
return 0
480
481
def __repr__(self):
482
return "Free hexadecimal string monoid"
483
484
def __call__(self, x, check=True):
485
r"""
486
Return ``x`` coerced into this free monoid.
487
488
One can create a free hexadecimal string monoid element from a
489
Python string of a list of integers in `\{ 0, \dots, 15 \}`.
490
491
EXAMPLES::
492
493
sage: S = HexadecimalStrings()
494
sage: S('0a0a0a019f019f')
495
0a0a0a019f019f
496
sage: S.gen(0)
497
0
498
sage: S.gen(1)
499
1
500
sage: S([ i for i in range(16) ])
501
0123456789abcdef
502
"""
503
## There should really some careful type checking here...
504
if isinstance(x, StringMonoidElement) and x.parent() == self:
505
return x
506
elif isinstance(x, list):
507
return StringMonoidElement(self, x, check)
508
elif isinstance(x, str):
509
return StringMonoidElement(self, x, check)
510
else:
511
raise TypeError("Argument x (= %s) is of the wrong type." % x)
512
513
def encoding(self, S, padic=False):
514
r"""
515
The encoding of the string ``S`` as a hexadecimal string element.
516
517
The default is to keep the standard right-to-left byte encoding, e.g.
518
519
::
520
521
A = '\x41' -> 41
522
B = '\x42' -> 42
523
.
524
.
525
.
526
Z = '\x5a' -> 5a
527
528
rather than a left-to-right representation A = 65 -> 14.
529
Although standard (e.g., in the Python constructor '\xhh'),
530
this can be confusing when the string reads left-to-right.
531
532
Set ``padic=True`` to reverse the character encoding.
533
534
EXAMPLES::
535
536
sage: S = HexadecimalStrings()
537
sage: S.encoding('A')
538
41
539
sage: S.encoding('A',padic=True)
540
14
541
sage: S.encoding(' ',padic=False)
542
20
543
sage: S.encoding(' ',padic=True)
544
02
545
"""
546
from Crypto.Util.number import bytes_to_long
547
hex_string = []
548
for i in range(len(S)):
549
n = int(bytes_to_long(S[i]))
550
n0 = n % 16
551
n1 = n // 16
552
if not padic:
553
hex_chars = [n1, n0]
554
else:
555
hex_chars = [n0, n1]
556
hex_string.extend(hex_chars)
557
return self(hex_string)
558
559
class Radix64StringMonoid(StringMonoid_class):
560
r"""
561
The free radix 64 string monoid on 64 generators.
562
"""
563
564
def __init__(self):
565
r"""
566
Create free radix 64 string monoid on 64 generators.
567
568
EXAMPLES::
569
570
sage: S = Radix64Strings(); S
571
Free radix 64 string monoid
572
sage: x = S.gens()
573
sage: (x[50]*x[10])**3 * (x[60]*x[1]*x[19]*x[35])**2
574
yKyKyK8BTj8BTj
575
sage: S([ i for i in range(64) ])
576
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
577
"""
578
alph = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
579
StringMonoid_class.__init__(self, 64, [ alph[i] for i in range(64) ])
580
581
def __cmp__(self, other):
582
if not isinstance(other, Radix64StringMonoid):
583
return -1
584
return 0
585
586
def __repr__(self):
587
return "Free radix 64 string monoid"
588
589
def __call__(self, x, check=True):
590
r"""
591
Return ``x`` coerced into this free monoid.
592
593
One can create a free radix 64 string monoid element from a
594
Python string or a list of integers in `0, \dots, 63`, as for
595
generic ``FreeMonoids``.
596
597
EXAMPLES::
598
599
sage: S = Radix64Strings()
600
sage: S.gen(0)
601
A
602
sage: S.gen(1)
603
B
604
sage: S.gen(62)
605
+
606
sage: S.gen(63)
607
/
608
sage: S([ i for i in range(64) ])
609
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
610
"""
611
## There should really some careful type checking here...
612
if isinstance(x, StringMonoidElement) and x.parent() == self:
613
return x
614
elif isinstance(x, list):
615
return StringMonoidElement(self, x, check)
616
elif isinstance(x, str):
617
return StringMonoidElement(self, x, check)
618
else:
619
raise TypeError("Argument x (= %s) is of the wrong type." % x)
620
621
class AlphabeticStringMonoid(StringMonoid_class):
622
"""
623
The free alphabetic string monoid on generators A-Z.
624
625
EXAMPLES::
626
627
sage: S = AlphabeticStrings(); S
628
Free alphabetic string monoid on A-Z
629
sage: S.gen(0)
630
A
631
sage: S.gen(25)
632
Z
633
sage: S([ i for i in range(26) ])
634
ABCDEFGHIJKLMNOPQRSTUVWXYZ
635
"""
636
637
def __init__(self):
638
r"""
639
Create free alphabetic string monoid on generators A-Z.
640
641
EXAMPLES::
642
643
sage: S = AlphabeticStrings(); S
644
Free alphabetic string monoid on A-Z
645
sage: S.gen(0)
646
A
647
sage: S.gen(25)
648
Z
649
sage: S([ i for i in range(26) ])
650
ABCDEFGHIJKLMNOPQRSTUVWXYZ
651
"""
652
from sage.rings.all import RealField
653
RR = RealField()
654
# The characteristic frequency probability distribution of
655
# Robert Edward Lewand.
656
self._characteristic_frequency_lewand = {
657
"A": RR(0.08167), "B": RR(0.01492),
658
"C": RR(0.02782), "D": RR(0.04253),
659
"E": RR(0.12702), "F": RR(0.02228),
660
"G": RR(0.02015), "H": RR(0.06094),
661
"I": RR(0.06966), "J": RR(0.00153),
662
"K": RR(0.00772), "L": RR(0.04025),
663
"M": RR(0.02406), "N": RR(0.06749),
664
"O": RR(0.07507), "P": RR(0.01929),
665
"Q": RR(0.00095), "R": RR(0.05987),
666
"S": RR(0.06327), "T": RR(0.09056),
667
"U": RR(0.02758), "V": RR(0.00978),
668
"W": RR(0.02360), "X": RR(0.00150),
669
"Y": RR(0.01974), "Z": RR(0.00074)}
670
# The characteristic frequency probability distribution of
671
# H. Beker and F. Piper.
672
self._characteristic_frequency_beker_piper = {
673
"A": RR(0.082), "B": RR(0.015),
674
"C": RR(0.028), "D": RR(0.043),
675
"E": RR(0.127), "F": RR(0.022),
676
"G": RR(0.020), "H": RR(0.061),
677
"I": RR(0.070), "J": RR(0.002),
678
"K": RR(0.008), "L": RR(0.040),
679
"M": RR(0.024), "N": RR(0.067),
680
"O": RR(0.075), "P": RR(0.019),
681
"Q": RR(0.001), "R": RR(0.060),
682
"S": RR(0.063), "T": RR(0.091),
683
"U": RR(0.028), "V": RR(0.010),
684
"W": RR(0.023), "X": RR(0.001),
685
"Y": RR(0.020), "Z": RR(0.001)}
686
alph = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
687
StringMonoid_class.__init__(self, 26, [ alph[i] for i in range(26) ])
688
689
def __cmp__(self, other):
690
if not isinstance(other, AlphabeticStringMonoid):
691
return -1
692
return 0
693
694
def __repr__(self):
695
return "Free alphabetic string monoid on A-Z"
696
697
def __call__(self, x, check=True):
698
r"""
699
Return ``x`` coerced into this free monoid.
700
701
One can create a free alphabetic string monoid element from a
702
Python string, or a list of integers in `0, \dots,25`.
703
704
EXAMPLES::
705
706
sage: S = AlphabeticStrings()
707
sage: S.gen(0)
708
A
709
sage: S.gen(1)
710
B
711
sage: S.gen(25)
712
Z
713
sage: S([ i for i in range(26) ])
714
ABCDEFGHIJKLMNOPQRSTUVWXYZ
715
"""
716
## There should really some careful type checking here...
717
if isinstance(x, StringMonoidElement) and x.parent() == self:
718
return x
719
elif isinstance(x, list):
720
return StringMonoidElement(self, x, check)
721
elif isinstance(x, str):
722
return StringMonoidElement(self, x, check)
723
else:
724
raise TypeError("Argument x (= %s) is of the wrong type." % x)
725
726
def characteristic_frequency(self, table_name="beker_piper"):
727
r"""
728
Return a table of the characteristic frequency probability
729
distribution of the English alphabet. In written English, various
730
letters of the English alphabet occur more frequently than others.
731
For example, the letter "E" appears more often than other
732
vowels such as "A", "I", "O", and "U". In long works of written
733
English such as books, the probability of a letter occurring tends
734
to stabilize around a value. We call this value the characteristic
735
frequency probability of the letter under consideration. When this
736
probability is considered for each letter of the English alphabet,
737
the resulting probabilities for all letters of this alphabet is
738
referred to as the characteristic frequency probability distribution.
739
Various studies report slightly different values for the
740
characteristic frequency probability of an English letter. For
741
instance, [Lew00]_ reports that "E" has a characteristic
742
frequency probability of 0.12702, while [BekPip82]_ reports this
743
value as 0.127. The concepts of characteristic frequency probability
744
and characteristic frequency probability distribution can also be
745
applied to non-empty alphabets other than the English alphabet.
746
747
The output of this method is different from that of the method
748
:func:`frequency_distribution()
749
<sage.monoids.string_monoid_element.StringMonoidElement.frequency_distribution>`.
750
One can think of the characteristic frequency probability of an
751
element in an alphabet `A` as the expected probability of that element
752
occurring. Let `S` be a string encoded using elements of `A`. The
753
frequency probability distribution corresponding to `S` provides us
754
with the frequency probability of each element of `A` as observed
755
occurring in `S`. Thus one distribution provides expected
756
probabilities, while the other provides observed probabilities.
757
758
INPUT:
759
760
- ``table_name`` -- (default ``"beker_piper"``) the table of
761
characteristic frequency probability distribution to use. The
762
following tables are supported:
763
764
- ``"beker_piper"`` -- the table of characteristic frequency
765
probability distribution by Beker and Piper [BekPip82]_. This is
766
the default table to use.
767
768
- ``"lewand"`` -- the table of characteristic frequency
769
probability distribution by Lewand as described on page 36
770
of [Lew00]_.
771
772
OUTPUT:
773
774
- A table of the characteristic frequency probability distribution
775
of the English alphabet. This is a dictionary of letter/probability
776
pairs.
777
778
EXAMPLES:
779
780
The characteristic frequency probability distribution table of
781
Beker and Piper [BekPip82]_::
782
783
sage: A = AlphabeticStrings()
784
sage: table = A.characteristic_frequency(table_name="beker_piper")
785
sage: sorted(table.items())
786
<BLANKLINE>
787
[('A', 0.0820000000000000),
788
('B', 0.0150000000000000),
789
('C', 0.0280000000000000),
790
('D', 0.0430000000000000),
791
('E', 0.127000000000000),
792
('F', 0.0220000000000000),
793
('G', 0.0200000000000000),
794
('H', 0.0610000000000000),
795
('I', 0.0700000000000000),
796
('J', 0.00200000000000000),
797
('K', 0.00800000000000000),
798
('L', 0.0400000000000000),
799
('M', 0.0240000000000000),
800
('N', 0.0670000000000000),
801
('O', 0.0750000000000000),
802
('P', 0.0190000000000000),
803
('Q', 0.00100000000000000),
804
('R', 0.0600000000000000),
805
('S', 0.0630000000000000),
806
('T', 0.0910000000000000),
807
('U', 0.0280000000000000),
808
('V', 0.0100000000000000),
809
('W', 0.0230000000000000),
810
('X', 0.00100000000000000),
811
('Y', 0.0200000000000000),
812
('Z', 0.00100000000000000)]
813
814
The characteristic frequency probability distribution table
815
of Lewand [Lew00]_::
816
817
sage: table = A.characteristic_frequency(table_name="lewand")
818
sage: sorted(table.items())
819
<BLANKLINE>
820
[('A', 0.0816700000000000),
821
('B', 0.0149200000000000),
822
('C', 0.0278200000000000),
823
('D', 0.0425300000000000),
824
('E', 0.127020000000000),
825
('F', 0.0222800000000000),
826
('G', 0.0201500000000000),
827
('H', 0.0609400000000000),
828
('I', 0.0696600000000000),
829
('J', 0.00153000000000000),
830
('K', 0.00772000000000000),
831
('L', 0.0402500000000000),
832
('M', 0.0240600000000000),
833
('N', 0.0674900000000000),
834
('O', 0.0750700000000000),
835
('P', 0.0192900000000000),
836
('Q', 0.000950000000000000),
837
('R', 0.0598700000000000),
838
('S', 0.0632700000000000),
839
('T', 0.0905600000000000),
840
('U', 0.0275800000000000),
841
('V', 0.00978000000000000),
842
('W', 0.0236000000000000),
843
('X', 0.00150000000000000),
844
('Y', 0.0197400000000000),
845
('Z', 0.000740000000000000)]
846
847
Illustrating the difference between :func:`characteristic_frequency`
848
and :func:`frequency_distribution() <sage.monoids.string_monoid_element.StringMonoidElement.frequency_distribution>`::
849
850
sage: A = AlphabeticStrings()
851
sage: M = A.encoding("abcd")
852
sage: FD = M.frequency_distribution().function()
853
sage: sorted(FD.items())
854
<BLANKLINE>
855
[(A, 0.250000000000000),
856
(B, 0.250000000000000),
857
(C, 0.250000000000000),
858
(D, 0.250000000000000)]
859
sage: CF = A.characteristic_frequency()
860
sage: sorted(CF.items())
861
<BLANKLINE>
862
[('A', 0.0820000000000000),
863
('B', 0.0150000000000000),
864
('C', 0.0280000000000000),
865
('D', 0.0430000000000000),
866
('E', 0.127000000000000),
867
('F', 0.0220000000000000),
868
('G', 0.0200000000000000),
869
('H', 0.0610000000000000),
870
('I', 0.0700000000000000),
871
('J', 0.00200000000000000),
872
('K', 0.00800000000000000),
873
('L', 0.0400000000000000),
874
('M', 0.0240000000000000),
875
('N', 0.0670000000000000),
876
('O', 0.0750000000000000),
877
('P', 0.0190000000000000),
878
('Q', 0.00100000000000000),
879
('R', 0.0600000000000000),
880
('S', 0.0630000000000000),
881
('T', 0.0910000000000000),
882
('U', 0.0280000000000000),
883
('V', 0.0100000000000000),
884
('W', 0.0230000000000000),
885
('X', 0.00100000000000000),
886
('Y', 0.0200000000000000),
887
('Z', 0.00100000000000000)]
888
889
TESTS:
890
891
The table name must be either "beker_piper" or "lewand"::
892
893
sage: table = A.characteristic_frequency(table_name="")
894
Traceback (most recent call last):
895
...
896
ValueError: Table name must be either 'beker_piper' or 'lewand'.
897
sage: table = A.characteristic_frequency(table_name="none")
898
Traceback (most recent call last):
899
...
900
ValueError: Table name must be either 'beker_piper' or 'lewand'.
901
902
REFERENCES:
903
904
.. [BekPip82] H. Beker and F. Piper. *Cipher Systems: The
905
Protection of Communications*. John Wiley and Sons, 1982.
906
907
.. [Lew00] Robert Edward Lewand. *Cryptological Mathematics*.
908
The Mathematical Association of America, 2000.
909
"""
910
supported_tables = ["beker_piper", "lewand"]
911
if table_name not in supported_tables:
912
raise ValueError(
913
"Table name must be either 'beker_piper' or 'lewand'.")
914
from copy import copy
915
if table_name == "beker_piper":
916
return copy(self._characteristic_frequency_beker_piper)
917
if table_name == "lewand":
918
return copy(self._characteristic_frequency_lewand)
919
920
def encoding(self, S):
921
r"""
922
The encoding of the string ``S`` in the alphabetic string monoid,
923
obtained by the monoid homomorphism
924
925
::
926
927
A -> A, ..., Z -> Z, a -> A, ..., z -> Z
928
929
and stripping away all other characters. It should be noted that
930
this is a non-injective monoid homomorphism.
931
932
EXAMPLES::
933
934
sage: S = AlphabeticStrings()
935
sage: s = S.encoding("The cat in the hat."); s
936
THECATINTHEHAT
937
sage: s.decoding()
938
'THECATINTHEHAT'
939
"""
940
return self(strip_encoding(S))
941
942