text: # $Id$
  #
  #  Copyright (C) 2003-2006  Rational Discovery LLC
  #
  #   @@ All Rights Reserved @@
  #  This file is part of the RDKit.
  #  The contents are covered by the terms of the BSD license
  #  which is included in the file license.txt, found at the root
  #  of the RDKit source tree.
  #
  """unit testing code for graph-theoretical descriptors
  
  """
  from __future__ import print_function
  from rdkit import RDConfig
  import unittest, os.path
  from rdkit import Chem
  from rdkit.Chem import GraphDescriptors, MolSurf, Lipinski, Crippen
  
  
  def feq(n1, n2, tol=1e-4):
    return abs(n1 - n2) <= tol
  
  
  class TestCase(unittest.TestCase):
  
    def setUp(self):
      if doLong:
        print('\n%s: ' % self.shortDescription(), end='')
  
    def testBertzCTShort(self):
      """ test calculation of Bertz 'C(T)' index
  
     """
      data = [('C=CC=C', 21.01955), ('O=CC=O', 25.01955), ('FCC(=O)CF', 46.7548875),
              ('O=C1C=CC(=O)C=C1', 148.705216), ('C12C(F)=C(O)C(F)C1C(F)=C(O)C(F)2', 315.250442),
              ('C12CC=CCC1C(=O)C3CC=CCC3C(=O)2', 321.539522)]
  
      for smi, CT in data:
        m = Chem.MolFromSmiles(smi)
        newCT = GraphDescriptors.BertzCT(m, forceDMat=1)
        assert feq(newCT, CT, 1e-3), 'mol %s (CT calc = %f) should have CT = %f' % (smi, newCT, CT)
  
    def _testBertzCTLong(self):
      """ test calculation of Bertz 'C(T)' index
  
      NOTE: this is a backwards compatibility test, because of the changes
        w.r.t. the treatment of aromatic atoms in the new version, we need
        to ignore molecules with aromatic rings...
  
       
     """
      col = 1
      with open(os.path.join(RDConfig.RDCodeDir, 'Chem', 'test_data', 'PP_descrs_regress.2.csv'),
                'r') as inF:
        lineNum = 0
        for line in inF:
          lineNum += 1
          if line[0] != '#':
            splitL = line.split(',')
            smi = splitL[0]
            m = Chem.MolFromSmiles(smi)
  
            assert m, 'line %d, smiles: %s' % (lineNum, smi)
            useIt = 1
            for atom in m.GetAtoms():
              if atom.GetIsAromatic():
                useIt = 0
                break
            if useIt:
              tgtVal = float(splitL[col])
              try:
                val = GraphDescriptors.BertzCT(m)
              except Exception:
                val = 666
              assert feq(val, tgtVal, 1e-4), 'line %d, mol %s (CT calc = %f) should have CT = %f' % (
                lineNum, smi, val, tgtVal)
  
    def __testDesc(self, fileN, col, func):
      with open(os.path.join(RDConfig.RDCodeDir, 'Chem', 'test_data', fileN), 'r') as inF:
        lineNum = 0
        for line in inF:
          lineNum += 1
          if line[0] != '#':
            splitL = line.split(',')
            smi = splitL[0]
            m = Chem.MolFromSmiles(smi)
            assert m, 'line %d, smiles: %s' % (lineNum, smi)
            useIt = 1
            if useIt:
              tgtVal = float(splitL[col])
              if not feq(tgtVal, 666.0):
                try:
                  val = func(m)
                except Exception:
                  val = 666
                assert feq(val, tgtVal, 1e-4), 'line %d, mol %s (calc = %f) should have val = %f' % (
                  lineNum, smi, val, tgtVal)
  
    def testChi0Long(self):
      """ test calculation of Chi0
       
     """
      col = 2
      self.__testDesc('PP_descrs_regress.csv', col, GraphDescriptors.Chi0)
  
    def _testChi0Long2(self):
      """ test calculation of Chi0
       
     """
      col = 2
      self.__testDesc('PP_descrs_regress.2.csv', col, GraphDescriptors.Chi0)
  
    def testHallKierAlphaLong(self):
      """ test calculation of the Hall-Kier Alpha value
       
     """
      col = 3
      self.__testDesc('PP_descrs_regress.csv', col, GraphDescriptors.HallKierAlpha)
  
    def _testHallKierAlphaLong2(self):
      """ test calculation of the Hall-Kier Alpha value
       
     """
      col = 3
      self.__testDesc('PP_descrs_regress.2.csv', col, GraphDescriptors.HallKierAlpha)
  
    def testIpc(self):
      """ test calculation of Ipc.
  
     """
      data = [('CCCCC', 1.40564, 11.24511), ('CCC(C)C', 1.37878, 9.65148),
              ('CC(C)(C)C', 0.72193, 3.60964), ('CN(CC)CCC', 1.67982, 31.91664),
              ('C1CCCCC1', 1.71997, 34.39946), ('CC1CCCCC1', 1.68562, 47.19725),
              ('Cc1ccccc1', 1.68562, 47.19725), ('CC(C)=C(C)C', 1.36096, 13.60964),
              ('C#N', 1.00000, 2.00000), ('OC#N', 0.91830, 2.75489)]
      for smi, res1, res2 in data:
        m = Chem.MolFromSmiles(smi)
        Ipc = GraphDescriptors.Ipc(m, forceDMat=1)
        Ipc_avg = GraphDescriptors.Ipc(m, avg=1, forceDMat=1)
        assert feq(Ipc_avg, res1, 1e-3), 'mol %s (Ipc_avg=%f) should have Ipc_avg=%f' % (smi, Ipc_avg,
                                                                                         res1)
        assert feq(Ipc, res2, 1e-3), 'mol %s (Ipc=%f) should have Ipc=%f' % (smi, Ipc, res2)
        Ipc = GraphDescriptors.Ipc(m)
        Ipc_avg = GraphDescriptors.Ipc(m, avg=1)
        assert feq(Ipc_avg, res1, 1e-3), '2nd pass: mol %s (Ipc_avg=%f) should have Ipc_avg=%f' % (
          smi, Ipc_avg, res1)
        assert feq(Ipc, res2, 1e-3), '2nd pass: mol %s (Ipc=%f) should have Ipc=%f' % (smi, Ipc, res2)
  
    def _testIpcLong(self):
      """ test calculation of Ipc
       
     """
      col = 4
      self.__testDesc('PP_descrs_regress.csv', col, GraphDescriptors.Ipc)
  
    def _testIpcLong2(self):
      """ test calculation of Ipc
       
     """
      col = 4
      self.__testDesc('PP_descrs_regress.2.csv', col, GraphDescriptors.Ipc)
  
    def testKappa1(self):
      """ test calculation of the Hall-Kier kappa1 value
  
       corrected data from Tables 3 and 6 of Rev. Comp. Chem. vol 2, 367-422, (1991)
  
      """
      data = [('C12CC2C3CC13', 2.344), ('C1CCC12CC2', 3.061), ('C1CCCCC1', 4.167), ('CCCCCC', 6.000),
              ('CCC(C)C1CCC(C)CC1', 9.091), ('CC(C)CC1CCC(C)CC1', 9.091),
              ('CC(C)C1CCC(C)CCC1', 9.091)]
      for smi, res in data:
        m = Chem.MolFromSmiles(smi)
        kappa = GraphDescriptors.Kappa1(m)
        assert feq(kappa, res, 1e-3), 'mol %s (kappa1=%f) should have kappa1=%f' % (smi, kappa, res)
  
    def testKappa2(self):
      """ test calculation of the Hall-Kier kappa2 value
  
       corrected data from Tables 5 and 6 of Rev. Comp. Chem. vol 2, 367-422, (1991)
  
      """
      data = [
        ('[C+2](C)(C)(C)(C)(C)C', 0.667), ('[C+](C)(C)(C)(C)(CC)', 1.240),
        ('C(C)(C)(C)(CCC)', 2.3444), ('CC(C)CCCC', 4.167), ('CCCCCCC', 6.000), ('CCCCCC', 5.000),
        ('CCCCCCC', 6.000), ('C1CCCC1', 1.440), ('C1CCCC1C', 1.633), ('C1CCCCC1', 2.222),
        ('C1CCCCCC1', 3.061), ('CCCCC', 4.00), ('CC=CCCC', 4.740), ('C1=CN=CN1', 0.884),
        ('c1ccccc1', 1.606), ('c1cnccc1', 1.552), ('n1ccncc1', 1.500), ('CCCCF', 3.930),
        ('CCCCCl', 4.290), ('CCCCBr', 4.480), ('CCC(C)C1CCC(C)CC1', 4.133),
        ('CC(C)CC1CCC(C)CC1', 4.133), ('CC(C)C1CCC(C)CCC1', 4.133)
      ]
      for smi, res in data:
        #print smi
        m = Chem.MolFromSmiles(smi)
        kappa = GraphDescriptors.Kappa2(m)
        assert feq(kappa, res, 1e-3), 'mol %s (kappa2=%f) should have kappa2=%f' % (smi, kappa, res)
  
    def testKappa3(self):
      """ test calculation of the Hall-Kier kappa3 value
  
       corrected data from Tables 3 and 6 of Rev. Comp. Chem. vol 2, 367-422, (1991)
  
      """
      data = [
        ('C[C+](C)(C)(C)C(C)(C)C', 2.000), ('CCC(C)C(C)(C)(CC)', 2.380), ('CCC(C)CC(C)CC', 4.500),
        ('CC(C)CCC(C)CC', 5.878), ('CC(C)CCCC(C)C', 8.000), ('CCC(C)C1CCC(C)CC1', 2.500),
        ('CC(C)CC1CCC(C)CC1', 3.265), ('CC(C)C1CCC(C)CCC1', 2.844)
      ]
      for smi, res in data:
        m = Chem.MolFromSmiles(smi)
        kappa = GraphDescriptors.Kappa3(m)
        assert feq(kappa, res, 1e-3), 'mol %s (kappa3=%f) should have kappa3=%f' % (smi, kappa, res)
  
    def testKappa3Long(self):
      """ test calculation of kappa3
       
     """
      col = 5
      self.__testDesc('PP_descrs_regress.csv', col, GraphDescriptors.Kappa3)
  
    def _testKappa3Long2(self):
      """ test calculation of kappa3
       
     """
      col = 5
      self.__testDesc('PP_descrs_regress.2.csv', col, GraphDescriptors.Kappa3)
  
    def _testLabuteASALong(self):
      """ test calculation of Labute's ASA value
       
     """
      col = 6
      self.__testDesc('PP_descrs_regress.csv', col, lambda x: MolSurf.LabuteASA(x, includeHs=1))
  
    def _testLabuteASALong2(self):
      """ test calculation of Labute's ASA value
       
     """
      col = 6
      self.__testDesc('PP_descrs_regress.2.csv', col, lambda x: MolSurf.LabuteASA(x, includeHs=1))
  
    def _testTPSAShortNCI(self):
      " Short TPSA test "
      inName = RDConfig.RDDataDir + '/NCI/first_200.tpsa.csv'
      with open(inName, 'r') as inF:
        lines = inF.readlines()
      for line in lines:
        if line[0] != '#':
          line.strip()
          smi, ans = line.split(',')
          ans = float(ans)
  
          mol = Chem.MolFromSmiles(smi)
  
          calc = MolSurf.TPSA(mol)
          assert feq(calc, ans), 'bad TPSA for SMILES %s (%.2f != %.2f)' % (smi, calc, ans)
  
    def _testTPSALongNCI(self):
      " Long TPSA test "
      fileN = 'tpsa_regr.csv'
      with open(os.path.join(RDConfig.RDCodeDir, 'Chem', 'test_data', fileN), 'r') as inF:
        lines = inF.readlines()
      lineNo = 0
      for line in lines:
        lineNo += 1
        if line[0] != '#':
          line.strip()
          smi, ans = line.split(',')
          ans = float(ans)
  
          mol = Chem.MolFromSmiles(smi)
          assert mol, "line %d, failed for smiles: %s" % (lineNo, smi)
  
          calc = MolSurf.TPSA(mol)
          assert feq(calc, ans), 'line %d: bad TPSA for SMILES %s (%.2f != %.2f)' % (lineNo, smi,
                                                                                     calc, ans)
  
    def testTPSALong(self):
      """ test calculation of TPSA
       
     """
      col = 28
      self.__testDesc('PP_descrs_regress.csv', col, MolSurf.TPSA)
  
    def _testTPSALong2(self):
      """ test calculation of TPSA
       
     """
      col = 28
      self.__testDesc('PP_descrs_regress.2.csv', col, MolSurf.TPSA)
  
    def _testLipinskiLong(self):
      """ test calculation of Lipinski params
       
     """
      fName = 'PP_descrs_regress.csv'
      # we can't do H Acceptors for these pyridine-containing molecules
      #  because the values will be wrong for EVERY one.
      #col = 29
      #self.__testDesc(fName,col,Lipinski.NumHAcceptors)
  
      col = 30
      self.__testDesc(fName, col, Lipinski.NumHDonors)
  
      col = 31
      self.__testDesc(fName, col, Lipinski.NumHeteroatoms)
  
      col = 32
      self.__testDesc(fName, col, Lipinski.NumRotatableBonds)
  
    def _testHAcceptorsLong(self):
      """ test calculation of Lipinski params
       
     """
      fName = 'Block_regress.Lip.csv'
      col = 1
      self.__testDesc(fName, col, Lipinski.NumHAcceptors)
  
    def _testHDonorsLong(self):
      """ test calculation of Lipinski params
       
     """
      fName = 'Block_regress.Lip.csv'
      col = 2
      self.__testDesc(fName, col, Lipinski.NumHDonors)
  
    def _testHeterosLong(self):
      """ test calculation of Lipinski params
       
     """
      fName = 'Block_regress.Lip.csv'
      col = 3
      self.__testDesc(fName, col, Lipinski.NumHeteroatoms)
  
    def _testRotBondsLong(self):
      """ test calculation of Lipinski params
       
     """
      fName = 'Block_regress.Lip.csv'
      col = 4
      self.__testDesc(fName, col, Lipinski.NumRotatableBonds)
  
    def _testLogPLong(self):
      """ test calculation of Lipinski params
       
     """
      fName = 'PP_descrs_regress.csv'
      col = 33
      self.__testDesc(fName, col, lambda x: Crippen.MolLogP(x, includeHs=1))
  
    def _testLogPLong2(self):
      """ test calculation of Lipinski params
       
     """
      fName = 'PP_descrs_regress.2.csv'
      col = 33
      self.__testDesc(fName, col, lambda x: Crippen.MolLogP(x, includeHs=1))
  
    def _testMOELong(self):
      """ test calculation of MOE-type descriptors
       
     """
      fName = 'PP_descrs_regress.VSA.csv'
      col = 1
      self.__testDesc(fName, col, MolSurf.SMR_VSA1)
      col = 2
      self.__testDesc(fName, col, MolSurf.SMR_VSA10)
      col = 3
      self.__testDesc(fName, col, MolSurf.SMR_VSA2)
      col = 4
      self.__testDesc(fName, col, MolSurf.SMR_VSA3)
      col = 5
      self.__testDesc(fName, col, MolSurf.SMR_VSA4)
      col = 6
      self.__testDesc(fName, col, MolSurf.SMR_VSA5)
      col = 7
      self.__testDesc(fName, col, MolSurf.SMR_VSA6)
      col = 8
      self.__testDesc(fName, col, MolSurf.SMR_VSA7)
      col = 9
      self.__testDesc(fName, col, MolSurf.SMR_VSA8)
      col = 10
      self.__testDesc(fName, col, MolSurf.SMR_VSA9)
      col = 11
      self.__testDesc(fName, col, MolSurf.SlogP_VSA1)
      col = 12
      self.__testDesc(fName, col, MolSurf.SlogP_VSA10)
      col = 13
      self.__testDesc(fName, col, MolSurf.SlogP_VSA11)
      col = 14
      self.__testDesc(fName, col, MolSurf.SlogP_VSA12)
  
    def _testMOELong2(self):
      """ test calculation of MOE-type descriptors
       
     """
      fName = 'PP_descrs_regress.VSA.2.csv'
      col = 1
      self.__testDesc(fName, col, MolSurf.SMR_VSA1)
      col = 2
      self.__testDesc(fName, col, MolSurf.SMR_VSA10)
      col = 11
      self.__testDesc(fName, col, MolSurf.SlogP_VSA1)
      col = 12
      self.__testDesc(fName, col, MolSurf.SlogP_VSA10)
      col = 13
      self.__testDesc(fName, col, MolSurf.SlogP_VSA11)
      col = 14
      self.__testDesc(fName, col, MolSurf.SlogP_VSA12)
  
    def testBalabanJ(self):
      """ test calculation of the Balaban J value 
  
        J values are from Balaban's paper and have had roundoff
        errors and typos corrected.
      """
      data = [  # alkanes
        ('CC', 1.0),
        ('CCC', 1.6330),
        ('CCCC', 1.9747),
        ('CC(C)C', 2.3238),
        ('CCCCC', 2.1906),
        ('CC(C)CC', 2.5396),
        ('CC(C)(C)C', 3.0237),
        ('CCCCCC', 2.3391),
        ('CC(C)CCC', 2.6272),
        ('CCC(C)CC', 2.7542),
        ('CC(C)(C)CC', 3.1685),
        ('CC(C)C(C)C', 2.9935),
  
        # cycloalkanes
        ('C1CCCCC1', 2.0000),
        ('C1C(C)CCCC1', 2.1229),
        ('C1C(CC)CCCC1', 2.1250),
        ('C1C(C)C(C)CCC1', 2.2794),
        ('C1C(C)CC(C)CC1', 2.2307),
        ('C1C(C)CCC(C)C1', 2.1924),
        ('C1C(CCC)CCCC1', 2.0779),
        ('C1C(C(C)C)CCCC1', 2.2284),
        ('C1C(CC)C(C)CCC1', 2.2973),
        ('C1C(CC)CC(C)CC1', 2.2317),
        ('C1C(CC)CCC(C)C1', 2.1804),
        ('C1C(C)C(C)C(C)CC1', 2.4133),
        ('C1C(C)C(C)CC(C)C1', 2.3462),
        ('C1C(C)CC(C)CC1(C)', 2.3409),
        # aromatics
        ('c1ccccc1', 3.0000),
        ('c1c(C)cccc1', 3.0215),
        ('c1c(CC)cccc1', 2.8321),
        ('c1c(C)c(C)ccc1', 3.1349),
        ('c1c(C)cc(C)cc1', 3.0777),
        ('c1c(C)ccc(C)c1', 3.0325),
        ('c1c(CCC)cccc1', 2.6149),
        ('c1c(C(C)C)cccc1', 2.8483),
        ('c1c(CC)c(C)ccc1', 3.0065),
        ('c1c(CC)cc(C)cc1', 2.9369),
        ('c1c(CC)ccc(C)c1', 2.8816),
        ('c1c(C)c(C)c(C)cc1', 3.2478),
        ('c1c(C)c(C)cc(C)c1', 3.1717),
        ('c1c(C)cc(C)cc1(C)', 3.1657)
      ]
      for smi, res in data:
        m = Chem.MolFromSmiles(smi)
        j = GraphDescriptors.BalabanJ(m, forceDMat=1)
        assert feq(j, res), 'mol %s (J=%f) should have J=%f' % (smi, j, res)
        j = GraphDescriptors.BalabanJ(m)
        assert feq(j, res), 'second pass: mol %s (J=%f) should have J=%f' % (smi, j, res)
  
    def _testBalabanJLong(self):
      """ test calculation of the balaban j value
       
     """
      fName = 'PP_descrs_regress.rest.2.csv'
      col = 1
      self.__testDesc(fName, col, GraphDescriptors.BalabanJ)
  
    def _testKappa1Long(self):
      """ test calculation of kappa1
       
     """
      fName = 'PP_descrs_regress.rest.2.csv'
      col = 31
      self.__testDesc(fName, col, GraphDescriptors.Kappa1)
  
    def _testKappa2Long(self):
      """ test calculation of kappa2
       
     """
      fName = 'PP_descrs_regress.rest.2.csv'
      col = 32
      self.__testDesc(fName, col, GraphDescriptors.Kappa2)
  
    def _testChi0Long(self):
      fName = 'PP_descrs_regress.rest.2.csv'
      col = 5
      self.__testDesc(fName, col, GraphDescriptors.Chi0)
  
    def _testChi1Long(self):
      fName = 'PP_descrs_regress.rest.2.csv'
      col = 8
      self.__testDesc(fName, col, GraphDescriptors.Chi1)
  
    def _testChi0v(self):
      """ test calculation of Chi0v
  
      """
      data = [('CCCCCC', 4.828), ('CCC(C)CC', 4.992), ('CC(C)CCC', 4.992), ('CC(C)C(C)C', 5.155),
              ('CC(C)(C)CC', 5.207), ('CCCCCO', 4.276), ('CCC(O)CC', 4.439), ('CC(O)(C)CC', 4.654),
              ('c1ccccc1O', 3.834), ('CCCl', 2.841), ('CCBr', 3.671), ('CCI', 4.242)]
      for smi, res in data:
        m = Chem.MolFromSmiles(smi)
        chi = GraphDescriptors.Chi0v(m)
        assert feq(chi, res, 1e-3), 'mol %s (Chi0v=%f) should have Chi0V=%f' % (smi, chi, res)
  
    def _testChi0vLong(self):
      fName = 'PP_descrs_regress.rest.2.csv'
      col = 7
      self.__testDesc(fName, col, GraphDescriptors.Chi0v)
  
    def testChi1v(self):
      """ test calculation of Chi1v
  
      """
      data = [('CCCCCC', 2.914), ('CCC(C)CC', 2.808), ('CC(C)CCC', 2.770), ('CC(C)C(C)C', 2.643),
              ('CC(C)(C)CC', 2.561), ('CCCCCO', 2.523), ('CCC(O)CC', 2.489), ('CC(O)(C)CC', 2.284),
              ('c1ccccc1O', 2.134)]
      for smi, res in data:
        m = Chem.MolFromSmiles(smi)
        chi = GraphDescriptors.Chi1v(m)
        assert feq(chi, res, 1e-3), 'mol %s (Chi1v=%f) should have Chi1V=%f' % (smi, chi, res)
  
    def _testChi1vLong(self):
      fName = 'PP_descrs_regress.rest.2.csv'
      col = 10
      self.__testDesc(fName, col, GraphDescriptors.Chi1v)
  
    def testPathCounts(self):
      """ FIX: this should be in some other file
  
      """
      data = [('CCCCCC', (6, 5, 4, 3, 2, 1)),
              ('CCC(C)CC', (6, 5, 5, 4, 1, 0)),
              ('CC(C)CCC', (6, 5, 5, 3, 2, 0)),
              ('CC(C)C(C)C', (6, 5, 6, 4, 0, 0)),
              ('CC(C)(C)CC', (6, 5, 7, 3, 0, 0)),
              ('CCCCCO', (6, 5, 4, 3, 2, 1)),
              ('CCC(O)CC', (6, 5, 5, 4, 1, 0)),
              ('CC(O)(C)CC', (6, 5, 7, 3, 0, 0)),
              ('c1ccccc1O', (7, 7, 8, 8, 8, 8)), ]
      for smi, res in data:
        m = Chem.MolFromSmiles(smi)
        for i in range(1, 6):
          cnt = len(Chem.FindAllPathsOfLengthN(m, i, useBonds=1))
          assert cnt == res[i], (smi, i, cnt, res[i], Chem.FindAllPathsOfLengthN(m, i, useBonds=1))
          cnt = len(Chem.FindAllPathsOfLengthN(m, i + 1, useBonds=0))
          assert cnt == res[i], (smi, i, cnt, res[i], Chem.FindAllPathsOfLengthN(m, i + 1,
                                                                                 useBonds=1))
  
    def testChi2v(self):
      """ test calculation of Chi2v
  
      """
      data = [('CCCCCC', 1.707),
              ('CCC(C)CC', 1.922),
              ('CC(C)CCC', 2.183),
              ('CC(C)C(C)C', 2.488),
              ('CC(C)(C)CC', 2.914),
              ('CCCCCO', 1.431),
              ('CCC(O)CC', 1.470),
              ('CC(O)(C)CC', 2.166),
              ('c1ccccc1O', 1.336), ]
      for smi, res in data:
        m = Chem.MolFromSmiles(smi)
        chi = GraphDescriptors.Chi2v(m)
        assert feq(chi, res, 1e-3), 'mol %s (Chi2v=%f) should have Chi2V=%f' % (smi, chi, res)
  
    def _testChi2vLong(self):
      fName = 'PP_descrs_regress.rest.2.csv'
      col = 12
      self.__testDesc(fName, col, GraphDescriptors.Chi2v)
  
    def testChi3v(self):
      """ test calculation of Chi3v
  
      """
      data = [('CCCCCC', 0.957), ('CCC(C)CC', 1.394), ('CC(C)CCC', 0.866), ('CC(C)C(C)C', 1.333),
              ('CC(C)(C)CC', 1.061), ('CCCCCO', 0.762), ('CCC(O)CC', 0.943), ('CC(O)(C)CC', 0.865),
              ('c1ccccc1O', 0.756)]
      for smi, res in data:
        m = Chem.MolFromSmiles(smi)
        chi = GraphDescriptors.Chi3v(m)
        assert feq(chi, res, 1e-3), 'mol %s (Chi3v=%f) should have Chi3V=%f' % (smi, chi, res)
  
    def _testChi3vLong(self):
      fName = 'PP_descrs_regress.rest.2.csv'
      col = 14
      self.__testDesc(fName, col, GraphDescriptors.Chi3v)
  
    def testChi4v(self):
      """ test calculation of Chi4v
  
      """
      data = [('CCCCCC', 0.500), ('CCC(C)CC', 0.289), ('CC(C)CCC', 0.577), ('CC(C)C(C)C', 0.000),
              ('CC(C)(C)CC', 0.000), ('CCCCCO', 0.362), ('CCC(O)CC', 0.289), ('CC(O)(C)CC', 0.000),
              ('c1ccccc1O', 0.428)]
      for smi, res in data:
        m = Chem.MolFromSmiles(smi)
        chi = GraphDescriptors.Chi4v(m)
        assert feq(chi, res, 1e-3), 'mol %s (Chi4v=%f) should have Chi4V=%f' % (smi, chi, res)
  
    def testChi5v(self):
      """ test calculation of Chi5v
  
      """
      data = [('CCCCCC', 0.250), ('CCC(C)CC', 0.000), ('CC(C)CCC', 0.000), ('CC(C)C(C)C', 0.000),
              ('CC(C)(C)CC', 0.000), ('CCCCCO', 0.112), ('CCC(O)CC', 0.000), ('CC(O)(C)CC', 0.000),
              ('c1ccccc1O', 0.242)]
      for smi, res in data:
        m = Chem.MolFromSmiles(smi)
        chi = GraphDescriptors.ChiNv_(m, 5)
        assert feq(chi, res, 1e-3), 'mol %s (Chi5v=%f) should have Chi5V=%f' % (smi, chi, res)
  
    def testChi0n(self):
      """ test calculation of Chi0n
  
      """
      data = [('CCCCCC', 4.828),
              ('CCC(C)CC', 4.992),
              ('CC(C)CCC', 4.992),
              ('CC(C)C(C)C', 5.155),
              ('CC(C)(C)CC', 5.207),
              ('CCCCCO', 4.276),
              ('CCC(O)CC', 4.439),
              ('CC(O)(C)CC', 4.654),
              ('c1ccccc1O', 3.834),
              ('CCCl', 2.085),
              ('CCBr', 2.085),
              ('CCI', 2.085), ]
      for smi, res in data:
        m = Chem.MolFromSmiles(smi)
        chi = GraphDescriptors.Chi0n(m)
        assert feq(chi, res, 1e-3), 'mol %s (Chi0n=%f) should have Chi0n=%f' % (smi, chi, res)
  
    def _testChi0nLong(self):
      fName = 'PP_descrs_regress.rest.2.csv'
      col = 6
      self.__testDesc(fName, col, GraphDescriptors.Chi0n)
  
    def testChi1n(self):
      """ test calculation of Chi1n
  
      """
      data = [('CCCCCC', 2.914), ('CCC(C)CC', 2.808), ('CC(C)CCC', 2.770), ('CC(C)C(C)C', 2.643),
              ('CC(C)(C)CC', 2.561), ('CCCCCO', 2.523), ('CCC(O)CC', 2.489), ('CC(O)(C)CC', 2.284),
              ('c1ccccc1O', 2.134)]
      for smi, res in data:
        m = Chem.MolFromSmiles(smi)
        chi = GraphDescriptors.Chi1n(m)
        assert feq(chi, res, 1e-3), 'mol %s (Chi1n=%f) should have Chi1N=%f' % (smi, chi, res)
  
    def _testChi1nLong(self):
      fName = 'PP_descrs_regress.rest.2.csv'
      col = 9
      self.__testDesc(fName, col, GraphDescriptors.Chi1n)
  
    def testChi2n(self):
      """ test calculation of Chi2n
  
      """
      data = [('CCCCCC', 1.707), ('CCC(C)CC', 1.922), ('CC(C)CCC', 2.183), ('CC(C)C(C)C', 2.488),
              ('CC(C)(C)CC', 2.914), ('CCCCCO', 1.431), ('CCC(O)CC', 1.470), ('CC(O)(C)CC', 2.166),
              ('c1ccccc1O', 1.336)]
      for smi, res in data:
        m = Chem.MolFromSmiles(smi)
        chi = GraphDescriptors.Chi2n(m)
        assert feq(chi, res, 1e-3), 'mol %s (Chi2n=%f) should have Chi2N=%f' % (smi, chi, res)
  
    def _testChi2nLong(self):
      fName = 'PP_descrs_regress.rest.2.csv'
      col = 11
      self.__testDesc(fName, col, GraphDescriptors.Chi2n)
  
    def testChi3n(self):
      """ test calculation of Chi3n
  
      """
      data = [('CCCCCC', 0.957), ('CCC(C)CC', 1.394), ('CC(C)CCC', 0.866), ('CC(C)C(C)C', 1.333),
              ('CC(C)(C)CC', 1.061), ('CCCCCO', 0.762), ('CCC(O)CC', 0.943), ('CC(O)(C)CC', 0.865),
              ('c1ccccc1O', 0.756)]
      for smi, res in data:
        m = Chem.MolFromSmiles(smi)
        chi = GraphDescriptors.Chi3n(m)
        assert feq(chi, res, 1e-3), 'mol %s (Chi3n=%f) should have Chi3N=%f' % (smi, chi, res)
  
    def _testChi3nLong(self):
      fName = 'PP_descrs_regress.rest.2.csv'
      col = 13
      self.__testDesc(fName, col, GraphDescriptors.Chi3n)
  
    def testChi4n(self):
      """ test calculation of Chi4n
  
      """
      data = [('CCCCCC', 0.500), ('CCC(C)CC', 0.289), ('CC(C)CCC', 0.577), ('CC(C)C(C)C', 0.000),
              ('CC(C)(C)CC', 0.000), ('CCCCCO', 0.362), ('CCC(O)CC', 0.289), ('CC(O)(C)CC', 0.000),
              ('c1ccccc1O', 0.428)]
      for smi, res in data:
        m = Chem.MolFromSmiles(smi)
        chi = GraphDescriptors.Chi4n(m)
        assert feq(chi, res, 1e-3), 'mol %s (Chi4n=%f) should have Chi4N=%f' % (smi, chi, res)
  
    def testIssue125(self):
      """ test an issue with calculating BalabanJ
      """
      smi = 'O=C(OC)C1=C(C)NC(C)=C(C(OC)=O)C1C2=CC=CC=C2[N+]([O-])=O'
      m1 = Chem.MolFromSmiles(smi)
      m2 = Chem.MolFromSmiles(smi)
      Chem.MolToSmiles(m1)
      j1 = GraphDescriptors.BalabanJ(m1)
      j2 = GraphDescriptors.BalabanJ(m2)
      assert feq(j1, j2)
  
    def testOrderDepend(self):
      """ test order dependence of some descriptors:
      """
      data = [('C=CC=C', 21.01955, 2.73205), ('O=CC=O', 25.01955, 2.73205),
              ('FCC(=O)CF', 46.7548875, 2.98816), ('O=C1C=CC(=O)C=C1', 148.705216, 2.8265),
              ('C12C(F)=C(O)C(F)C1C(F)=C(O)C(F)2', 315.250442, 2.4509),
              ('C12CC=CCC1C(=O)C3CC=CCC3C(=O)2', 321.539522, 1.95986)]
  
      for smi, CT, bal in data:
        m = Chem.MolFromSmiles(smi)
        newBal = GraphDescriptors.BalabanJ(m, forceDMat=1)
        assert feq(newBal, bal, 1e-4), 'mol %s %f!=%f' % (smi, newBal, bal)
        m = Chem.MolFromSmiles(smi)
        newCT = GraphDescriptors.BertzCT(m, forceDMat=1)
        assert feq(newCT, CT, 1e-4), 'mol %s (CT calc = %f) should have CT = %f' % (smi, newCT, CT)
        m = Chem.MolFromSmiles(smi)
        newCT = GraphDescriptors.BertzCT(m, forceDMat=1)
        assert feq(newCT, CT, 1e-4), 'mol %s (CT calc = %f) should have CT = %f' % (smi, newCT, CT)
        newBal = GraphDescriptors.BalabanJ(m, forceDMat=1)
        assert feq(newBal, bal, 1e-4), 'mol %s %f!=%f' % (smi, newBal, bal)
  
        m = Chem.MolFromSmiles(smi)
        newBal = GraphDescriptors.BalabanJ(m, forceDMat=1)
        assert feq(newBal, bal, 1e-4), 'mol %s %f!=%f' % (smi, newBal, bal)
        newCT = GraphDescriptors.BertzCT(m, forceDMat=1)
        assert feq(newCT, CT, 1e-4), 'mol %s (CT calc = %f) should have CT = %f' % (smi, newCT, CT)
  
  
  if __name__ == '__main__':
    import sys, getopt, re
    doLong = 0
    if len(sys.argv) > 1:
      args, extras = getopt.getopt(sys.argv[1:], 'l')
      for arg, val in args:
        if arg == '-l':
          doLong = 1
        sys.argv.remove('-l')
    if doLong:
      for methName in dir(TestCase):
        if re.match('_test', methName):
          newName = re.sub('_test', 'test', methName)
          exec('TestCase.%s = TestCase.%s' % (newName, methName))
  
    unittest.main()
  