1
1
using System ;
2
2
using System . Collections ;
3
+ using System . Collections . Frozen ;
3
4
using System . Collections . Generic ;
4
5
using System . Linq ;
5
6
@@ -9,26 +10,34 @@ public class Alphabet : IReadOnlyList<char>, IEquatable<Alphabet>
9
10
{
10
11
public Alphabet ( ReadOnlySpan < char > characters )
11
12
{
12
- // convert to uppercase
13
- var upper = new char [ characters . Length ] ;
14
- characters . ToUpperInvariant ( upper ) ;
13
+ // build index lookup
14
+ var lookup = new Dictionary < char , int > ( characters . Length ) ;
15
+ for ( int i = 0 ; i < characters . Length ; i ++ )
16
+ {
17
+ lookup . Add ( characters [ i ] , i ) ;
18
+ }
19
+ _indexLookup = lookup . ToFrozenDictionary ( ) ;
15
20
16
- // check for duplicates
17
- for ( int i = 0 ; i < upper . Length - 1 ; i ++ )
21
+ // build index lookup (case-insensitive)
22
+ var lookupUpper = new Dictionary < char , int > ( characters . Length ) ;
23
+ for ( int i = 0 ; i < characters . Length ; i ++ )
18
24
{
19
- var remaining = upper . AsSpan ( ) [ ( i + 1 ) ..] ;
20
- if ( remaining . Contains ( upper [ i ] ) )
21
- {
22
- throw new ArgumentException ( "Duplicate character found in the alphabet." , nameof ( characters ) ) ;
23
- }
25
+ lookupUpper . Add ( characters [ i ] . ToUpperInvariant ( ) , i ) ;
24
26
}
27
+ _indexLookupUpper = lookupUpper . ToFrozenDictionary ( ) ;
25
28
26
- _chars = upper ;
27
- _str = new string ( _chars ) ;
29
+ _chars = characters . ToArray ( ) ;
30
+ _str = new string ( characters ) ;
31
+ DoubleLength = characters . Length * 2 ;
32
+ MinusLength = - characters . Length ;
28
33
}
29
34
30
35
private readonly char [ ] _chars ;
31
36
private readonly string _str ;
37
+ private readonly FrozenDictionary < char , int > _indexLookup ;
38
+ private readonly FrozenDictionary < char , int > _indexLookupUpper ;
39
+ private readonly int DoubleLength ;
40
+ private readonly int MinusLength ;
32
41
33
42
public static Alphabet FromKeyword ( string keyword , Alphabet @base , bool throwOnDuplicates = false )
34
43
{
@@ -60,13 +69,15 @@ public ReadOnlySpan<char> this[Range index]
60
69
public int Length => _chars . Length ;
61
70
int IReadOnlyCollection < char > . Count => Length ;
62
71
63
- public bool Contains ( char c ) => _chars . Contains ( c ) ;
72
+ public bool Contains ( char c ) => _indexLookup . ContainsKey ( c ) ;
64
73
65
74
public bool Contains ( char c , IEqualityComparer < char > comparer ) => IndexOf ( c , comparer ) != - 1 ;
66
75
67
76
public bool Contains ( char c , StringComparison comparison ) => _str . Contains ( c , comparison ) ;
68
77
69
- public int IndexOf ( char c ) => _str . IndexOf ( c ) ;
78
+ public bool ContainsIgnoreCase ( char c ) => _indexLookupUpper . ContainsKey ( c . ToUpperInvariant ( ) ) ;
79
+
80
+ public int IndexOf ( char c ) => _indexLookup . TryGetValue ( c , out var index ) ? index : - 1 ;
70
81
71
82
public int IndexOf ( char c , StringComparison comparison ) => _str . IndexOf ( c , comparison ) ;
72
83
@@ -86,14 +97,42 @@ public int IndexOf(char c, IEqualityComparer<char> comparer)
86
97
}
87
98
}
88
99
89
- public int IndexOfIgnoreCase ( char c ) => _str . IndexOf ( c , StringComparison . OrdinalIgnoreCase ) ;
100
+ public int IndexOfIgnoreCase ( char c ) => _indexLookupUpper . TryGetValue ( c . ToUpperInvariant ( ) , out var index ) ? index : - 1 ;
90
101
91
- public char AtMod ( int index ) => _chars [ index switch
102
+ public char AtMod ( int index )
92
103
{
93
- < 0 => ( Length - ( - index % Length ) ) % Length ,
94
- _ => index % Length ,
95
- } ] ;
104
+ // negative
105
+ if ( index < 0 )
106
+ {
107
+ // spare modulo operation
108
+ if ( index > MinusLength )
109
+ {
110
+ index = index - MinusLength ;
111
+ }
96
112
113
+ else
114
+ {
115
+ index = ( Length - ( - index % Length ) ) % Length ;
116
+ }
117
+ }
118
+
119
+ // overflow
120
+ else if ( index >= Length )
121
+ {
122
+ // spare modulo operation
123
+ if ( index < DoubleLength )
124
+ {
125
+ index = index - Length ;
126
+ }
127
+
128
+ else
129
+ {
130
+ index = index % Length ;
131
+ }
132
+ }
133
+
134
+ return _chars [ index ] ;
135
+ }
97
136
98
137
public override bool Equals ( object obj ) => Equals ( obj as Alphabet ) ;
99
138
@@ -115,7 +154,12 @@ public int IndexOf(char c, IEqualityComparer<char> comparer)
115
154
116
155
public override string ToString ( ) => _str ;
117
156
118
- public char [ ] ToCharArray ( ) => _chars . ToArray ( ) ;
157
+ public char [ ] ToCharArray ( )
158
+ {
159
+ var copy = new char [ _chars . Length ] ;
160
+ _chars . CopyTo ( copy , 0 ) ;
161
+ return copy ;
162
+ }
119
163
120
164
public ReadOnlyMemory < char > ToMemory ( ) => _str . AsMemory ( ) ;
121
165
0 commit comments