Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/monoids/string_monoid.py
8815 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
bit_string = []
368
for i in range(len(S)):
369
n = ord(S[i])
370
bits = []
371
for i in range(8):
372
bits.append(n%2)
373
n = n >> 1
374
if not padic:
375
bits.reverse()
376
bit_string.extend(bits)
377
return self(bit_string)
378
379
# def ngens(self):
380
# r"""
381
# Return the number of generators of this free binary string monoid.
382
# There are only 2 elements in the binary number system. Hence, this
383
# is the number of generators.
384
385
# EXAMPLES::
386
387
# sage: S = BinaryStrings()
388
# sage: S.ngens()
389
# 2
390
# """
391
# return 2
392
393
class OctalStringMonoid(StringMonoid_class):
394
r"""
395
The free octal string monoid on generators `\{ 0, 1, \dots, 7 \}`.
396
"""
397
398
def __init__(self):
399
r"""
400
Create free octal string monoid on generators `\{ 0, 1, \dots, 7 \}`.
401
402
EXAMPLES::
403
404
sage: S = OctalStrings(); S
405
Free octal string monoid
406
sage: x = S.gens()
407
sage: (x[0]*x[7])**3 * (x[0]*x[1]*x[6]*x[5])**2
408
07070701650165
409
sage: S([ i for i in range(8) ])
410
01234567
411
"""
412
StringMonoid_class.__init__(self, 8, [ str(i) for i in range(8) ])
413
414
def __cmp__(self, other):
415
if not isinstance(other, OctalStringMonoid):
416
return -1
417
return 0
418
419
def __repr__(self):
420
return "Free octal string monoid"
421
422
def __call__(self, x, check=True):
423
r"""
424
Return ``x`` coerced into this free monoid.
425
426
One can create a free octal string monoid element from a
427
Python string of 0's to 7's or list of integers.
428
429
EXAMPLES::
430
431
sage: S = OctalStrings()
432
sage: S('07070701650165')
433
07070701650165
434
sage: S.gen(0)
435
0
436
sage: S.gen(1)
437
1
438
sage: S([ i for i in range(8) ])
439
01234567
440
"""
441
## There should really some careful type checking here...
442
if isinstance(x, StringMonoidElement) and x.parent() == self:
443
return x
444
elif isinstance(x, list):
445
return StringMonoidElement(self, x, check)
446
elif isinstance(x, str):
447
return StringMonoidElement(self, x, check)
448
else:
449
raise TypeError("Argument x (= %s) is of the wrong type." % x)
450
451
class HexadecimalStringMonoid(StringMonoid_class):
452
r"""
453
The free hexadecimal string monoid on generators
454
`\{ 0, 1, \dots, 9, a, b, c, d, e, f \}`.
455
"""
456
457
def __init__(self):
458
r"""
459
Create free hexadecimal string monoid on generators
460
`\{ 0, 1, \dots, 9, a, b, c, d, e, f \}`.
461
462
EXAMPLES::
463
464
sage: S = HexadecimalStrings(); S
465
Free hexadecimal string monoid
466
sage: x = S.gens()
467
sage: (x[0]*x[10])**3 * (x[0]*x[1]*x[9]*x[15])**2
468
0a0a0a019f019f
469
sage: S([ i for i in range(16) ])
470
0123456789abcdef
471
"""
472
alph = '0123456789abcdef'
473
StringMonoid_class.__init__(self, 16, [ alph[i] for i in range(16) ])
474
475
def __cmp__(self, other):
476
if not isinstance(other, HexadecimalStringMonoid):
477
return -1
478
return 0
479
480
def __repr__(self):
481
return "Free hexadecimal string monoid"
482
483
def __call__(self, x, check=True):
484
r"""
485
Return ``x`` coerced into this free monoid.
486
487
One can create a free hexadecimal string monoid element from a
488
Python string of a list of integers in `\{ 0, \dots, 15 \}`.
489
490
EXAMPLES::
491
492
sage: S = HexadecimalStrings()
493
sage: S('0a0a0a019f019f')
494
0a0a0a019f019f
495
sage: S.gen(0)
496
0
497
sage: S.gen(1)
498
1
499
sage: S([ i for i in range(16) ])
500
0123456789abcdef
501
"""
502
## There should really some careful type checking here...
503
if isinstance(x, StringMonoidElement) and x.parent() == self:
504
return x
505
elif isinstance(x, list):
506
return StringMonoidElement(self, x, check)
507
elif isinstance(x, str):
508
return StringMonoidElement(self, x, check)
509
else:
510
raise TypeError("Argument x (= %s) is of the wrong type." % x)
511
512
def encoding(self, S, padic=False):
513
r"""
514
The encoding of the string ``S`` as a hexadecimal string element.
515
516
The default is to keep the standard right-to-left byte encoding, e.g.
517
518
::
519
520
A = '\x41' -> 41
521
B = '\x42' -> 42
522
.
523
.
524
.
525
Z = '\x5a' -> 5a
526
527
rather than a left-to-right representation A = 65 -> 14.
528
Although standard (e.g., in the Python constructor '\xhh'),
529
this can be confusing when the string reads left-to-right.
530
531
Set ``padic=True`` to reverse the character encoding.
532
533
EXAMPLES::
534
535
sage: S = HexadecimalStrings()
536
sage: S.encoding('A')
537
41
538
sage: S.encoding('A',padic=True)
539
14
540
sage: S.encoding(' ',padic=False)
541
20
542
sage: S.encoding(' ',padic=True)
543
02
544
"""
545
hex_string = []
546
for i in range(len(S)):
547
n = ord(S[i])
548
n0 = n % 16
549
n1 = n // 16
550
if not padic:
551
hex_chars = [n1, n0]
552
else:
553
hex_chars = [n0, n1]
554
hex_string.extend(hex_chars)
555
return self(hex_string)
556
557
class Radix64StringMonoid(StringMonoid_class):
558
r"""
559
The free radix 64 string monoid on 64 generators.
560
"""
561
562
def __init__(self):
563
r"""
564
Create free radix 64 string monoid on 64 generators.
565
566
EXAMPLES::
567
568
sage: S = Radix64Strings(); S
569
Free radix 64 string monoid
570
sage: x = S.gens()
571
sage: (x[50]*x[10])**3 * (x[60]*x[1]*x[19]*x[35])**2
572
yKyKyK8BTj8BTj
573
sage: S([ i for i in range(64) ])
574
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
575
"""
576
alph = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
577
StringMonoid_class.__init__(self, 64, [ alph[i] for i in range(64) ])
578
579
def __cmp__(self, other):
580
if not isinstance(other, Radix64StringMonoid):
581
return -1
582
return 0
583
584
def __repr__(self):
585
return "Free radix 64 string monoid"
586
587
def __call__(self, x, check=True):
588
r"""
589
Return ``x`` coerced into this free monoid.
590
591
One can create a free radix 64 string monoid element from a
592
Python string or a list of integers in `0, \dots, 63`, as for
593
generic ``FreeMonoids``.
594
595
EXAMPLES::
596
597
sage: S = Radix64Strings()
598
sage: S.gen(0)
599
A
600
sage: S.gen(1)
601
B
602
sage: S.gen(62)
603
+
604
sage: S.gen(63)
605
/
606
sage: S([ i for i in range(64) ])
607
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
608
"""
609
## There should really some careful type checking here...
610
if isinstance(x, StringMonoidElement) and x.parent() == self:
611
return x
612
elif isinstance(x, list):
613
return StringMonoidElement(self, x, check)
614
elif isinstance(x, str):
615
return StringMonoidElement(self, x, check)
616
else:
617
raise TypeError("Argument x (= %s) is of the wrong type." % x)
618
619
class AlphabeticStringMonoid(StringMonoid_class):
620
"""
621
The free alphabetic string monoid on generators A-Z.
622
623
EXAMPLES::
624
625
sage: S = AlphabeticStrings(); S
626
Free alphabetic string monoid on A-Z
627
sage: S.gen(0)
628
A
629
sage: S.gen(25)
630
Z
631
sage: S([ i for i in range(26) ])
632
ABCDEFGHIJKLMNOPQRSTUVWXYZ
633
"""
634
635
def __init__(self):
636
r"""
637
Create free alphabetic string monoid on generators A-Z.
638
639
EXAMPLES::
640
641
sage: S = AlphabeticStrings(); S
642
Free alphabetic string monoid on A-Z
643
sage: S.gen(0)
644
A
645
sage: S.gen(25)
646
Z
647
sage: S([ i for i in range(26) ])
648
ABCDEFGHIJKLMNOPQRSTUVWXYZ
649
"""
650
from sage.rings.all import RealField
651
RR = RealField()
652
# The characteristic frequency probability distribution of
653
# Robert Edward Lewand.
654
self._characteristic_frequency_lewand = {
655
"A": RR(0.08167), "B": RR(0.01492),
656
"C": RR(0.02782), "D": RR(0.04253),
657
"E": RR(0.12702), "F": RR(0.02228),
658
"G": RR(0.02015), "H": RR(0.06094),
659
"I": RR(0.06966), "J": RR(0.00153),
660
"K": RR(0.00772), "L": RR(0.04025),
661
"M": RR(0.02406), "N": RR(0.06749),
662
"O": RR(0.07507), "P": RR(0.01929),
663
"Q": RR(0.00095), "R": RR(0.05987),
664
"S": RR(0.06327), "T": RR(0.09056),
665
"U": RR(0.02758), "V": RR(0.00978),
666
"W": RR(0.02360), "X": RR(0.00150),
667
"Y": RR(0.01974), "Z": RR(0.00074)}
668
# The characteristic frequency probability distribution of
669
# H. Beker and F. Piper.
670
self._characteristic_frequency_beker_piper = {
671
"A": RR(0.082), "B": RR(0.015),
672
"C": RR(0.028), "D": RR(0.043),
673
"E": RR(0.127), "F": RR(0.022),
674
"G": RR(0.020), "H": RR(0.061),
675
"I": RR(0.070), "J": RR(0.002),
676
"K": RR(0.008), "L": RR(0.040),
677
"M": RR(0.024), "N": RR(0.067),
678
"O": RR(0.075), "P": RR(0.019),
679
"Q": RR(0.001), "R": RR(0.060),
680
"S": RR(0.063), "T": RR(0.091),
681
"U": RR(0.028), "V": RR(0.010),
682
"W": RR(0.023), "X": RR(0.001),
683
"Y": RR(0.020), "Z": RR(0.001)}
684
alph = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
685
StringMonoid_class.__init__(self, 26, [ alph[i] for i in range(26) ])
686
687
def __cmp__(self, other):
688
if not isinstance(other, AlphabeticStringMonoid):
689
return -1
690
return 0
691
692
def __repr__(self):
693
return "Free alphabetic string monoid on A-Z"
694
695
def __call__(self, x, check=True):
696
r"""
697
Return ``x`` coerced into this free monoid.
698
699
One can create a free alphabetic string monoid element from a
700
Python string, or a list of integers in `0, \dots,25`.
701
702
EXAMPLES::
703
704
sage: S = AlphabeticStrings()
705
sage: S.gen(0)
706
A
707
sage: S.gen(1)
708
B
709
sage: S.gen(25)
710
Z
711
sage: S([ i for i in range(26) ])
712
ABCDEFGHIJKLMNOPQRSTUVWXYZ
713
"""
714
## There should really some careful type checking here...
715
if isinstance(x, StringMonoidElement) and x.parent() == self:
716
return x
717
elif isinstance(x, list):
718
return StringMonoidElement(self, x, check)
719
elif isinstance(x, str):
720
return StringMonoidElement(self, x, check)
721
else:
722
raise TypeError("Argument x (= %s) is of the wrong type." % x)
723
724
def characteristic_frequency(self, table_name="beker_piper"):
725
r"""
726
Return a table of the characteristic frequency probability
727
distribution of the English alphabet. In written English, various
728
letters of the English alphabet occur more frequently than others.
729
For example, the letter "E" appears more often than other
730
vowels such as "A", "I", "O", and "U". In long works of written
731
English such as books, the probability of a letter occurring tends
732
to stabilize around a value. We call this value the characteristic
733
frequency probability of the letter under consideration. When this
734
probability is considered for each letter of the English alphabet,
735
the resulting probabilities for all letters of this alphabet is
736
referred to as the characteristic frequency probability distribution.
737
Various studies report slightly different values for the
738
characteristic frequency probability of an English letter. For
739
instance, [Lew00]_ reports that "E" has a characteristic
740
frequency probability of 0.12702, while [BekPip82]_ reports this
741
value as 0.127. The concepts of characteristic frequency probability
742
and characteristic frequency probability distribution can also be
743
applied to non-empty alphabets other than the English alphabet.
744
745
The output of this method is different from that of the method
746
:func:`frequency_distribution()
747
<sage.monoids.string_monoid_element.StringMonoidElement.frequency_distribution>`.
748
One can think of the characteristic frequency probability of an
749
element in an alphabet `A` as the expected probability of that element
750
occurring. Let `S` be a string encoded using elements of `A`. The
751
frequency probability distribution corresponding to `S` provides us
752
with the frequency probability of each element of `A` as observed
753
occurring in `S`. Thus one distribution provides expected
754
probabilities, while the other provides observed probabilities.
755
756
INPUT:
757
758
- ``table_name`` -- (default ``"beker_piper"``) the table of
759
characteristic frequency probability distribution to use. The
760
following tables are supported:
761
762
- ``"beker_piper"`` -- the table of characteristic frequency
763
probability distribution by Beker and Piper [BekPip82]_. This is
764
the default table to use.
765
766
- ``"lewand"`` -- the table of characteristic frequency
767
probability distribution by Lewand as described on page 36
768
of [Lew00]_.
769
770
OUTPUT:
771
772
- A table of the characteristic frequency probability distribution
773
of the English alphabet. This is a dictionary of letter/probability
774
pairs.
775
776
EXAMPLES:
777
778
The characteristic frequency probability distribution table of
779
Beker and Piper [BekPip82]_::
780
781
sage: A = AlphabeticStrings()
782
sage: table = A.characteristic_frequency(table_name="beker_piper")
783
sage: sorted(table.items())
784
<BLANKLINE>
785
[('A', 0.0820000000000000),
786
('B', 0.0150000000000000),
787
('C', 0.0280000000000000),
788
('D', 0.0430000000000000),
789
('E', 0.127000000000000),
790
('F', 0.0220000000000000),
791
('G', 0.0200000000000000),
792
('H', 0.0610000000000000),
793
('I', 0.0700000000000000),
794
('J', 0.00200000000000000),
795
('K', 0.00800000000000000),
796
('L', 0.0400000000000000),
797
('M', 0.0240000000000000),
798
('N', 0.0670000000000000),
799
('O', 0.0750000000000000),
800
('P', 0.0190000000000000),
801
('Q', 0.00100000000000000),
802
('R', 0.0600000000000000),
803
('S', 0.0630000000000000),
804
('T', 0.0910000000000000),
805
('U', 0.0280000000000000),
806
('V', 0.0100000000000000),
807
('W', 0.0230000000000000),
808
('X', 0.00100000000000000),
809
('Y', 0.0200000000000000),
810
('Z', 0.00100000000000000)]
811
812
The characteristic frequency probability distribution table
813
of Lewand [Lew00]_::
814
815
sage: table = A.characteristic_frequency(table_name="lewand")
816
sage: sorted(table.items())
817
<BLANKLINE>
818
[('A', 0.0816700000000000),
819
('B', 0.0149200000000000),
820
('C', 0.0278200000000000),
821
('D', 0.0425300000000000),
822
('E', 0.127020000000000),
823
('F', 0.0222800000000000),
824
('G', 0.0201500000000000),
825
('H', 0.0609400000000000),
826
('I', 0.0696600000000000),
827
('J', 0.00153000000000000),
828
('K', 0.00772000000000000),
829
('L', 0.0402500000000000),
830
('M', 0.0240600000000000),
831
('N', 0.0674900000000000),
832
('O', 0.0750700000000000),
833
('P', 0.0192900000000000),
834
('Q', 0.000950000000000000),
835
('R', 0.0598700000000000),
836
('S', 0.0632700000000000),
837
('T', 0.0905600000000000),
838
('U', 0.0275800000000000),
839
('V', 0.00978000000000000),
840
('W', 0.0236000000000000),
841
('X', 0.00150000000000000),
842
('Y', 0.0197400000000000),
843
('Z', 0.000740000000000000)]
844
845
Illustrating the difference between :func:`characteristic_frequency`
846
and :func:`frequency_distribution() <sage.monoids.string_monoid_element.StringMonoidElement.frequency_distribution>`::
847
848
sage: A = AlphabeticStrings()
849
sage: M = A.encoding("abcd")
850
sage: FD = M.frequency_distribution().function()
851
sage: sorted(FD.items())
852
<BLANKLINE>
853
[(A, 0.250000000000000),
854
(B, 0.250000000000000),
855
(C, 0.250000000000000),
856
(D, 0.250000000000000)]
857
sage: CF = A.characteristic_frequency()
858
sage: sorted(CF.items())
859
<BLANKLINE>
860
[('A', 0.0820000000000000),
861
('B', 0.0150000000000000),
862
('C', 0.0280000000000000),
863
('D', 0.0430000000000000),
864
('E', 0.127000000000000),
865
('F', 0.0220000000000000),
866
('G', 0.0200000000000000),
867
('H', 0.0610000000000000),
868
('I', 0.0700000000000000),
869
('J', 0.00200000000000000),
870
('K', 0.00800000000000000),
871
('L', 0.0400000000000000),
872
('M', 0.0240000000000000),
873
('N', 0.0670000000000000),
874
('O', 0.0750000000000000),
875
('P', 0.0190000000000000),
876
('Q', 0.00100000000000000),
877
('R', 0.0600000000000000),
878
('S', 0.0630000000000000),
879
('T', 0.0910000000000000),
880
('U', 0.0280000000000000),
881
('V', 0.0100000000000000),
882
('W', 0.0230000000000000),
883
('X', 0.00100000000000000),
884
('Y', 0.0200000000000000),
885
('Z', 0.00100000000000000)]
886
887
TESTS:
888
889
The table name must be either "beker_piper" or "lewand"::
890
891
sage: table = A.characteristic_frequency(table_name="")
892
Traceback (most recent call last):
893
...
894
ValueError: Table name must be either 'beker_piper' or 'lewand'.
895
sage: table = A.characteristic_frequency(table_name="none")
896
Traceback (most recent call last):
897
...
898
ValueError: Table name must be either 'beker_piper' or 'lewand'.
899
900
REFERENCES:
901
902
.. [BekPip82] H. Beker and F. Piper. *Cipher Systems: The
903
Protection of Communications*. John Wiley and Sons, 1982.
904
905
.. [Lew00] Robert Edward Lewand. *Cryptological Mathematics*.
906
The Mathematical Association of America, 2000.
907
"""
908
supported_tables = ["beker_piper", "lewand"]
909
if table_name not in supported_tables:
910
raise ValueError(
911
"Table name must be either 'beker_piper' or 'lewand'.")
912
from copy import copy
913
if table_name == "beker_piper":
914
return copy(self._characteristic_frequency_beker_piper)
915
if table_name == "lewand":
916
return copy(self._characteristic_frequency_lewand)
917
918
def encoding(self, S):
919
r"""
920
The encoding of the string ``S`` in the alphabetic string monoid,
921
obtained by the monoid homomorphism
922
923
::
924
925
A -> A, ..., Z -> Z, a -> A, ..., z -> Z
926
927
and stripping away all other characters. It should be noted that
928
this is a non-injective monoid homomorphism.
929
930
EXAMPLES::
931
932
sage: S = AlphabeticStrings()
933
sage: s = S.encoding("The cat in the hat."); s
934
THECATINTHEHAT
935
sage: s.decoding()
936
'THECATINTHEHAT'
937
"""
938
return self(strip_encoding(S))
939
940