line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Chemistry::Harmonia; |
2
|
|
|
|
|
|
|
|
3
|
8
|
|
|
8
|
|
311519
|
use 5.008008; |
|
8
|
|
|
|
|
30
|
|
|
8
|
|
|
|
|
523
|
|
4
|
8
|
|
|
8
|
|
73
|
use strict; |
|
8
|
|
|
|
|
17
|
|
|
8
|
|
|
|
|
1493
|
|
5
|
8
|
|
|
8
|
|
45
|
use warnings; |
|
8
|
|
|
|
|
50
|
|
|
8
|
|
|
|
|
322
|
|
6
|
|
|
|
|
|
|
|
7
|
8
|
|
|
8
|
|
125
|
use Carp; |
|
8
|
|
|
|
|
15
|
|
|
8
|
|
|
|
|
975
|
|
8
|
8
|
|
|
8
|
|
18284
|
use Chemistry::File::Formula; |
|
8
|
|
|
|
|
7651042
|
|
|
8
|
|
|
|
|
115
|
|
9
|
8
|
|
|
8
|
|
21519
|
use Algorithm::Combinatorics qw( variations_with_repetition subsets ); |
|
8
|
|
|
|
|
1186969
|
|
|
8
|
|
|
|
|
819
|
|
10
|
8
|
|
|
8
|
|
9328
|
use String::CRC::Cksum qw( cksum ); |
|
8
|
|
|
|
|
925482
|
|
|
8
|
|
|
|
|
717
|
|
11
|
8
|
|
|
8
|
|
19574
|
use Math::BigInt qw( blcm bgcd ); |
|
8
|
|
|
|
|
2510123
|
|
|
8
|
|
|
|
|
50
|
|
12
|
8
|
|
|
8
|
|
194738
|
use Math::BigRat; |
|
8
|
|
|
|
|
417543
|
|
|
8
|
|
|
|
|
68
|
|
13
|
8
|
|
|
8
|
|
22811
|
use Math::Assistant qw(:algebra); |
|
8
|
|
|
|
|
25990
|
|
|
8
|
|
|
|
|
1307
|
|
14
|
|
|
|
|
|
|
|
15
|
8
|
|
|
8
|
|
19406
|
use Data::Dumper; |
|
8
|
|
|
|
|
72302
|
|
|
8
|
|
|
|
|
1557
|
|
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
require Exporter; |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
our @ISA = qw(Exporter); |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
our %EXPORT_TAGS = ( 'all' => [ qw( |
23
|
|
|
|
|
|
|
parse_chem_mix |
24
|
|
|
|
|
|
|
prepare_mix |
25
|
|
|
|
|
|
|
oxidation_state |
26
|
|
|
|
|
|
|
redox_test |
27
|
|
|
|
|
|
|
class_cir_brutto |
28
|
|
|
|
|
|
|
good_formula |
29
|
|
|
|
|
|
|
brutto_formula |
30
|
|
|
|
|
|
|
stoichiometry |
31
|
|
|
|
|
|
|
ttc_reaction |
32
|
|
|
|
|
|
|
) ], |
33
|
|
|
|
|
|
|
'redox' => [ qw( |
34
|
|
|
|
|
|
|
parse_chem_mix |
35
|
|
|
|
|
|
|
prepare_mix |
36
|
|
|
|
|
|
|
oxidation_state |
37
|
|
|
|
|
|
|
redox_test |
38
|
|
|
|
|
|
|
) ], |
39
|
|
|
|
|
|
|
'equation' => [ qw( |
40
|
|
|
|
|
|
|
parse_chem_mix |
41
|
|
|
|
|
|
|
prepare_mix |
42
|
|
|
|
|
|
|
class_cir_brutto |
43
|
|
|
|
|
|
|
good_formula |
44
|
|
|
|
|
|
|
brutto_formula |
45
|
|
|
|
|
|
|
stoichiometry |
46
|
|
|
|
|
|
|
ttc_reaction |
47
|
|
|
|
|
|
|
) ], |
48
|
|
|
|
|
|
|
); |
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); |
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
our @EXPORT = qw( ); |
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
our $VERSION = '0.118'; |
55
|
|
|
|
|
|
|
|
56
|
8
|
|
|
8
|
|
159
|
use base qw(Exporter); |
|
8
|
|
|
|
|
13
|
|
|
8
|
|
|
|
|
118165
|
|
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
# Returns array of "good" chemical formulas |
60
|
|
|
|
|
|
|
sub good_formula{ |
61
|
46
|
|
50
|
46
|
1
|
46952
|
my $substance = shift || return; |
62
|
46
|
|
|
|
|
80
|
my $opts = shift; |
63
|
|
|
|
|
|
|
|
64
|
46
|
|
|
|
|
87
|
for( $substance ){ |
65
|
46
|
|
|
|
|
71
|
my $m = '(?:1|!|\|)'; # mask |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
# Replacement by iodine |
68
|
46
|
|
|
|
|
441
|
s/^(\W*)$m(\d*)$/$1I$2/; |
69
|
46
|
|
|
|
|
106
|
s/j/I/ig; |
70
|
|
|
|
|
|
|
|
71
|
46
|
|
|
|
|
86
|
s/\$/s/g; # for 'S' or 's' |
72
|
|
|
|
|
|
|
|
73
|
46
|
|
|
|
|
344
|
s/((?:^|[^BCGLNPRT])A)$m/$1l/ig; # for 'Al' |
74
|
46
|
|
|
|
|
280
|
s/((?:^|[^ACT])L)$m/$1i/ig; # for 'Li' |
75
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
# Is it 'o' or oxigen? |
77
|
46
|
|
|
|
|
198
|
1 while s/(?<=[CHNP])0(?![,.]\d)/\$/g; # temporarilly to replace --> '$' |
78
|
46
|
|
|
|
|
165
|
1 while s/(? 'o' |
79
|
|
|
|
|
|
|
|
80
|
46
|
|
|
|
|
298
|
s/(?<=[ACT])$m(?=\D|$)/l/ig; # for Al, Cl, Tl |
81
|
46
|
|
|
|
|
258
|
s/(?<=[BLNS])$m(?=\D|$)/i/ig; # for Bi, Li, Ni, Si (without Ti) |
82
|
|
|
|
|
|
|
|
83
|
46
|
|
|
|
|
78
|
s/Q/O/g; # for oxigen |
84
|
46
|
|
|
|
|
208
|
s/(?<=[AHMRS])q/g/ig; # for Aq, Hq, Mq, Rq, Sq |
85
|
|
|
|
|
|
|
} |
86
|
|
|
|
|
|
|
|
87
|
46
|
|
|
|
|
68
|
my(@maU, @maUl); |
88
|
0
|
|
|
|
|
0
|
my %sa; # Letters of possible atoms |
89
|
|
|
|
|
|
|
|
90
|
46
|
|
|
|
|
107
|
my $adb = &_atoms_db; |
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
# Possible atoms of substance |
93
|
46
|
|
|
|
|
347
|
for( my $i = 0; $i < @$adb; $i+=5 ){ |
94
|
5382
|
|
|
|
|
6836
|
$_ = $adb->[$i]; |
95
|
|
|
|
|
|
|
|
96
|
5382
|
100
|
|
|
|
47255
|
next if $substance !~ /$_/i; |
97
|
|
|
|
|
|
|
|
98
|
181
|
100
|
|
|
|
338
|
if( length > 1 ){ |
99
|
78
|
|
|
|
|
131
|
push @maUl, $_; # Double chem.symbol: 1th - CAPITAL, 2th - small |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
}else{ |
102
|
103
|
|
|
|
|
195
|
push @maU, $_; # Unary chem.symbol |
103
|
|
|
|
|
|
|
} |
104
|
181
|
|
|
|
|
1185
|
$sa{ uc $_ } = '' for split //; # String of CAPITAL letters |
105
|
|
|
|
|
|
|
} |
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
# All atom letters (CAPITAL) || return 0 |
108
|
46
|
|
50
|
|
|
232
|
my $sas = join('', keys %sa) || return; |
109
|
|
|
|
|
|
|
|
110
|
46
|
|
|
|
|
83
|
my $mz = '(?
|
111
|
|
|
|
|
|
|
|
112
|
46
|
|
|
|
|
83
|
for( $substance ){ |
113
|
46
|
|
|
|
|
3567
|
s/,/./g; |
114
|
46
|
|
|
|
|
2070
|
s/[^0-9\$\.\*\(\)\[\]\{\}$sas]+//ig; # to clean bad symbols |
115
|
|
|
|
|
|
|
|
116
|
46
|
|
|
|
|
267
|
s/\.+$//; # to clean points |
117
|
46
|
|
|
|
|
268
|
s/(?
|
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
# to clean sequences not unary symbols |
120
|
46
|
|
100
|
|
|
1423
|
while( @maU && $sas =~ /([^@maU])/gx ){ |
121
|
42
|
|
|
|
|
273
|
my $ul = $1; |
122
|
42
|
|
|
|
|
1374
|
s/($ul)$ul+($ul)/$1$2/ig; |
123
|
|
|
|
|
|
|
} |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
# remove bordering brackets |
126
|
46
|
|
|
|
|
85
|
our( $maskr, $maskf, $maskq ); |
127
|
46
|
|
|
|
|
315
|
$maskr = qr/\((?:(?>[^\(\)]+)|(??{$maskr}))*\)/; |
128
|
46
|
|
|
|
|
202
|
$maskf = qr/{(?:(?>[^{}]+)|(??{$maskf}))*}/; |
129
|
46
|
|
|
|
|
213
|
$maskq = qr/\[(?:(?>[^\[\]]+)|(??{$maskq}))*\]/; |
130
|
|
|
|
|
|
|
|
131
|
46
|
|
100
|
|
|
3678
|
s/^.|.$//g while /($maskr|$maskf|$maskq)/g && $_ eq $1; |
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
# to do small the last symbol of double |
134
|
46
|
|
|
|
|
188
|
s/([AEGLMR])(?=[^a-z]|$)/\l$1/g; |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
# Double symbol similar unary with coefficient in the end |
137
|
46
|
100
|
100
|
|
|
886
|
if( /^@maUl\d+$/i && @maU && /^(?i:[@maU])\L[@maU]\d+$/ ){ |
|
|
|
100
|
|
|
|
|
138
|
3
|
|
|
|
|
17
|
my @cf = ( "\u$_", uc ); |
139
|
|
|
|
|
|
|
|
140
|
3
|
100
|
100
|
|
|
32
|
if( /$mz/ && exists( $opts->{'zero2oxi'} ) ){ |
141
|
1
|
|
|
|
|
4
|
my @a = @cf; |
142
|
1
|
|
|
|
|
15
|
s/$mz/O/g for @a; |
143
|
1
|
|
|
|
|
50
|
return [ @cf, @a ]; |
144
|
|
|
|
|
|
|
} |
145
|
2
|
|
|
|
|
92
|
return \@cf; |
146
|
|
|
|
|
|
|
} |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
# Normalization of oxide-coated formulas (written through '*') |
149
|
43
|
100
|
|
|
|
130
|
if(/\*/){ |
150
|
4
|
|
|
|
|
7
|
my %k; |
151
|
4
|
|
|
|
|
9
|
my $l = 1; |
152
|
4
|
|
|
|
|
18
|
for my $i (split /\*/){ |
153
|
8
|
100
|
|
|
|
138
|
if($i =~ /^(\d+)[a-zA-Z]/){ # Integer coefficient |
|
|
100
|
|
|
|
|
|
154
|
1
|
|
|
|
|
6
|
$k{ $1 } = $1; |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
}elsif($i =~ /^(\d*\.(\d+))/){ # Fractional coefficient |
157
|
2
|
|
|
|
|
56
|
$k{ $1 } = $1; |
158
|
2
|
100
|
|
|
|
12
|
$l *= 10 if length $2 >= length $l; |
159
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
}else{ # without coefficient (=1) |
161
|
5
|
|
|
|
|
17
|
$k{ '' } = 1; |
162
|
|
|
|
|
|
|
} |
163
|
|
|
|
|
|
|
} |
164
|
|
|
|
|
|
|
|
165
|
4
|
100
|
|
|
|
15
|
if( $l > 1 ){ |
166
|
1
|
|
|
|
|
8
|
$k{ $_ } *= $l for keys %k; |
167
|
|
|
|
|
|
|
} |
168
|
|
|
|
|
|
|
# by GCD reduce order of coefficients |
169
|
4
|
|
|
|
|
32
|
my $gcd = bgcd(values %k); |
170
|
|
|
|
|
|
|
|
171
|
4
|
100
|
|
|
|
743
|
if( $gcd > 1 ){ |
172
|
1
|
|
|
|
|
118
|
$k{ $_ } /= $gcd for keys %k; |
173
|
|
|
|
|
|
|
} |
174
|
|
|
|
|
|
|
|
175
|
4
|
|
|
|
|
559
|
my $f; |
176
|
4
|
|
|
|
|
15
|
for my $i (split /\*/){ |
177
|
8
|
100
|
|
|
|
47
|
$i =~ s/^(\d*\.?\d*)([\w\(\)\[\]\{\}]+)$/$k{$1}>1?"{$2}".$k{$1}:"{$2}"/e; |
|
8
|
|
|
|
|
39
|
|
178
|
8
|
|
|
|
|
211
|
$f .= $i; |
179
|
|
|
|
|
|
|
} |
180
|
4
|
|
|
|
|
25
|
$_ = $f; |
181
|
|
|
|
|
|
|
} |
182
|
|
|
|
|
|
|
|
183
|
43
|
|
|
|
|
218
|
1 while s/[\(\{\[][\)\}\]]//g; # clean empty brackets |
184
|
|
|
|
|
|
|
} |
185
|
|
|
|
|
|
|
|
186
|
43
|
|
|
|
|
100
|
my $cf = ['']; |
187
|
43
|
|
|
|
|
352
|
for my $s (split /((?:\$|\d|\(|\)|\[|\]|\{|\})+)/,$substance){ |
188
|
|
|
|
|
|
|
|
189
|
127
|
100
|
100
|
|
|
1431
|
if( $s =~ /[$sas]/i ){ # Atom symbols |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
190
|
67
|
|
|
|
|
206
|
&_in_gf($s, \@maU, \@maUl, $cf); |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
}elsif( $s =~ /\$/ ){ # Zero symbols: 'o' && 'O' |
193
|
4
|
|
|
|
|
15
|
$s =~ s/^\$//; |
194
|
4
|
|
|
|
|
22
|
&_add_zero( $cf, "O$s", "o$s" ); # add oxigen to { C H N P } -> { CO HO NO PO } |
195
|
|
|
|
|
|
|
# add 'o' to { C H N P } -> { Co Ho No Po } |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
}elsif($s =~ /$mz/ && exists( $opts->{'zero2oxi'} ) ){ # Zero symbols |
198
|
4
|
|
|
|
|
9
|
my $z = $s; # zero |
199
|
4
|
|
|
|
|
25
|
$s =~ s/$mz/O/g; # oxigen |
200
|
|
|
|
|
|
|
|
201
|
4
|
|
|
|
|
15
|
&_add_zero( $cf, $s, $z ); |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
}else{ # Others |
204
|
52
|
|
|
|
|
212
|
$_ .= $s for @$cf; |
205
|
|
|
|
|
|
|
} |
206
|
|
|
|
|
|
|
} |
207
|
|
|
|
|
|
|
|
208
|
43
|
50
|
|
|
|
2475
|
$cf->[0] =~ /\w/ ? return $cf : return; |
209
|
|
|
|
|
|
|
} |
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
sub _add_zero{ |
212
|
8
|
|
|
8
|
|
15
|
my( $cf, $oxi, $z ) = @_; |
213
|
8
|
|
|
|
|
12
|
my $n = $#{$cf}; |
|
8
|
|
|
|
|
19
|
|
214
|
|
|
|
|
|
|
|
215
|
8
|
|
|
|
|
33
|
for( my $i = 0; $i <= $n; $i++ ){ |
216
|
8
|
|
|
|
|
30
|
$cf->[$i + $n + 1] = $cf->[$i].$oxi; # add oxigen to { C H N P } -> { CO HO NO PO } |
217
|
8
|
|
|
|
|
65
|
$cf->[$i] .= $z; # add 'o' to { C H N P } -> { Co Ho No Po } |
218
|
|
|
|
|
|
|
} |
219
|
|
|
|
|
|
|
} |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
sub _in_gf{ |
222
|
25770
|
|
|
25770
|
|
43778
|
my($s, $maU, $maUl, $cf) = @_; |
223
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
# Unary symbols of fragment |
225
|
25770
|
|
|
|
|
497909
|
my @i_maU = grep $s =~ /$_/i, @$maU; |
226
|
|
|
|
|
|
|
|
227
|
25770
|
100
|
|
|
|
75510
|
if( length($s) == 1 ){ # Unary atom symbol |
228
|
9167
|
100
|
|
|
|
31288
|
@i_maU || return; |
229
|
|
|
|
|
|
|
|
230
|
9155
|
|
|
|
|
31516
|
$_ .= $i_maU[0] for @$cf; |
231
|
9155
|
|
|
|
|
35250
|
return 1; |
232
|
|
|
|
|
|
|
} |
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
# Double symbols of fragment |
235
|
16603
|
100
|
100
|
|
|
297934
|
( my @i_maUl = grep $s =~ /$_/i, @$maUl ) || @i_maU || return; # not present |
236
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
# Strings of unary and double atom letters |
238
|
16565
|
|
|
|
|
31510
|
my $saU = join '', @i_maU; # String of unary atom letters |
239
|
16565
|
|
|
|
|
24796
|
my $saUl = join '|', @i_maUl; # 1th - CAPITAL, 2th - small |
240
|
|
|
|
|
|
|
|
241
|
16565
|
|
|
|
|
17886
|
my @w; # Anchor symbols |
242
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
# Unique unary symbols (is not present in double) |
244
|
16565
|
|
|
|
|
27228
|
for( @i_maU ){ |
245
|
34405
|
100
|
|
|
|
276930
|
next if $saUl =~ /$_/i; |
246
|
2834
|
|
|
|
|
23097
|
$s =~ s/\L$_/$_/g; # to CAPITAL |
247
|
2834
|
|
|
|
|
7135
|
push @w, $_; |
248
|
|
|
|
|
|
|
} |
249
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
# Unique double (is not present in others) |
251
|
16565
|
|
|
|
|
28309
|
for my $ul (@i_maUl){ |
252
|
24919
|
|
|
|
|
211114
|
(my $m = $saUl) =~ s/$ul//; # To copy and clean the tested |
253
|
|
|
|
|
|
|
|
254
|
24919
|
|
|
|
|
62814
|
for(split //,$ul){ |
255
|
46713
|
100
|
100
|
|
|
644296
|
next if $m =~ /$_/i || $saU =~ /$_/i; |
256
|
|
|
|
|
|
|
|
257
|
5910
|
|
|
|
|
49725
|
$s =~ s/$ul/$ul/ig; |
258
|
5910
|
|
|
|
|
9826
|
push @w, $ul; |
259
|
5910
|
|
|
|
|
16083
|
last; |
260
|
|
|
|
|
|
|
} |
261
|
|
|
|
|
|
|
} |
262
|
|
|
|
|
|
|
|
263
|
16565
|
|
|
|
|
27378
|
$_ = $s; |
264
|
|
|
|
|
|
|
|
265
|
16565
|
100
|
|
|
|
35160
|
if( length == 2){ # Two symbols |
266
|
|
|
|
|
|
|
|
267
|
9367
|
|
|
|
|
47981
|
(my $salU = $saUl) =~ s/(\w+)/\l\U$1/g; # 1th - small, 2th - CAPITAL |
268
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
# The order is important!!! |
270
|
9367
|
|
|
|
|
12622
|
my @in_cf; |
271
|
9367
|
100
|
100
|
|
|
305716
|
if( /$saUl/ || /[$saU]{2}/ ){ # Usual writing of atom: 1th - CAPITAL, 2th - small |
|
|
50
|
66
|
|
|
|
|
|
|
100
|
33
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
272
|
|
|
|
|
|
|
# or both - CAPITAL unary letters |
273
|
4561
|
|
|
|
|
7094
|
push @in_cf, $_; |
274
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
}elsif( /\U$saUl/ ){ # 1th and 2th - CAPITAL letters of double symbol |
276
|
0
|
|
|
|
|
0
|
push @in_cf, ucfirst lc; |
277
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
}elsif( @i_maU && ( /[$saU][\L$saU\E]/ || /[\L$saU\E][$saU]/ ) ){ # For unary symbols: |
279
|
|
|
|
|
|
|
# 1th - CAPITAL, 2th - small or 1th - small, 2th - CAPITAL letter |
280
|
1
|
|
|
|
|
3
|
push @in_cf, uc; |
281
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
}elsif( /$salU/ ){ # 1th - small, 2th - CAPITAL letter of double symbol |
283
|
0
|
|
|
|
|
0
|
push @in_cf, ucfirst lc; |
284
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
}elsif( length($saUl) == 0 ){ # Without double symbol |
286
|
1
|
|
|
|
|
22
|
s/.?([$saU]+).?/$1/i; # unary symbol(s) |
287
|
1
|
|
|
|
|
5
|
push @in_cf, uc; |
288
|
|
|
|
|
|
|
|
289
|
|
|
|
|
|
|
}else{ # 1th and 2th small letters - two alternative variants |
290
|
4804
|
50
|
|
|
|
50253
|
if( /[\L$saU]{2}/ ){ # unary symbols |
291
|
4804
|
|
|
|
|
12854
|
push @in_cf, uc; |
292
|
|
|
|
|
|
|
} |
293
|
4804
|
50
|
|
|
|
38930
|
if( /\L$saUl/ ){ # double symbol |
294
|
4804
|
|
|
|
|
10398
|
push @in_cf, ucfirst; |
295
|
|
|
|
|
|
|
} |
296
|
|
|
|
|
|
|
|
297
|
4804
|
50
|
|
|
|
11274
|
@in_cf || return; |
298
|
|
|
|
|
|
|
|
299
|
4804
|
50
|
|
|
|
9900
|
if( @in_cf == 2 ){ |
300
|
|
|
|
|
|
|
|
301
|
4804
|
|
|
|
|
5099
|
my $n = $#{$cf}; |
|
4804
|
|
|
|
|
7779
|
|
302
|
4804
|
|
|
|
|
10891
|
for( my $i = 0; $i <= $n; $i++ ){ |
303
|
13473
|
|
|
|
|
31473
|
$cf->[$i + $n + 1] = $cf->[$i].$in_cf[1]; |
304
|
13473
|
|
|
|
|
33419
|
$cf->[$i] .= $in_cf[0]; |
305
|
|
|
|
|
|
|
} |
306
|
4804
|
|
|
|
|
43532
|
return 1; |
307
|
|
|
|
|
|
|
} |
308
|
|
|
|
|
|
|
} |
309
|
|
|
|
|
|
|
|
310
|
4563
|
|
|
|
|
14056
|
$_ .= $in_cf[0] for @$cf; |
311
|
4563
|
|
|
|
|
22509
|
return 1; |
312
|
|
|
|
|
|
|
} |
313
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
# More 2 symbols |
315
|
|
|
|
|
|
|
|
316
|
7198
|
|
|
|
|
10068
|
for( $s ){ |
317
|
7198
|
|
|
|
|
15143
|
my $m = join '|', $saUl, @i_maU; |
318
|
|
|
|
|
|
|
|
319
|
7198
|
|
|
|
|
237117
|
while( ! /^(?:$m)/i ){ s/^.// } # Clear head |
|
5
|
|
|
|
|
45
|
|
320
|
7198
|
|
|
|
|
253538
|
while( ! /(?:$m)$/i ){ s/.$// } # Clear tail |
|
31
|
|
|
|
|
304
|
|
321
|
|
|
|
|
|
|
|
322
|
|
|
|
|
|
|
# Search double and unary symbols |
323
|
7198
|
|
|
|
|
18456
|
my $z = ''; |
324
|
7198
|
|
|
|
|
202829
|
while( /^((?:$m)+)/g ){ $z .= $1 } |
|
2222
|
|
|
|
|
15321
|
|
325
|
|
|
|
|
|
|
|
326
|
7198
|
100
|
|
|
|
29031
|
if( length $z ){ |
327
|
|
|
|
|
|
|
|
328
|
2218
|
|
|
|
|
7054
|
$_ .= $z for @$cf; |
329
|
2218
|
100
|
|
|
|
8049
|
return 1 if length($z) == length; # The end |
330
|
|
|
|
|
|
|
|
331
|
1738
|
|
|
|
|
14163
|
s/^$z//; |
332
|
1738
|
100
|
|
|
|
7421
|
return &_in_gf($_, \@i_maU, \@i_maUl, $cf) if 3 > length; |
333
|
|
|
|
|
|
|
} |
334
|
|
|
|
|
|
|
} |
335
|
|
|
|
|
|
|
|
336
|
5749
|
100
|
|
|
|
14411
|
if( @w ){ # Search by fragments of anchor symbols |
337
|
1802
|
|
|
|
|
3159
|
my $m = join '|', @w; |
338
|
|
|
|
|
|
|
|
339
|
1802
|
|
|
|
|
19498
|
for my $f (split /($m)/, $s){ |
340
|
|
|
|
|
|
|
|
341
|
2926
|
|
|
|
|
7558
|
&_in_gf($f, \@i_maU, \@i_maUl, $cf); |
342
|
|
|
|
|
|
|
|
343
|
2926
|
100
|
|
|
|
4514
|
if( $#{$cf} ){ # For >1 formula |
|
2926
|
|
|
|
|
8004
|
|
344
|
|
|
|
|
|
|
# To leave the longest fragments |
345
|
2058
|
|
|
|
|
7371
|
for( my $i = $#$cf; $i > 0; $i-- ){ |
346
|
8072
|
50
|
|
|
|
28581
|
splice @$cf, $i, 1 if length( $cf->[$i] ) < length( $cf->[0] ); |
347
|
|
|
|
|
|
|
} |
348
|
|
|
|
|
|
|
} |
349
|
|
|
|
|
|
|
} |
350
|
1802
|
|
|
|
|
14753
|
return 1; |
351
|
|
|
|
|
|
|
} |
352
|
|
|
|
|
|
|
|
353
|
|
|
|
|
|
|
# Difficult fragment |
354
|
3947
|
|
|
|
|
9290
|
my $cf_copy = [ @$cf ]; |
355
|
3947
|
|
|
|
|
4315
|
$#{$cf} = -1; # Reset |
|
3947
|
|
|
|
|
11640
|
|
356
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
_M_gf_1: # Letter by letter search |
358
|
3947
|
|
|
|
|
10534
|
for( my $i = 1; $i < length($s); $i++ ){ |
359
|
|
|
|
|
|
|
|
360
|
10907
|
|
|
|
|
23295
|
my $cf_new = [ @$cf_copy ]; |
361
|
|
|
|
|
|
|
|
362
|
10907
|
|
|
|
|
474258
|
for( $s =~ /^(\w{$i})(\w+)/ ){ |
363
|
21808
|
100
|
|
|
|
49475
|
&_in_gf($_, \@i_maU, \@i_maUl, $cf_new) || next _M_gf_1; |
364
|
|
|
|
|
|
|
} |
365
|
|
|
|
|
|
|
# To accumulate only unique, big fragments |
366
|
10895
|
|
|
|
|
29057
|
for my $k ( @$cf_new ){ |
367
|
47611
|
100
|
100
|
|
|
1368674
|
scalar( grep length($_) > length($k) || $_ eq $k, @$cf ) || push @$cf, $k; |
368
|
|
|
|
|
|
|
} |
369
|
|
|
|
|
|
|
} |
370
|
3947
|
|
|
|
|
19526
|
return 1; |
371
|
|
|
|
|
|
|
} |
372
|
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
|
374
|
|
|
|
|
|
|
# Tactico-technical characteristics of chemical reaction. |
375
|
|
|
|
|
|
|
# Return quantity hash (SAR): |
376
|
|
|
|
|
|
|
# 's'ubstance |
377
|
|
|
|
|
|
|
# 'a'toms |
378
|
|
|
|
|
|
|
# 'r'ank |
379
|
|
|
|
|
|
|
sub ttc_reaction{ |
380
|
2
|
|
50
|
2
|
1
|
15
|
my $ce = shift || return; |
381
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
# Hash atom matrix and quantity of atoms |
383
|
2
|
|
|
|
|
9
|
my( $atoms_substance, $atoms ) = &_search_atoms_subs( $ce ); |
384
|
|
|
|
|
|
|
|
385
|
2
|
|
50
|
|
|
20
|
my $rank = Rank( [ values %$atoms_substance ] ) || return; |
386
|
|
|
|
|
|
|
|
387
|
2
|
|
|
|
|
19894
|
return { 's' => scalar( keys %$atoms_substance ), |
388
|
|
|
|
|
|
|
'a' => $atoms, |
389
|
|
|
|
|
|
|
'r' => $rank, |
390
|
|
|
|
|
|
|
}; |
391
|
|
|
|
|
|
|
} |
392
|
|
|
|
|
|
|
|
393
|
|
|
|
|
|
|
# Calculation common identifier for reaction: |
394
|
|
|
|
|
|
|
# Alphabetic CLASS |
395
|
|
|
|
|
|
|
# and |
396
|
|
|
|
|
|
|
# Chemical Interger Reaction (CIR) identifier |
397
|
|
|
|
|
|
|
# also |
398
|
|
|
|
|
|
|
# brutto (gross) formulas of substances |
399
|
|
|
|
|
|
|
sub class_cir_brutto{ |
400
|
66
|
|
|
66
|
1
|
120
|
my( $ce, $coef ) = @_; |
401
|
66
|
50
|
|
|
|
142
|
$ce || return; |
402
|
|
|
|
|
|
|
|
403
|
66
|
|
|
|
|
81
|
my( %elm, %bf, @cir ); |
404
|
|
|
|
|
|
|
|
405
|
66
|
|
|
|
|
141
|
for my $c ( @$ce ){ |
406
|
132
|
|
|
|
|
205
|
for my $s ( @$c ){ |
407
|
|
|
|
|
|
|
|
408
|
|
|
|
|
|
|
# stoichiometry coefficient |
409
|
358
|
50
|
|
|
|
884
|
my $k = exists $coef->{$s} ? $coef->{$s} : 1; |
410
|
|
|
|
|
|
|
|
411
|
358
|
|
|
|
|
1143
|
my %e = Chemistry::File::Formula->parse_formula( $s ); |
412
|
|
|
|
|
|
|
|
413
|
|
|
|
|
|
|
# for brutto |
414
|
358
|
|
|
|
|
58248
|
$bf{ $s } = join '', map( "$_$e{$_}", sort{ $a cmp $b } keys %e ); |
|
687
|
|
|
|
|
2476
|
|
415
|
|
|
|
|
|
|
|
416
|
|
|
|
|
|
|
# for cir |
417
|
358
|
|
|
|
|
991
|
push @cir, $k.$bf{ $s }; |
418
|
|
|
|
|
|
|
|
419
|
|
|
|
|
|
|
# For class |
420
|
358
|
|
|
|
|
1520
|
@elm{ keys %e } = ''; # reaction atoms |
421
|
|
|
|
|
|
|
} |
422
|
|
|
|
|
|
|
} |
423
|
|
|
|
|
|
|
|
424
|
|
|
|
|
|
|
# CLASS, CIR of reaction and hash: formula => brutto of substances |
425
|
520
|
|
|
|
|
691
|
return [ join( '', sort { $a cmp $b } keys %elm ), |
|
622
|
|
|
|
|
956
|
|
426
|
66
|
|
|
|
|
195
|
( cksum( join '+', sort { $a cmp $b } @cir ) )[0], |
427
|
|
|
|
|
|
|
\%bf ]; |
428
|
|
|
|
|
|
|
} |
429
|
|
|
|
|
|
|
|
430
|
|
|
|
|
|
|
|
431
|
|
|
|
|
|
|
# Transformation substance arrays to chemical mix (equation) |
432
|
|
|
|
|
|
|
# $ce -- ref to array of substance arrays |
433
|
|
|
|
|
|
|
# $opts ->{ } (facultative parameters) |
434
|
|
|
|
|
|
|
# ->'substances' -- ref to array of real (required) substances |
435
|
|
|
|
|
|
|
# ->'coefficients' -- ref to hash stoichiometry coefficients for substances |
436
|
|
|
|
|
|
|
# ->'norma' -- chemical mix to brutto-normalize |
437
|
|
|
|
|
|
|
sub prepare_mix{ |
438
|
829
|
|
|
829
|
1
|
1198
|
my( $ce, $opts ) = @_; |
439
|
|
|
|
|
|
|
|
440
|
|
|
|
|
|
|
# Check empty reactants & products |
441
|
1658
|
|
|
|
|
4695
|
croak('Bad equation!') if @$ce != 2 || |
442
|
|
|
|
|
|
|
scalar( grep ! defined, @$ce ) || |
443
|
829
|
50
|
50
|
|
|
4733
|
scalar( grep { grep ! defined, @{$_} } @$ce ); |
|
1658
|
|
50
|
|
|
1613
|
|
444
|
|
|
|
|
|
|
|
445
|
829
|
|
|
|
|
1110
|
my $sign = 0; |
446
|
|
|
|
|
|
|
# Any coefficient isn't set --> to bypass |
447
|
829
|
100
|
66
|
|
|
2273
|
if( exists( $opts->{'norma'} ) && |
|
98
|
|
|
|
|
639
|
|
448
|
|
|
|
|
|
|
scalar( grep { |
449
|
98
|
|
|
|
|
109
|
grep ! exists( $opts->{'coefficients'}{$_} ), @{$_} |
450
|
|
|
|
|
|
|
} @$ce ) == 0 ){ # All coefficients are set |
451
|
|
|
|
|
|
|
|
452
|
|
|
|
|
|
|
# Test stoichiometry coefficient sign on initial (first) substances |
453
|
49
|
|
|
|
|
71
|
$sign = 1; |
454
|
49
|
|
|
|
|
60
|
for( @{ $ce->[0] } ){ |
|
49
|
|
|
|
|
123
|
|
455
|
55
|
100
|
|
|
|
339
|
$opts->{'coefficients'}{$_} || next; # Zero stoichiometry coefficient |
456
|
49
|
100
|
|
|
|
1115
|
$sign = -1 if $opts->{'coefficients'}{$_} < 0; |
457
|
49
|
|
|
|
|
5932
|
last; |
458
|
|
|
|
|
|
|
} |
459
|
|
|
|
|
|
|
} |
460
|
|
|
|
|
|
|
|
461
|
829
|
100
|
|
|
|
1952
|
if( $sign != 0 ){ # chemical mix to brutto-normalize |
462
|
|
|
|
|
|
|
|
463
|
|
|
|
|
|
|
# if assume all coefficients positive '+' |
464
|
49
|
50
|
|
|
|
127
|
if( $opts->{'norma'} != 1 ){ |
465
|
0
|
|
|
|
|
0
|
$opts->{'coefficients'}{$_} *= -1 for @{ $ce->[1] }; |
|
0
|
|
|
|
|
0
|
|
466
|
|
|
|
|
|
|
} |
467
|
|
|
|
|
|
|
|
468
|
49
|
|
|
|
|
97
|
my @cir; # for brutto-formula |
469
|
49
|
|
|
|
|
78
|
while( my($s, $c) = each %{ $opts->{'coefficients'} } ){ |
|
306
|
|
|
|
|
2902
|
|
470
|
257
|
100
|
|
|
|
755
|
$c || next; |
471
|
234
|
100
|
|
|
|
6529
|
my $ip = $sign * $c > 0 ? 0 : 1; |
472
|
|
|
|
|
|
|
|
473
|
234
|
|
|
|
|
55555
|
my $ff = abs($c)." $s"; |
474
|
|
|
|
|
|
|
|
475
|
234
|
|
|
|
|
11761
|
my %e = Chemistry::File::Formula->parse_formula( $ff ); |
476
|
234
|
|
|
|
|
45258
|
$cir[$ip]{$ff} = join '', map( "$_$e{$_}", sort{ $a cmp $b} keys %e ); |
|
431
|
|
|
|
|
2338
|
|
477
|
|
|
|
|
|
|
} |
478
|
|
|
|
|
|
|
# Result |
479
|
49
|
|
|
|
|
104
|
$_ = join(' == ', map{ join ' + ',sort {$_->{$a} cmp $_->{$b}} keys %{$_} } @cir); |
|
98
|
|
|
|
|
134
|
|
|
194
|
|
|
|
|
682
|
|
|
98
|
|
|
|
|
313
|
|
480
|
|
|
|
|
|
|
|
481
|
|
|
|
|
|
|
}else{ |
482
|
780
|
100
|
66
|
|
|
5577
|
if( exists( $opts->{'substances'} ) && @{ $opts->{'substances'} } ){ |
|
779
|
|
|
|
|
3272
|
|
483
|
|
|
|
|
|
|
# List of real substances is specified |
484
|
779
|
|
|
|
|
869
|
my %bs; |
485
|
779
|
|
|
|
|
1024
|
@bs{ @{ $opts->{'substances'} } } = (); |
|
779
|
|
|
|
|
2682
|
|
486
|
|
|
|
|
|
|
|
487
|
779
|
|
|
|
|
1233
|
$_ = join(' == ', map{ join ' + ', map{ |
488
|
5642
|
100
|
|
|
|
9290
|
if( exists $bs{$_} ){ |
|
1558
|
|
|
|
|
2162
|
|
489
|
2928
|
100
|
|
|
|
10779
|
exists( $opts->{'coefficients'}{$_} ) ? abs( $opts->{'coefficients'}{$_} )." $_" : $_ |
490
|
|
|
|
|
|
|
}else{ |
491
|
|
|
|
|
|
|
( ) |
492
|
2714
|
|
|
|
|
4886
|
} |
493
|
1558
|
|
|
|
|
1662
|
} @{$_} } @$ce ); |
494
|
|
|
|
|
|
|
|
495
|
|
|
|
|
|
|
}else{ # List isn't specified |
496
|
1
|
100
|
|
|
|
3
|
$_ = join(' == ', map{ join ' + ', map{ |
|
6
|
|
|
|
|
32
|
|
497
|
2
|
|
|
|
|
3
|
exists( $opts->{'coefficients'}{$_} ) ? abs( $opts->{'coefficients'}{$_} )." $_" : $_ } @{$_} } @$ce ); |
|
2
|
|
|
|
|
4
|
|
498
|
|
|
|
|
|
|
} |
499
|
|
|
|
|
|
|
} |
500
|
|
|
|
|
|
|
|
501
|
829
|
|
|
|
|
6462
|
s/^\s*==|==\s*$//g; |
502
|
829
|
50
|
|
|
|
6912
|
/\S\s*[=+]+\s*\S/ ? $_ : return; |
503
|
|
|
|
|
|
|
} |
504
|
|
|
|
|
|
|
|
505
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
# Расчёт (поиск) стехиометрических коэффициентов |
507
|
|
|
|
|
|
|
# Возвращает : |
508
|
|
|
|
|
|
|
# решение в $cheq |
509
|
|
|
|
|
|
|
# undef --- нет решения |
510
|
|
|
|
|
|
|
sub stoichiometry{ |
511
|
16
|
|
|
16
|
1
|
16996
|
my($mix, $opts) = @_; |
512
|
16
|
100
|
|
|
|
58
|
$opts->{'redox_pairs'} = 1 unless exists $opts->{'redox_pairs'}; # вкл. redox-уравниватель |
513
|
|
|
|
|
|
|
|
514
|
16
|
|
|
|
|
23
|
my @cheq; # возвращаемые уравнения |
515
|
|
|
|
|
|
|
|
516
|
16
|
|
|
|
|
51
|
&_in_search_stoich($mix, \@cheq, $opts ); # Рекурсивный поиск |
517
|
|
|
|
|
|
|
|
518
|
16
|
50
|
|
|
|
113
|
@cheq ? \@cheq : return undef; |
519
|
|
|
|
|
|
|
} |
520
|
|
|
|
|
|
|
|
521
|
|
|
|
|
|
|
|
522
|
|
|
|
|
|
|
# Рекурсивный поиск стехиометрических коэффициентов |
523
|
|
|
|
|
|
|
sub _in_search_stoich{ |
524
|
1389
|
|
|
1389
|
|
2530
|
my($mix, $cheq, $opts) = @_; |
525
|
|
|
|
|
|
|
|
526
|
1389
|
|
|
|
|
2164
|
my %coef; # на случай заданных коэф. в $mix |
527
|
1389
|
|
|
|
|
3224
|
my $chem_eq = parse_chem_mix($mix, \%coef); |
528
|
|
|
|
|
|
|
|
529
|
1389
|
50
|
|
|
|
3698
|
croak('Bad equation!') if @$chem_eq != 2; |
530
|
|
|
|
|
|
|
|
531
|
|
|
|
|
|
|
# Хеш Атомной матрицы и кол-во атомов |
532
|
1389
|
|
|
|
|
2936
|
my ( $atoms_substance, $num_atoms ) = &_search_atoms_subs( $chem_eq ); |
533
|
|
|
|
|
|
|
|
534
|
|
|
|
|
|
|
# Список Всех веществ и копия атомной матрицы (для проверки баланса в конце) |
535
|
672
|
|
|
|
|
2684
|
my $All_substances = [ keys %$atoms_substance ]; |
536
|
672
|
|
|
|
|
1420
|
my $ini_am = { map{ $_, [ @{ $atoms_substance->{$_} } ] } @$All_substances }; |
|
4558
|
|
|
|
|
4421
|
|
|
4558
|
|
|
|
|
4271059
|
|
537
|
|
|
|
|
|
|
|
538
|
672
|
|
|
|
|
2709
|
my $R_free = [ (0) x $num_atoms ]; # R-вектор свободных членов (правая часть) |
539
|
672
|
|
|
|
|
1429
|
my $R_zero = 1; # Признак нулевого R-вектора |
540
|
|
|
|
|
|
|
|
541
|
672
|
|
|
|
|
2449
|
while( my($s, $c) = each %coef ){ # В-во и стех.коэф. |
542
|
2
|
50
|
|
|
|
12
|
$opts->{'coefficients'}{$s} = $c unless exists( $opts->{'coefficients'}{$s} ); |
543
|
|
|
|
|
|
|
} |
544
|
|
|
|
|
|
|
|
545
|
|
|
|
|
|
|
# Если заданы стех.коэффициенты |
546
|
672
|
100
|
100
|
|
|
2299
|
if( exists $opts->{'coefficients'} && keys %{ $opts->{'coefficients'} } ){ |
|
662
|
|
|
|
|
3422
|
|
547
|
|
|
|
|
|
|
|
548
|
597
|
|
|
|
|
1471
|
my $sign = 1; # знак переноса вправо по исходным в-вам |
549
|
|
|
|
|
|
|
|
550
|
597
|
|
|
|
|
1126
|
for my $ip ( @$chem_eq ){ |
551
|
1194
|
|
|
|
|
1610
|
for my $s ( @$ip ){ |
552
|
|
|
|
|
|
|
|
553
|
4176
|
100
|
|
|
|
10309
|
if( exists $opts->{'coefficients'}{$s} ){ |
554
|
|
|
|
|
|
|
|
555
|
|
|
|
|
|
|
# устанавливаем знак-признак исх./продукт |
556
|
2875
|
|
|
|
|
6685
|
$opts->{'coefficients'}{$s} = $sign * abs( $opts->{'coefficients'}{$s} ); |
557
|
|
|
|
|
|
|
|
558
|
2875
|
|
|
|
|
2859
|
my $i; |
559
|
|
|
|
|
|
|
# формируем Вектор правой части СЛАУ |
560
|
2875
|
|
|
|
|
19099
|
$R_free->[$i++] -= $_ * $opts->{'coefficients'}{$s} |
561
|
2875
|
|
|
|
|
2762
|
for @{ $atoms_substance->{$s} }; |
562
|
|
|
|
|
|
|
# удаляем вещество после переноса вправо |
563
|
2875
|
|
|
|
|
7844
|
delete $atoms_substance->{$s}; |
564
|
|
|
|
|
|
|
} |
565
|
|
|
|
|
|
|
} |
566
|
1194
|
|
|
|
|
2081
|
$sign = -1; # знак переноса вправо по продуктам |
567
|
|
|
|
|
|
|
} |
568
|
|
|
|
|
|
|
|
569
|
|
|
|
|
|
|
# Поиск полностью нулевых атомных строк |
570
|
597
|
|
|
|
|
1582
|
my @sum_atom = (0) x $num_atoms; |
571
|
597
|
|
|
|
|
703
|
for my $s ( keys %{ $atoms_substance} ) { |
|
597
|
|
|
|
|
1342
|
|
572
|
1301
|
|
|
|
|
1383
|
my $i; |
573
|
1301
|
|
|
|
|
1239
|
$sum_atom[$i++] += $_ for @{ $atoms_substance->{$s} }; |
|
1301
|
|
|
|
|
6862
|
|
574
|
|
|
|
|
|
|
} |
575
|
|
|
|
|
|
|
|
576
|
597
|
|
|
|
|
920
|
my @atom_del; # удаляемые 0-строки |
577
|
597
|
|
|
|
|
1755
|
for( my $i = $#sum_atom; $i >= 0; $i--){ # Обратный отсчёт |
578
|
|
|
|
|
|
|
|
579
|
1864
|
100
|
|
|
|
3757
|
unless( $sum_atom[$i] ){ # Все атомы нули |
580
|
590
|
100
|
|
|
|
8899
|
return if $R_free->[$i]; # Нет баланса, нет решения |
581
|
52
|
|
|
|
|
118
|
push @atom_del, $i; |
582
|
|
|
|
|
|
|
} |
583
|
|
|
|
|
|
|
|
584
|
1326
|
100
|
|
|
|
5699
|
$R_zero = 0 if $R_free->[$i]; # R-вектор не 0 |
585
|
|
|
|
|
|
|
} |
586
|
|
|
|
|
|
|
|
587
|
|
|
|
|
|
|
# Удаление 0-х атомных строк |
588
|
59
|
|
|
|
|
106
|
for my $i ( @atom_del ){ |
589
|
52
|
|
|
|
|
167
|
splice @$R_free, $i, 1; |
590
|
|
|
|
|
|
|
|
591
|
52
|
|
|
|
|
92
|
for( values %{ $atoms_substance} ) { |
|
52
|
|
|
|
|
126
|
|
592
|
113
|
|
|
|
|
141
|
splice @{ $_ }, $i, 1; |
|
113
|
|
|
|
|
288
|
|
593
|
|
|
|
|
|
|
} |
594
|
|
|
|
|
|
|
} |
595
|
|
|
|
|
|
|
|
596
|
|
|
|
|
|
|
# Уменьшаем число атомов |
597
|
59
|
50
|
|
|
|
217
|
unless( $num_atoms -= @atom_del ){ |
598
|
|
|
|
|
|
|
|
599
|
|
|
|
|
|
|
# Заданы все правильные стех.коэф. |
600
|
0
|
|
|
|
|
0
|
push @$cheq, prepare_mix( $chem_eq, |
601
|
|
|
|
|
|
|
{ 'norma' => 1, 'coefficients' => $opts->{'coefficients'} } ); |
602
|
0
|
|
|
|
|
0
|
return; |
603
|
|
|
|
|
|
|
} |
604
|
|
|
|
|
|
|
|
605
|
|
|
|
|
|
|
# Одно в-во без стех.коэф. |
606
|
59
|
50
|
|
|
|
372
|
if( (my @v = values %$atoms_substance) == 1 ){ |
607
|
|
|
|
|
|
|
|
608
|
|
|
|
|
|
|
# Должна быть пропорция между атомами в в-ве и правом векторе |
609
|
0
|
|
|
|
|
0
|
for( my $i = 0; $i < $#{ $v[0] }; $i++){ |
|
0
|
|
|
|
|
0
|
|
610
|
0
|
0
|
|
|
|
0
|
return if $v[0]->[$i] * $R_free->[$i+1] != $v[0]->[$i+1] * $R_free->[$i] |
611
|
|
|
|
|
|
|
} |
612
|
|
|
|
|
|
|
} |
613
|
|
|
|
|
|
|
} |
614
|
|
|
|
|
|
|
|
615
|
|
|
|
|
|
|
# Список искомых веществ |
616
|
134
|
|
|
|
|
688
|
my $substances_X = [ keys %$atoms_substance ]; |
617
|
|
|
|
|
|
|
|
618
|
134
|
50
|
33
|
|
|
554
|
return if @$substances_X < 2 && $R_zero; # 0|1 вещество и не заданы коэф. |
619
|
|
|
|
|
|
|
|
620
|
|
|
|
|
|
|
# Ранг матрицы --- кол-ва независимых строк, столбцов |
621
|
134
|
|
|
|
|
1113
|
my $order = Rank( [ values %$atoms_substance ] ); |
622
|
|
|
|
|
|
|
|
623
|
|
|
|
|
|
|
# print "$mix\n"; |
624
|
|
|
|
|
|
|
# print "Atoms = $num_atoms |
625
|
|
|
|
|
|
|
#Substances = ",scalar(keys %$atoms_substance), |
626
|
|
|
|
|
|
|
#"\nRank = $order\n--------\n"; |
627
|
|
|
|
|
|
|
|
628
|
|
|
|
|
|
|
# не задан R-вектор (коэффициенты) и ранг == веществам |
629
|
134
|
100
|
100
|
|
|
370568
|
return if $R_zero && $order == @$substances_X; # нет решения |
630
|
|
|
|
|
|
|
|
631
|
117
|
100
|
|
|
|
468
|
if( @$substances_X - $order > 1 ){ # Веществ больше Ранга на 2 и более |
632
|
|
|
|
|
|
|
|
633
|
|
|
|
|
|
|
# Ограничить рекурсию самым Верхним!!! уровнем перебора |
634
|
12
|
100
|
|
|
|
135
|
return if @$cheq; # || @$substances_X < 3; |
635
|
|
|
|
|
|
|
|
636
|
6
|
100
|
100
|
|
|
35
|
if( $opts->{'redox_pairs'} && ( my $redox = &redox_test($chem_eq) ) ){ |
637
|
|
|
|
|
|
|
# Проверить реакцию на ОВР |
638
|
|
|
|
|
|
|
# if( my $redox = &redox_test($chem_eq) ){ # Это ОВР |
639
|
|
|
|
|
|
|
|
640
|
2
|
|
|
|
|
15
|
my @nm_rx = sort keys %$redox; # 0='oxidant' и 1='reducer' |
641
|
2
|
|
|
|
|
4
|
my %pairs; |
642
|
|
|
|
|
|
|
|
643
|
2
|
|
|
|
|
3
|
for my $nm ( @nm_rx ){ |
644
|
4
|
|
|
|
|
5
|
for my $e ( keys %{ $redox->{$nm} } ){ # Элементы |
|
4
|
|
|
|
|
11
|
|
645
|
|
|
|
|
|
|
# Исходное->Конечное состояние элемента |
646
|
6
|
|
|
|
|
8
|
for my $i_k ( keys %{ $redox->{$nm}{$e} } ){ |
|
6
|
|
|
|
|
12
|
|
647
|
8
|
|
|
|
|
7
|
push @{ $pairs{$nm} }, $i_k; |
|
8
|
|
|
|
|
29
|
|
648
|
|
|
|
|
|
|
} |
649
|
|
|
|
|
|
|
} |
650
|
|
|
|
|
|
|
} |
651
|
|
|
|
|
|
|
|
652
|
|
|
|
|
|
|
# print Dumper \%pairs; |
653
|
|
|
|
|
|
|
|
654
|
2
|
|
|
|
|
14
|
my $iter_oxi = subsets( $pairs{ $nm_rx[0] }); # можно задать ,1 - один окислитель |
655
|
|
|
|
|
|
|
|
656
|
2
|
|
|
|
|
87
|
while(my $pair_oxi = $iter_oxi->next){ # Набор пар |
657
|
12
|
100
|
|
|
|
297
|
next unless @$pair_oxi; # убрать пустые (фича subsets) |
658
|
|
|
|
|
|
|
|
659
|
10
|
|
|
|
|
18
|
my $sum_oxi_e = 0; # сумма e- нов принимаемых окислителями |
660
|
|
|
|
|
|
|
|
661
|
10
|
|
|
|
|
16
|
for my $e ( keys %{ $redox->{ $nm_rx[0] } } ){ # Элементы окислителя |
|
10
|
|
|
|
|
32
|
|
662
|
20
|
|
|
|
|
306
|
for my $i_k ( @{ $pair_oxi } ){ |
|
20
|
|
|
|
|
37
|
|
663
|
32
|
100
|
|
|
|
938
|
$sum_oxi_e += $redox->{ $nm_rx[0] }{$e}{$i_k}[0] if exists( $redox->{ $nm_rx[0] }{$e}{$i_k} ); |
664
|
|
|
|
|
|
|
} |
665
|
|
|
|
|
|
|
} |
666
|
|
|
|
|
|
|
|
667
|
|
|
|
|
|
|
# 1 - один восстановитель (надо настраивать!!!) |
668
|
10
|
|
|
|
|
763
|
my $iter_red = subsets( $pairs{ $nm_rx[1] } ); |
669
|
|
|
|
|
|
|
|
670
|
10
|
|
|
|
|
371
|
while(my $pair_red = $iter_red->next){ # Набор пар |
671
|
34
|
50
|
|
|
|
2087
|
next if $sum_oxi_e < @{ $pair_red }; # принимаемых e- нов меньше восст-лей |
|
34
|
|
|
|
|
129
|
|
672
|
|
|
|
|
|
|
|
673
|
34
|
|
|
|
|
3301
|
my $sum_red_e = 0; # сумма e- нов отдаваемых восстановителями |
674
|
|
|
|
|
|
|
|
675
|
34
|
|
|
|
|
53
|
for my $e ( keys %{ $redox->{ $nm_rx[1] } } ){ # Элементы восстановителя |
|
34
|
|
|
|
|
132
|
|
676
|
34
|
|
|
|
|
47
|
for my $i_k ( @{ $pair_red } ){ |
|
34
|
|
|
|
|
82
|
|
677
|
31
|
50
|
|
|
|
1392
|
$sum_red_e += $redox->{ $nm_rx[1] }{$e}{$i_k}[0] if exists( $redox->{ $nm_rx[1] }{$e}{$i_k} ); |
678
|
|
|
|
|
|
|
} |
679
|
|
|
|
|
|
|
} |
680
|
|
|
|
|
|
|
|
681
|
34
|
100
|
|
|
|
3447
|
next if $sum_red_e < @{ $pair_oxi }; # отдаваемых e- нов меньше окисл-ей |
|
34
|
|
|
|
|
139
|
|
682
|
|
|
|
|
|
|
|
683
|
|
|
|
|
|
|
# Формируем вектор правых частей |
684
|
|
|
|
|
|
|
|
685
|
|
|
|
|
|
|
# из окислителей |
686
|
23
|
|
|
|
|
80
|
my $oxi_in = variations_with_repetition( [ (1..($sum_red_e - @{ $pair_oxi } + 1) ) ], |
|
23
|
|
|
|
|
6989
|
|
687
|
23
|
|
|
|
|
1878
|
scalar(@{ $pair_oxi }) ); |
688
|
23
|
|
|
|
|
990
|
while (my $e_oxi = $oxi_in->next) { |
689
|
|
|
|
|
|
|
|
690
|
|
|
|
|
|
|
# Все e-ны должны быть распределены по окислителям |
691
|
1198
|
|
|
|
|
117285
|
my $sum_e = 0; |
692
|
1198
|
|
|
|
|
3760
|
$sum_e += $_ for @$e_oxi; |
693
|
1198
|
100
|
|
|
|
3480
|
next if $sum_e != $sum_red_e; |
694
|
|
|
|
|
|
|
|
695
|
|
|
|
|
|
|
# из восстановителей |
696
|
122
|
|
|
|
|
459
|
my $red_in = variations_with_repetition( [ (1..($sum_oxi_e - @{ $pair_red } + 1) ) ], |
|
122
|
|
|
|
|
47939
|
|
697
|
122
|
|
|
|
|
9327
|
scalar(@{ $pair_red }) ); |
698
|
122
|
|
|
|
|
5573
|
while (my $e_red = $red_in->next) { |
699
|
|
|
|
|
|
|
|
700
|
|
|
|
|
|
|
# Все e-ны должны быть распределены по восстановителям |
701
|
5204
|
|
|
|
|
448594
|
my $sum_e = 0; |
702
|
5204
|
|
|
|
|
15883
|
$sum_e += $_ for @$e_red; |
703
|
5204
|
100
|
|
|
|
14572
|
next if $sum_e != $sum_oxi_e; |
704
|
|
|
|
|
|
|
|
705
|
|
|
|
|
|
|
# Копируем во временный хеш опций |
706
|
595
|
|
|
|
|
44067
|
my %in_opts; |
707
|
595
|
|
|
|
|
1662
|
$in_opts{'redox_pairs'} = $opts->{'redox_pairs'}; |
708
|
595
|
|
|
|
|
742
|
$in_opts{'coefficients'}{ $_ } = $opts->{'coefficients'}{ $_ } for keys %{ $opts->{'coefficients'} }; |
|
595
|
|
|
|
|
2099
|
|
709
|
|
|
|
|
|
|
|
710
|
|
|
|
|
|
|
# Доформировываем внутренний хеш опций для R-вектора |
711
|
595
|
|
|
|
|
1026
|
my $j = 0; |
712
|
595
|
|
|
|
|
643
|
for my $i_k ( @{ $pair_oxi } ){ # пары для окислителей |
|
595
|
|
|
|
|
1160
|
|
713
|
1555
|
|
|
|
|
4176
|
for( split "->",$i_k ){ # Получаем Исходное и конечное |
714
|
3110
|
50
|
|
|
|
6883
|
unless( exists( $opts->{'coefficients'}{$_} ) ){ |
715
|
3110
|
|
|
|
|
7925
|
$in_opts{'coefficients'}{$_} += $e_oxi->[$j]; |
716
|
|
|
|
|
|
|
} |
717
|
|
|
|
|
|
|
# last; |
718
|
|
|
|
|
|
|
} |
719
|
1555
|
|
|
|
|
3408
|
$j++; |
720
|
|
|
|
|
|
|
} |
721
|
595
|
|
|
|
|
910
|
$j = 0; |
722
|
595
|
|
|
|
|
1100
|
for my $i_k ( @$pair_red ){ # пары для восстановителей |
723
|
1134
|
|
|
|
|
2596
|
for( split "->",$i_k ){ |
724
|
2268
|
50
|
|
|
|
5394
|
unless( exists( $opts->{'coefficients'}{$_} ) ){ |
725
|
2268
|
|
|
|
|
4766
|
$in_opts{'coefficients'}{$_} += $e_red->[$j]; |
726
|
|
|
|
|
|
|
} |
727
|
|
|
|
|
|
|
} |
728
|
1134
|
|
|
|
|
2046
|
$j++; |
729
|
|
|
|
|
|
|
} |
730
|
|
|
|
|
|
|
|
731
|
595
|
|
|
|
|
1037
|
eval{ &_in_search_stoich($mix, $cheq, \%in_opts) }; |
|
595
|
|
|
|
|
1491
|
|
732
|
|
|
|
|
|
|
} |
733
|
|
|
|
|
|
|
} |
734
|
|
|
|
|
|
|
} |
735
|
|
|
|
|
|
|
} |
736
|
|
|
|
|
|
|
} |
737
|
|
|
|
|
|
|
|
738
|
|
|
|
|
|
|
# Ordinary no-ОВР reactions |
739
|
|
|
|
|
|
|
|
740
|
6
|
|
|
|
|
49
|
my $iter = subsets($All_substances); |
741
|
6
|
|
|
|
|
260
|
$iter->next; # to discard 1st full substances list |
742
|
|
|
|
|
|
|
|
743
|
6
|
|
|
|
|
98
|
while(my $ys = $iter->next){ # Substances list |
744
|
826
|
100
|
|
|
|
36184
|
next if @$ys < 2; # it isn't enough substances for a variation (not last!) |
745
|
|
|
|
|
|
|
|
746
|
778
|
|
50
|
|
|
2593
|
my $new_mix = prepare_mix( $chem_eq, { 'substances' => $ys } ) || next; |
747
|
|
|
|
|
|
|
|
748
|
778
|
|
|
|
|
1859
|
eval{ &_in_search_stoich($new_mix, $cheq, $opts) }; # recursion |
|
778
|
|
|
|
|
1521
|
|
749
|
|
|
|
|
|
|
} |
750
|
6
|
|
|
|
|
215
|
return; |
751
|
|
|
|
|
|
|
} |
752
|
|
|
|
|
|
|
|
753
|
105
|
|
|
|
|
365
|
my $base_subst = [ @$substances_X ]; # Base substances list |
754
|
105
|
|
|
|
|
202
|
my $var_subst; # Varied substance |
755
|
|
|
|
|
|
|
|
756
|
|
|
|
|
|
|
# R-vector не задан (0) или задан (1) and matrix недоопределённая |
757
|
105
|
100
|
33
|
|
|
722
|
if( $R_zero || ( $R_zero == 0 && @$substances_X == $order + 1 ) ){ |
|
|
|
66
|
|
|
|
|
758
|
|
|
|
|
|
|
|
759
|
|
|
|
|
|
|
# Search linear-dependent substance (column) |
760
|
46
|
|
|
|
|
75
|
for( 1..$#{ $substances_X } ){ |
|
46
|
|
|
|
|
130
|
|
761
|
50
|
|
|
|
|
98
|
$var_subst = pop @$base_subst; # взять последнее |
762
|
50
|
100
|
|
|
|
463
|
last if Rank( [ ( map $atoms_substance->{$_}, @$base_subst ) ] ) == $order; |
763
|
4
|
|
|
|
|
9984
|
unshift @$base_subst, $var_subst; # add в начало |
764
|
|
|
|
|
|
|
} |
765
|
|
|
|
|
|
|
|
766
|
|
|
|
|
|
|
# return if @$base_subst == @$substances_X; # no found (?) |
767
|
|
|
|
|
|
|
|
768
|
46
|
|
|
|
|
162668
|
my $i; |
769
|
|
|
|
|
|
|
# доформировываем R-vector |
770
|
46
|
|
|
|
|
77
|
$R_free->[$i++] -= $_ for @{ $atoms_substance->{$var_subst} }; |
|
46
|
|
|
|
|
298
|
|
771
|
|
|
|
|
|
|
} |
772
|
|
|
|
|
|
|
|
773
|
|
|
|
|
|
|
# Переопределённая матрица (atoms >= substances > rank) |
774
|
105
|
|
|
|
|
222
|
my $ao = $num_atoms - $order; |
775
|
105
|
100
|
|
|
|
337
|
if( $ao > 0 ){ |
776
|
|
|
|
|
|
|
|
777
|
|
|
|
|
|
|
# Copy vector & atom matrix |
778
|
82
|
|
|
|
|
211
|
my $R_copy = [ @$R_free ]; |
779
|
82
|
|
|
|
|
277
|
my $cam = { map{ $_, [ @{ $atoms_substance->{$_} } ] } keys %$atoms_substance }; |
|
234
|
|
|
|
|
278
|
|
|
234
|
|
|
|
|
835
|
|
780
|
|
|
|
|
|
|
|
781
|
82
|
|
|
|
|
570
|
my $it = subsets( [ ( 0..$num_atoms - 1 ) ], $ao ); |
782
|
82
|
|
|
|
|
5116
|
while( my $p = $it->next ){ |
783
|
|
|
|
|
|
|
|
784
|
|
|
|
|
|
|
# Удаление атомных строк (от старших) |
785
|
88
|
|
|
|
|
986
|
for my $i ( reverse @$p ){ |
786
|
149
|
|
|
|
|
302
|
splice @$R_free, $i, 1; # из вектора |
787
|
149
|
|
|
|
|
340
|
splice @{ $_ }, $i, 1 for values %$atoms_substance; # из атомной матрицы |
|
410
|
|
|
|
|
896
|
|
788
|
|
|
|
|
|
|
} |
789
|
|
|
|
|
|
|
|
790
|
|
|
|
|
|
|
# All linear-dependent atom rows removed |
791
|
88
|
100
|
|
|
|
518
|
last if Rank( [ map $atoms_substance->{$_}, @$base_subst ] ) == $order; |
792
|
|
|
|
|
|
|
|
793
|
|
|
|
|
|
|
# Recovery and search repetition |
794
|
6
|
|
|
|
|
7214
|
$R_free = [ @$R_copy ]; # vector |
795
|
6
|
|
|
|
|
21
|
$atoms_substance = { map{ $_, [ @{ $cam->{$_} } ] } keys %$cam }; # matrix |
|
23
|
|
|
|
|
25
|
|
|
23
|
|
|
|
|
97
|
|
796
|
|
|
|
|
|
|
|
797
|
|
|
|
|
|
|
} |
798
|
|
|
|
|
|
|
# $num_atoms -= $ao; # уменьшаем число атомов |
799
|
|
|
|
|
|
|
} |
800
|
|
|
|
|
|
|
|
801
|
|
|
|
|
|
|
# Находим решение |
802
|
|
|
|
|
|
|
# Формируем атомную матрицу, |
803
|
105
|
50
|
|
|
|
5580326
|
my $steX = Solve_Det( |
804
|
|
|
|
|
|
|
[ map $atoms_substance->{$_}, @$base_subst ], |
805
|
|
|
|
|
|
|
$R_free, |
806
|
|
|
|
|
|
|
{ 'eqs' => 'column', 'int' => 1 } ) |
807
|
|
|
|
|
|
|
or |
808
|
|
|
|
|
|
|
croak("No solver!"); # Нет решения |
809
|
|
|
|
|
|
|
|
810
|
|
|
|
|
|
|
# Решение НАЙДЕНО! |
811
|
105
|
|
|
|
|
331023
|
my @den; # Знаменатели |
812
|
105
|
|
|
|
|
388
|
for( @$steX ){ |
813
|
330
|
|
|
|
|
8633
|
my($n, $d) = Math::BigRat->new("$_")->parts(); |
814
|
330
|
|
|
|
|
83139
|
$_ = $n; # числитель |
815
|
|
|
|
|
|
|
|
816
|
330
|
|
50
|
|
|
1298
|
push @den, $d || 1; |
817
|
|
|
|
|
|
|
} |
818
|
|
|
|
|
|
|
|
819
|
|
|
|
|
|
|
# lowest common multiplicator |
820
|
105
|
|
|
|
|
3194
|
my $lcm = blcm(@den); |
821
|
|
|
|
|
|
|
|
822
|
105
|
|
|
|
|
47186
|
my %cfs; # Стех. коэффициенты |
823
|
105
|
100
|
|
|
|
427
|
$cfs{$var_subst} = $lcm if defined $var_subst; # варьируемого в-ва |
824
|
|
|
|
|
|
|
|
825
|
105
|
100
|
|
|
|
300
|
unless( $R_zero ){ # Некоторые в-ва заданы |
826
|
59
|
|
|
|
|
92
|
while( my($s, $c) = each %{ $opts->{'coefficients'} } ){ |
|
332
|
|
|
|
|
31913
|
|
827
|
273
|
|
|
|
|
673
|
$cfs{$s} = $lcm * $c; |
828
|
|
|
|
|
|
|
} |
829
|
|
|
|
|
|
|
} |
830
|
|
|
|
|
|
|
|
831
|
105
|
|
|
|
|
228
|
my $i = 0; |
832
|
105
|
|
|
|
|
253
|
for( @$base_subst ){ # Искомые |
833
|
330
|
|
|
|
|
1139
|
$cfs{$_} = $steX->[$i] * $lcm / $den[$i]; |
834
|
330
|
|
|
|
|
6914603
|
$i++; |
835
|
|
|
|
|
|
|
} |
836
|
|
|
|
|
|
|
|
837
|
105
|
100
|
|
|
|
347
|
if( $ao > 0 ){ # Для переопределённых - Проверка мат.баланса |
838
|
|
|
|
|
|
|
|
839
|
82
|
|
|
|
|
146
|
my @balance; |
840
|
82
|
|
|
|
|
489
|
for my $s (keys %cfs){ # в-во |
841
|
505
|
|
|
|
|
24700
|
my $i = 0; |
842
|
|
|
|
|
|
|
|
843
|
505
|
|
|
|
|
565
|
$balance[ $i++ ] += $_ * $cfs{$s}->numify() for @{ $ini_am->{$s} }; |
|
505
|
|
|
|
|
2047
|
|
844
|
|
|
|
|
|
|
} |
845
|
|
|
|
|
|
|
|
846
|
82
|
100
|
|
|
|
6353
|
return if scalar(grep $_, @balance); # no balance |
847
|
|
|
|
|
|
|
} |
848
|
|
|
|
|
|
|
|
849
|
|
|
|
|
|
|
# С помощью НОД (GCD) уменьшаем порядок стех.коэф. |
850
|
49
|
50
|
|
|
|
188
|
if( ( my $gcd = bgcd(values %cfs) ) > 1 ){ |
851
|
0
|
|
|
|
|
0
|
$cfs{$_} /= $gcd for keys %cfs; |
852
|
|
|
|
|
|
|
} |
853
|
|
|
|
|
|
|
|
854
|
49
|
|
|
|
|
8004
|
my $keq = prepare_mix( $chem_eq, { 'norma' => 1, 'coefficients' => \%cfs } ); |
855
|
|
|
|
|
|
|
|
856
|
|
|
|
|
|
|
# Save only unique chemical equation |
857
|
49
|
100
|
|
|
|
2000
|
scalar(grep $_ eq $keq, @$cheq) || push @$cheq, $keq; |
858
|
|
|
|
|
|
|
} |
859
|
|
|
|
|
|
|
|
860
|
|
|
|
|
|
|
|
861
|
|
|
|
|
|
|
# Возвращает массив хешей: |
862
|
|
|
|
|
|
|
# $redox{oxidant/reducer}{Элементы}{исх.->прод.} |
863
|
|
|
|
|
|
|
# [0] - число e- нов принимаемых/отдаваемых |
864
|
|
|
|
|
|
|
# [1] - OS элемента в исходном |
865
|
|
|
|
|
|
|
# [2] - ... в продукте |
866
|
|
|
|
|
|
|
# [3] - число атомов элемента в исходном |
867
|
|
|
|
|
|
|
# [4] - ... в продукте |
868
|
|
|
|
|
|
|
# [5] - множитель на MAX число атомов исх. |
869
|
|
|
|
|
|
|
# [6] - ... продукта |
870
|
|
|
|
|
|
|
|
871
|
|
|
|
|
|
|
# $el_half_eqs{oxidant/reducer}[..] - 2 массива полуреакций (в проекте для другой п/п) |
872
|
|
|
|
|
|
|
|
873
|
|
|
|
|
|
|
sub redox_test{ |
874
|
4
|
|
|
4
|
0
|
6
|
my $chem_eq = shift; |
875
|
|
|
|
|
|
|
|
876
|
4
|
|
|
|
|
8
|
my @rx; # $rx[исх/прод] {элементы} {OS элемента} {вещества} = Кол-во атомов элемента |
877
|
|
|
|
|
|
|
my %ve; # $ve{в-во} = количество элементов |
878
|
|
|
|
|
|
|
|
879
|
4
|
|
|
|
|
7
|
my $z = 0; # Признак исх.в-в |
880
|
4
|
|
|
|
|
6
|
for my $eq (@{ $chem_eq }){ |
|
4
|
|
|
|
|
8
|
|
881
|
8
|
|
|
|
|
12
|
for my $s (@{ $eq }){ |
|
8
|
|
|
|
|
12
|
|
882
|
|
|
|
|
|
|
|
883
|
29
|
|
|
|
|
84
|
my $os = oxidation_state($s); |
884
|
|
|
|
|
|
|
# Рез-ты: $os->{элемент} |
885
|
|
|
|
|
|
|
# {num}[0 .. n-1] - кол-во по каждому 1..n атому элемента |
886
|
|
|
|
|
|
|
# {OS}[0 .. n-1][ ] - степень(и) окисления (OS) 1..n атому элемента |
887
|
|
|
|
|
|
|
|
888
|
29
|
|
|
|
|
92
|
$ve{$s} = keys %$os; # кол-во Элементов |
889
|
|
|
|
|
|
|
|
890
|
29
|
|
|
|
|
40
|
for my $e ( keys %{$os} ){ # Элемент |
|
29
|
|
|
|
|
71
|
|
891
|
|
|
|
|
|
|
|
892
|
|
|
|
|
|
|
# Берём OS атомов элемента в в-ве |
893
|
67
|
|
|
|
|
101
|
for ( my $i=0; $i<=$#{ $os->{$e}{'OS'} }; $i++ ){ |
|
134
|
|
|
|
|
509
|
|
894
|
|
|
|
|
|
|
|
895
|
|
|
|
|
|
|
# !!! Продумать дробные степени окисления (сделано) |
896
|
|
|
|
|
|
|
# Объединяем множественные OS |
897
|
67
|
|
|
|
|
127
|
my($a, $sum, $n); |
898
|
67
|
|
|
|
|
95
|
for( @{ $os->{$e}{'OS'}[$i] } ){ |
|
67
|
|
|
|
|
189
|
|
899
|
67
|
|
|
|
|
75
|
$sum += $_; |
900
|
67
|
|
|
|
|
126
|
$n++; # кол-во |
901
|
|
|
|
|
|
|
} |
902
|
|
|
|
|
|
|
|
903
|
67
|
50
|
|
|
|
145
|
if($n > 1){ |
904
|
0
|
|
|
|
|
0
|
my $gcd = bgcd(abs($sum), $n); # НОД |
905
|
0
|
0
|
|
|
|
0
|
if($gcd > 1){ |
906
|
0
|
|
|
|
|
0
|
$sum /= $gcd; |
907
|
0
|
|
|
|
|
0
|
$n /= $gcd; |
908
|
|
|
|
|
|
|
} |
909
|
0
|
|
|
|
|
0
|
$a = "$sum/$n"; |
910
|
|
|
|
|
|
|
|
911
|
|
|
|
|
|
|
}else{ |
912
|
67
|
|
|
|
|
95
|
$a = $sum; |
913
|
|
|
|
|
|
|
} |
914
|
|
|
|
|
|
|
# Кол-во атомов элемента |
915
|
67
|
|
|
|
|
295
|
$rx[$z]{$e}{$a}{$s} = $os->{$e}{'num'}[$i]; |
916
|
|
|
|
|
|
|
} |
917
|
|
|
|
|
|
|
} |
918
|
|
|
|
|
|
|
} |
919
|
8
|
|
|
|
|
25
|
$z = 1; # продукты |
920
|
|
|
|
|
|
|
} |
921
|
|
|
|
|
|
|
|
922
|
|
|
|
|
|
|
_M_redox_1: |
923
|
4
|
|
|
|
|
11
|
for my $e ( keys %{ $rx[0] } ){ # Элементы (берём из исходных) |
|
4
|
|
|
|
|
19
|
|
924
|
22
|
|
|
|
|
25
|
for( keys %{ $rx[0]{$e} } ) { # OS элемента |
|
22
|
|
|
|
|
55
|
|
925
|
23
|
100
|
|
|
|
86
|
next _M_redox_1 unless exists( $rx[1]{$e}{$_} ); # Другая в продуктах |
926
|
|
|
|
|
|
|
} |
927
|
|
|
|
|
|
|
|
928
|
10
|
|
|
|
|
17
|
for( keys %{ $rx[1]{$e} } ) { # OS элемента |
|
10
|
|
|
|
|
25
|
|
929
|
10
|
50
|
|
|
|
32
|
next _M_redox_1 unless exists( $rx[0]{$e}{$_} ); # Другая в исходных |
930
|
|
|
|
|
|
|
} |
931
|
|
|
|
|
|
|
# Удалить не ОВР элементы |
932
|
10
|
|
|
|
|
57
|
delete $rx[$_]{$e} for 0,1; |
933
|
|
|
|
|
|
|
} |
934
|
|
|
|
|
|
|
|
935
|
|
|
|
|
|
|
# не ОВР |
936
|
4
|
50
|
33
|
|
|
9
|
return if ! keys( %{ $rx[0] } ) || ! keys( %{ $rx[1] } ); |
|
4
|
|
|
|
|
18
|
|
|
4
|
|
|
|
|
13
|
|
937
|
|
|
|
|
|
|
# return undef unless keys( %{ $rx[1] } ); |
938
|
|
|
|
|
|
|
|
939
|
4
|
|
|
|
|
7
|
my %del_rx; # хеш исключённых в-в |
940
|
|
|
|
|
|
|
my %redox; # результаты |
941
|
|
|
|
|
|
|
|
942
|
|
|
|
|
|
|
_M_redox_2: |
943
|
4
|
|
|
|
|
7
|
while( 1 ){ |
944
|
6
|
|
|
|
|
12
|
my %vv; # $vv{в-во} {'yes'} = встечаемость в-ва в redox-парах |
945
|
|
|
|
|
|
|
# {'os'}{элемент} = 1 - Элементы меняющие свою OS |
946
|
|
|
|
|
|
|
|
947
|
6
|
|
|
|
|
15
|
for my $e ( keys %{ $rx[0] } ){ # Элементы (берём из исходных) |
|
6
|
|
|
|
|
20
|
|
948
|
16
|
|
|
|
|
22
|
for my $os_i ( keys %{ $rx[0]{$e} } ) { # OS элемента в исх. |
|
16
|
|
|
|
|
44
|
|
949
|
19
|
|
|
|
|
23
|
for my $os_p ( keys %{ $rx[1]{$e} } ) { # OS элемента в продуктах |
|
19
|
|
|
|
|
63
|
|
950
|
|
|
|
|
|
|
|
951
|
|
|
|
|
|
|
|
952
|
|
|
|
|
|
|
# !!! Продумать дробные степени окисления (сделано) |
953
|
18
|
|
|
|
|
95
|
my $n_os_i = Math::BigRat -> new("$os_i") -> numify(); |
954
|
18
|
|
|
|
|
2470
|
my $n_os_p = Math::BigRat -> new("$os_p") -> numify(); |
955
|
|
|
|
|
|
|
|
956
|
18
|
|
|
|
|
2000
|
my $nm; # название: oxidant / reducer |
957
|
18
|
100
|
|
|
|
75
|
if($n_os_i > $n_os_p){ # Окислитель? |
|
|
100
|
|
|
|
|
|
958
|
8
|
|
|
|
|
15
|
$nm = 'oxidant'; |
959
|
|
|
|
|
|
|
|
960
|
|
|
|
|
|
|
}elsif($n_os_i < $n_os_p){ # Восстановитель? |
961
|
7
|
|
|
|
|
17
|
$nm = 'reducer'; |
962
|
|
|
|
|
|
|
|
963
|
|
|
|
|
|
|
}else{ |
964
|
3
|
|
|
|
|
12
|
next; |
965
|
|
|
|
|
|
|
} |
966
|
|
|
|
|
|
|
|
967
|
|
|
|
|
|
|
# Составляем пару |
968
|
15
|
|
|
|
|
20
|
while( my($v_i, $n_i) = each %{ $rx[0]{$e}{$os_i} } ){ # исх. в-ва и кол-во атомов |
|
33
|
|
|
|
|
187
|
|
969
|
18
|
100
|
|
|
|
53
|
next if exists $del_rx{$v_i}; # исключённое в-во |
970
|
|
|
|
|
|
|
|
971
|
17
|
|
|
|
|
41
|
$vv{$v_i}{'yes'}++; |
972
|
17
|
|
|
|
|
43
|
$vv{$v_i}{'os'}{$e} = 1; # Элементы меняющие свою OS |
973
|
|
|
|
|
|
|
|
974
|
17
|
|
|
|
|
21
|
while( my($v_p, $n_p) = each %{ $rx[1]{$e}{$os_p} } ){ # продукты и кол-во атомов |
|
40
|
|
|
|
|
202
|
|
975
|
23
|
100
|
|
|
|
57
|
next if exists $del_rx{$v_p}; # исключённое в-во |
976
|
|
|
|
|
|
|
|
977
|
21
|
|
|
|
|
48
|
$vv{$v_p}{'yes'}++; |
978
|
21
|
|
|
|
|
48
|
$vv{$v_p}{'os'}{$e} = 1; # Элементы меняющие свою OS |
979
|
|
|
|
|
|
|
|
980
|
21
|
|
|
|
|
66
|
my $lcm = blcm($n_i, $n_p); # наименьший общий множитель кол-ва атомов |
981
|
|
|
|
|
|
|
|
982
|
21
|
|
|
|
|
6064
|
my($c, $z) = Math::BigRat->new("$os_i")->parts(); |
983
|
21
|
|
|
|
|
4184
|
my $ee = $lcm * $c / $z; |
984
|
|
|
|
|
|
|
|
985
|
21
|
|
|
|
|
3102
|
($c, $z) = Math::BigRat->new("$os_p")->parts(); |
986
|
21
|
|
|
|
|
4261
|
$ee -= $lcm * $c / $z; |
987
|
|
|
|
|
|
|
|
988
|
|
|
|
|
|
|
# Принятые элек-ны, OS, число атомов и |
989
|
|
|
|
|
|
|
# множители на MAX число атомов исх. и прод. |
990
|
21
|
|
|
|
|
4416
|
@{ $redox{$nm}{$e}{"$v_i->$v_p"} } = ( abs($ee), |
|
21
|
|
|
|
|
6654
|
|
991
|
|
|
|
|
|
|
$os_i, $os_p, |
992
|
|
|
|
|
|
|
$n_i, $n_p, |
993
|
|
|
|
|
|
|
$lcm / $n_i, $lcm / $n_p ); |
994
|
|
|
|
|
|
|
} |
995
|
|
|
|
|
|
|
} |
996
|
|
|
|
|
|
|
} |
997
|
|
|
|
|
|
|
} |
998
|
|
|
|
|
|
|
} |
999
|
|
|
|
|
|
|
|
1000
|
|
|
|
|
|
|
# !!! Интуитивно исключаем из redox-списков не ОВР в-ва |
1001
|
6
|
|
|
|
|
22
|
for my $nm ( 'oxidant','reducer' ){ |
1002
|
11
|
|
|
|
|
20
|
for my $e ( keys %{ $redox{$nm} } ){ # Элементы |
|
11
|
|
|
|
|
44
|
|
1003
|
|
|
|
|
|
|
|
1004
|
|
|
|
|
|
|
# Redox-пары элемента |
1005
|
11
|
100
|
|
|
|
15
|
next if keys %{ $redox{$nm}{$e} } < 2; # одна пара |
|
11
|
|
|
|
|
48
|
|
1006
|
|
|
|
|
|
|
|
1007
|
5
|
|
|
|
|
10
|
for my $i_k ( keys %{ $redox{$nm}{$e} } ){ |
|
5
|
|
|
|
|
21
|
|
1008
|
10
|
|
|
|
|
38
|
for( split "->",$i_k ){ |
1009
|
19
|
100
|
|
|
|
68
|
next if $ve{$_} < 3; # кол-во Элементов в в-ве |
1010
|
6
|
100
|
|
|
|
26
|
next if $vv{$_}{'yes'} > 1; # в-во встречается часто |
1011
|
2
|
50
|
|
|
|
3
|
next if keys %{ $vv{$_}{'os'} } > 1; # OS меняет более 1 Элемента |
|
2
|
|
|
|
|
13
|
|
1012
|
|
|
|
|
|
|
|
1013
|
2
|
|
|
|
|
7
|
$del_rx{$_} = ''; # внести в список исключённых в-в |
1014
|
2
|
|
|
|
|
43
|
%redox = (); # очистить |
1015
|
2
|
|
|
|
|
53
|
next _M_redox_2; |
1016
|
|
|
|
|
|
|
} |
1017
|
|
|
|
|
|
|
} |
1018
|
|
|
|
|
|
|
} |
1019
|
|
|
|
|
|
|
|
1020
|
|
|
|
|
|
|
|
1021
|
|
|
|
|
|
|
} |
1022
|
4
|
100
|
66
|
|
|
19
|
return keys( %{ $redox{'oxidant'} } ) && keys( %{ $redox{'reducer'} } ) ? \%redox : undef; # не ОВР |
1023
|
|
|
|
|
|
|
} |
1024
|
|
|
|
|
|
|
} |
1025
|
|
|
|
|
|
|
|
1026
|
|
|
|
|
|
|
|
1027
|
|
|
|
|
|
|
sub _search_atoms_subs{ |
1028
|
1391
|
|
|
1391
|
|
1569
|
my $chem_eq = shift; |
1029
|
|
|
|
|
|
|
|
1030
|
1391
|
|
|
|
|
1378
|
my %tmp_subs; # Atoms of substance |
1031
|
|
|
|
|
|
|
my %atoms; # Atom hash |
1032
|
|
|
|
|
|
|
|
1033
|
1391
|
|
|
|
|
2183
|
for my $i ( @$chem_eq ){ |
1034
|
2782
|
|
|
|
|
4068
|
for my $s ( @$i ){ |
1035
|
|
|
|
|
|
|
# Atoms of substance |
1036
|
7213
|
50
|
|
|
|
9137
|
my %f = eval{ Chemistry::File::Formula->parse_formula( $s ) } or |
|
7213
|
|
|
|
|
22999
|
|
1037
|
|
|
|
|
|
|
croak("'$s' is not substance!"); |
1038
|
|
|
|
|
|
|
|
1039
|
7213
|
|
|
|
|
1226380
|
for( keys %f ){ |
1040
|
17235
|
|
|
|
|
38960
|
$tmp_subs{$s}{$_} = $f{$_}; |
1041
|
17235
|
|
|
|
|
44603
|
$atoms{$_}++; # Atom balance |
1042
|
|
|
|
|
|
|
} |
1043
|
|
|
|
|
|
|
} |
1044
|
|
|
|
|
|
|
} |
1045
|
|
|
|
|
|
|
|
1046
|
1391
|
|
|
|
|
4862
|
while( my($k,$v) = each %atoms){ |
1047
|
4792
|
100
|
|
|
|
148463
|
croak("No balance of '$k' atom!") if $v == 1; |
1048
|
|
|
|
|
|
|
} |
1049
|
|
|
|
|
|
|
|
1050
|
|
|
|
|
|
|
# Atom (stoichiometry) matrix vectors (quantity of atoms for each substance) |
1051
|
674
|
|
|
|
|
1218
|
my %atoms_substance; |
1052
|
674
|
|
|
|
|
2125
|
for my $subs (keys %tmp_subs){ |
1053
|
4574
|
100
|
|
|
|
8773
|
$atoms_substance{$subs} = [ map { $tmp_subs{$subs}{$_} || 0 } keys %atoms ]; |
|
23109
|
|
|
|
|
89361
|
|
1054
|
|
|
|
|
|
|
} |
1055
|
|
|
|
|
|
|
|
1056
|
674
|
|
|
|
|
17041
|
return( \%atoms_substance, scalar(keys %atoms) ); |
1057
|
|
|
|
|
|
|
} |
1058
|
|
|
|
|
|
|
|
1059
|
|
|
|
|
|
|
# Transform classic into brutto (gross) formula |
1060
|
|
|
|
|
|
|
sub brutto_formula{ |
1061
|
3
|
|
|
3
|
1
|
1518
|
my $s = shift; |
1062
|
|
|
|
|
|
|
|
1063
|
3
|
50
|
|
|
|
6
|
my %e = eval{ Chemistry::File::Formula->parse_formula( $s ) } or |
|
3
|
|
|
|
|
18
|
|
1064
|
|
|
|
|
|
|
croak("'$s' is not substance!"); |
1065
|
|
|
|
|
|
|
|
1066
|
3
|
|
|
|
|
2568
|
return( join( '', map{ "$_$e{$_}" } sort { $a cmp $b } keys %e ) ); |
|
14
|
|
|
|
|
55
|
|
|
19
|
|
|
|
|
25
|
|
1067
|
|
|
|
|
|
|
} |
1068
|
|
|
|
|
|
|
|
1069
|
|
|
|
|
|
|
# Decomposes the chemical equation to |
1070
|
|
|
|
|
|
|
# arrays of initial substances and products |
1071
|
|
|
|
|
|
|
sub parse_chem_mix{ |
1072
|
1498
|
|
|
1498
|
1
|
65472
|
$_ = shift; # List of substances with delimiters (spaces, comma, + ) |
1073
|
1498
|
|
|
|
|
1977
|
my $coef = shift; # hash of coefficients |
1074
|
|
|
|
|
|
|
|
1075
|
1498
|
|
|
|
|
1542
|
my %cf; # temp hash of coefficients |
1076
|
|
|
|
|
|
|
|
1077
|
1498
|
|
|
|
|
3850
|
s/^\s+//; |
1078
|
1498
|
|
|
|
|
5363
|
s/\s+$//; |
1079
|
|
|
|
|
|
|
|
1080
|
1498
|
|
|
|
|
5505
|
my $m = qr/\s*[ ,;+]+\s*/; # mask of delimeters |
1081
|
|
|
|
|
|
|
|
1082
|
|
|
|
|
|
|
# to unite the separeted numerals ( coefficient ? ) |
1083
|
1498
|
|
|
|
|
36049
|
1 while s/((?:$m|^)[1-9]+)$m([1-9]+(?:$m|$))/$1$2/g; |
1084
|
|
|
|
|
|
|
|
1085
|
|
|
|
|
|
|
# to unite the separeted substance & index |
1086
|
1498
|
|
|
|
|
10985
|
s/([)}\]a-zA-Z1-9])\s+([1-9]+\s*[)}\]\-=+])/$1$2/g; |
1087
|
|
|
|
|
|
|
|
1088
|
|
|
|
|
|
|
# remove spaces around brackets |
1089
|
1498
|
|
|
|
|
2703
|
s/([({\[])\s+/$1/g; |
1090
|
1498
|
|
|
|
|
7747
|
s/\s+([)}\]])/$1/g; |
1091
|
|
|
|
|
|
|
|
1092
|
|
|
|
|
|
|
# The chemical equation is set: |
1093
|
|
|
|
|
|
|
# A + B --> C + D || |
1094
|
|
|
|
|
|
|
# A, B, C, D |
1095
|
1498
|
100
|
|
|
|
45897
|
my $chem_eq = /(.+?)\s*[=-]+>*\s*(.+)/ ? |
1096
|
|
|
|
|
|
|
[ map [ split "$m" ], ($1,$2) ] : |
1097
|
|
|
|
|
|
|
[ map [ split "$m" ], ( /(.+)$m(\S+)/ ) ]; |
1098
|
|
|
|
|
|
|
|
1099
|
1498
|
50
|
|
|
|
3912
|
return unless @$chem_eq; |
1100
|
|
|
|
|
|
|
|
1101
|
1498
|
|
|
|
|
4404
|
for my $ip ( @$chem_eq ){ |
1102
|
|
|
|
|
|
|
|
1103
|
2996
|
|
|
|
|
3886
|
my @ip_del; # For removal from an array of coefficients |
1104
|
|
|
|
|
|
|
|
1105
|
2996
|
|
|
|
|
5433
|
for(my $i=0; $i<=$#{ $ip }; $i++ ){ |
|
11112
|
|
|
|
|
25181
|
|
1106
|
8116
|
|
|
|
|
11868
|
$_ = $ip->[$i]; |
1107
|
|
|
|
|
|
|
|
1108
|
8116
|
100
|
|
|
|
41546
|
if( /^(\d+)([({\[a-zA-Z].*)/ ){ # together coefficient and substance |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
1109
|
|
|
|
|
|
|
|
1110
|
39
|
|
|
|
|
83
|
my ($c, $s) = ($1, $2); # coefficient, substance |
1111
|
|
|
|
|
|
|
|
1112
|
|
|
|
|
|
|
# zero coefficient |
1113
|
39
|
100
|
66
|
|
|
160
|
if( $c =~ /^0$/ && ( $i==0 || ( $i>0 && $ip->[$i-1] =~ /\D/ ) ) ){ |
|
|
100
|
66
|
|
|
|
|
1114
|
2
|
|
|
|
|
6
|
push @ip_del, $i; # remove substance |
1115
|
|
|
|
|
|
|
|
1116
|
|
|
|
|
|
|
}elsif( s/^0/O/ ){ # oxigen? |
1117
|
2
|
|
|
|
|
5
|
&_p_c_m( $_, $i, \%cf, $ip ); |
1118
|
|
|
|
|
|
|
|
1119
|
|
|
|
|
|
|
}else{ |
1120
|
35
|
|
|
|
|
118
|
$cf{$s} = $c; # coefficient |
1121
|
35
|
|
|
|
|
80
|
$ip->[$i] = $s; # substance |
1122
|
|
|
|
|
|
|
} |
1123
|
|
|
|
|
|
|
|
1124
|
|
|
|
|
|
|
}elsif( s/^0(.)/O$1/ ){ # oxigen |
1125
|
6
|
|
|
|
|
19
|
&_p_c_m( $_, $i, \%cf, $ip ); |
1126
|
|
|
|
|
|
|
|
1127
|
|
|
|
|
|
|
}elsif( /^0$/ ){ # zero coefficient |
1128
|
12
|
100
|
100
|
|
|
35
|
if( $i < $#{ $ip } && ! exists( $coef->{'zero2oxi'} ) ){ # remove |
|
12
|
|
|
|
|
58
|
|
1129
|
5
|
|
|
|
|
6
|
push @ip_del, $i; # coefficient |
1130
|
5
|
|
|
|
|
11
|
push @ip_del, ++$i; # substance |
1131
|
|
|
|
|
|
|
|
1132
|
|
|
|
|
|
|
}else{ |
1133
|
7
|
|
|
|
|
20
|
s/^0/O/; # oxigen |
1134
|
7
|
|
|
|
|
17
|
&_p_c_m( $_, $i, \%cf, $ip ); |
1135
|
|
|
|
|
|
|
} |
1136
|
|
|
|
|
|
|
|
1137
|
|
|
|
|
|
|
}elsif( /^\d+$/ ){ # only numerals |
1138
|
|
|
|
|
|
|
# coefficient |
1139
|
411
|
100
|
66
|
|
|
832
|
if( defined $ip->[$i+1] ){ |
|
5
|
100
|
66
|
|
|
41
|
|
1140
|
402
|
|
|
|
|
992
|
$cf{ $ip->[$i+1] } = $_; |
1141
|
|
|
|
|
|
|
|
1142
|
|
|
|
|
|
|
}elsif( $ip eq $chem_eq->[0] && |
1143
|
|
|
|
|
|
|
@{ $chem_eq->[1] } == 1 && |
1144
|
|
|
|
|
|
|
$chem_eq->[1][0] =~ /[a-zA-Z]/ ){ |
1145
|
|
|
|
|
|
|
|
1146
|
|
|
|
|
|
|
# coefficient of product |
1147
|
5
|
|
|
|
|
14
|
$chem_eq->[1][0] = $_.$chem_eq->[1][0]; |
1148
|
|
|
|
|
|
|
} |
1149
|
411
|
|
|
|
|
724
|
push @ip_del, $i; |
1150
|
|
|
|
|
|
|
} |
1151
|
|
|
|
|
|
|
} |
1152
|
2996
|
|
|
|
|
11077
|
splice @$ip, $_, 1 for( reverse @ip_del ); |
1153
|
|
|
|
|
|
|
} |
1154
|
|
|
|
|
|
|
|
1155
|
|
|
|
|
|
|
# To removal identical reactants and products |
1156
|
1498
|
|
|
|
|
2480
|
for my $ip ( @$chem_eq ){ |
1157
|
|
|
|
|
|
|
|
1158
|
2996
|
|
|
|
|
3183
|
my @ip_del; |
1159
|
2996
|
|
|
|
|
3847
|
for(my $i = 0; $i < $#{ $ip }; $i++ ){ |
|
7706
|
|
|
|
|
15799
|
|
1160
|
|
|
|
|
|
|
|
1161
|
4710
|
|
|
|
|
5842
|
for(my $j = $i + 1; $j <= $#{ $ip }; $j++ ){ |
|
13143
|
|
|
|
|
27528
|
|
1162
|
|
|
|
|
|
|
|
1163
|
8443
|
100
|
|
|
|
18416
|
if( $ip->[$i] eq $ip->[$j] ){ |
1164
|
10
|
|
|
|
|
11
|
push @ip_del, $j; |
1165
|
10
|
|
|
|
|
19
|
last; |
1166
|
|
|
|
|
|
|
} |
1167
|
|
|
|
|
|
|
} |
1168
|
|
|
|
|
|
|
} |
1169
|
2996
|
|
|
|
|
9748
|
splice @$ip, $_, 1 for( sort{ $b <=> $a } @ip_del ); |
|
4
|
|
|
|
|
15
|
|
1170
|
|
|
|
|
|
|
} |
1171
|
|
|
|
|
|
|
|
1172
|
|
|
|
|
|
|
# Check empty reactants |
1173
|
1498
|
100
|
|
|
|
2263
|
if( ! @{ $chem_eq->[0] } ){ |
|
1498
|
|
|
|
|
4170
|
|
1174
|
3
|
100
|
|
|
|
4
|
return if @{ $chem_eq->[1] } < 2; # bad mix (one or no substance) |
|
3
|
|
|
|
|
13
|
|
1175
|
2
|
|
|
|
|
3
|
push @{ $chem_eq->[0] }, shift @{ $chem_eq->[1] }; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
26
|
|
1176
|
|
|
|
|
|
|
} |
1177
|
|
|
|
|
|
|
|
1178
|
|
|
|
|
|
|
# To removal identical products with reactants |
1179
|
1497
|
|
|
|
|
1651
|
for my $s ( @{ $chem_eq->[0] } ){ |
|
1497
|
|
|
|
|
2660
|
|
1180
|
3882
|
|
|
|
|
3732
|
my @ip_del; |
1181
|
3882
|
|
|
|
|
5041
|
for(my $i=0; $i<=$#{ $chem_eq->[1] }; $i++ ){ |
|
14261
|
|
|
|
|
29024
|
|
1182
|
10379
|
100
|
|
|
|
21644
|
push @ip_del, $i if $s eq $chem_eq->[1][$i]; |
1183
|
|
|
|
|
|
|
} |
1184
|
3882
|
|
|
|
|
11420
|
splice @{ $chem_eq->[1] }, $_, 1 for( reverse @ip_del ); |
|
2
|
|
|
|
|
9
|
|
1185
|
|
|
|
|
|
|
} |
1186
|
|
|
|
|
|
|
|
1187
|
|
|
|
|
|
|
# Check empty products |
1188
|
1497
|
100
|
|
|
|
1779
|
if( ! @{ $chem_eq->[1] } ){ |
|
1497
|
|
|
|
|
3616
|
|
1189
|
6
|
100
|
|
|
|
7
|
return if @{ $chem_eq->[0] } < 2; # bad mix (one or no substance) |
|
6
|
|
|
|
|
38
|
|
1190
|
4
|
|
|
|
|
6
|
push @{ $chem_eq->[1] }, pop @{ $chem_eq->[0] }; |
|
4
|
|
|
|
|
6
|
|
|
4
|
|
|
|
|
8
|
|
1191
|
|
|
|
|
|
|
} |
1192
|
|
|
|
|
|
|
|
1193
|
1495
|
|
|
|
|
10948
|
@$coef{ keys %cf} = values %cf; # join (copy) |
1194
|
|
|
|
|
|
|
|
1195
|
1495
|
|
|
|
|
2374
|
delete $coef->{'zero2oxi'}; |
1196
|
|
|
|
|
|
|
|
1197
|
|
|
|
|
|
|
# Lists of substances: |
1198
|
|
|
|
|
|
|
# fist --- reactants or '=','-','>' |
1199
|
|
|
|
|
|
|
# last --- products or '=','-','>' |
1200
|
1495
|
|
|
|
|
6004
|
return $chem_eq; |
1201
|
|
|
|
|
|
|
} |
1202
|
|
|
|
|
|
|
|
1203
|
|
|
|
|
|
|
sub _p_c_m{ |
1204
|
15
|
|
|
15
|
|
26
|
my( $s, $i, $cf, $ip ) = @_; |
1205
|
|
|
|
|
|
|
|
1206
|
15
|
100
|
|
|
|
39
|
if( exists $cf->{ $ip->[$i] } ){ |
1207
|
8
|
|
|
|
|
17
|
$cf->{$s} = $cf->{ $ip->[$i] }; |
1208
|
8
|
|
|
|
|
18
|
delete $cf->{ $ip->[$i] }; |
1209
|
|
|
|
|
|
|
} |
1210
|
15
|
|
|
|
|
44
|
$ip->[$i] = $s; # replacement |
1211
|
|
|
|
|
|
|
} |
1212
|
|
|
|
|
|
|
|
1213
|
|
|
|
|
|
|
|
1214
|
|
|
|
|
|
|
# Calculation of oxidation state |
1215
|
|
|
|
|
|
|
sub oxidation_state{ |
1216
|
199
|
|
50
|
199
|
1
|
562612
|
my $s = shift || return; |
1217
|
|
|
|
|
|
|
|
1218
|
199
|
|
|
|
|
359
|
our $mask; |
1219
|
199
|
|
|
|
|
1917
|
$mask = qr/{(?:(?>[^{}]+)|(??{$mask}))*}\d*/; |
1220
|
199
|
|
|
|
|
1847
|
my @species = $s =~ /$mask/g; |
1221
|
|
|
|
|
|
|
|
1222
|
|
|
|
|
|
|
# One substance |
1223
|
199
|
100
|
66
|
|
|
1296
|
return &_in_os($s) if @species < 2 || $s ne join('',@species); |
1224
|
|
|
|
|
|
|
|
1225
|
1
|
|
|
|
|
2
|
my $r; # Result: |
1226
|
|
|
|
|
|
|
# HASH{element}{num}[0 .. n-1] - Element amount on everyone 1..n atom |
1227
|
|
|
|
|
|
|
# HASH{element}{OS}[0 .. n-1][ .. ] - Oxidation States of Element (OSE) arrays 1..n atom |
1228
|
|
|
|
|
|
|
|
1229
|
|
|
|
|
|
|
# Mix of substances |
1230
|
1
|
|
|
|
|
3
|
for( @species ){ |
1231
|
|
|
|
|
|
|
|
1232
|
|
|
|
|
|
|
# remove bordering brackets |
1233
|
2
|
|
|
|
|
9
|
s/^\{//; |
1234
|
2
|
|
|
|
|
9
|
s/\}(\d*)$//; |
1235
|
|
|
|
|
|
|
# save possible coefficient |
1236
|
2
|
|
|
|
|
6
|
my $d = $1; |
1237
|
|
|
|
|
|
|
|
1238
|
2
|
|
50
|
|
|
6
|
my $p = &_in_os($_) || return; |
1239
|
|
|
|
|
|
|
|
1240
|
2
|
|
|
|
|
7
|
for my $e ( keys %$p ){ |
1241
|
4
|
|
|
|
|
7
|
push @{ $r->{$e}{'OS'} }, @{ $p->{$e}{'OS'} }; |
|
4
|
|
|
|
|
11
|
|
|
4
|
|
|
|
|
11
|
|
1242
|
|
|
|
|
|
|
|
1243
|
4
|
100
|
|
|
|
12
|
if( $d ){ |
1244
|
2
|
|
|
|
|
4
|
$_ *= $d for @{ $p->{$e}{'num'} } |
|
2
|
|
|
|
|
8
|
|
1245
|
|
|
|
|
|
|
} |
1246
|
4
|
|
|
|
|
5
|
push @{ $r->{$e}{'num'} }, @{ $p->{$e}{'num'} }; |
|
4
|
|
|
|
|
11
|
|
|
4
|
|
|
|
|
19
|
|
1247
|
|
|
|
|
|
|
} |
1248
|
|
|
|
|
|
|
} |
1249
|
|
|
|
|
|
|
|
1250
|
1
|
|
|
|
|
9
|
$r; |
1251
|
|
|
|
|
|
|
} |
1252
|
|
|
|
|
|
|
|
1253
|
|
|
|
|
|
|
sub _in_os{ |
1254
|
200
|
|
|
200
|
|
436
|
my $chem_sub = shift; |
1255
|
|
|
|
|
|
|
|
1256
|
|
|
|
|
|
|
# prepare atomic composition of substance |
1257
|
200
|
|
|
|
|
454
|
my %nf = eval{ Chemistry::File::Formula->parse_formula( $chem_sub ) }; |
|
200
|
|
|
|
|
1687
|
|
1258
|
200
|
50
|
|
|
|
58676
|
return unless keys %nf; |
1259
|
|
|
|
|
|
|
|
1260
|
|
|
|
|
|
|
# Count of "pure" atoms each element of substance |
1261
|
200
|
|
|
|
|
515
|
$_ = $chem_sub; |
1262
|
200
|
|
|
|
|
1061
|
s/\d+//g; # remove digits |
1263
|
200
|
|
|
|
|
843
|
my %num = Chemistry::File::Formula->parse_formula( $_ ); |
1264
|
|
|
|
|
|
|
|
1265
|
|
|
|
|
|
|
# Ions: { element }{ length }{ ion-pattern }[ [ array OSE ] ] |
1266
|
200
|
|
|
|
|
42202
|
my $ions = &_read_ions( $chem_sub ); |
1267
|
|
|
|
|
|
|
|
1268
|
|
|
|
|
|
|
# Read Pauling electronegativity and OSE: |
1269
|
|
|
|
|
|
|
# atom electronegativity, oxidation state, intermetallic compound |
1270
|
200
|
|
|
|
|
811
|
my( $atom_el_neg, $atom_OS, $intermet ) = &_read_atoms( \%nf ); |
1271
|
200
|
50
|
|
|
|
944
|
return if keys( %$atom_el_neg ) != keys( %nf ); |
1272
|
|
|
|
|
|
|
|
1273
|
200
|
|
|
|
|
246
|
my $prop; # Result: |
1274
|
|
|
|
|
|
|
|
1275
|
|
|
|
|
|
|
# Substance is intermetallic compound or Simple substance (one element) |
1276
|
200
|
100
|
66
|
|
|
1467
|
if( $intermet || keys %nf == 1 ){ |
1277
|
|
|
|
|
|
|
|
1278
|
2
|
|
|
|
|
11
|
while( my( $e, $n ) = each %nf ){ |
1279
|
|
|
|
|
|
|
# Total quantity of atoms for element |
1280
|
2
|
|
|
|
|
10
|
$prop->{ $e }{ 'num' }[ 0 ] = $n; |
1281
|
|
|
|
|
|
|
|
1282
|
|
|
|
|
|
|
# By default in the list of OSE only 0th charge |
1283
|
2
|
|
|
|
|
13
|
$prop->{ $e }{ 'OS' }[ 0 ] = [ 0 ]; |
1284
|
|
|
|
|
|
|
} |
1285
|
|
|
|
|
|
|
|
1286
|
2
|
|
|
|
|
17
|
return $prop ; |
1287
|
|
|
|
|
|
|
} |
1288
|
|
|
|
|
|
|
|
1289
|
|
|
|
|
|
|
# Sort atoms in decreasing order of electronegativity: |
1290
|
|
|
|
|
|
|
# 1th --- the most electronegative |
1291
|
198
|
|
|
|
|
275
|
my @neg = sort { $atom_el_neg->{$b} <=> $atom_el_neg->{$a} } keys %{ $atom_el_neg }; |
|
471
|
|
|
|
|
4516
|
|
|
198
|
|
|
|
|
1589
|
|
1292
|
|
|
|
|
|
|
|
1293
|
198
|
|
|
|
|
513
|
my %bOS; # by default OSE basic list |
1294
|
|
|
|
|
|
|
|
1295
|
|
|
|
|
|
|
# Basic OSE list of atoms on decrease of electronegativities |
1296
|
198
|
|
|
|
|
1378
|
for(my $i = 0; $i <= $#neg; $i++){ |
1297
|
|
|
|
|
|
|
|
1298
|
556
|
|
|
|
|
974
|
my $e = $neg[$i]; # element |
1299
|
|
|
|
|
|
|
|
1300
|
|
|
|
|
|
|
# Electronegative 1th is identical with next elements |
1301
|
556
|
100
|
66
|
|
|
4828
|
if( ( $i < $#neg && |
|
|
100
|
66
|
|
|
|
|
1302
|
|
|
|
|
|
|
$atom_el_neg->{ $neg[0] } == $atom_el_neg->{ $neg[$i+1] } ) || |
1303
|
|
|
|
|
|
|
$i == 1 ){ |
1304
|
|
|
|
|
|
|
|
1305
|
|
|
|
|
|
|
# '-' and '+' OSE, without 0 |
1306
|
198
|
|
|
|
|
742
|
$bOS{ $e } = [ grep $_, @{ $atom_OS->{$e} } ]; |
|
198
|
|
|
|
|
878
|
|
1307
|
|
|
|
|
|
|
|
1308
|
|
|
|
|
|
|
}elsif( $i == 0 ) { # 1th (most electronegative) -> only '-' OSE |
1309
|
198
|
|
|
|
|
321
|
$bOS{ $e } = [ grep $_ < 0, @{ $atom_OS->{$e} } ]; |
|
198
|
|
|
|
|
2571
|
|
1310
|
|
|
|
|
|
|
|
1311
|
|
|
|
|
|
|
}else{ # Others -> only '+' OSE |
1312
|
160
|
|
|
|
|
246
|
$bOS{ $e } = [ grep $_ > 0, @{ $atom_OS->{$e} } ]; |
|
160
|
|
|
|
|
696
|
|
1313
|
|
|
|
|
|
|
} |
1314
|
|
|
|
|
|
|
|
1315
|
|
|
|
|
|
|
# Inert elements |
1316
|
556
|
100
|
|
|
|
1775
|
$bOS{ $e } = [ @{ $atom_OS->{$e} } ] if $e=~/He|Ne|Ar|Kr|Xe|Rn/; |
|
5
|
|
|
|
|
17
|
|
1317
|
|
|
|
|
|
|
|
1318
|
|
|
|
|
|
|
# mask for search ions |
1319
|
556
|
100
|
|
|
|
2463
|
my $m = length($e)==1 ? "$e\\d+|$e(?![a-gik-pr-u])" : "$e\\d*"; |
1320
|
|
|
|
|
|
|
|
1321
|
556
|
|
|
|
|
1516
|
for(my $j = 0; $j < $num{$e}; $j++){ |
1322
|
|
|
|
|
|
|
|
1323
|
|
|
|
|
|
|
# Number of various atoms for element in substance |
1324
|
586
|
100
|
|
|
|
1317
|
if( $num{$e} == 1 ){ # One atom of element in substance |
1325
|
526
|
|
|
|
|
564
|
push @{ $prop->{$e}{'num'} }, $nf{$e}; |
|
526
|
|
|
|
|
4131
|
|
1326
|
|
|
|
|
|
|
|
1327
|
|
|
|
|
|
|
}else{ |
1328
|
60
|
|
|
|
|
92
|
my $count = 0; |
1329
|
60
|
|
|
|
|
102
|
$_ = $chem_sub; |
1330
|
|
|
|
|
|
|
|
1331
|
|
|
|
|
|
|
# Search ion-group. Remove all atoms of element, except current |
1332
|
60
|
100
|
|
|
|
1314
|
s{ ($m) }{ $1 if $count++ == $j }gex; |
|
120
|
|
|
|
|
747
|
|
1333
|
|
|
|
|
|
|
|
1334
|
60
|
|
|
|
|
600
|
my %f = Chemistry::File::Formula->parse_formula( $_ ); |
1335
|
60
|
|
|
|
|
28077
|
push @{ $prop->{ $e }{'num'} }, $f{ $e }; |
|
60
|
|
|
|
|
465
|
|
1336
|
|
|
|
|
|
|
} |
1337
|
|
|
|
|
|
|
} |
1338
|
|
|
|
|
|
|
} |
1339
|
|
|
|
|
|
|
|
1340
|
|
|
|
|
|
|
# Two pass: 1st -- ion recognition, 0th -- without ions (possible) |
1341
|
198
|
|
|
|
|
632
|
for my $yni (1, 0){ |
1342
|
201
|
|
|
|
|
299
|
my $no_ion = 1; # no ions |
1343
|
|
|
|
|
|
|
|
1344
|
201
|
|
|
|
|
296
|
my $balance_A = 0; # for Electronic balance |
1345
|
201
|
|
|
|
|
204
|
my $max_n_OS = 0; # Number of OSE in maximum list |
1346
|
201
|
|
|
|
|
321
|
my @atoms; # Varied atoms of elements |
1347
|
|
|
|
|
|
|
my %osin; # lists of OSE ions |
1348
|
|
|
|
|
|
|
|
1349
|
201
|
|
|
|
|
907
|
for(my $i = 0; $i <= $#neg; $i++){ |
1350
|
|
|
|
|
|
|
|
1351
|
566
|
|
|
|
|
878
|
my $e = $neg[$i]; # element |
1352
|
|
|
|
|
|
|
|
1353
|
|
|
|
|
|
|
# mask for search ions |
1354
|
566
|
100
|
|
|
|
1661
|
my $m = length($e)==1 ? "$e\\d+|$e(?![a-gik-pr-u])" : "$e\\d*"; |
1355
|
|
|
|
|
|
|
|
1356
|
|
|
|
|
|
|
_SO_M1: # Number of various atoms for element in substance |
1357
|
566
|
|
|
|
|
1565
|
for(my $j = 0; $j < $num{$e}; $j++){ |
1358
|
|
|
|
|
|
|
|
1359
|
598
|
|
|
|
|
933
|
$_ = $chem_sub; |
1360
|
|
|
|
|
|
|
|
1361
|
598
|
|
|
|
|
689
|
my $count = 0; |
1362
|
|
|
|
|
|
|
# Search ion-group. Remove all atoms of element, except current |
1363
|
598
|
100
|
|
|
|
16242
|
s{ ($m) }{ $1 if $count++ == $j }gex; |
|
662
|
|
|
|
|
9865
|
|
1364
|
|
|
|
|
|
|
|
1365
|
598
|
100
|
|
|
|
1677
|
if( $yni ){ |
1366
|
|
|
|
|
|
|
# Sort by decrease length of ion-group |
1367
|
586
|
|
|
|
|
719
|
for my $l (sort {$b <=> $a} keys %{ $ions->{$e} } ) { |
|
57
|
|
|
|
|
227
|
|
|
586
|
|
|
|
|
2534
|
|
1368
|
|
|
|
|
|
|
|
1369
|
339
|
|
|
|
|
436
|
for my $mg ( keys %{ $ions->{$e}{$l} } ){ |
|
339
|
|
|
|
|
1003
|
|
1370
|
343
|
100
|
|
|
|
3800
|
next unless /$mg/; # Ion-group isn't found |
1371
|
|
|
|
|
|
|
|
1372
|
321
|
|
|
|
|
547
|
$no_ion = 0; # yes ions |
1373
|
|
|
|
|
|
|
|
1374
|
|
|
|
|
|
|
# Ion-group is found. Save list of OSE |
1375
|
|
|
|
|
|
|
# for j-th atom of element |
1376
|
321
|
100
|
|
|
|
369
|
if( @{ $ions->{$e}{$l}{$mg} } == 1 ){ # One list OSE ion |
|
321
|
|
|
|
|
942
|
|
1377
|
|
|
|
|
|
|
|
1378
|
298
|
|
|
|
|
373
|
push @{ $prop->{$e}{'OS'} }, $ions->{$e}{$l}{$mg}[0]; |
|
298
|
|
|
|
|
1136
|
|
1379
|
|
|
|
|
|
|
|
1380
|
|
|
|
|
|
|
# Calculation total and mean OSE |
1381
|
298
|
|
|
|
|
570
|
my $os; |
1382
|
298
|
|
|
|
|
337
|
$os += $_ for @{ $prop->{$e}{'OS'}[$j] }; |
|
298
|
|
|
|
|
1542
|
|
1383
|
|
|
|
|
|
|
|
1384
|
|
|
|
|
|
|
# sum OSE * number of atoms / number of OSE |
1385
|
298
|
|
|
|
|
785
|
$balance_A += $os * $prop->{$e}{'num'}[$j] / @{ $prop->{$e}{'OS'}[$j] }; |
|
298
|
|
|
|
|
919
|
|
1386
|
|
|
|
|
|
|
|
1387
|
|
|
|
|
|
|
}else{ # Many OSE ion lists |
1388
|
|
|
|
|
|
|
# Add in array varied OSE for j-th atom of element |
1389
|
23
|
|
|
|
|
73
|
push @atoms, "$e:$j"; |
1390
|
23
|
|
|
|
|
104
|
$osin{"$e:$j"} = $ions->{$e}{$l}{$mg}; |
1391
|
|
|
|
|
|
|
|
1392
|
|
|
|
|
|
|
# Define from a basic list of OSE |
1393
|
23
|
|
|
|
|
33
|
push @{ $prop->{$e}{'OS'} }, [ -999 ]; |
|
23
|
|
|
|
|
92
|
|
1394
|
|
|
|
|
|
|
|
1395
|
|
|
|
|
|
|
# Quantity of OSE from basic list |
1396
|
23
|
|
|
|
|
31
|
my $n_OS = $#{ $ions->{$e}{$l}{$mg} }; |
|
23
|
|
|
|
|
60
|
|
1397
|
23
|
100
|
|
|
|
65
|
$max_n_OS = $n_OS if $n_OS > $max_n_OS; # max list of OSE |
1398
|
|
|
|
|
|
|
} |
1399
|
321
|
|
|
|
|
2095
|
next _SO_M1; # Ion-group is found |
1400
|
|
|
|
|
|
|
} |
1401
|
|
|
|
|
|
|
} |
1402
|
|
|
|
|
|
|
} |
1403
|
|
|
|
|
|
|
|
1404
|
|
|
|
|
|
|
# Quantity of OSE from basic list |
1405
|
277
|
|
|
|
|
546
|
my $n_OS = $#{ $bOS{ $e } }; |
|
277
|
|
|
|
|
554
|
|
1406
|
277
|
100
|
|
|
|
667
|
$max_n_OS = $n_OS if $n_OS > $max_n_OS; # max list of OSE |
1407
|
|
|
|
|
|
|
|
1408
|
277
|
100
|
|
|
|
644
|
if( $n_OS ) { # number of OSE > 1 |
1409
|
|
|
|
|
|
|
# Add in array varied OSE for j-th atom of element |
1410
|
144
|
|
|
|
|
394
|
push @atoms, "$e:$j"; |
1411
|
|
|
|
|
|
|
|
1412
|
|
|
|
|
|
|
# Define from a basic list of OSE |
1413
|
144
|
|
|
|
|
186
|
push @{ $prop->{$e}{'OS'} }, [ -999 ]; |
|
144
|
|
|
|
|
1075
|
|
1414
|
|
|
|
|
|
|
|
1415
|
|
|
|
|
|
|
}else{ # One OSE in list |
1416
|
133
|
|
|
|
|
182
|
push @{ $prop->{$e}{'OS'} }, [ $bOS{ $e }[0] ]; |
|
133
|
|
|
|
|
533
|
|
1417
|
|
|
|
|
|
|
|
1418
|
|
|
|
|
|
|
# charge * number of atoms |
1419
|
133
|
|
|
|
|
873
|
$balance_A += $bOS{ $e }[0] * $prop->{$e}{'num'}[$j]; |
1420
|
|
|
|
|
|
|
} |
1421
|
|
|
|
|
|
|
} |
1422
|
|
|
|
|
|
|
} |
1423
|
|
|
|
|
|
|
|
1424
|
201
|
|
|
|
|
344
|
my $balance_B = 0; # for Electronic balance |
1425
|
|
|
|
|
|
|
|
1426
|
|
|
|
|
|
|
# Select oxidation states |
1427
|
201
|
100
|
|
|
|
478
|
if( $max_n_OS ){ |
1428
|
106
|
|
|
|
|
918
|
my $iter = variations_with_repetition( [ (0..$max_n_OS) ], scalar( @atoms ) ); |
1429
|
|
|
|
|
|
|
_SO_M2: |
1430
|
106
|
|
|
|
|
4339
|
while (my $p = $iter->next) { |
1431
|
|
|
|
|
|
|
|
1432
|
467
|
|
|
|
|
4537
|
$balance_B = 0; |
1433
|
467
|
|
|
|
|
649
|
for(my $i = 0; $i<=$#{ $p }; $i++){ |
|
1430
|
|
|
|
|
7738
|
|
1434
|
|
|
|
|
|
|
|
1435
|
1041
|
|
|
|
|
1733
|
my $x = $atoms[$i]; |
1436
|
1041
|
|
|
|
|
2592
|
my($e, $j) = split /:/,$x; |
1437
|
|
|
|
|
|
|
|
1438
|
1041
|
|
|
|
|
1296
|
my($sum, @os); |
1439
|
|
|
|
|
|
|
|
1440
|
1041
|
100
|
|
|
|
2033
|
if( exists $osin{ $x } ){ # Some OSE ion lists |
1441
|
|
|
|
|
|
|
|
1442
|
135
|
|
|
|
|
172
|
$sum += $_ for @{ $osin{ $x }[ $p->[$i] ] }; |
|
135
|
|
|
|
|
493
|
|
1443
|
135
|
|
|
|
|
168
|
@os = @{ $osin{ $x }[ $p->[$i] ] }; |
|
135
|
|
|
|
|
340
|
|
1444
|
|
|
|
|
|
|
|
1445
|
|
|
|
|
|
|
}else{ |
1446
|
906
|
|
|
|
|
1948
|
$os[0] = $sum = $bOS{ $e }[ $p->[$i] ]; |
1447
|
|
|
|
|
|
|
} |
1448
|
|
|
|
|
|
|
|
1449
|
1041
|
100
|
|
|
|
2290
|
next _SO_M2 unless defined $sum; # OSE have ended |
1450
|
|
|
|
|
|
|
|
1451
|
|
|
|
|
|
|
# sum OSE * number of atoms / number of OSE |
1452
|
963
|
|
|
|
|
2909
|
$balance_B += $sum * $prop->{ $e }{'num'}[$j] / @os; |
1453
|
963
|
|
|
|
|
3732
|
$prop->{ $e }{'OS'}[$j] = [ @os ]; |
1454
|
|
|
|
|
|
|
|
1455
|
|
|
|
|
|
|
} |
1456
|
389
|
100
|
|
|
|
2219
|
last unless $balance_A + $balance_B; # balance is found |
1457
|
|
|
|
|
|
|
} |
1458
|
|
|
|
|
|
|
} |
1459
|
|
|
|
|
|
|
|
1460
|
201
|
50
|
66
|
|
|
802
|
return if $no_ion && ($balance_A + $balance_B); # balance is not found |
1461
|
|
|
|
|
|
|
|
1462
|
201
|
|
|
|
|
360
|
$balance_A = 0; # for electronic balance |
1463
|
|
|
|
|
|
|
|
1464
|
|
|
|
|
|
|
# Check electronic balance |
1465
|
201
|
|
|
|
|
759
|
for my $e (keys %$prop ){ |
1466
|
566
|
|
|
|
|
845
|
my $i=0; |
1467
|
566
|
|
|
|
|
641
|
for my $os ( @{ $prop->{$e}{'OS'} } ){ |
|
566
|
|
|
|
|
1189
|
|
1468
|
|
|
|
|
|
|
|
1469
|
598
|
|
|
|
|
620
|
my $sum; |
1470
|
598
|
|
|
|
|
1549
|
$sum += $_ for @$os; |
1471
|
|
|
|
|
|
|
|
1472
|
|
|
|
|
|
|
# sum OSE * number of atoms / number of OSE |
1473
|
598
|
|
|
|
|
2501
|
$balance_A += $sum * $prop->{$e}{'num'}[ $i++ ] / @$os; |
1474
|
|
|
|
|
|
|
} |
1475
|
|
|
|
|
|
|
} |
1476
|
|
|
|
|
|
|
|
1477
|
201
|
100
|
|
|
|
466
|
if( $balance_A ){ |
1478
|
3
|
50
|
|
|
|
11
|
return if $no_ion; |
1479
|
|
|
|
|
|
|
}else{ |
1480
|
198
|
|
|
|
|
3351
|
return $prop; |
1481
|
|
|
|
|
|
|
} |
1482
|
3
|
|
|
|
|
26
|
delete $prop->{$_}{'OS'} for keys %$prop; |
1483
|
|
|
|
|
|
|
} |
1484
|
|
|
|
|
|
|
} |
1485
|
|
|
|
|
|
|
|
1486
|
|
|
|
|
|
|
# Read Pauling electronegativity and OSE |
1487
|
|
|
|
|
|
|
# only for given elements of substance |
1488
|
|
|
|
|
|
|
# input: |
1489
|
|
|
|
|
|
|
# %atoms -- elements |
1490
|
|
|
|
|
|
|
# return: |
1491
|
|
|
|
|
|
|
# %atom_el_neg |
1492
|
|
|
|
|
|
|
# %atom_OS |
1493
|
|
|
|
|
|
|
# $intermet |
1494
|
|
|
|
|
|
|
|
1495
|
|
|
|
|
|
|
sub _read_atoms{ |
1496
|
200
|
|
|
200
|
|
361
|
my $atoms = shift; |
1497
|
|
|
|
|
|
|
|
1498
|
200
|
|
|
|
|
343
|
my %atom_el_neg; # atom electronegativity |
1499
|
|
|
|
|
|
|
my %atom_OS; # oxidation state |
1500
|
200
|
|
|
|
|
377
|
my $intermet = 1; # for intermetallic compound |
1501
|
|
|
|
|
|
|
|
1502
|
200
|
|
|
|
|
963
|
my $adb = &_atoms_db; |
1503
|
|
|
|
|
|
|
|
1504
|
200
|
|
|
|
|
1690
|
for( my $i = 0; $i < @$adb; $i+=5 ){ |
1505
|
23400
|
|
|
|
|
27479
|
$_ = $adb->[$i]; |
1506
|
23400
|
100
|
|
|
|
61052
|
next if !exists $atoms->{ $_ }; |
1507
|
|
|
|
|
|
|
|
1508
|
558
|
|
|
|
|
1390
|
$atom_el_neg{ $_ } = $adb->[$i+2]; |
1509
|
558
|
100
|
|
|
|
1376
|
$intermet = 0 unless $adb->[$i+3]; # Not intermetallic compound |
1510
|
558
|
|
|
|
|
1668
|
$atom_OS{ $_ } = $adb->[$i+4]; |
1511
|
|
|
|
|
|
|
} |
1512
|
|
|
|
|
|
|
|
1513
|
200
|
|
|
|
|
8039
|
\%atom_el_neg, \%atom_OS, $intermet; |
1514
|
|
|
|
|
|
|
} |
1515
|
|
|
|
|
|
|
|
1516
|
|
|
|
|
|
|
# Read necessary ion-group |
1517
|
|
|
|
|
|
|
# input: |
1518
|
|
|
|
|
|
|
# $Chemistry_substance |
1519
|
|
|
|
|
|
|
# return: |
1520
|
|
|
|
|
|
|
# $ions |
1521
|
|
|
|
|
|
|
sub _read_ions { |
1522
|
200
|
|
|
200
|
|
454
|
my $chem_sub = shift; |
1523
|
200
|
|
|
|
|
277
|
my %ions; |
1524
|
|
|
|
|
|
|
|
1525
|
200
|
|
|
|
|
511
|
my $idb = &_ions_db; |
1526
|
|
|
|
|
|
|
|
1527
|
|
|
|
|
|
|
# Construct pattern |
1528
|
200
|
|
|
|
|
798
|
for( my $j = 0; $j < @$idb; $j+=2 ){ |
1529
|
21400
|
|
|
|
|
113241
|
my $frm = $idb->[$j]; |
1530
|
21400
|
|
|
|
|
53122
|
my $os = $idb->[$j+1]; |
1531
|
|
|
|
|
|
|
|
1532
|
21400
|
|
|
|
|
167404
|
my %a = split /_|=/,$os; # Parse to element end OSE |
1533
|
|
|
|
|
|
|
|
1534
|
21400
|
100
|
|
|
|
69806
|
if($os =~ /~/){ # Macro-substitutions |
1535
|
|
|
|
|
|
|
|
1536
|
5800
|
|
|
|
|
6934
|
my %ek; |
1537
|
5800
|
|
|
|
|
6666
|
my $max_n_ek = 0; # max number of element-pattern in macro-substitutions |
1538
|
|
|
|
|
|
|
|
1539
|
5800
|
|
|
|
|
30554
|
while( my($e, $v) = each %a ){ |
1540
|
|
|
|
|
|
|
|
1541
|
12400
|
100
|
|
|
|
53315
|
if($e =~ /(\w+~)(.*)/){ |
1542
|
5800
|
|
|
|
|
42951
|
$ek{$1}[0] = [ split ',',$2 ]; # elements |
1543
|
5800
|
|
|
|
|
22815
|
$ek{$1}[1] = $v; # OSE for group |
1544
|
|
|
|
|
|
|
|
1545
|
5800
|
|
|
|
|
6435
|
my $n_ek = $#{ $ek{$1}[0] }; # number of element-substitutions |
|
5800
|
|
|
|
|
14361
|
|
1546
|
5800
|
100
|
|
|
|
27279
|
$max_n_ek = $n_ek if $n_ek > $max_n_ek; # max list |
1547
|
|
|
|
|
|
|
} |
1548
|
|
|
|
|
|
|
} |
1549
|
|
|
|
|
|
|
|
1550
|
5800
|
|
|
|
|
39127
|
my $iter = variations_with_repetition( [ (0..$max_n_ek) ], scalar( keys %ek ) ); |
1551
|
|
|
|
|
|
|
ELEMENT_MACRO_1: |
1552
|
5800
|
|
|
|
|
215284
|
while (my $p = $iter->next) { |
1553
|
27800
|
|
|
|
|
263361
|
my $m = $frm; # macro-formula (mask) |
1554
|
27800
|
|
|
|
|
31531
|
my $i = 0; |
1555
|
27800
|
|
|
|
|
68279
|
for my $em (sort keys %ek){ |
1556
|
27800
|
|
|
|
|
55758
|
my $e = $ek{ $em }[0][ $p->[$i++] ]; # element |
1557
|
27800
|
50
|
|
|
|
52346
|
next ELEMENT_MACRO_1 unless defined $e; # pattern have ended |
1558
|
|
|
|
|
|
|
|
1559
|
27800
|
|
|
|
|
165216
|
$m =~ s/$em/$e/g; # Construct ion mask |
1560
|
|
|
|
|
|
|
} |
1561
|
|
|
|
|
|
|
|
1562
|
27800
|
100
|
|
|
|
485171
|
next unless $chem_sub =~ /($m)/; # Ions in substance aren't present |
1563
|
|
|
|
|
|
|
|
1564
|
57
|
|
|
|
|
338
|
my $l = length($1); # Length of ion mask |
1565
|
|
|
|
|
|
|
|
1566
|
57
|
|
|
|
|
95
|
$i = 0; |
1567
|
57
|
|
|
|
|
392
|
for my $em (sort keys %ek){ |
1568
|
57
|
|
|
|
|
158
|
my $e = $ek{ $em }[0][ $p->[$i++] ]; # element |
1569
|
|
|
|
|
|
|
|
1570
|
57
|
|
|
|
|
219
|
for my $z ( split /!/, $ek{ $em }[1] ){ |
1571
|
|
|
|
|
|
|
# list OSE |
1572
|
61
|
|
|
|
|
266
|
push @{ $ions{ $e }{ $l }{ $m } }, [ split /;/,$z ]; |
|
61
|
|
|
|
|
889
|
|
1573
|
|
|
|
|
|
|
} |
1574
|
|
|
|
|
|
|
} |
1575
|
|
|
|
|
|
|
|
1576
|
57
|
|
|
|
|
431
|
while(my ($e, $v) = each %a ){ |
1577
|
124
|
100
|
|
|
|
782
|
next if $e =~ /~/; |
1578
|
|
|
|
|
|
|
|
1579
|
|
|
|
|
|
|
# list OSE |
1580
|
67
|
|
|
|
|
194
|
for my $z ( split /!/, $v ){ |
1581
|
67
|
|
|
|
|
93
|
push @{ $ions{ $e }{ $l }{ $m } }, [ split /;/,$z ]; |
|
67
|
|
|
|
|
3371
|
|
1582
|
|
|
|
|
|
|
} |
1583
|
|
|
|
|
|
|
} |
1584
|
|
|
|
|
|
|
} |
1585
|
|
|
|
|
|
|
|
1586
|
|
|
|
|
|
|
}else{ |
1587
|
15600
|
100
|
|
|
|
245442
|
next unless $chem_sub =~ /($frm)/; # no ions in substance |
1588
|
|
|
|
|
|
|
|
1589
|
116
|
|
|
|
|
376
|
my $l = length($1); # Length of the found group |
1590
|
|
|
|
|
|
|
|
1591
|
116
|
|
|
|
|
480
|
while(my ($e, $v) = each %a ){ |
1592
|
|
|
|
|
|
|
# list OSE |
1593
|
234
|
|
|
|
|
569
|
for my $z ( split /!/, $v ){ |
1594
|
265
|
|
|
|
|
291
|
push @{ $ions{ $e }{ $l }{ $frm } }, [ split /;/,$z ]; |
|
265
|
|
|
|
|
2630
|
|
1595
|
|
|
|
|
|
|
} |
1596
|
|
|
|
|
|
|
} |
1597
|
|
|
|
|
|
|
} |
1598
|
|
|
|
|
|
|
} |
1599
|
|
|
|
|
|
|
|
1600
|
200
|
|
|
|
|
4444
|
\%ions; |
1601
|
|
|
|
|
|
|
} |
1602
|
|
|
|
|
|
|
|
1603
|
|
|
|
|
|
|
|
1604
|
|
|
|
|
|
|
# Pauling scale (adapted). |
1605
|
|
|
|
|
|
|
# Atomic weights from NIST. |
1606
|
|
|
|
|
|
|
# Attention! |
1607
|
|
|
|
|
|
|
# order of OSE is important (last are exotic OSE) |
1608
|
|
|
|
|
|
|
sub _atoms_db{ |
1609
|
|
|
|
|
|
|
return [ |
1610
|
246
|
|
|
246
|
|
39783
|
'Ac', 227, 110, 1, [0, 3], |
1611
|
|
|
|
|
|
|
'Ag', 107.8682, 193, 1, [0, 1, 2, 3, 5], |
1612
|
|
|
|
|
|
|
'Al', 26.9815386, 161, 1, [-3, 0, 3, 1, 2], |
1613
|
|
|
|
|
|
|
'Am', 243, 113, 1, [0, 2, 3, 4, 5, 6], |
1614
|
|
|
|
|
|
|
'Ar', 39.948, 0, 0, [0], |
1615
|
|
|
|
|
|
|
'As', 74.9216, 221, 0, [-3, 0, 3, 5], # 2 |
1616
|
|
|
|
|
|
|
'At', 210, 225, 0, [-1, 0, 1, 5, 7], # 3 |
1617
|
|
|
|
|
|
|
'Au', 196.966569, 254, 1, [0, 1, 2, 3, 5, 7], # -1 |
1618
|
|
|
|
|
|
|
'B', 10.811, 204, 0, [-3, 0, 1, 2, 3], |
1619
|
|
|
|
|
|
|
'Ba', 137.327, 89, 1, [0, 2], |
1620
|
|
|
|
|
|
|
'Be', 9.012182, 157, 1, [0, 2], |
1621
|
|
|
|
|
|
|
'Bh', 272.13803, 0, 1, [0, 7], |
1622
|
|
|
|
|
|
|
'Bi', 208.9804, 221, 0, [-3, 0, 2, 3, 5], |
1623
|
|
|
|
|
|
|
'Bk', 247, 130, 1, [0, 3, 4], |
1624
|
|
|
|
|
|
|
'Br', 79.904, 296, 0, [-1, 0, 1, 3, 4, 5, 6, 7], |
1625
|
|
|
|
|
|
|
'C', 12.0107, 255, 0, [-4, -3, -1, 0, 2, 3, 4], # -2, 1 |
1626
|
|
|
|
|
|
|
'Ca', 40.078, 100, 1, [0, 2], |
1627
|
|
|
|
|
|
|
'Cd', 112.411, 169, 1, [0, 2], |
1628
|
|
|
|
|
|
|
'Ce', 140.116, 112, 1, [0, 3, 4], # 2 |
1629
|
|
|
|
|
|
|
'Cf', 251, 130, 1, [0, 2, 3, 4], |
1630
|
|
|
|
|
|
|
'Cl', 35.453, 316, 0, [-1, 0, 1, 3, 4, 5, 6, 7], # 2 |
1631
|
|
|
|
|
|
|
'Cm', 247, 128, 1, [0, 3, 4], |
1632
|
|
|
|
|
|
|
'Cn', 285.17411, 205, 1, [0, 2, 4], |
1633
|
|
|
|
|
|
|
'Co', 58.933195, 188, 1, [0, 1, 2, 3, 4], # -1, 5 |
1634
|
|
|
|
|
|
|
'Cr', 51.9961, 166, 1, [0, 1, 2, 3, 4, 5, 6], # -2, -1 |
1635
|
|
|
|
|
|
|
'Cs', 132.9054519, 79, 1, [0, 1], |
1636
|
|
|
|
|
|
|
'Cu', 63.546, 190, 1, [0, 2, 1, 3], # 4 |
1637
|
|
|
|
|
|
|
'D', 2.0141017778, 220, 0, [-1, 0, 1], |
1638
|
|
|
|
|
|
|
'Db', 268.12545, 0, 1, [0, 5], # old Ns |
1639
|
|
|
|
|
|
|
'Ds', 281.16206, 0, 1, [0, 6, 4, 2, 5], # as Pt |
1640
|
|
|
|
|
|
|
'Dy', 162.5, 122, 1, [0, 3, 4], # 2 |
1641
|
|
|
|
|
|
|
'Er', 167.259, 124, 1, [0, 3], |
1642
|
|
|
|
|
|
|
'Es', 252.08298, 130, 1, [0, 2, 3], |
1643
|
|
|
|
|
|
|
'Eu', 151.964, 120, 1, [0, 2, 3], |
1644
|
|
|
|
|
|
|
'F', 18.9984032, 400, 0, [-1, 0], |
1645
|
|
|
|
|
|
|
'Fe', 55.845, 183, 1, [0, 2, 3, 6, 8, 4, 5], # -2, -1, 1 |
1646
|
|
|
|
|
|
|
'Fm', 257.095105, 130, 1, [0, 2, 3], |
1647
|
|
|
|
|
|
|
'Fr', 223.0197359, 70, 1, [0, 1], |
1648
|
|
|
|
|
|
|
'Ga', 69.723, 181, 1, [0, 1, 2, 3], |
1649
|
|
|
|
|
|
|
'Gd', 157.25, 120, 1, [0, 3], # 1, 2 |
1650
|
|
|
|
|
|
|
'Ge', 72.64, 201, 0, [-4, -2, 0, 2, 4], # 1, 3 |
1651
|
|
|
|
|
|
|
'H', 1.00794, 220, 0, [-1, 0, 1], |
1652
|
|
|
|
|
|
|
'He', 4.002602, 0, 0, [0], |
1653
|
|
|
|
|
|
|
'Hf', 178.49, 130, 1, [0, 2, 3, 4], |
1654
|
|
|
|
|
|
|
'Hg', 200.59, 200, 1, [0, 1, 2], # 4 |
1655
|
|
|
|
|
|
|
'Ho', 164.93032, 123, 1, [0, 3], |
1656
|
|
|
|
|
|
|
'Hs', 270.13465, 0, 1, [0, 7], |
1657
|
|
|
|
|
|
|
'I', 126.90447, 266, 0, [-1, 0, 1, 3, 5, 7], |
1658
|
|
|
|
|
|
|
'In', 114.818, 178, 1, [0, 1, 2, 3], |
1659
|
|
|
|
|
|
|
'Ir', 192.217, 220, 1, [0, 1, 2, 3, 4, 5, 6, 8], # -3, -1 |
1660
|
|
|
|
|
|
|
'K', 39.0983, 82, 1, [0, 1], |
1661
|
|
|
|
|
|
|
'Kr', 83.798, 0, 0, [0, 2, 4, 6], |
1662
|
|
|
|
|
|
|
'Ku', 265.1167, 0, 1, [0, 4], # now Rf |
1663
|
|
|
|
|
|
|
'La', 138.90547, 110, 1, [0, 3], # 2 |
1664
|
|
|
|
|
|
|
'Li', 6.941, 98, 1, [0, 1], |
1665
|
|
|
|
|
|
|
'Lr', 262.10963, 129, 1, [0, 3], |
1666
|
|
|
|
|
|
|
'Lu', 174.9668, 127, 1, [0, 3], |
1667
|
|
|
|
|
|
|
'Md', 258.098431, 130, 1, [0, 2, 3], |
1668
|
|
|
|
|
|
|
'Mg', 24.305, 131, 1, [0, 2], |
1669
|
|
|
|
|
|
|
'Mn', 54.938045, 155, 1, [0, 1, 2, 3, 4, 5, 6, 7], # -3, -2, -1 |
1670
|
|
|
|
|
|
|
'Mo', 95.96, 216, 1, [0, 2, 3, 4, 5, 6], # -2, -1, 1 |
1671
|
|
|
|
|
|
|
'Mt', 276.15116, 0, 1, [0, 4], |
1672
|
|
|
|
|
|
|
'N', 14.0067, 304, 0, [-3, -2, -1, 0, 1, 2, 3, 4, 5], |
1673
|
|
|
|
|
|
|
'Na', 22.98976928, 93, 1, [0, 1], |
1674
|
|
|
|
|
|
|
'Nb', 92.90638, 160, 1, [0, 1, 2, 3, 4, 5], # -1 |
1675
|
|
|
|
|
|
|
'Nd', 144.242, 114, 1, [0, 3], # 2 |
1676
|
|
|
|
|
|
|
'Ne', 20.1797, 0, 0, [0], |
1677
|
|
|
|
|
|
|
'Ni', 58.6934, 191, 1, [0, 2, 3, 4, 1], # -1 |
1678
|
|
|
|
|
|
|
'No', 259.10103, 130, 1, [0, 2, 3], |
1679
|
|
|
|
|
|
|
'Np', 237, 136, 1, [0, 3, 4, 5, 6], # 7 |
1680
|
|
|
|
|
|
|
'Ns', 268.12545, 0, 1, [0, 5], # now Db |
1681
|
|
|
|
|
|
|
'O', 15.9994, 344, 0, [-2, -1, 0, 1, 2], |
1682
|
|
|
|
|
|
|
'Os', 190.23, 220, 1, [0, 2, 3, 4, 5, 6, 7, 8], # -2, -1, 1 |
1683
|
|
|
|
|
|
|
'P', 30.973762, 221, 0, [-3, -2, 0, 1, 3, 4, 5], # -1, 2 |
1684
|
|
|
|
|
|
|
'Pa', 231.03588, 150, 1, [0, 3, 4, 5], |
1685
|
|
|
|
|
|
|
'Pb', 207.2, 233, 1, [-4, 0, 2, 4], |
1686
|
|
|
|
|
|
|
'Pd', 106.42, 220, 1, [0, 1, 2, 3, 4], |
1687
|
|
|
|
|
|
|
'Pm', 145, 113, 1, [0, 3], |
1688
|
|
|
|
|
|
|
'Po', 209, 200, 1, [-2, 0, 2, 4, 6], |
1689
|
|
|
|
|
|
|
'Pr', 140.90765, 113, 1, [0, 3, 4], # 2 |
1690
|
|
|
|
|
|
|
'Pt', 195.084, 228, 1, [0, 2, 3, 4, 5, 6, 1], |
1691
|
|
|
|
|
|
|
'Pu', 244, 128, 1, [0, 2, 3, 4, 5, 6], # 7 |
1692
|
|
|
|
|
|
|
'Ra', 226, 90, 1, [0, 2, 4], |
1693
|
|
|
|
|
|
|
'Rb', 85.4678, 82, 1, [0, 1], |
1694
|
|
|
|
|
|
|
'Re', 186.207, 190, 1, [0, 1, 2, 3, 4, 5, 6, 7], # -3, -1 |
1695
|
|
|
|
|
|
|
'Rf', 265.1167, 0, 1, [0, 4], # old Ku |
1696
|
|
|
|
|
|
|
'Rg', 280.16447, 0, 1, [0, 3, 1, 2], # as Au |
1697
|
|
|
|
|
|
|
'Rh', 102.9055, 228, 1, [0, 1, 2, 3, 4, 6], # -1, 5 |
1698
|
|
|
|
|
|
|
'Rn', 222, 0, 0, [0, 2, 4, 6, 8], |
1699
|
|
|
|
|
|
|
'Ru', 101.07, 220, 1, [0, 2, 3, 4, 5, 6, 7, 8], # -2, 1 |
1700
|
|
|
|
|
|
|
'S', 32.065, 258, 0, [-2, 0, 1, 2, 3, 4, 6], #-1, 5 |
1701
|
|
|
|
|
|
|
'Sb', 121.76, 221, 1, [-3, 0, 3, 4, 5], |
1702
|
|
|
|
|
|
|
'Sc', 44.955912, 136, 1, [0, 3], # 1, 2 |
1703
|
|
|
|
|
|
|
'Se', 78.96, 255, 0, [-2, 0, 2, 4, 6], |
1704
|
|
|
|
|
|
|
'Sg', 271.13347, 0, 1, [0, 6], |
1705
|
|
|
|
|
|
|
'Si', 28.0855, 190, 0, [-4, 0, 2, 4], # -3, -2, -1, 1, 3 |
1706
|
|
|
|
|
|
|
'Sm', 150.36, 117, 1, [0, 2, 3], |
1707
|
|
|
|
|
|
|
'Sn', 118.71, 196, 1, [-4, -2, 0, 2, 4], |
1708
|
|
|
|
|
|
|
'Sr', 87.62, 95, 1, [0, 2], |
1709
|
|
|
|
|
|
|
'T', 3.0160492777, 220, 0, [-1, 0, 1], |
1710
|
|
|
|
|
|
|
'Ta', 180.94788, 150, 1, [0, 1, 2, 3, 4, 5], # -1 |
1711
|
|
|
|
|
|
|
'Tb', 158.92535, 110, 1, [0, 3, 4], # 1 |
1712
|
|
|
|
|
|
|
'Tc', 97.9072, 190, 1, [0, 1, 2, 3, 4, 5, 6, 7], # -3, -1 |
1713
|
|
|
|
|
|
|
'Te', 127.6, 221, 0, [-2, 0, 2, 4, 6], # 5 |
1714
|
|
|
|
|
|
|
'Th', 232.03806, 130, 1, [0, 2, 3, 4], |
1715
|
|
|
|
|
|
|
'Ti', 47.867, 154, 1, [-2, 0, 2, 3, 4], # -1 |
1716
|
|
|
|
|
|
|
'Tl', 204.3833, 162, 1, [0, 1, 3], |
1717
|
|
|
|
|
|
|
'Tm', 168.93421, 125, 1, [0, 2, 3], |
1718
|
|
|
|
|
|
|
'Tn', 220, 0, 0, [0, 2, 4, 6, 8], # as Rn^220 |
1719
|
|
|
|
|
|
|
'U', 238.02891, 138, 1, [0, 3, 4, 5, 6], |
1720
|
|
|
|
|
|
|
'V', 50.9415, 163, 1, [0, 2, 3, 4, 5], # -1, 1 |
1721
|
|
|
|
|
|
|
'W', 183.84, 220, 1, [0, 2, 3, 4, 5, 6], # -2, -1, 1 |
1722
|
|
|
|
|
|
|
'Xe', 131.293, 0, 0, [0, 1, 2, 4, 6, 8], |
1723
|
|
|
|
|
|
|
'Y', 88.90585, 122, 1, [0, 3], # 1, 2 |
1724
|
|
|
|
|
|
|
'Yb', 173.054, 110, 1, [0, 2, 3], |
1725
|
|
|
|
|
|
|
'Zn', 65.38, 165, 1, [0, 2], |
1726
|
|
|
|
|
|
|
'Zr', 91.224, 133, 1, [0, 2, 3, 4], # 1 |
1727
|
|
|
|
|
|
|
] |
1728
|
|
|
|
|
|
|
} |
1729
|
|
|
|
|
|
|
|
1730
|
|
|
|
|
|
|
|
1731
|
|
|
|
|
|
|
# Attention! |
1732
|
|
|
|
|
|
|
# ion-group mask consist individual atoms only |
1733
|
|
|
|
|
|
|
sub _ions_db{ |
1734
|
|
|
|
|
|
|
return [ |
1735
|
|
|
|
|
|
|
# hydroxide |
1736
|
200
|
|
|
200
|
|
8798
|
'[^O]OH(?![efgos])', 'H=1_O=-2', |
1737
|
|
|
|
|
|
|
'.a~O', 'a~Cl,Br=1_O=-2', |
1738
|
|
|
|
|
|
|
'.IO', 'I=1!3_O=-2', |
1739
|
|
|
|
|
|
|
# oxychloride: phosgene (carbonyl dichloride), thionyl... |
1740
|
|
|
|
|
|
|
'.OCl2', 'Cl=-1_O=-2', |
1741
|
|
|
|
|
|
|
# '.OCl2', 'Cl=-1;1_O=-2', |
1742
|
|
|
|
|
|
|
# meta- antimonites, arsenites and others |
1743
|
|
|
|
|
|
|
'.a~O2', 'a~Sb,Al,Ni,As,Au,Co,Ga,Cl,Br,B=3_O=-2', |
1744
|
|
|
|
|
|
|
# nitrites, dioxynitrates |
1745
|
|
|
|
|
|
|
'.NO2', 'N=3!2_O=-2', |
1746
|
|
|
|
|
|
|
'.BO3', 'B=3_O=-2', |
1747
|
|
|
|
|
|
|
# carbonates, selenates, tellurates |
1748
|
|
|
|
|
|
|
'.a~O3', 'a~C,Se,Si,Ni,Te,Pt,Mo,Po,Mn,Fe,Ti,Zr,Hf,Re=4_O=-2', |
1749
|
|
|
|
|
|
|
# bismuthates, nitrates and others |
1750
|
|
|
|
|
|
|
'.a~O3', 'a~Bi,N,V,Cl,Br,I,Nb,Ta=5_O=-2', |
1751
|
|
|
|
|
|
|
# plumbates, silicates |
1752
|
|
|
|
|
|
|
'.a~O4', 'a~Pb,Si,Ge,Ti=4_O=-2', |
1753
|
|
|
|
|
|
|
# ortho- antimonates, arsenates, phosphates and others |
1754
|
|
|
|
|
|
|
'.a~O4', 'a~Sb,As,P,V,Ta,Nb=5_O=-2', |
1755
|
|
|
|
|
|
|
# molybdates, tungstates, chromates and others (excepting peroxides) |
1756
|
|
|
|
|
|
|
'.a~O4(?=[^O]|Os|$)', 'a~Kr,U,S,Se,Te,Mo,W,Cr,Pu,Os=6_O=-2', |
1757
|
|
|
|
|
|
|
# per- chlorates, bromates |
1758
|
|
|
|
|
|
|
'.a~O4', 'a~Cl,Br=7_O=-2', |
1759
|
|
|
|
|
|
|
# rhodanides (thiocyanates) and for selenium |
1760
|
|
|
|
|
|
|
'.(?:a~CN|a~NC|CNa~|Ca~N|Na~C|NCa~)(?![a-gik-pr-u])', 'a~S,Se=-2_C=4_N=-3', |
1761
|
|
|
|
|
|
|
# ortho-/meta- : /phosphi(-a)tes, antimoni(-a)tes, arseni(-a)tes |
1762
|
|
|
|
|
|
|
'.a~O3', 'a~P,Sb,As=5!3_O=-2', |
1763
|
|
|
|
|
|
|
|
1764
|
|
|
|
|
|
|
'.a~O4', 'a~I,Re=6!7_O=-2', |
1765
|
|
|
|
|
|
|
'.a~O4', 'a~Tc,Ru=5!6!7_O=-2', |
1766
|
|
|
|
|
|
|
'.MnO4', 'Mn=3!4!5!6!7_O=-2', |
1767
|
|
|
|
|
|
|
# ferrate |
1768
|
|
|
|
|
|
|
'.FeO4', 'Fe=3!4!6_O=-2', |
1769
|
|
|
|
|
|
|
|
1770
|
|
|
|
|
|
|
'.a~O5', 'a~Fe,Pu=6_O=-2', |
1771
|
|
|
|
|
|
|
'.ReO5', 'Re=7_O=-2', |
1772
|
|
|
|
|
|
|
|
1773
|
|
|
|
|
|
|
'.I(?:O[56]|2O9)', 'I=7_O=-2', |
1774
|
|
|
|
|
|
|
'^I2O4$', 'I=3;5_O=-2', |
1775
|
|
|
|
|
|
|
'^I4O9$', 'I=3;5;5;5_O=-2', |
1776
|
|
|
|
|
|
|
|
1777
|
|
|
|
|
|
|
'.SnO6', 'Sn=4_O=-2', |
1778
|
|
|
|
|
|
|
'.SbO6', 'Sb=5_O=-2', |
1779
|
|
|
|
|
|
|
'.a~O6', 'a~Te,Am=6_O=-2', |
1780
|
|
|
|
|
|
|
# perxenic acid |
1781
|
|
|
|
|
|
|
'.XeO6', 'Xe=6!8_O=-2', |
1782
|
|
|
|
|
|
|
|
1783
|
|
|
|
|
|
|
'.MoO3F3', 'Mo=6_F=-1_O=-2', |
1784
|
|
|
|
|
|
|
# sulfamic acid salts |
1785
|
|
|
|
|
|
|
'.NSO3', 'S=6_O=-2_N=-3', |
1786
|
|
|
|
|
|
|
# thiazates, thionitrites |
1787
|
|
|
|
|
|
|
'.(?:NSO|SNO|NOS)(?=[\]\)\}]|$)', 'N=3_S=-2_O=-2', |
1788
|
|
|
|
|
|
|
# sulphites |
1789
|
|
|
|
|
|
|
'.SO3(?=[^OS]|Os|S[bcegimnr]|$)', 'S=4!6_O=-2', |
1790
|
|
|
|
|
|
|
# thiosulphates |
1791
|
|
|
|
|
|
|
'.S2O3', 'S=6;-2_O=-2', |
1792
|
|
|
|
|
|
|
# peroxydisulfuric (marshal's) acid |
1793
|
|
|
|
|
|
|
'.S2O8', 'S=6_O=-2;-2;-2;-2;-2;-2;-1;-1', |
1794
|
|
|
|
|
|
|
|
1795
|
|
|
|
|
|
|
'.a~2O7', 'a~P,Re=5_O=-2', |
1796
|
|
|
|
|
|
|
# pirosulphates, bichromates |
1797
|
|
|
|
|
|
|
'.a~2O7', 'a~S,Cr=6_O=-2', |
1798
|
|
|
|
|
|
|
# rhodane |
1799
|
|
|
|
|
|
|
'^\((?:SCN|SNC|CNS|CSN|NSC|NCS)\)2$', 'S=1_C=2_N=-3', |
1800
|
|
|
|
|
|
|
# cyan (dicyan) |
1801
|
|
|
|
|
|
|
'^\((?:CN|NC)\)2$', 'C=4;2_N=-3', |
1802
|
|
|
|
|
|
|
# cyanamides |
1803
|
|
|
|
|
|
|
'.CN2', 'C=4_N=-3', |
1804
|
|
|
|
|
|
|
# cyanides |
1805
|
|
|
|
|
|
|
'.CN', 'C=2_N=-3', |
1806
|
|
|
|
|
|
|
# cyanates (salts of cyanic, isocyanic acid) |
1807
|
|
|
|
|
|
|
'.CNO', 'C=4_N=-3_O=-2', |
1808
|
|
|
|
|
|
|
# fulminates (salts of fulminic acid) |
1809
|
|
|
|
|
|
|
'.ONC', 'C=-2_N=3_O=-2', |
1810
|
|
|
|
|
|
|
# flaveanic hydrogen |
1811
|
|
|
|
|
|
|
'^C2(?:H2N2S|N2SH2|SH2N2|SN2H2)$', 'C=2;4_H=1_S=-2_N=-3', |
1812
|
|
|
|
|
|
|
# rubeanic hydrogen |
1813
|
|
|
|
|
|
|
'^C2S2N2H4$', 'C=2;4_H=1_S=-2_N=-3', |
1814
|
|
|
|
|
|
|
# salts of peroxonitric acid or orthonitrates |
1815
|
|
|
|
|
|
|
'.NO4', 'N=5_O=-1;-1;-2;-2!-2', |
1816
|
|
|
|
|
|
|
# salts of hyponitrous acid |
1817
|
|
|
|
|
|
|
'.N2O2', 'N=1_O=-2', |
1818
|
|
|
|
|
|
|
|
1819
|
|
|
|
|
|
|
# salts of hyponitrates (триоксодиазотной) acid |
1820
|
|
|
|
|
|
|
'.N2O3', 'N=2_O=-2', |
1821
|
|
|
|
|
|
|
|
1822
|
|
|
|
|
|
|
# salts of nitroxylic acid |
1823
|
|
|
|
|
|
|
'.N2O4', 'N=2_O=-2', |
1824
|
|
|
|
|
|
|
# salts of hydrazonic acid and azides (pernitrides) |
1825
|
|
|
|
|
|
|
'(?:.[\[\(]?N3[\]\)]?|^N3H$)', 'N=-3;4;-2', |
1826
|
|
|
|
|
|
|
'^(?:a~3\(N2\)2|a~3N4)$', 'a~Ca=2_N=-2;-2;-2;0', # OSE ? |
1827
|
|
|
|
|
|
|
# diimide |
1828
|
|
|
|
|
|
|
'^N2H2$', 'H=1_N=-2;0', # OSE ? |
1829
|
|
|
|
|
|
|
# nitrosyl- group | ion |
1830
|
|
|
|
|
|
|
'(?:[\[\(]NO[\]\)])', 'N=2!3_O=-2', |
1831
|
|
|
|
|
|
|
'^a~(?:[\[\(]NO[\]\)])\d*$', 'a~Fe,Ru,Cr=0_N=2_O=-2', |
1832
|
|
|
|
|
|
|
# hydroxylamine & its salts |
1833
|
|
|
|
|
|
|
'NH[23]O.', 'H=1_N=-1_O=-2', |
1834
|
|
|
|
|
|
|
# ammonia, amide |
1835
|
|
|
|
|
|
|
'NH[234]', 'H=1_N=-3', |
1836
|
|
|
|
|
|
|
|
1837
|
|
|
|
|
|
|
'.BF4', 'B=3_F=-1', |
1838
|
|
|
|
|
|
|
# hypophosphorous |
1839
|
|
|
|
|
|
|
'.PO2', 'P=1_O=-2', |
1840
|
|
|
|
|
|
|
# polyphosphates |
1841
|
|
|
|
|
|
|
'.P2O4', 'P=2_O=-2', |
1842
|
|
|
|
|
|
|
'.P2O5', 'P=3;3!2;4_O=-2', |
1843
|
|
|
|
|
|
|
'.P2O6', 'P=4;4!3;5_O=-2', |
1844
|
|
|
|
|
|
|
'.P3O8', 'P=3;4;4_O=-2', |
1845
|
|
|
|
|
|
|
'.P6O12', 'P=3_O=-2', |
1846
|
|
|
|
|
|
|
|
1847
|
|
|
|
|
|
|
# Neutral ligands |
1848
|
|
|
|
|
|
|
'CO\(NH2\)2', 'C=4_H=1_N=-3_O=-2', |
1849
|
|
|
|
|
|
|
'H2O(?=[^2O]|Os|$)', 'H=1_O=-2', |
1850
|
|
|
|
|
|
|
'C2H4', 'H=1_C=-2', |
1851
|
|
|
|
|
|
|
# metal ammiakaty |
1852
|
|
|
|
|
|
|
'^a~\(NH3\)\d*$', 'a~Ca=0_H=1_N=-3', |
1853
|
|
|
|
|
|
|
|
1854
|
|
|
|
|
|
|
'^(?:HOF|HFO|OFH|FOH)$', 'H=1_F=1_O=-2', |
1855
|
|
|
|
|
|
|
|
1856
|
|
|
|
|
|
|
'^Bi2O4$', 'Bi=3;5_O=-2', |
1857
|
|
|
|
|
|
|
'^B4H10$', 'B=2;2;3;3_H=-1', |
1858
|
|
|
|
|
|
|
'^Fe3C$', 'Fe=2;2;0_C=-4', |
1859
|
|
|
|
|
|
|
'^Fe3P$', 'Fe=3;0;0_P=-3', |
1860
|
|
|
|
|
|
|
'^P4S3$', 'P=1_S=-2;-2;0', |
1861
|
|
|
|
|
|
|
'^P4S7$', 'P=1_S=-2;-2;0;0;0;0;0', |
1862
|
|
|
|
|
|
|
'^P4S10$', 'P=1_S=-2;-2;0;0;0;0;0;0;0;0', |
1863
|
|
|
|
|
|
|
'^P12H6$', 'H=1_P=-3;-3;0;0;0;0;0;0;0;0;0;0', |
1864
|
|
|
|
|
|
|
# compound oxide |
1865
|
|
|
|
|
|
|
'^U3O8$', 'U=5;5;6_O=-2', # triuranium octoxide |
1866
|
|
|
|
|
|
|
'^Pb2O3$', 'Pb=2;4_O=-2', |
1867
|
|
|
|
|
|
|
'^Sb2O4$', 'Sb=3;5_O=-2', |
1868
|
|
|
|
|
|
|
'^Ag2O2$', 'Ag=1;3_O=-2', # silver peroxide |
1869
|
|
|
|
|
|
|
'^a~3O4$', 'a~Pb,Pt=2;2;4_O=-2', |
1870
|
|
|
|
|
|
|
'^a~3O4$', 'a~Fe,Co,Mn=2;3;3_O=-2', |
1871
|
|
|
|
|
|
|
# carbonyls |
1872
|
|
|
|
|
|
|
'^a~\d*\(CO\)\d*$', 'a~V,W,Cr,Ir,Mn,Fe,Co,Ni,Mo,Tc,Re,Ru,Rh,Os=0_C=2_O=-2', |
1873
|
|
|
|
|
|
|
# alkaline metals and others |
1874
|
|
|
|
|
|
|
'^a~2O2', 'a~H,Li,Na,K,Rb,Cs,Fr,Hg=1_O=-1', # peroxides |
1875
|
|
|
|
|
|
|
'^(?:a~O2|a~2O4)', 'a~Li,Na,K,Rb,Cs,Fr=1_O=-1;0', # superoxides |
1876
|
|
|
|
|
|
|
'^a~O3', 'a~Li,Na,K,Rb,Cs,Fr=1_O=-1;0;0', # ozonide |
1877
|
|
|
|
|
|
|
# alkaline-earth metals and others |
1878
|
|
|
|
|
|
|
'^a~O2', 'a~Mg,Ca,Sr,Ba,Ra,Zn,Cd,Hg,Cu=2_O=-1', # peroxides |
1879
|
|
|
|
|
|
|
'^(?:a~\(O2\)2|a~O4)', 'a~Mg,Ca,Sr,Ba,Ra=2_O=-1;0', # superoxides |
1880
|
|
|
|
|
|
|
'^(?:a~\(O3\)2|a~O6)', 'a~Mg,Ca,Sr,Ba,Ra=2_O=-1;0;0', # ozonide |
1881
|
|
|
|
|
|
|
# all peroxides |
1882
|
|
|
|
|
|
|
'.\(O2\)', 'O=-1', |
1883
|
|
|
|
|
|
|
# dioxygenils, O2PtF6 ... (except O2F2) |
1884
|
|
|
|
|
|
|
'^(?:\(O2\)|O2)(?![F])', 'O=1;0', |
1885
|
|
|
|
|
|
|
# chromium peroxide |
1886
|
|
|
|
|
|
|
'^CrO5$', 'Cr=6_O=-2;-1;-1;-1;-1', |
1887
|
|
|
|
|
|
|
'^Cr2O8$', 'Cr=6_O=-2;-2;-2;-2;-1;-1;-1;-1', |
1888
|
|
|
|
|
|
|
# rhenium, iodine, chlorine peroxide |
1889
|
|
|
|
|
|
|
'^a~2O8$', 'a~Re,I,Cl=7_O=-2;-2;-2;-2;-2;-2;-1;-1', |
1890
|
|
|
|
|
|
|
# sulfur peroxide |
1891
|
|
|
|
|
|
|
'^SO4$', 'S=6_O=-2;-2;-1;-1', |
1892
|
|
|
|
|
|
|
'^S2O7$', 'S=6_O=-2;-2;-2;-2;-2;-1;-1', |
1893
|
|
|
|
|
|
|
# peroxymonosulfuric | persulfuric | Caro's acid |
1894
|
|
|
|
|
|
|
'.SO5', 'S=6_O=-2;-2;-2;-1;-1', |
1895
|
|
|
|
|
|
|
# per-carbonates (percarbonic acid) |
1896
|
|
|
|
|
|
|
'.C2O6', 'C=4_O=-2;-2;-2;-2;-1;-1', |
1897
|
|
|
|
|
|
|
# acid chlorine peroxide ? |
1898
|
|
|
|
|
|
|
'.ClO5', 'Cl=7_O=-2;-2;-2;-1;-1', |
1899
|
|
|
|
|
|
|
# dioxodifluorochlorate |
1900
|
|
|
|
|
|
|
'.ClO2F2', 'Cl=5_O=-2_F=-1', |
1901
|
|
|
|
|
|
|
# oxotetrafluorochlorate |
1902
|
|
|
|
|
|
|
'.ClOF4', 'Cl=5_O=-2_F=-1', |
1903
|
|
|
|
|
|
|
# oxofluorides |
1904
|
|
|
|
|
|
|
'.ClO3F2', 'Cl=7_O=-2_F=-1', |
1905
|
|
|
|
|
|
|
'.ClO2F4', 'Cl=7_O=-2_F=-1', |
1906
|
|
|
|
|
|
|
# platinum hexafluoride (strongest oxidizer) |
1907
|
|
|
|
|
|
|
'.PtF[6-9][\]\)]?$', 'Pt=5_F=-1', |
1908
|
|
|
|
|
|
|
# chlorine nitrides |
1909
|
|
|
|
|
|
|
'^(?:Cl3N|NCl3)$', 'Cl=1_N=-3', |
1910
|
|
|
|
|
|
|
'.(?:ClN|NCl)', 'Cl=1_N=-3', |
1911
|
|
|
|
|
|
|
|
1912
|
|
|
|
|
|
|
'^Fe2P', 'P=5_Fe=-2;-3', |
1913
|
|
|
|
|
|
|
# exotic |
1914
|
|
|
|
|
|
|
'^FNO3$', 'F=1_N=5_O=-2', |
1915
|
|
|
|
|
|
|
] |
1916
|
|
|
|
|
|
|
} |
1917
|
|
|
|
|
|
|
|
1918
|
|
|
|
|
|
|
|
1919
|
|
|
|
|
|
|
1; |
1920
|
|
|
|
|
|
|
__END__ |