Новосибирский институт органической химии им. Н.Н. Ворожцова СО РАН

Лаборатория изучения механизмов органических реакций

subst


#!/usr/bin/perl -ws

use strict;
#use Data::Dump 'pp';
our ($h,$help,$ijk,$IJK,$rm_in_core,$rm_dist,$R,$ii,$rot,$make_R); # Опции

if ($h or $help) {
  (my $prog = $0) =~ s/.+\///;
  print <<HELP;
Совмещает молекулы и выбрасывает совпадающие атомы.
Usage: $prog [-ijk=i,j,k] [-IJK=I,J,K] core.xyz substitutent.xyz [> core_subst.xyz]
Зависимости: Perl

Совмещение происходит по тройкам атомов из опций -ijk и -IJK.
По умолчанию -ijk=1,2,3 и -IJK=1,2,3.
-ijk относится к замещаемой молекуле/молекулам (core.xyz), 
-IJK - к заместителю (substitutent.xyz или последняя из структур). 

При совмещении происходит переориентация:
      x     y     z
 i:  0.00  0.00  0.00    Атом i в центре координат
 j:        0.00  0.00    Атом j на оси x
 k:              0.00    Атом k в плоскости xy

Опции:
-ijk=i,j,k  номера тройки атомов в замещаемых молекулах
-IJK=I,J,K  номера тройки атомов в заместителе (последней структуре)
-rm_in_core  накладывающиеся атомы удаляются в остове, а не в заместителе
-rm_dist=0.8 расстояние-признак накладывающихся атомов (ангстремы)

Использование в режиме аннелирования:
$prog -ijk=2,3,12 -IJK=1,2,3 Indole.xyz Benzene.xyz > Benzindole.xyz
</pre><img src="Fusing.png"><pre>

Использование в режиме замещения:
$prog -ijk=2,11 -IJK=8,2 Indole.xyz Benzene.xyz > Phenylindole.xyz
</pre><img src="Substitution.png"><pre>
В режиме замещения номера k и K можно не указывать, и если они не указаны
(по крайней мере, K в заместителе), то будет действовать опция -rot. 

-R=substitutent  $prog -ijk=i,j,k -R=Me core.xyz
   Из __DATA__ зачитывается заместитель и помещается на место атома i.
   Расстояние между атомом j и первым атомом заместителя - сумма их 
     ковалентных радиусов.
-rot=degrees   Если задано, то заместитель поворачивается на degrees градусов.
   Одновалентность замещаемого атома не проверяется.
   
   Вместо -ijk можно задать -ii (только замещаемый атом), тогда j и k будут 
     определены автоматически. Дополнительные возможности -ii:
     -ii=m,n,... означает, что будут замещены атомы номер m,n,...
     -ii=element - будут замещены все элементы. Например, перфторировать:
     $0 -ii=H -R=F cores.xyz > coresF.xyz
 
-R (без указания заместителя) - будет напечатан список заместителей, 
    определенных в __DATA__, и сразу после этого выход.
    
-make_R=name  Сделать из молекулы заместитель. Нужно задать -ijk=i,j,k
   i - одновалентный атом, который потом будет удален из молекулы
   j - замещающий атом, к которому присоединен i
   k - еще какой-нибудь атом, задающий плоскость xy
   Например, $prog -make_R=Ph -ijk=7,1,2 Benzene.xyz
   Результат сохранить, записав его в __DATA__ (в конец файла программы $prog ).

HELP
  exit(0);
}

$rm_dist ||= 0.8;

if ($ii && $ijk) {
  die "Несовместимые опции -i и -ijk\n";
}
if ($ii && !$R) {
  die "-ii д.б. только для -R\n";
}

my ($i,$j,$k) = (1,2,3);
my $by_bond = 0;
if ($ijk) {
  $ijk=~/(\d+)[\s[:punct:]]+(\d+)(?:[\s[:punct:]]+(\d+))?/ ?
  ($i,$j,$k) = ($1,$2,$3) :
  die "Номера после -ijk должны быть целыми числами!\n";
  do {$by_bond = 1; $k = 0} unless $k;
  #die "Номера после -ijk не должны быть нулевыми!\n" if $i==0 or $j==0 or $k==0;
  die "Номера после -ijk не должны быть одинаковыми!\n" if $i==$j or $j==$k or $k==$i;
}
my ($I,$J,$K) = (1,2,3);
if ($IJK) {
  $IJK=~/(\d+)[\s[:punct:]]+(\d+)(?:[\s[:punct:]]+(\d+))?/ ?
  ($I,$J,$K) = ($1,$2,$3) :
  die "Номера после -IJK должны быть целыми числами!\n";
  $K ||= 0;
  #die "Номера после -IJK не должны быть нулевыми!\n" if $I==0 or $J==0 or $K==0;
  die "Номера после -IJK не должны быть одинаковыми!\n" if $I==$J or $J==$K or $K==$I;
}

if ($R && $R eq '1') {
  print 'Defined substitutents: ', join(' ', map {$_->[0]{Title}} &read_subst), "\n";
  exit;
}

if ($make_R) {
  my ($mol) = (read_molden())[-1];
  if ($i>$#$mol or $j>$#$mol or $k>$#$mol) {
    warn "Номер после -ijk больше числа атомов!\n";
    next;
  }
  if (colinearity($mol,$i,$j,$k)) {
    die "Atoms $i,$j,$k in the core lie on a single line\n";
  }
  re_orientation($mol,$i,$j,$k);
  splice @$mol,$i,1; # Удаляем Hi
  my $aj = splice @$mol, $j<$i ? $j : $j-1, 1; # Временно удаляем замещающий атом
  splice @$mol,1,0,$aj; # Вставляем его в начало
  $mol->[0]{Name} = $make_R;
  for (my $i=1; $i<@$mol; $i++) {
    $mol->[$i][1] *= -1;
  }
  write_molden($mol);
  exit;
}

my @mols = read_molden();
die "Замещение в 1- и 2-атомных молекулах не предусмотрено!\n" if grep {@$_<4} @mols;

# Ковалентные радиусы из Dalton Trans., 2008, 2832-2838
my %radius = qw(
 H 0.31  He 0.28  Li 1.28  Be 0.96   B 0.84   C 0.73   N 0.71   O 0.66   F 0.57  
Ne 0.58  Na 1.66  Mg 1.41  Al 1.21  Si 1.11   P 1.07   S 1.05  Cl 1.02  Ar 1.06  
 K 2.03  Ca 1.76  Sc 1.70  Ti 1.60   V 1.53  Cr 1.39  Mn 1.50  Fe 1.44  Co 1.38   
Ni 1.24  Cu 1.32  Zn 1.22  Ga 1.22  Ge 1.20  As 1.19  Se 1.20  Br 1.20  Kr 1.16  
Rb 2.20  Sr 1.95   Y 1.90  Zr 1.75  Nb 1.64  Mo 1.54  Tc 1.47  Ru 1.46  Rh 1.42   
Pd 1.39  Ag 1.45  Cd 1.44  In 1.42  Sn 1.39  Sb 1.39  Te 1.38   I 1.39  Xe 1.40   
Cs 2.44  Ba 2.15  La 2.07  Ce 2.04  Pr 2.03  Nd 2.01  Pm 1.99  Sm 1.98  Eu 1.98   
Gd 1.96  Tb 1.94  Dy 1.92  Ho 1.92  Er 1.89  Tm 1.90  Yb 1.87  Lu 1.87  Hf 1.75   
Ta 1.70   W 1.62  Re 1.51  Os 1.44  Ir 1.41  Pt 1.36  Au 1.36  Hg 1.32  Tl 1.45  
Pb 1.46  Bi 1.48  Po 1.40  At 1.50  Rn 1.50  Fr 2.60  Ra 2.21  Ac 2.15  Th 2.06  
Pa 2.00   U 1.96  Np 1.90  Pu 1.87  Am 1.80  Cm 1.69  
);
  
if ($R) {
  my $degrees = defined($rot) ? $rot : '';
#  if ($R =~ s/,(-?\d+(?:\.\d+)?)$//) {
#    $degrees = $1;
#  }
  (my $subst0) = grep {$_->[0]{Title} eq $R} &read_subst;
  die "No $R in __DATA__\n" unless $subst0;
  # Будем немного поворачивать метилы, чтобы убрать симметрию
  if ($degrees eq '' && $R eq 'Me') {
    $degrees = 15;
  }
  $degrees ||= 0;
  # Зануляем координаты 1-го атома заместителя
  my ($x0,$y0,$z0) = ($subst0->[1][1],$subst0->[1][2],$subst0->[1][3]);
  for (my $i=1; $i<@$subst0; $i++) {
    $subst0->[$i][1] -= $x0;
    $subst0->[$i][2] -= $y0;
    $subst0->[$i][3] -= $z0;
  }
  # Поворачиваем
  if ($degrees) {
    rot_x($subst0,$degrees);
  }
  
  if ($ii) {
    # Парсим $ii
    if ($ii =~ /^[a-z][a-z]?$/i) {
      my $el = ucfirst lc $ii;
      $ii = '';
      for (my $n=1; $n<@{$mols[0]}; $n++) {
        $ii .= "$n," if $mols[0][$n][0] eq $el;
      }
      $ii =~ s/,$//;
      die "Нет элемента $el в 1-й структуре\n" unless $ii;
    }
    $ii =~ s/\.\./-/g;
    die "Invalid -ii=$ii\n" if $ii !~ /^\d+([,-]\d+)*/;
    my @ii;
    foreach (split /,/, $ii) {
      if (/(\d+)-(\d+)/) { # Раскрываем интервалы (i-n)
		    my @a = $1<=$2 ? $1..$2 : reverse $2..$1;
		    push @ii, @a;
	    }
      else {push @ii, $_}
    } 
    #print "@ii\n"; exit;
    
    foreach my $ii (@ii) {
      # Определяем соседей, т.к. они не заданы явно
      $i = $ii; # Замещаемый атом
      # Его ближайший сосед (в 1-й молекуле)
      $j = nearest($i, $mols[0]);
      # Ближайший сосед соседа
      $k = nearest($j, $mols[0], $i);
      
      # Расстояние между замещаемым атомом (i) и соседним к нему (j) в 1-й молекуле
      # Полагаем, что в остальных молекулах эти атомы такие же
      my $rij = dist($mols[0],$i,$j);
      
      # Локальная версия заместителя (будет модифицироваться)
      # $subst = $subst0; не годится (ссылки)
      my $subst = copy_mol($subst0); 
      #write_molden($subst);
      
      # Расстояние между соседним к замещаемому атомом и первым заместителя
      my $r = $radius{$mols[0][$j][0]}+$radius{$subst->[1][0]};
      # Сдвигаем заместитель по x, 
      # чтобы между соседним к замещаемому атомом и первым заместителя было $r
      for (my $i=1; $i<@$subst; $i++) {
        $subst->[$i][1] = $subst->[$i][1]+$rij-$r;
      }
      # Удаляем из заместителя 1-ю (служебную) строку
      shift @$subst;
      # 1-й атом заместителя
      my $first_atom = shift @$subst;
      my @mols_tmp;
      foreach  (@mols) {
        my $mol = copy_mol($_);
        # Подстраиваем молекулу под заместитель
        re_orientation($mol,$i,$j,$k);
        # Удаляем из молекулы замещаемый атом и на его место - 1-й атом заместителя
        splice @$mol, $i, 1, $first_atom;
        # Соединяем молекулу и заместитель без 1-го атома
        push @$mol, @$subst;
        push @mols_tmp, $mol;
      }
      @mols = @mols_tmp;
      #write_molden(@mols);
    }
  }
}
else {
  die "Нужно минимум две структуры\n" if @mols<2;

  my $subst = pop(@mols);
  $K = nearest($I,$subst,$J) if $K==0;
  #write_molden($subst); exit;
  if ($I>$#$subst or $J>$#$subst or $K>$#$subst) {
    die "Номер после -IJK больше числа атомов!\n";
  }
  if (colinearity($subst,$I,$J,$K)) {
    die "Atoms $I,$J,$K in the substitute lie on a single line\n";
  }
  re_orientation($subst,$I,$J,$K);
  if ($by_bond && $rot) {
    rot_x($subst,$rot);
  }

  foreach my $mol (@mols) {
    $k = nearest($i,$mol,$j) if $k==0;
    if ($i>$#$mol or $j>$#$mol or $k>$#$mol) {
      warn "Номер после -ijk больше числа атомов!\n";
      next;
    }
    if (colinearity($mol,$i,$j,$k)) {
      warn "Atoms $i,$j,$k in the core lie on a single line\n";
      next;
    }
    re_orientation($mol,$i,$j,$k);
    
    my @dist = distance($mol,$subst);
  #  # For debug
  #  for (my $i=1; $i<@$mol; $i++) {
  #    my @tmp;
  #    for (my $j=1; $j<@$subst; $j++) {
  #      push @tmp, sprintf("%d,%d=%.2f",$i,$j,$dist[$i][$j]) if $dist[$i][$j]<0.7;
  #    }
  #    print "@tmp\n" if @tmp;
  #  }
    
    for (my $i=1; $i<@$mol; $i++) {
      for (my $j=1; $j<@$subst; $j++) {
        if ($dist[$i][$j] < $rm_dist) {
          
          # Помечаем строчным шрифтом накладывающиеся водороды и др. одновалентные
          if ($mol->[$i][0] =~ /^(H|F|Cl|Br)$/ && $subst->[$j][0] !~ /^(H|F|Cl|Br)$/) {
            $mol->[$i][0] = lc($mol->[$i][0])
          }
          elsif ($mol->[$i][0] !~ /^(H|F|Cl|Br)$/ && $subst->[$j][0] =~ /^(H|F|Cl|Br)$/) {
            $subst->[$j][0] = lc($subst->[$j][0])
          }
          else {
          # Помечаем пробелом другие накладывающиеся атомы, в остове или в заместителе
          $rm_in_core ? $mol->[$i][0] = ' ' . $mol->[$i][0] :
                        $subst->[$j][0] = ' ' . $subst->[$j][0];
          }
        }
      }
    }
    if ($by_bond) {
      (my $at_m = $mol->[$i][0]) =~ s/\s+//g; $at_m = ucfirst lc $at_m;
      (my $at_s = $subst->[$J][0]) =~ s/\s+//g; $at_s = ucfirst lc $at_s;
      my $dx = $radius{$at_m}+$radius{$at_s}-$subst->[$J][1];
      for (my $i=1; $i<@$subst; $i++) {
        $subst->[$i][1] += $dx;
      }
    }
    push @$mol, @{$subst}[1..@$subst-1];
    $mol->[0] = '';
    @$mol = grep {$_ eq '' or $_->[0] !~ /^( |[a-z])/} @$mol;
    $mol->[0] = {};
  }
}
#pp @mols;
write_molden(@mols);


# Читает xyz. Параметры - имена xyz-файлов. Если параметров нет, то <>.
# Возвращает массив найденных молекул.
sub read_molden {
  local @ARGV = @_ ? @_ : @ARGV;
  my $num = qr/-?\d+(?:\.\d+)?/;
  my @mols;
  my $line;
  LOOP:
  while ($line || defined($line = <>)) {
    #print $line;
    if ($line =~/^\s*(\d+)\s*$/) {
      my @mol;
      my $N = $1;
      last LOOP if eof();
      next LOOP if eof(ARGV);
      $line = <>;
      if ($line =~ /^\s*(.*?)\s*$/) {
        $mol[0]{Title} = $1;
      }
      if ($line =~ /\s($num)\s/o) {
        $mol[0]{Energy} = $1;
      }
      if ($line =~ /Symmetry\s+(\w+)/) {
        $mol[0]{Symmetry} = $1;
      }
      #($mol[0]) = $line =~ /($num)/o;
      for (my $i=1; $i<=$N; $i++) {
        last LOOP if eof();
        next LOOP if eof(ARGV);
        $line = <>;
        #print $line;
        if ($line =~ /^\s*([A-Za-z]{1,2})\s+($num)\s+($num)\s+($num)\s*(.*)/io) {
          $mol[$i] = [$1,$2,$3,$4,$5];
          $mol[$i][0] = ucfirst(lc $mol[$i][0]);
        } else {
          next LOOP;
        }
      }
      push @mols, \@mol;
      last LOOP if eof();
    } else {
      undef $line;
    }
  }
  return @mols;
}

# Печатает xyz. Параметры -- список молекул, в конце м.б. имя файла.
# Если последний элемент списка - имя файла (не ссылка на массив),
# то печать в этот файл, иначе - на stdout
sub write_molden {
	my $fh = \*STDOUT;
  if (ref($_[-1]) ne 'ARRAY') {
    my $file = pop @_;
    open $fh, '>', $file or die "Can't write to $file: $!\n";
  }
  
  foreach my $mol (@_) {
		my $N = $#{$mol};
    print $fh " $N\n";
		print $fh "$mol->[0]{Name} " if exists $mol->[0]{Name};
		#print $fh " Symmetry $mol->[0]{Symmetry} " if $mol->[0]{Symmetry};
    print $fh "\n";
		for (my $i=1; $i<=$N; $i++) {
      my ($atom,$x,$y,$z) = @{$mol->[$i]};
      printf $fh " %-2s %12.8f %12.8f %12.8f", $atom, $x, $y, $z;
      #printf $fh uc($atom) eq 'H' ? " %10.3f" : " %9.2f"  , $ppm if $ppm;
      print $fh "\n";
		}
	}
  #close $fh;
}

# my ($x,$y,$z) = get_xyz($mol);
# Возвращает список ссылок на массивы 1..$N (@x,@y,@z) молекулы
# (нулевые элементы пустые)
sub get_xyz {
  my $mol = shift;
  my (@x,@y,@z);
  my $N = $#{$mol};
  for (my $i=1; $i<=$N; $i++) {
    $x[$i] = $mol->[$i][1];
    $y[$i] = $mol->[$i][2];
    $z[$i] = $mol->[$i][3];
  }
  return (\@x,\@y,\@z);
}

# put_xyz($mol,\@x,\@y,\@z);
# Помещает координаты из массивов 1..$N (@x,@y,@z) в молекулу
sub put_xyz {
  my ($mol,$x,$y,$z) = @_;
  my $N = $#{$mol};
  return undef if $#{$x} != $N || $#{$y} != $N || $#{$z} != $N;
  for (my $i=1; $i<=$N; $i++) {
    $mol->[$i][1] = $x->[$i];
    $mol->[$i][2] = $y->[$i];
    $mol->[$i][3] = $z->[$i];
  }
  1
}

# centre_to_atom($mol,$i)
# Помещает центр координат молекулы на атом $i
sub centre_to_atom {
  my ($mol,$i) = @_;
	my $N = $#{$mol};
  return undef if $i > $N;
	my ($xi,$yi,$zi) = ($mol->[$i][1],$mol->[$i][2],$mol->[$i][3]);
	for (my $n=1; $n<=$N; $n++) {
	  $mol->[$n][1] -= $xi;
    $mol->[$n][2] -= $yi;
    $mol->[$n][3] -= $zi; 
	}
  1
}

# ($A,$B,$C) = plane_normal($mol,$i,$j,$k);
# Возвращает вектор нормаль к плоскости, проходящей через атомы $i, $j, $k.
# Если атомов больше трех, плоскость через них проводится по наименьшим квадратам.
sub plane_normal {
	my ($mol,@nums) = @_;
  my ($x,$y,$z) = get_xyz($mol);
  my @x = @$x; my @y = @$y; my @z = @$z;
  my ($A,$B,$C);
	return undef if @nums<3;
	if (@nums == 3) {
	  my ($i,$j,$k) = @nums;
		#print "@nums\n";
    $A = ($y[$j]-$y[$i])*($z[$k]-$z[$i])-($z[$j]-$z[$i])*($y[$k]-$y[$i]);
		$B = ($z[$j]-$z[$i])*($x[$k]-$x[$i])-($x[$j]-$x[$i])*($z[$k]-$z[$i]);
		$C = ($x[$j]-$x[$i])*($y[$k]-$y[$i])-($y[$j]-$y[$i])*($x[$k]-$x[$i]);
		#print "@nums  $A $B $C\n";
	} else {
		my ($X,$Y,$Z,$XX,$YY,$ZZ,$XY,$YZ,$XZ);
		foreach (@nums) {
			$X  += $x[$_];        $Y  += $y[$_];        $Z  += $z[$_];
			$XX += $x[$_]*$x[$_]; $YY += $y[$_]*$y[$_]; $ZZ += $z[$_]*$z[$_];
			$XY += $x[$_]*$y[$_]; $YZ += $y[$_]*$z[$_]; $XZ += $z[$_]*$x[$_];
		}
		# A*XX + B*XY + C*XZ = -X   XX XY XZ | -X 	 -X XY XZ 	XX -X XZ	 XX XY -X
		# A*XY + B*YY + C*YZ = -Y   XY YY YZ | -Y 	 -Y YY YZ 	XY -Y YZ	 XY YY -Y
		# A*XZ + B*YZ + C*ZZ = -Z   XZ YZ ZZ | -Z 	 -Z YZ ZZ 	XZ -Z ZZ	 XZ YZ -Z
		$A = $X*($YZ*$YZ-$YY*$ZZ) + $Y*($XY*$ZZ-$YZ*$XZ) + $Z*($YY*$XZ-$XY*$YZ);
		$B = $X*($XY*$ZZ-$XZ*$YZ) + $Y*($XZ*$XZ-$XX*$ZZ) + $Z*($XX*$YZ-$XY*$XZ);
		$C = $X*($YY*$XZ-$YZ*$XY) + $Y*($YZ*$XX-$XY*$XZ) + $Z*($XY*$XY-$YY*$XX);
	}
  my $normal = sqrt($A**2 + $B**2 +$C**2);
  $A /= $normal; $B /= $normal; $C /= $normal;
	#print "@nums  $A $B $C\n";
	return ($A,$B,$C);
}

# re_orientation($mol,$i,$j,$k);
# Переориентирует молекулу так чтобы у атомов $i,$j,$k были координаты
# $i:  0.00  0.00  0.00
# $j:  f.ff  0.00  0.00
# $k:  f.ff  f.ff  0.00
# Dependecies: plane_normal, colinearity, put_xyz
sub re_orientation {
  my ($mol,$i,$j,$k) = @_;
	centre_to_atom($mol,$i);
	#&write_molden;
  my ($x,$y,$z) = get_xyz($mol);
  my @x = @$x; my @y = @$y; my @z = @$z;
 	
  my $xj = sqrt($x[$j]**2 + $y[$j]**2 + $z[$j]**2);
	my $xk = ($x[$k]*$x[$j] + $y[$k]*$y[$j] + $z[$k]*$z[$j]) / $xj;
	my $yk = sqrt($x[$k]**2 + $y[$k]**2 + $z[$k]**2 - $xk**2);
	my ($A1,$B1,$C1) = plane_normal($mol,$i,$j,$k);
  
	for (my $n=1; $n<=$#{$mol}; $n++) {
	  if ($n != $i && $n != $j && $n != $k) {
			my ($xn,$yn,$zn);
      if (colinearity($mol,$j,$k,$n)) {     # Try colinearity
        my $slope;
        my $xkj = $x[$k]-$x[$j];
        my $ykj = $y[$k]-$y[$j];
        my $zkj = $z[$k]-$z[$j];
        if (abs($xkj)>=abs($ykj) && abs($xkj)>=abs($zkj)) {
          $slope = ($x[$n]-$x[$j])/$xkj;
        }
        elsif (abs($ykj)>=abs($zkj)) {
          $slope = ($y[$n]-$y[$j])/$ykj;
        }
        else {
          $slope = ($z[$n]-$z[$j])/$zkj;
        }
        $zn = 0;
        $yn = $slope*$yk;
        $xn = $slope*($xk-$xj)+$xj;
      }
      else {
        $xn = ($x[$n]*$x[$j] + $y[$n]*$y[$j] + $z[$n]*$z[$j])/$xj;
        $yn = ($x[$n]*$x[$k] + $y[$n]*$y[$k] + $z[$n]*$z[$k] - $xn*$xk) / $yk;
        my $sqr = $x[$n]**2 + $y[$n]**2 + $z[$n]**2 - $xn**2 - $yn**2;
        $sqr = 0 if $sqr < 0;
        $zn = sqrt($sqr);
        my ($A2,$B2,$C2) = plane_normal($mol,$j,$k,$n);
        my $Vec = ($B1*$C2-$C1*$B2)*($x[$k]-$x[$j])+
                  ($C1*$A2-$A1*$C2)*($y[$k]-$y[$j])+
                  ($A1*$B2-$B1*$A2)*($z[$k]-$z[$j]);
        $zn = -$zn if $Vec < 0;
        #print "$Vec\n";
      }
			($x[$n],$y[$n],$z[$n]) =	($xn,$yn,$zn);
		}
	}
	($x[$j],$y[$j],$z[$j],$x[$k],$y[$k],$z[$k]) =	($xj,0,0,$xk,$yk,0);
	put_xyz($mol,\@x,\@y,\@z);
  #&write_molden;
}

sub colinearity {
  my ($mol,$i,$j,$k) = @_;
  my $eps = 1e-6;
  my $rij = sqrt(($mol->[$i][1]-$mol->[$j][1])**2+
                 ($mol->[$i][2]-$mol->[$j][2])**2+
                 ($mol->[$i][3]-$mol->[$j][3])**2);
  my $rik = sqrt(($mol->[$i][1]-$mol->[$k][1])**2+
                 ($mol->[$i][2]-$mol->[$k][2])**2+
                 ($mol->[$i][3]-$mol->[$k][3])**2);
  my $rjk = sqrt(($mol->[$k][1]-$mol->[$j][1])**2+
                 ($mol->[$k][2]-$mol->[$j][2])**2+
                 ($mol->[$k][3]-$mol->[$j][3])**2);
  my ($r1,$r2,$r3) = sort {$a<=>$b} ($rij,$rik,$rjk);
  if ($r1<$eps) {
    warn "Two atoms from $i,$j,$k coincide\n";
    return 1;
  }
  if ($r1+$r2-$r3<$eps) {
    return 1;
  }
  return undef;
}

sub distance {
  my ($mol,$subst) = @_;
  my @dist;
  for (my $i=1; $i<@$mol; $i++) {
    my ($x,$y,$z) = @{$mol->[$i]}[1..3];
    for (my $j=1; $j<@$subst; $j++) {
      my ($X,$Y,$Z) = @{$subst->[$j]}[1..3];
      $dist[$i][$j] = sqrt(($x-$X)**2+($y-$Y)**2+($z-$Z)**2);
    }
  }
  return @dist;
}

sub dist {
  my ($mol,$i,$j) = @_;
  my $rij = sqrt(($mol->[$i][1]-$mol->[$j][1])**2 +
                 ($mol->[$i][2]-$mol->[$j][2])**2 +
                 ($mol->[$i][3]-$mol->[$j][3])**2 );
  return $rij;
}

sub nearest {
 # Ближайший сосед к атому i (за исключением j, если он задан)
  my ($i,$mol,$j) = @_;
  my $N = @$mol;
  die "Index $i more then number of atoms ($N)\n" if $i > $N;
  my $k;
  my $r_min = 1e6;
   for (my $n=1; $n<$N; $n++) {
     next if $n==$i;
     next if $j and $n==$j;
     my $r = dist($mol,$i,$n);
     if ($r < $r_min) {
       $r_min = $r;
       $k = $n;
     }
   }
   return $k;
}

sub rot_x {
  my ($mol,$degrees) = @_;
  my $rad = $degrees/180*3.14159265358979;
  my $cos = cos($rad);
  my $sin = sin($rad);
  for (my $i=1; $i<@$mol; $i++) {
    my ($y,$z) = ($mol->[$i][2],$mol->[$i][3]);
    $mol->[$i][2] = $sin*$z+$cos*$y;
    $mol->[$i][3] = $cos*$z-$sin*$y;
  }
}

sub read_subst {
  my $tmp = "subst_DATA.tmp";
  open L, '>', $tmp or die "Can't write to file $tmp: $!\n";
  while (<DATA>) {
    print L;
  }
  close L;
  my @mols = read_molden($tmp);
  unlink $tmp;
  return @mols;
}

sub copy_mol {
  my $mol = shift;
  my $new_mol;
  my $N = $#{$mol};
  $new_mol->[0] = $mol->[0] if $mol->[0];
  for (my $i=1; $i<=$N; $i++) {
    $new_mol->[$i] = [@{$mol->[$i]}];
  }
  return $new_mol;
}

# Многоатомные заместители (-R) д.б. направлены вдоль связи в минус по x
# и замещающий атом д.б. первым
# Например, берем карбоксил C(1)O(2)OH. Делаем муравьиную кислоту H(5)C(1)O(2)OH, 
# reorient -ijk=1,5,2 и удаляем последний H
__DATA__ 
1
H
 H    0.00000000   0.00000000   0.00000000
1
F
 F    0.00000000   0.00000000   0.00000000
1
Cl
 Cl    0.00000000   0.00000000   0.00000000
1
Br
 Br    0.00000000   0.00000000   0.00000000
1
I
 I    0.00000000   0.00000000   0.00000000
2
OH
 O    0.00000000   0.00000000   0.00000000
 H   -0.36578731   0.89924971  -0.00160080
3
NH2
 N    0.00000000   0.00000000   0.00000000
 H   -0.46317533   0.84528872   0.31152275
 H   -0.46328438  -0.84608500   0.30949621
3
NO2
 N    0.00000000   0.00000000   0.00000000
 O   -0.58263507  -1.09524818   0.00003862
 O   -0.58242824   1.09527120  -0.00021915
6
Ac
 C    0.00000000   0.00000000   0.00000000
 O   -0.63621472   1.05590662  -0.01044610
 C   -0.72946410  -1.32068058   0.01263019
 H   -1.80838042  -1.13662904   0.01920459
 H   -0.47664668  -1.88329053   0.91498375
 H   -0.48874032  -1.89425334  -0.88611337
7
OAc
 O    0.00000000   0.00000000   0.00000000
 C   -2.09330995  -0.51410490   0.90453115
 C   -0.60525311  -0.59682033   1.07615371
 O   -0.06128596  -1.12023185   2.03931056
 H   -2.58431301  -0.97683133   1.76547623
 H   -2.39259641  -1.05232417   0.00170809
 H   -2.40264923   0.53269479   0.85010802
7
Tf
 S    0.00000000   0.00000000   0.00000000
 O   -0.46990098   1.25129727   0.58378706
 O   -0.46963891  -1.25449179   0.57725421
 C   -0.32886683   0.00453706  -1.78988747
 F    0.19848152   1.09993472  -2.40786790
 F    0.20192515  -1.08562582  -2.41423083
 F   -1.66239701   0.00318499  -2.06980455
7
OAcf
 O    0.00000000   0.00000000   0.00000000
 C   -0.56097445   0.02867405  -1.26068702
 O    0.05438301   0.05268514  -2.32316466
 C   -2.07555897   0.02563674  -1.11649931
 F   -2.53069833   1.10468748  -0.41666554
 F   -2.53061486  -1.07491367  -0.45104008
 F   -2.73184914   0.04468208  -2.31039581
8
OTf
 O    0.00000000   0.00000000   0.00000000
 S   -0.93299857   1.34153210   0.00648950
 O   -0.72457998   2.03808068   1.26715589
 O   -0.73477125   2.04327390  -1.25297271
 C   -2.54432913   0.48967807   0.01138638
 F   -3.59061694   1.36309622   0.03407336
 F   -2.71519003  -0.29706564  -1.09003834
 F   -2.69303641  -0.32724073   1.09398841
5
OSO2F
 O    0.00000000   0.00000000   0.00000000
 S   -0.92591248   1.33942816  -0.00447774
 O   -0.76864144   2.02962357  -1.26910829
 O   -0.78109517   2.02890033   1.26210959
 F   -2.43960961   0.79721767  -0.01189763
4
Me
 C    0.00000000   0.00000000   0.00000000
 H   -0.36633733   1.03626161   0.00000000
 H   -0.36654080  -0.51811160  -0.89737613
 H   -0.36644444  -0.51815003   0.89737614
7
Et
 C    0.00000000   0.00000000   0.00000000
 H   -0.36504683  -0.89498626   0.53324939
 H   -0.36497446   0.87692574   0.56249613
 C   -0.58915384   0.02348760  -1.42230384
 H   -1.69099508   0.02433998  -1.39139030
 H   -0.25883522   0.92167552  -1.96832362
 H   -0.26095308  -0.85749903  -1.99687644
10
i-Pr
 C    0.00000000   0.00000000   0.00000000
 C   -0.57117591   0.65646117  -1.26095149
 C   -0.57440975   0.65848134   1.25838770
 H   -0.35398868  -1.04062877   0.00063689
 H   -1.66134362   0.55232549  -1.28627761
 H   -0.34005114   1.72626932  -1.30559252
 H   -0.17014059   0.18423811  -2.16451648
 H   -1.66478093   0.55345206   1.28160660
 H   -0.34454695   1.72857276   1.30193579
 H   -0.17565424   0.18831549   2.16392337
13
t-Bu
 C    0.00000000   0.00000000   0.00000000
 C   -0.48577368  -0.12183199   1.45840990
 C   -0.60894131   1.28909972  -0.60458711
 C   -0.59218571  -1.17598353  -0.81566374
 H   -1.58061646  -0.12263987   1.51246765
 H   -0.12570329   0.71400367   2.06971604
 H   -0.13267812  -1.04943249   1.92370931
 H   -1.70126324   1.21732095  -0.67157203
 H   -0.22927260   1.47094779  -1.61659307
 H   -0.39700901   2.17244297   0.00741868
 H   -1.68403037  -1.09971749  -0.88483855
 H   -0.19694856  -1.18725041  -1.83796447
 H   -0.38301810  -2.14807743  -0.35655940
10
Pr
 C    0.00000000   0.00000000   0.00000000
 C   -0.57253822   0.01328753   1.41944046
 C   -2.09299017  -0.00217992   1.40571899
 H   -0.37170184   0.86886507  -0.55928362
 H   -0.37232757  -0.87887159  -0.54313761
 H   -0.21137897  -0.85722960   1.98004913
 H   -0.22970179   0.90623024   1.95584359
 H   -2.48991413   0.87520273   0.88530192
 H   -2.48071472   0.00587743   2.42910044
 H   -2.47167279  -0.89985000   0.90662268
13
Bu
 C    0.00000000   0.00000000   0.00000000
 C   -0.57360691  -0.01947928   1.42012259
 C   -2.10241636  -0.00781196   1.40006447
 C   -2.67808513  -0.03021399   2.80707062
 H   -0.37162490   0.88155696  -0.53899430
 H   -0.37217276  -0.86629993  -0.56278915
 H   -0.22182412  -0.91287419   1.95150042
 H   -0.20953414   0.85080336   1.98064781
 H   -2.47494048  -0.87655317   0.84504174
 H   -2.46032447   0.88810527   0.87979525
 H   -3.77174006  -0.00725817   2.76942039
 H   -2.33946895   0.83646218   3.38380497
 H   -2.37539023  -0.93729828   3.33986305
5
CH2OH
 C    0.00000000   0.00000000   0.00000000
 O   -0.49630939  -0.70500320  -1.13264246
 H   -0.38639904  -0.48020672   0.90523548
 H   -0.40889541   1.01524443  -0.03595079
 H    0.07185703  -0.46122780  -1.88399755
5
OMe
 O    0.00000000   0.00000000   0.00000000
 C   -0.66956608  -1.25374046   0.00960609
 H   -1.74622758  -1.05970707   0.01455469
 H   -0.42659423  -1.82134099   0.91379745
 H   -0.43651836  -1.82978356  -0.89174250
4
CF3
 C    0.00000000   0.00000000   0.00000000
 F   -0.52144780  -1.08032120  -0.63991068
 F   -0.52063277   1.09753291  -0.61070772
 F   -0.49918527  -0.01671036   1.26320528
9
NMe2
 N    0.00000000   0.00000000   0.00000000
 C   -0.61774579  -1.21677782   0.52254975
 C   -0.61727376   1.19677460   0.56753717
 H   -1.71141762  -1.14876426   0.48744881
 H   -0.32262348  -1.40629297   1.56031517
 H   -0.36208023  -2.08826095  -0.08796591
 H   -1.71102230   1.13008490   0.53095136
 H   -0.32121346   1.34786586   1.61138665
 H   -0.36214312   2.09032342  -0.01047449
6
NHMe
 N    0.00000000   0.00000000   0.00000000
 C   -0.70019673   1.11953005   0.59824877
 H   -0.39652901  -0.89636421   0.27035688
 H   -1.77897029   0.93536684   0.56074501
 H   -0.52270908   2.05218844   0.05387282
 H   -0.42268500   1.25218243   1.64906909
4
SO2F
 S    0.00000000   0.00000000   0.00000000
 O   -0.49967280  -1.27599923   0.48263179
 O   -0.49972807   1.28787352   0.44986049
 F   -0.24464621  -0.02088426  -1.62430688
5
SO2H2
 S    0.00000000   0.00000000   0.00000000
 O   -0.19595455   1.16046815   1.13338642
 O   -0.21196581  -1.44107391   0.74563706
 H   -1.08932176   1.57173901   1.02582623
 H   -1.11580778  -1.78055834   0.53241897
13
SiMe3
 Si   0.00000000   0.00000000   0.00000000
 C   -0.63147386  -1.74918443   0.00471649
 C   -0.54039508   0.89640044  -1.54115557
 C   -0.54052888   0.90477339   1.53664156
 H   -1.72592624  -1.76363375   0.00336355
 H   -0.29194381  -2.29707491  -0.87960166
 H   -0.29402492  -2.29193898   0.89299893
 H   -1.63212810   0.92071191  -1.61227560
 H   -0.18173646   1.93014167  -1.54578721
 H   -0.15649590   0.40061656  -2.43796010
 H   -1.63199732   0.91552354   1.61574437
 H   -0.14344544   0.42320469   2.43549151
 H   -0.19533199   1.94311668   1.52820068
4
SiCl3
 Si   0.00000000   0.00000000   0.00000000
 Cl  -0.61928575  -0.02601367  -1.93199234
 Cl  -0.70519761   1.66425977   0.92481108
 Cl  -0.69890157  -1.64352683   0.96658154
5
OCF3
 O    0.00000000   0.00000000   0.00000000
 C   -0.63172280  -0.17883524   1.23310657
 F   -1.93781571  -0.21795343   0.98423981
 F   -0.25051234  -1.32188671   1.82809634
 F   -0.36433675   0.83687844   2.07236479
25
Ad
 C    0.00000000   0.00000000   0.00000000
 C    1.54198334   0.00000000   0.00000000
 C   -0.51133498   1.45472796   0.00000000
 C   -0.51157620  -0.72201904   1.26278090
 C    2.06101588   0.72853793   1.25601024
 C    1.54566008   0.00513332   2.51644614
 C    0.00371049   0.00517810   2.52107383
 C    1.54583374   2.18190863   1.25366149
 C    0.00385296   2.18596390   1.25603170
 C   -0.50766549   1.45986852   2.51647342
 H    1.92001424  -1.03893166  -0.01771768
 H    1.91995436   0.50061616  -0.91055943
 H   -0.16511455  -1.77204384   1.26436657
 H   -1.61709255  -0.74294800   1.26441490
 H   -0.36394009  -0.51376145   3.42536585
 H    1.92643650   0.50928722   3.42388373
 H    1.92370460  -1.03373968   2.53718813
 H    3.16655244   0.72757383   1.25436565
 H    1.92367950   2.71530006   0.36185054
 H    1.92673074   2.71960815   2.14156998
 H   -0.36369185   3.22861167   1.25440295
 H   -1.61682934   1.46694509  -0.01785233
 H   -0.16416207   1.97732192  -0.91046922
 H   -0.15829637   1.98628418   3.42390724
 H   -1.61312020   1.47198756   2.53725220
9
HOTf1
 H    0.00000000   0.00000000   0.00000000
 O   -0.98162922   0.00000000   0.00000000
 S   -1.45258609   1.57384489   0.00000000
 O   -1.10467936   2.11530184  -1.29820541
 O   -1.10991783   2.17103437   1.28442734
 C   -3.33263188   1.30340313   0.00000108
 F   -3.70120578   0.63398927  -1.09655970
 F   -3.68043493   0.60277027   1.09239431
 F   -3.93292072   2.49932250   0.02480991
9
HOTf
 H   -0.00000000   0.00000000   0.00000000
 O   -0.66577160   0.72134880   0.00000050
 S   -2.14172689   0.00000000   0.00000000
 O   -2.30365453  -0.62289267   1.29820498
 O   -2.34816239  -0.65684095  -1.28442779
 C   -3.21809895   1.56497096   0.00000000
 F   -2.97615948   2.28983359   1.09656128
 F   -2.93913081   2.29574538  -1.09239273
 F   -4.50405320   1.19498248  -0.02480909
7
COOMe
 C    -1.08900009   0.00000000   0.00000000
 O    -1.61230432   1.09602388   0.00000000
 O    -1.76278306  -1.16702606   0.00000006
 C    -3.20233111  -1.02342070  -0.00539596
 H    -3.59269073  -2.04765642  -0.01221806
 H    -3.53979578  -0.48307191   0.89138050
 H    -3.53177852  -0.47348547  -0.89922268
11
Ph 
 C    -1.08900029   0.00000000   0.00000000
 C    -1.78900010   1.21243567   0.00000000
 C    -3.18900048   1.21243567   0.00000000
 C    -3.88900018   0.00000050   0.00000000
 C    -3.18900036  -1.21243518   0.00000000
 C    -1.78900085  -1.21243567   0.00000000
 H    -1.24449967   2.15553742   0.00000000
 H    -3.73350034   2.15553742   0.00000000
 H    -4.97800047   0.00000050   0.00000000
 H    -3.73350080  -2.15553693   0.00000000
 H    -1.24450100  -2.15553742   0.00000000