'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' ACD CHEMBASIC DEMO PROGRAM                                          '
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'                                                                     '
' Carbohydrate Builder//SUGARSK.BAS                                   '
'                                                                     '
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'                                                                     '
' The program accepts carbohydrate short-cut name                     '
' and makes the structure from 3D-blocks (Glc,Fuc, etc.)              '
' On user's request, 3D-optimization is performed.                    '
'                                                                     '
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

CONST TITLE="Carbohydrate Builder"
CONST PI=3.14159265359
CONST PI2=1.570796326795
CONST RAD_TO_DEG = 57.29577951308
CONST DB_RAD_ERR = "Error reading DataBase: conformation file for "
CONST USER_ASK_EXIT = "QUIT"
'CONST PROMPT_FOR_EXIT = " (or 'QUIT' to terminate macro)"
CONST ERR_MESS = "ShortHand is not valid"
CONST DB_FORMAT = 1
CONST GRAD_LIMIT = 0.1
CONST DEFAULT_LINK = 10                 ' To be linked as default (usualy to anomeric center)
CONST TO_ANOMERIC_CENTER = 0            ' substituent should be linked to anomeric center
CONST ALPHA = 1                         ' anomeric configurations
CONST BETA = 2
CONST L_MARGIN = 200
CONST T_MARGIN = 200
CONST B_MARGIN = 2000
CONST H_STRING = 60
CONST STR_SHFT = 100
'DataBase atom markers
CONST DB_GLYC_C = "C"                   'Then carbon number
CONST DB_RAD_L = "L"                    'Then radical linkage type
CONST DB_ANOMERIC_CENTER = "A"          'In 3rd position (after "C" and carbon number)
'Radical linkage types
CONST LNK_TO_O = 0                      'Default
CONST LNK_TO_C_NOT_O = 1
CONST LNK_TO_C_NOT_HA = 2
'Global variables
Dim NUnitsDB As Integer                 'Number of monosaccharides in DataBase
Dim NRadsDB As Integer                  'Number of radicals in DataBase
Dim StdLinkDihedral As Double           'Initial value of dihedral angles of glycosidic
                                        'linkage (Phi and Psi)
Dim StdLinkAngle As Double              'Standard (initial) value of C-O-C angle of glicosidic
                                        'linkage
Dim UnitDBSHand() As String             'ShortHands of monosaccharides in DataBase
Dim RadDBSHand() As String              'ShortHands of radicals in DataBase
Dim DefaultUnitLink() As Integer        'To which carbon substituent should be linked if
                                        'not defined (default is TO_ANOMERIC_CENTER)
Dim UnitDBFile() As String              'File names of structures
Dim RadDBFile() As String               'File names of structures

Dim ResultsPage As Object               'Current page for results output
Dim CurPosOnPage As Integer             'Area under this position is already used

' *0* GENERAL MANAGEMENT

'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function Main As String
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' SUGARSK.BAS                                                         '
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Dim PHI, PSI, Opt, ShortHand, SHand, SeqString(),UnitString(), RadString() As String
Dim NUnits, UnitRadLink(), Linkage(), UnitIsomer() As Integer
Dim UnitStruct(), DimerStruct(), OligomerStruct As Object, OK As Boolean
Dim Form As Object

  ' House-keeping
  CurPosOnPage = T_MARGIN
  ResultsPage = ActiveDocument.AddEmpty
  Call NewTB("Expanding carbohydrate short-hand:", 500)

  ' Initialize carbohydrate data base
  Call CarbohydrateDataSetup

  ' Get and parse the short-hand
  ShortHand = "Glc(a1-2b)Gal/4Me1N"
  PHI = "60"
  PSI = "60"
  Opt = ""
  OK = FALSE

  Do
    Form = ReadForm("Sugarsk.frm")
    While Not OK
      ' Display form
      Form.SetStrValue("Shorthand", ShortHand)
      Form.SetDblValue("PHI", Val(PHI))
      Form.SetDblValue("PSI", Val(PSI))
      Form.SetStrValue("3DOptimization",Opt)
         If Form.ExecForm Then
            ShortHand = Form.GetStrValue("Shorthand")
            Opt = Form.GetStrValue("3DOptimization")
            PHI = Str(Form.GetDblValue("PHI"))
            PSI = Str(Form.GetDblValue("PSI"))
         Else
            Kill(ActiveDocument.ActivePage)
            Main="Cancelled"
            Exit Function
         End If
         SHand = GetSHand(ShortHand)
         OK = ParseSHand(ShortHand, NUnits, Linkage, UnitIsomer, SeqString, UnitString, RadString)
    WEnd
 
    'Decrypt the short-hand and build units
    OK = BuildUnits(NUnits, UnitString, UnitIsomer, RadString, UnitRadLink, UnitStruct)
  Loop While Not OK

  'Find the conformation
  OK = ConfAnalysis(NUnits, UnitStruct, Linkage, UnitIsomer, SeqString, Opt, PHI, PSI)
  If Not OK Then
    Main = "Failed at creating oligomer conformation"
  Else
    Main = "Completed."
  End If

End Function


'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Sub NewTB(ByVal Cont As String, ByVal Length As Integer)
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'Create a new TextBox on the results page                             '
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Dim TB As Object
  'Create a new page if the current one is full
  If CurPosOnPage + STR_SHFT > B_MARGIN Then
    ResultsPage = ActiveDocument.AddEmpty : CurPosOnPage = T_MARGIN
  End If
  TB = ResultsPage.TextBoxes.AddEmpty
  TB.SetContent(Cont)
  TB.SetBound(L_MARGIN, CurPosOnPage, Length, H_STRING)
  CurPosOnPage = CurPosOnPage + STR_SHFT
End Sub


'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function GetSHand(SHand As String) As String
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'Get the short-hand and write it out to the Results Page              '
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Dim UserResponse As String, ShortHandTextBox As Object

  GetSHand=""
  UserResponse=SHand

  ' Type the string
  Select Case ResultsPage.TextBoxes.Count
    Case 1
            Call NewTB(UserResponse, 2000)
    Case 2
            Call ResultsPage.TextBoxes.Item(2).SetBound(10000, 10000, 0, 0)
            CurPosOnPage = CurPosOnPage - STR_SHFT
            Call NewTB(UserResponse, 2000)
    Case Else
            Stop "INTERNAL ERROR @ Function 'GetSHand'  >> EXITING"
  End Select

End Function 'GetSHand


' *1* PARSE SHORT HAND FORMULA

'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function ParseSHand(ByVal SHand As String, NUnits As Integer, Linkage() As Integer, UnitIsomer() As Integer, SeqString() As String, UnitString() As String, RadString() As String) As Boolean
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Parsing short-hand                                                  '
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Dim i,j,pos,NRadicals,RLink,NBrOpened,UnitNum(255),LinkPos(255),BrOpened(255) As Integer
Dim RadStr As String, OK As Boolean
  ParseSHand = FALSE

  NBrOpened = 0                         ' Number of opened parentheses in current position
  NUnits = 1                            ' Number of current sequence

  'Counting square brackets and number of sequences
  For pos = 1 To Len(SHand)
   UnitNum(pos) = NUnits
   If IsLinkPos(SHand, pos) Then
    LinkPos(NUnits) = pos : NUnits = NUnits + 1
   End If
   BrOpened(pos) = NBrOpened
   If Mid(SHand, pos, 1) = "[" Then NBrOpened = NBrOpened + 1
   If Mid(SHand, pos, 1) = "]" Then NBrOpened = NBrOpened - 1
   If NBrOpened < 0 Then Call ErrOut(ERR_MESS + ": Error in parentheses")
  Next pos
  If NBrOpened <> 0 Then
    Call ErrOut(ERR_MESS + ": Error in parentheses") : Exit Function
  End If
  If NUnits < 2 Then
    ErrOut("At least 2 units should exist") : Exit Function
  End If

  'Create the units linkage matrix
  OK = CreateLinkages(SHand, NUnits, UnitNum, LinkPos, BrOpened, Linkage, UnitIsomer)
  If Not OK Then Exit Function

  'Re-allocate memory
  ReDim UnitString(NUnits)
  ReDim RadString(NUnits, 10)
  ReDim SeqString(NUnits)               'Short-hand of current sequence (unit with radicals)

  ' Loop through monomeric units
  pos = 0
  For i = 1 To NUnits

    SeqString(i) = GetSequence(SHand, NUnits, LinkPos, i)
    ' Extract unit from sequence
    OK = ExtractUnit(SeqString(i), UnitString(i), pos)
    If Not OK Then Exit Function
    If UnitIsomer(i) = 0 Then
      ErrOut("Anomer not defined for " + UnitString(i)) : Exit Function
    End If
    pos = pos + 1
    NRadicals = 0

    ' Initializate RadString with empty strings
    ' (no substituent in this position)
    For j = 1 To 10
      RadString(i, j) = ""
    Next j
    While pos <= Len(SeqString(i))
      'Extract radical from sequence
      NRadicals = NRadicals + 1
      RadStr = ExtractRadical(SeqString(i), pos, RLink)
      RadString(i, RLink) = RadStr
    Wend

  Next i

  ParseSHand = True
End Function 'ParseSHand





'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function GetSequence(ByVal SHand As String, ByVal NUnits As Integer, LinkPos() As Integer, ByVal UnitN As Integer) As String
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'Extract sequence from short-hand                                     '
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Dim b,e As Integer              ' Start and end of sequence in oligomer short-hand
Dim SeqString As String         ' Short-hand of sequence

  If UnitN = 1 Then
    b = 1
  Else
    If Mid(SHand, LinkPos(UnitN - 1) + 2, 1) = ")" Then
      b = LinkPos(UnitN - 1) + 3
    Else
      b = LinkPos(UnitN - 1) + 4
    End If
  End If

  If UnitN = NUnits Then
    e = Len(SHand)
  Else
    e = LinkPos(UnitN) - 2
    If Mid(SHand, LinkPos(UnitN) - 2, 1) = "(" Then
      e = LinkPos(UnitN) - 3
    Else
      e = LinkPos(UnitN) - 4
    End If
  End If

  SeqString = Mid(SHand, b, e - b + 1)
  Call RmPar(SeqString)                 'Remove terminal parentheses

  GetSequence = SeqString
End Function 'GetSequence




'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function ExtractUnit(ByVal SeqString As String, CurUnitString As String, pos As Integer) As Boolean
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Extract an unit short-hand from the sequence short-hand             '
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Dim l As Integer
  ExtractUnit = FALSE
  l=Len(SeqString)
  For pos = 1 To l
    If Mid(SeqString, pos, 1) = "/" Then Exit For 'Backslash  "/" separates unit and substituent
  Next pos
  CurUnitString = Left(SeqString, pos - 1)
  ExtractUnit = TRUE
End Function 'ExtractUnit




'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function ExtractRadical(ByVal SeqString As String, BegPos As Integer, RLink As Integer) As String
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Extract substituents from sequence                                  '
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Dim pos As Integer
  If IsNumber(Mid(SeqString, BegPos, 1)) Then
    RLink = FixValMid(SeqString, BegPos, 1) : BegPos = BegPos + 1
  Else
    RLink = DEFAULT_LINK
  End If
  'Search for the end of current radical ShortHand
  For pos = BegPos + 1 To Len(SeqString)
    If IsNumber(Mid(SeqString, pos, 1)) Then Exit For
  Next pos
  ExtractRadical = Mid(SeqString, BegPos, pos - BegPos)
  BegPos = pos
End Function 'ExtractRadical




'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function IsLinkPos(ByVal SHand As String, ByVal pos As Integer) As Boolean
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'Checks if there a symbol in pos position of SHand Unit Linkage?      '
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Dim Res As Boolean, Char As String
  If Mid(SHand, pos, 1) = "-" Then
    Res = IsNumber(Mid(SHand, pos - 1, 1)) And IsNumber(Mid(SHand, pos + 1, 1))
    Char = Mid(SHand, pos - 2, 1)
    Res = Res And (Char = "(" Or ((Char = "a" Or Char = "b") And (Mid(SHand, pos - 3, 1) = "(")))
    Char = Mid(SHand, pos + 2, 1)
    Res = Res And (Char = ")" Or ((Char = "a" Or Char = "b") And (Mid(SHand, pos + 3, 1) = ")")))
    IsLinkPos = Res
  Else
    IsLinkPos = False
  End If
End Function 'IsLinkPos



'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function SetIsomer(ByVal Char As String, Isomer As Integer) As Boolean
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Set isomer according to char                                        '
' (allowed chars are "a" and "b" (for ALPHA and BETA, resp.))         '
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
  SetIsomer = FALSE
  Select Case Char
    Case "a"
            If Isomer = BETA Then
              Call ErrOut(ERR_MESS + "Different anomer definitions") : Exit Function
            Else
              Isomer = ALPHA
            End If
    Case "b"
            If Isomer = ALPHA Then
              Call ErrOut(ERR_MESS + "Different anomer definitions") : Exit Function
            Else
              Isomer = BETA
            End If
    Case Else
            Call ErrOut(ERR_MESS + "Anomer definitions not valid")   : Exit Function
  End Select
  SetIsomer = TRUE
End Function 'SetIsomer




'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function DimerString(Seq1 As String, Seq2 As String, LinkC1 As Integer, LinkC2 As Integer, Isomer1 As Integer, Isomer2 As Integer) As String
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Returns short-hand of dimer from short-hands                        '
' of sequences or empty string (error)                                '
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Dim res As String

  res = Seq1 + "("

  Select Case Isomer1
    Case ALPHA
              res = res + "a"
    Case BETA
              res = res + "b"
    Case Else
              DimerString = "" : Exit Function
  End Select
  res = res + Right(Str(LinkC1), 1) + "-" + Right(Str(LinkC2), 1)

  Select Case Isomer2
    Case ALPHA
              res = res + "a"
    Case BETA
              res = res + "b"
    Case Else
              DimerString = "" : Exit Function
  End Select

  DimerString = res + ")" + Seq2
End Function 'DimerString



' *2* CREATE AN OLIGOMERIC MOLECULE



'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function CreateLinkages(ByVal SHand As String, ByVal NUnits As Integer, UnitNum() As Integer, LinkPos() As Integer, BrOpened() As Integer, Linkage() As Integer, UnitIsomer() As Integer) As Boolean
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Create the units linkage matrix                                     '
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Dim NBrOpened As Integer                'Number of currently opened parentheses
Dim pos As Integer                      'Current position in short-hand string
Dim Cl1 As String, Cl2 As String        'Number characters of carbons to take part in linkage
Dim i As Integer, j As Integer
Dim LeftEdge As Integer, RightEdge As Integer, Unit1Num As Integer, Unit2Num As Integer
Dim Unit1Fl As Boolean, Unit2Fl As Boolean, OK As Boolean


  ' Prepare
  CreateLinkages = FALSE
  ReDim Linkage(NUnits, NUnits)         'Units linkages matrix
  ReDim UnitIsomer(NUnits)              'Units isomer matrix
  For i = 1 To NUnits
    For j = 1 To NUnits
      Linkage(i, j) = 0                 'Not linked
    Next j
    UnitIsomer(i) = 0                   'Not defined
  Next i

  ' For each glicosidic linkage
  For i = 1 To NUnits - 1

    Unit1Fl = False: Unit2Fl = False

    If Mid(SHand, LinkPos(i) - 2, 1) = "(" Then
      LeftEdge = LinkPos(i) - 3
    Else
      LeftEdge = LinkPos(i) - 4 : Unit1Fl = True
    End If
    If Mid(SHand, LinkPos(i) + 2, 1) = ")" Then
      RightEdge = LinkPos(i) + 3
    Else
      RightEdge = LinkPos(i) + 4 : Unit2Fl = True
    End If

    If Mid(SHand, LeftEdge, 1) = "]" Then
      NBrOpened = BrOpened(LinkPos(i))
      For pos = LeftEdge - 1 To 1 Step -1
        If BrOpened(pos) = NBrOpened Then Exit For
      Next pos
      If pos = 1 Then
        Call ErrOut(ERR_MESS + ": Error in parentheses") : Exit Function
      End If
      Unit1Num = UnitNum(pos)
    Else
      Unit1Num = UnitNum(LinkPos(i))
    End If

    If Mid(SHand, RightEdge, 1) = "[" Then
      NBrOpened = BrOpened(LinkPos(i))
      For pos = RightEdge + 1 To Len(SHand)
        If BrOpened(pos) = NBrOpened Then Exit For
      Next pos
      If pos = Len(SHand) Then
        Call ErrOut(ERR_MESS + ": Error in parentheses") : Exit Function
      End If
      Unit2Num = UnitNum(pos)
    Else
      Unit2Num = UnitNum(LinkPos(i) + 1)
    End If


    'Extract carbon number characters around "-"
    Cl1 = Mid(SHand, LinkPos(i) - 1, 1)
    Cl2 = Mid(SHand, LinkPos(i) + 1, 1)
    ' If they are not numbers
    If Not (IsNumber(Cl1) And IsNumber(Cl2)) Then
      Call ErrOut(ERR_MESS) : Exit Function
    End If

    'Fill in linkages matrix
    Linkage(Unit1Num, Unit2Num) = Fix(Val(Cl1))
    Linkage(Unit2Num, Unit1Num) = Fix(Val(Cl2))

    'Setting isomers
    If Unit1Fl Then
      OK = SetIsomer(Mid(SHand, LinkPos(i) - 2, 1), UnitIsomer(Unit1Num))
      If Not OK Then Exit Function
    End If
    If Unit2Fl Then
      OK = SetIsomer(Mid(SHand, LinkPos(i) + 2, 1), UnitIsomer(Unit2Num))
      If Not OK Then Exit Function
    End If

  Next i

  CreateLinkages = True
End Function 'CreateLinkages






'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function BuildUnits(NUnits As Integer, UnitString() As String, UnitIsomer() As Integer, RadString() As String, UnitRadLink() As Integer, UnitStruct() As Object) As Boolean
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'Expand the short-hand and build units                                '
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Dim UnitDBNum As Integer                'Unit number in DataBase
Dim RadDBNum As Integer                 'DataBase number of radical
Dim i As Integer, j As Integer, OK As Boolean, ErrStr As String, Asm As Object

  BuildUnits = FALSE
  ReDim UnitStruct(NUnits)

  For i = 1 To NUnits
    ' Search for short-hand UnitString in DataBase
    OK = SearchUnitDB(UnitString(i), UnitDBNum)
    If Not OK Then Exit Function
    If UnitDBFile(UnitDBNum, UnitIsomer(i)) = "" Then
      ErrStr = "Molecule file not defined for "
      Select Case UnitIsomer(i)
        Case ALPHA
                    ErrStr = ErrStr + "a-"
        Case BETA
                    ErrStr = ErrStr + "b-"
        Case Else
                    Call ErrOut("INTERNAL ERROR @ Function 'BuildUnits'  >> EXITING")
                    STOP
      End Select
      Call ErrOut(ErrStr + UnitString(i))
      Exit Function
    End If

    Asm=Assemblies.AddFromFile(UnitDBFile(UnitDBNum, UnitIsomer(i)), DB_FORMAT)
    If Asm=Null Then
      Call ErrOut("File " + UnitDBFile(UnitDBNum, UnitIsomer(i)) + " not exist or corrupt")
      Exit Function
    End If
    UnitStruct(i)=Asm.Structures.Item(1)

    ' Expand radicals if present
    For j = 1 To 10
      If RadString(i, j) <> "" Then
        OK = SearchRadicalDB(RadString(i, j), RadDBNum) 'Search for ShortHand RadString in DataBase
        If Not OK Then Exit Function
        OK = AddRadical(UnitStruct(i), j, RadDBNum)
        If Not OK Then Exit Function
      End If
    Next j
  Next i

  BuildUnits = True
End Function 'BuildUnits



'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function AddRadical(UnitStruct As Object, UnitRadLink As Integer, RadDBNum As Integer) As Boolean
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Add radical (RadDBNum) to UnitStruct in UnitRadLink position        '
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Const DB_RAD_L = "L"                    'Linkage atom
Const DB_RAD_R = "R"                    'Atom to be replaced
Dim At, UnitAt, AtomR, repR,RadAsm, RadStruct, UnitCarbon, LinkAtom, ReplaceAtom As Object
Dim LinkType, RadLinkType, CarbonNum As Integer
Dim s, label As String, Err1, Err2, OK As Boolean

  AddRadical = FALSE

  RadAsm = Assemblies.AddFromFile(RadDBFile(RadDBNum), DB_FORMAT)
  If RadAsm=Null Then
    Call ErrOut("File " + RadDBFile(RadDBNum) + " not exist or corrupt")
    Exit Function
  End If
  RadStruct = RadAsm.Structures.Item(1)

  Err1 = True: Err2 = True
  For Each At In RadAsm
    label=At.GetName
    s = Left(label, 1)
    If s = DB_RAD_L Then
      LinkAtom = At : RadLinkType = FixValMid(label, 2, 1) : Err1 = False
    End If
    If s = DB_RAD_R Then
      ReplaceAtom = At : Err2 = False
    End If
  Next At

  If Err1 Or Err2 Then
    Call ErrOut(DB_RAD_ERR + RadDBSHand(RadDBNum) + "is not valid")
    Exit Function
  End If

  If UnitRadLink = DEFAULT_LINK Then
    If DefaultUnitLink(RadDBNum) = TO_ANOMERIC_CENTER Then
      CarbonNum = AnomericCenter(UnitStruct)
    Else
      CarbonNum = DefaultUnitLink(RadDBNum)
    End If
  Else
    CarbonNum = UnitRadLink
  End If

  Call CarbonAtom(UnitStruct, CarbonNum, UnitCarbon)
  If UnitCarbon = Null Then
    Call ErrOut("Cannot append substituent to" + Str(CarbonNum) + " carbon") : Exit Function
  End If

  OK = AppendRadical(UnitStruct, RadStruct, UnitCarbon, LinkAtom, ReplaceAtom, RadLinkType)
  If Not OK Then
    Call ErrOut("Cannot append substituent " + RadDBSHand(RadDBNum)) : Exit Function
  End If

  AddRadical = TRUE
End Function 'AddRadical



'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function AddBranch(ByVal NUnits As Integer, DimerNum As Integer, ByVal Unit1Num As Integer, UnitStruct() As Object, DimerStruct() As Object, OligomerStruct As Object, Linkage() As Integer, UnitAdded() As Boolean, DimerUnits() As Integer, PhiAndPsi() As Object) As Boolean
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Recursively add branch of units to oligomer.                        '
' Search for Phi and Psi atoms                                        '
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Dim i,C1,C2,Unit2Num,Unit1Sw, Unit2Sw  As Integer, OK As Boolean
Dim At,C1Atom, C2Atom, C1Sw, C2Sw, GlycOAtom As Object

  AddBranch = FALSE

  For Unit2Num = 1 To NUnits
    ' If Unit2Num should be connected to Unit1Num and was not added to oligomer
    If Linkage(Unit1Num, Unit2Num) <> 0 And (Not UnitAdded(Unit2Num)) Then

      UnitAdded(Unit2Num) = True
      DimerNum = DimerNum + 1

      C2 = Linkage(Unit2Num, Unit1Num)
      C1 = Linkage(Unit1Num, Unit2Num)
      Call CarbonAtom(UnitStruct(Unit2Num), C2, C2Atom)
      Call CarbonAtom(UnitStruct(Unit1Num), C1, C1Atom)

      Call OhxlAt(UnitStruct(Unit1Num), C1Atom, GlycOAtom)

      'Setting PhiAndPsi atoms

      'Where is Phi and where is Psi?
      If Not IsAssocRingOxygen(UnitStruct(Unit2Num), C2Atom) Then
        Unit1Sw = Unit1Num
        Unit2Sw = Unit2Num
      Else
        Unit1Sw = Unit2Num
        Unit2Sw = Unit1Num
      End If
      DimerUnits(DimerNum, 1) = Unit1Sw
      DimerUnits(DimerNum, 2) = Unit2Sw

      C1 = Linkage(Unit1Sw, Unit2Sw)
      C2 = Linkage(Unit2Sw, Unit1Sw)

      Call CarbonAtom(UnitStruct(Unit1Sw), C1, C1Sw)
      Call CarbonAtom(UnitStruct(Unit2Sw), C2, C2Sw)

      If IsAssocRingOxygen(UnitStruct(Unit1Sw), C1Sw) Then
        Call RingOxygen(UnitStruct(Unit1Sw), At)
      Else
        Call CarbonAtom(UnitStruct(Unit1Sw), C1 - 1, At)
      End If
      Set PhiAndPsi(DimerNum, 1) = At

      Set PhiAndPsi(DimerNum, 2) = C1Sw
      Set PhiAndPsi(DimerNum, 3) = GlycOAtom
      Set PhiAndPsi(DimerNum, 4) = C2Sw

      If IsAssocRingOxygen(UnitStruct(Unit2Sw), C2Sw) Then
        Call RingOxygen(UnitStruct(Unit2Sw), At)
      Else
        Call CarbonAtom(UnitStruct(Unit2Sw), C2 - 1, At)
      End If
      Set PhiAndPsi(DimerNum, 5) = At

      For i = 1 To 5
        If PhiAndPsi(DimerNum, i) = Null Then Exit Function   'If not succeed setting PhiAndPsi
      Next i

      'Setting PhiAndPsi atoms completed
      DimerStruct(DimerNum)=UnitStruct(Unit1Num).Assembly.CopyTree(True).Structures.Item(1)

      OK = AppendThroughGlycoBond(OligomerStruct, UnitStruct(Unit2Num), C1Atom, C2Atom)
      If Not OK Then Exit Function

      OK = AddBranch(NUnits, DimerNum, Unit2Num, UnitStruct, DimerStruct, OligomerStruct, Linkage, UnitAdded, DimerUnits, PhiAndPsi)
      If Not OK Then Exit Function

    End If
  Next Unit2Num

  AddBranch = True
End Function 'AddBranch




' *3* FIND THE BEST (HOPEFULLY) CONFORMATION




'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function ConfAnalysis(NUnits As Integer, UnitStruct() As Object, Linkage() As Integer, UnitIsomer() As Integer, SeqString() As String, Opt As String, PHI As String, PSI As String) As Boolean
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Const DIHEDRAL_GRID_SIZE_DEFAULT = "10"
Dim DihedralGridPoints,Unit1Num,Unit2Num,DimerNum,DimerUnits() As Integer
Dim i,w,h,l,t,InitHeight,InitWidth,InitLeft,InitTop As Integer
Dim DihedralGridSize,EnMatr(),InitPhi,InitPsi,MinPhi,MinPsi As Double
Dim UserResponse,ResString, CR_Symbol As String, UnitAdded(),OK As Boolean
Dim DimerStruct(),OligomerStruct,PhiAndPsi() As Object
Dim Diagrm,OligomerDiagr,ResultsBox,a1, a2, a3, a4 As Object

  ReDim DimerStruct(NUnits - 1)
  ReDim PhiAndPsi(NUnits - 1, 5)
  ReDim UnitAdded(NUnits)
  ReDim DimerUnits(NUnits - 1, 2)       'Contains units dimer were made from
  For i = 1 To NUnits
    UnitAdded(i) = False
  Next i

  ConfAnalysis = FALSE
  DimerNum = 0
  OligomerStruct = UnitStruct(1).Assembly.CopyTree(True).Structures.Item(1)

  UnitAdded(1) = TRUE
  OK = AddBranch(NUnits, DimerNum, 1, UnitStruct, DimerStruct, OligomerStruct, Linkage, UnitAdded, DimerUnits, PhiAndPsi)
  If Not OK Then
    Call ErrOut(ERR_MESS + " Cannot build the oligomer molecule.")
    Exit Function
  End If

  If DimerNum <> NUnits - 1 Then        'just check
    Call ErrOut("INTERNAL ERROR : number of dimers does not equal to (number of units - 1)")
    STOP
  End If

  ' Build glycobonds conformation
  For DimerNum = 1 To NUnits - 1
    ' For each dimer
    Unit1Num = DimerUnits(DimerNum, 1) : Unit2Num = DimerUnits(DimerNum, 2)
    ResString = DimerString(SeqString(Unit1Num), SeqString(Unit2Num), Linkage(Unit1Num, Unit2Num), Linkage(Unit2Num, Unit1Num), UnitIsomer(Unit1Num), UnitIsomer(Unit2Num))
    UserResponse = PHI
    InitPhi = Val(UserResponse)/RAD_TO_DEG
    UserResponse = PSI
    InitPsi = Val(UserResponse)/RAD_TO_DEG
    OligomerStruct.SetTAngle(PhiAndPsi(DimerNum, 1), PhiAndPsi(DimerNum, 2), PhiAndPsi(DimerNum, 3), PhiAndPsi(DimerNum, 4), InitPhi)
    OligomerStruct.SetTAngle(PhiAndPsi(DimerNum, 2), PhiAndPsi(DimerNum, 3), PhiAndPsi(DimerNum, 4), PhiAndPsi(DimerNum, 5), InitPsi)
  Next DimerNum 'Next dimer

  'Optimize
  If Opt="" Then
    WriteSoftMin3DCFG()                                        ' Write CFG-file for optimizer
    OligomerStruct = OligomerStruct.Do3DOptimize(GRAD_LIMIT)   ' Spawn optimizer
    WriteEmpty3DCFG()                                          ' Restore optimizer's CFG
    For DimerNum = 1 To NUnits - 1
      a1=PhiAndPsi(DimerNum, 1):a2= PhiAndPsi(DimerNum, 2)
      a3=PhiAndPsi(DimerNum, 3):a4= PhiAndPsi(DimerNum, 4)
      MinPhi = OligomerStruct.GetTAngle(a1,a2,a3,a4)
      a1=PhiAndPsi(DimerNum, 5)
      MinPsi = OligomerStruct.GetTAngle(a2,a3,a4,a1)
      ' Writing out short-hand of dimer and Phi and Psi in minimum
      Unit1Num = DimerUnits(DimerNum, 1)
      Unit2Num = DimerUnits(DimerNum, 2)
      ResString = DimerString(SeqString(Unit1Num), SeqString(Unit2Num), Linkage(Unit1Num, Unit2Num), Linkage(Unit2Num, Unit1Num), UnitIsomer(Unit1Num), UnitIsomer(Unit2Num))
      Call NewTB(ResString + "    Found optimal Phi = " + FStr(MinPhi*RAD_TO_DEG,1,0) + ",  Psi = " + FStr(MinPsi*RAD_TO_DEG,1,0), 2000)
    Next DimerNum
  End If

  ' Show the result
  Show_the_molecule(OligomerStruct)

  ConfAnalysis = True
End Function 'ConfAnalysis




' *4* CARBOHYDRATE DATABASE SERVICES




'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Sub CarbohydrateDataSetup()
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Initialize global variables with DataBase info                      '
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Dim i As Integer
  NUnitsDB = 44                         'Number of monosaccharides in DataBase
  NRadsDB = 5                           'Number of radicals in DataBase
  StdLinkDihedral = 60/RAD_TO_DEG       'Initial value of dihedral angles of glycosidic
                                        'linkage (Phi and Psi)
  StdLinkAngle = 117/RAD_TO_DEG         'Standard (initial) value of C-O-C angle of glicosidic
                                        'linkage
  'Re-allocate dynamic memory
  ReDim UnitDBSHand(NUnitsDB)           'ShortHands of monosaccharides in DataBase
  ReDim UnitDBFile(NUnitsDB, 2)         'File names of structures
  If NRadsDB <> 0 Then
    ReDim RadDBSHand(NRadsDB)           'ShortHands of radicals in DataBase
    ReDim RadDBFile(NRadsDB)            'File names of structures
    ReDim DefaultUnitLink(NRadsDB)
  End If

  'Settle unit short-hands
  UnitDBSHand(1) = "Abe"        :       UnitDBSHand(2) = "All"
  UnitDBSHand(3) = "Alt"        :       UnitDBSHand(4) = "Api"
  UnitDBSHand(5) = "Ara"        :       UnitDBSHand(6) = "Ara-ol"
  UnitDBSHand(7) = "dRib"       :       UnitDBSHand(8) = "Fru"
  UnitDBSHand(9) = "Fuc"        :       UnitDBSHand(10) = "Fuc-ol"
  UnitDBSHand(11) = "Gal"       :       UnitDBSHand(12) = "GalN"
  UnitDBSHand(13) = "GalNAc"    :       UnitDBSHand(14) = "Glc"
  UnitDBSHand(15) = "GlcN"      :       UnitDBSHand(16) = "GlcN3N"
  UnitDBSHand(17) = "Glc-ol"    :       UnitDBSHand(18) = "GlcNAc"
  UnitDBSHand(19) = "GlcA"      :       UnitDBSHand(20) = "GlcpA6Et"
  UnitDBSHand(21) = "Gul"       :       UnitDBSHand(22) = "Ido"
  UnitDBSHand(23) = "IdoA"      :       UnitDBSHand(24) = "Lyx"
  UnitDBSHand(25) = "Man"       :       UnitDBSHand(26) = "Mur"
  UnitDBSHand(27) = "Neu"       :       UnitDBSHand(28) = "Neu5Ac"
  UnitDBSHand(29) = "Neu2en5Ac" :       UnitDBSHand(30) = "Neu5Gc"
  UnitDBSHand(31) = "Kdo"       :       UnitDBSHand(32) = "Rha"
  UnitDBSHand(33) = "Rha3,4Me2" :       UnitDBSHand(34) = "Psi"
  UnitDBSHand(35) = "Qui"       :       UnitDBSHand(36) = "Rib"
  UnitDBSHand(37) = "Rib5P"     :       UnitDBSHand(38) = "Rul"
  UnitDBSHand(39) = "Sor"       :       UnitDBSHand(40) = "Tag"
  UnitDBSHand(41) = "Tal"       :       UnitDBSHand(42) = "Xyl"
  UnitDBSHand(43) = "Xul"       :       UnitDBSHand(44) = "Xyl2CMe"

  'Assign file names of existing unit structures
  For i = 1 To NUnitsDB
    UnitDBFile(i, ALPHA) = "" : UnitDBFile(i, BETA) = ""
  Next i
  UnitDBFile(8, ALPHA) = "UNITS\fru_a.mol"    'alpha-fructose
  UnitDBFile(8, BETA) = "UNITS\fru_b.mol"     'beta-fructose
  UnitDBFile(9, ALPHA) = "UNITS\fuc_a.mol"    'alpha-fucose
  UnitDBFile(11, ALPHA) = "UNITS\gal_a.mol"   'alpha-galactose
  UnitDBFile(11, BETA) = "UNITS\gal_b.mol"    'beta-galactose
  UnitDBFile(12, BETA) = "UNITS\galn_b.mol"   'beta-galactosamine
  UnitDBFile(14, ALPHA) = "UNITS\glc_a.mol"   'alpha-glucose
  UnitDBFile(14, BETA) = "UNITS\glc_b.mol"    'beta-glucose
  UnitDBFile(15, BETA) = "UNITS\glcn_b.mol"   'beta-glucosamine
  UnitDBFile(18, BETA) = "UNITS\glcnac_b.mol" 'beta-N-acetylglucose
  UnitDBFile(25, ALPHA) = "UNITS\man_a.mol"   'alpha-mannose
  UnitDBFile(27, ALPHA) = "UNITS\neu_a.mol"   'alpha-neuraminic acid
  UnitDBFile(27, BETA) = "UNITS\neu_b.mol"    'beta-neuraminic acid
  UnitDBFile(28, BETA) = "UNITS\neu5ac_b.mol" 'beta-5-acetyl-neuraminic acid

  'Substituent short-hands
  RadDBSHand(1) = "Me"           :      RadDBSHand(2) = "OMe"
  RadDBSHand(3) = "N"            :      RadDBSHand(4) = "Ac"
  RadDBSHand(5) = "NAc"

  'Assign file names of existing substituent structures and DefaultUnitLink
  If NRadsDB <> 0 Then
    For i = 1 To NRadsDB
      RadDBFile(i) = "" : DefaultUnitLink(i) = TO_ANOMERIC_CENTER
    Next i
  End If
  RadDBFile(1) = "UNITS\me.mol"
  RadDBFile(2) = "UNITS\me.mol"
  RadDBFile(3) = "UNITS\n.mol"    :     DefaultUnitLink(3) = 2
  RadDBFile(4) = "UNITS\ac.mol"
  RadDBFile(5) = "UNITS\nac.mol"  :     DefaultUnitLink(5) = 2
End Sub '..Setup




'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function SearchUnitDB(UnitString As String, UnitDBNum As Integer) As Boolean
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Search for short-hand UnitString in DataBase                        '
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Dim i As Integer, Err As Boolean
  SearchUnitDB = TRUE

  Err = TRUE
  For i = 1 To NUnitsDB
    If UnitDBSHand(i) = UnitString Then
      Err = False : UnitDBNum = i :Exit For
    End If
  Next i
  If Err Then
    SearchUnitDB = False : Call ErrOut("Unit " + UnitString + " not found in DataBase")
  End If

End Function 'SearchUnitDB



'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function SearchRadicalDB(RadString As String, RadDBNum As Integer) As Boolean
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Search for ShortHand RadString in DataBase                          '
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Dim i As Integer, Err As Boolean

  SearchRadicalDB = True

  Err = True
  For i = 1 To NRadsDB
    If RadDBSHand(i) = RadString Then
      RadDBNum = i : Err = False : Exit For
    End If
  Next i
  If Err Then
    SearchRadicalDB = False : Call ErrOut("Radical " + RadString + " not found in DataBase")
  End If

End Function 'SearchRadicalDB



'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function AnomericCenter(Unit As Object) As Integer
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Returns anomeric center Atom or Null if not exist                   '
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Dim Atom As Object,label As String
  AnomericCenter = 0
  For Each Atom In Unit.Assembly
    label=Atom.GetName
    If Mid(label, 3, 1) = DB_ANOMERIC_CENTER Then
      AnomericCenter = FixValMid(label, 2, 1) : Exit Function
    End If
  Next Atom
End Function 'AnomericCenter




'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Sub CarbonAtom(Unit As Object, ByVal Num As Integer, Res As Object)
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Returns carbon Atom by its (carbohydrate specific) number           '
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Dim Atom As Object, label As String
  Res = Null
  For Each Atom In Unit.Assembly
    label=Atom.GetName
    If FixValMid(label, 2, 1) = Num Then
      Res = Atom : Exit Sub
    End If
  Next Atom
End Sub 'CarbonAtom




' *5* ANALYZE/EDIT THE STRUCTURE (GENERAL)




'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Sub HAt(Entity As Object, CentralAtom As Object, Hydrogen As Object)
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Returns a single hydrogen atached to CentralAtom or Null if no H    '
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Dim Atom As Object, Environment As Object
  Environment=Entity.AssocAtoms(CentralAtom)
  For Each Atom In Environment
    If Atom.GetElNumber = 1 Then
      Hydrogen = Atom : Exit Sub
    End If
  Next Atom
  Hydrogen = Null  'found nothing
End Sub



'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Sub OhxlAt(Entity As Object, CentralAtom As Object, Oxygen As Object)
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Returns oxygen atom of hydroxyl attached to Atom in Entity or Null  '
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Dim Atom As Object, AtomAtO As Object, Environment As Object, OEnvironment As Object
  Environment = Entity.AssocAtoms(CentralAtom)
  For Each Atom In Environment
    If Atom.GetElNumber = 8 Then              'Is oxygen there?
      OEnvironment=Entity.AssocAtoms(Atom)
      For Each AtomAtO In OEnvironment        'Is there H's at it?
        If (AtomAtO <> Atom) And (AtomAtO.GetElNumber = 1) Then
          Oxygen = Atom : Exit Sub
        End If
      Next AtomAtO
    End If
  Next Atom
  Oxygen = Null  'found nothing
End Sub 'OhxlAt



'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Sub RingOxygen(Unit As Object, ROxygen As Object)
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Returns [1st] ring oxygen Atom or Null if there is nothing          '
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Dim Atom As Object
  With Unit
    For Each Atom In .Assembly
      If Atom.GetElNumber = 8 And .IsRing(Atom) Then
        ROxygen = Atom : Exit Sub
      End If
    Next Atom
    ROxygen = Null
  End With
End Sub 'RingOxygen



'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function IsAssocRingOxygen(Unit As Object, Atom As Object) As Boolean
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'Returns True if Atom is asociated with ring oxygen  in Unit          '
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Dim At As Object, Environment As Object
  WITH Unit
    IsAssocRingOxygen = True
    Environment = .AssocAtoms(Atom)
    For Each At In Environment
      If .Molecule.IsRing(At) And At.GetElNumber = 8 Then Exit Function
    Next At
    IsAssocRingOxygen = False
  End WITH
End Function 'IsAssocRingOxygen




'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function ReplaceGroup(Entity As Object, Subst As Object, atE As Object, repE As Object, atS As Object, repS As Object) As Boolean
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Appends Subst to Entity by replacing the group                      '
' at Entity's atom repE with Subst                                    '
'                                                                     '
' Atoms atE and atS will become bonded in (thus grown up) Entity      '
' Atoms repE and repS will be deleted with all their terminal atoms   '
'                                                                     '
' Bending angles at atoms atE and atS keep the source values          '
' Torsional angle around atE-atS will have arbitrary value            '
' If bondlen>0 then bond atE-atS length will be equal to bondlen      '
'                                                                     '
'NB: Entity and Subst should be both either molecules or structures   '
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Dim EType As Integer, OK As Boolean
Dim EMol As Object, Bnd As Object
Dim xre As Double, yre As Double, zre As Double
Dim xrs As Double, yrs As Double, zrs As Double
Dim xas As Double, yas As Double, zas As Double
Dim dx As Double, dy As Double, dz As Double, D As Double

  With Entity
    ReplaceGroup = False
    ' Check types
    EType = .GetType
    If Subst.GetType <> EType Then Exit Function
    Select Case EType
      Case CB_MOLECULE
        EMol = Entity
      Case CB_STRUCTURE
        EMol = .Molecule
      Case Else
        Exit Function
    End Select
    Call .Assembly.Merge(Subst)           'Append
    If EType = CB_STRUCTURE Then          'If we deal with structure do some movements
      Bnd = EMol.AddBond(atE, repS, 1)   'Make a bond
      Entity.SetBLen(atE, repS, 0.0)
      ' If we deal with structure adjust a valence dash
      Entity.GetAtomXyz(atS,xas,yas,zas)
      Entity.GetAtomXyz(repS,xrs,yrs,zrs)
      Entity.GetAtomXyz(repE,xre,yre,zre)
      dx=(yas-yrs)*(zre-zrs)-(yre-yrs)*(zas-zrs)
      dy=(zas-zrs)*(xre-xrs)-(zre-zrs)*(xas-xrs)
      dz=(xas-xrs)*(yre-yrs)-(xre-xrs)*(yas-yrs)
      D=Sqrt(dx*dx+dy*dy+dz*dz)
      If D>1e-10 Then 'if D<1e-10 then valence position is adjusted
        dx=dx/D: dy=dy/D: dz=dz/D
        Entity.SetAtomXyz(repS,xrs+dx,yrs+dy,zrs+dz)
        Entity.SetTAngle(atS, repS, atE, repE, 0.0)
      End If
      Kill(Bnd)
    End If
                                        'Delete leaving groups
    Call DeleteTerminalGroup(Entity, repE)
    Call DeleteTerminalGroup(Entity, repS)
    Bnd = EMol.AddBond(atE, atS, 1)   'Make a bond
    ReplaceGroup = TRUE

  End WITH
End Function 'ReplaceGroup



'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function BaseAt(Entity As Object, MainAtom As Object, NotThis As Object,Base As Object) As Boolean
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Finds a base atom Base -- the atom connected to                     '
' MainAtom and not equal to NotThis                                   '
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Dim Atom As Object, Environment As Object
  BaseAt=False
  Environment = Entity.AssocAtoms(MainAtom)
  For Each Atom In Environment
      If Atom<>NotThis Then
        Base = Atom : BaseAt=True : Exit Function
      End If
  Next Atom
End Function  'BaseAt



'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Sub DeleteTerminalGroup(Entity As Object, Terminal As Object)
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Deletes Terminal atom and all associated single atoms from Entity   '
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Dim Asm As Object, Environment As Object, Atom As Object
  WITH Entity
    Asm=.Assembly : Environment =.Molecule.AssocAtoms(Terminal)
    For Each Atom In Environment
      'delete associated single atoms
      If .AssocAtoms(Atom).Count = 1 Then Call Asm.AtRemove(Asm.Index(Atom))
    Next Atom
    Call Asm.AtRemove(Asm.Index(Terminal))  'delete central atom itself
  End WITH
End Sub



' *6* PARSE/EDIT THE STRUCTURE (CARBOHYDRATES SPECIFIC)



'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function AppendThroughGlycoBond(Entity As Object, Unit As Object, CarbonE As Object, CarbonU As Object) As Boolean
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Append Unit to Entity and create a glycosidic linkage between       '
' CarbonE (of Entity) and CarbonU (of Unit). Unit is absorbed         '
' by Entity (which thus expands).                                     '
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' NB: Glycosidic oxygen comes from Entity                             '
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Const GBLENGTH = 1.4
Const GBANGLE=112.0/RAD_TO_DEG
Dim HE,OxygenE, OxygenU As Object
Dim setOK As Boolean,va1 As Double
  AppendThroughGlycoBond = FALSE

  Call OhxlAt(Entity, CarbonE, OxygenE)
  Call OhxlAt(Unit, CarbonU, OxygenU)
  If OxygenE = Null Or OxygenU = Null Then Exit Function

  Call HAt(Entity, OxygenE, HE)
  If HE = Null Then Exit Function

  AppendThroughGlycoBond = ReplaceGroup(Entity, Unit, OxygenE, HE, CarbonU, OxygenU)
End Function 'AppendThroughGlycoBond




'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function AppendRadical(Entity As Object, Radical As Object, CarbonE As Object, AtomR As Object, repR As Object, LinkType As Integer) As Boolean
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'Appends (saccharide) Entity with Radical at CarbonE                  '
' depending on LinkType                                               '
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Dim AtomE,repE As Object
  AppendRadical = TRUE
  Select Case LinkType
            Case LNK_TO_O
                          Call OhxlAt(Entity, CarbonE, AtomE)
                          Call HAt(Entity, AtomE, repE)
            Case LNK_TO_C_NOT_O
                          AtomE = CarbonE
                          Call OhxlAt(Entity, CarbonE, repE)
            Case LNK_TO_C_NOT_HA
                          AtomE = CarbonE
                          Call HAt(Entity, CarbonE, repE)
          Case Else
                          AppendRadical = False
                          Exit Function
  End Select

  AppendRadical = ReplaceGroup(Entity, Radical, AtomE, repE, AtomR, repR)
End Function 'AppendRadical



' ** MISC SERVICES



'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Sub ErrOut(ByVal strg As String)
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Error message                                                       '
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Dim i As Integer
  i = MessageBox(strg, TITLE, MBB_OK + MBI_EXCLAMATION)
End Sub
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function FixValMid(ByVal s As String, ByVal i As Integer,ByVal j As Integer) As Integer
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
  FixValMid=Fix(Val(Mid(s, i, j)))
End Function
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Sub RmPar(strg As String)
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Removing symbols [] at beginning and end of string                  '
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Dim Char As String
  Char = Left(strg, 1)
  While Char = "[" Or Char = "]"
    strg = Right(strg, Len(strg) - 1) : Char = Left(strg, 1)
  Wend
  Char = Right(strg, 1)
  While Char = "[" Or Char = "]"
    strg = Left(strg, Len(strg) - 1) : Char = Right(strg, 1)
  Wend
End Sub
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function IsNumber(ByVal Char As String) As Boolean
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Check whether a char is a digit                                     '
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
  IsNumber = True
  Select Case Char
    Case "0"
    Case "1"
    Case "2"
    Case "3"
    Case "4"
    Case "5"
    Case "6"
    Case "7"
    Case "8"
    Case "9"
    Case Else
              IsNumber = False
  End Select
End Function 'IsNumber
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Sub GetAtomX3(Entity As Object, at As Object, x() As Double)
  Call Entity.Conformation.GetAtomXYZ(at, x(1), x(2), x(3))
End Sub
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Sub SetAtomX3(Entity As Object, at As Object, x() As Double)
  Call Entity.Conformation.SetAtomXYZ(at, x(1), x(2), x(3))
End Sub
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Sub Show_the_molecule(Struc As Object)
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Depict structure as ChemSketch's diagram                            '
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Dim Atom, Diagrm As Object, w As Integer, h As Integer, l As Integer, t As Integer, OK As Boolean
' Remove those annoying labels at atoms
For Each Atom in Struc.Assembly
  Call Atom.SetName("")
Next Atom

WITH ActiveDocument
  Diagrm=.ActivePage.Diagrams.AddEmpty

  Call Diagrm.Depict(Struc)

  Call Diagrm.GetBound(l, t, w, h)
  If (CurPosOnPage + h > B_MARGIN) And h < (B_MARGIN - T_MARGIN) Then
    ResultsPage = .AddEmpty : CurPosOnPage = T_MARGIN
  End If
  Call Diagrm.SetBound(L_MARGIN, CurPosOnPage, w, h)
  CurPosOnPage = CurPosOnPage + h + STR_SHFT - H_STRING
  'OK = Diagrm.ExportToMOL("Oligomer.mol")
End WITH
End Sub
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Sub WriteSoftMin3DCFG()
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Write necessary config for DM3DOPT.DLL                              '
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Const cfgfile="DM3DOPT.CFG"
  'Open cfgfile Access Write As 2
  'Print #2, "OPTIMIZATION = SOFT"
  'Close #2
End Sub
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Sub WriteEmpty3DCFG()
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Write an empty config for DM3DOPT.DLL                               '
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Const cfgfile="DM3DOPT.CFG"
  'Open cfgfile Access Write As 2
  'Close #2
End Sub
