Idea or challenge ?!

General FreeBASIC programming questions.
Lost Zergling
Posts: 634
Joined: Dec 02, 2011 22:51
Location: France

Re: Idea or challenge ?!

Post by Lost Zergling »

Reason : serious bug fixes. Much more deeply tested.
Looks like it works now. edited

Code: Select all

#Include once "D:\Basic\LZLE_.bi"  
' WITH CONST MAX_ASIDE=100
Function InstrMulti(Str_Input As String, ListeQC As List, uBStopFirstFound As uByte=0) As String
    Dim As String Str_Result="", str_tmp_1 , str_tmp_2
    Dim as zString Ptr z_Input
    Dim As uInteger uInt_InputLen, i, u, t
    Dim As uByte ubRevocated(100), uBsuccess=0
    While ListeQC.Up : Wend
    For t=1 to Len(Str_Input) Step 50
        str_tmp_1= Mid(Str_Input, t, 100)
        uInt_InputLen=Len(str_tmp_1)
        z_Input= StrPtr(str_tmp_1)
        For i=1 to uInt_InputLen
            ubRevocated(i)=0
            For u=1 to i
                If ubRevocated(u)=0 Then 
                    If uBsuccess=1 Then : uBsuccess=0 : While ListeQC.Up : Wend : End If
                    ListeQC.Recover(u)
                    If ListeQC.HasTag(Chr((*z_Input)[0])) Then                        
                        If ListeQC.Check Then ' ? "*" & ListeQC.HashTag
                            If uBStopFirstFound=1 Then : Return ListeQC.HashTag : End If
                            If Instr(Str_Result, ";" + ListeQC.HashTag + ";" )=0 Then
                                Str_Result+=ListeQC.HashTag + ";" : uBsuccess=1
                                If ListeQC.Down Then : ListeQC.Aside(u) : While ListeQC.Up : Wend
                                Else : ubRevocated(u)=1 : End If
                            Else : While ListeQC.Up : Wend : ubRevocated(u)=0 
                            End If
                        Else
                            If ListeQC.Down Then : ListeQC.Aside(u) : ubRevocated(u)=0 : Else : ubRevocated(u)=1 : End If 
                            While ListeQC.Up : Wend
                        End If
                    Else : ubRevocated(u)=1 : While ListeQC.Up : Wend : ListeQC.Aside(u)
                    End If
                End If
            Next u
            z_Input+=1
        Next i 
    Next t
    u=1 : i=1
    While i<>0
        i = Instr( u, Str_Result, ";") : str_tmp_1 = Right(Left(Str_Result, i-1 ), i-u)
        u=i+1 : If Instr(Str_Input, str_tmp_1)<>0 Then : str_tmp_2+= str_tmp_1+";" : End If
    Wend
    Return str_tmp_2 ' Return Str_Result
End Function 

Dim ListeQC As List
Dim As string Str_Input = "78poissonchatTGHVBNI98virgulepointavionordinateurchienboite7654titiimprimantesourisAzertyuiopqsdfghjklmwxcvbn" 'Len Max=200
'Dim As string Str_Input = "*NePYT98qsazerfdcxwAzertyuiopAzertylmwxcvbnAzertyuiopqsdfghjklmwxcvbnpoissonchatvirgulepointavionordinateurchienboite7654titiZimprimantesouris"
'Dim As string Str_Input = "GTCCYCTRCTTXTXTRCTTXTXT7654EXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNEXTRRCKYNI?INYDRESXeYTYOPNFGHFsourisAzertyREZAWTYTYUIOPMLPOUNBGNOFDH*NedfghjklmwxcvqsazerfdcxwAzertyuiopAzertylmwxcvbnAzertyuiopqsdfghjklmwxcvbnpoissonchat"
Dim As string Str_Res_1, Str_Res_2
Dim As uByte uBStopFirstFound=0
Dim as double T1, T2
Dim As Integer i

ListeQC.HashTag("virgule")
ListeQC.HashTag("poissonchatTGHVBNI")
ListeQC.HashTag("chienboite")
ListeQC.HashTag("Bar")
ListeQC.HashTag("ordi")
ListeQC.HashTag("poiss")
ListeQC.HashTag("isso")
ListeQC.HashTag("imprimante")
ListeQC.HashTag("oisson")
ListeQC.HashTag("oissonch")
ListeQC.HashTag("titi")

? "Loading values to FT search"
For i=5 to 100000
    ListeQC.HashTag(str(i)  )
Next i

While ListeQC.Up : Wend : ListeQC.FastAside(1)
 ? "Loaded"
    ' uBStopFirstFound=1

dim as double t=timer
Str_Res_1 = "Result=" & InstrMulti(Str_Input, ListeQC, uBStopFirstFound)

T1= timer-t : 
t=timer
For i=5 to 100000
    If Instr(Str_Input, str(i)) <>0 Then : Str_Res_2+=str(i)+";"  : End If
Next i
T2= timer-t

ListeQC.Root
While ListeQC.KeyStep
    If Cint(ListeQC.Tag)=0 Then
        If Instr(Str_Input, ListeQC.HashTag) <>0 Then : Str_Res_2+=ListeQC.HashTag+ ";"  : 
        End If
    End If    
Wend

? Str_Res_1
?  "Result=" & InstrMulti(Str_Input, ListeQC, uBStopFirstFound)
?  "Result=" & InstrMulti(Str_Input, ListeQC, uBStopFirstFound)
'?  "Result=" & InstrMulti(Str_Input, ListeQC, uBStopFirstFound)
'?  "Result=" & InstrMulti(Str_Input, ListeQC, uBStopFirstFound)
? "Result=" & Str_Res_2
? T1
? T2
? "InstrMulti Warp Factor=" & Cint(T2/T1)

sleep : system

Lost Zergling
Posts: 634
Joined: Dec 02, 2011 22:51
Location: France

Re: Idea or challenge ?!

Post by Lost Zergling »

Okay, I'm posting a fix because there's a list consistency issue with excessive use of List.Up: it seems there's an unmanaged context with this property. Speed ​​must have degraded further. My tests seem to indicate that it should be possible to gain another 40-50% by adapting/correcting lzle, but probably not much more. This would require serious maintenance.
Results:
InstrMulti seems stable, but has limitations:
Maximum length of strings to search: 50 characters
Minimum number of strings to search for and from which equivalent performance can be obtained: around 700
Furthermore, there remains some behavior that escapes me, in particular the limits on strings thresholds (50 and 100) and the Str_Result variable, which seems to be initialized to empty to avoid a side effect.

Code: Select all

#Include once "D:\Basic\LZLE_.bi"  
' WITH CONST MAX_ASIDE=100
Function InstrMulti(Str_Input As String, ListeQC As List, uBStopFirstFound As uByte=0, uBNoFalsePositive As uByte=1, Str_Sep As String=";") As String
    Dim As String Str_Result="", str_tmp_1 , str_tmp_2
    Dim as zString Ptr z_Input
    Dim As uInteger uInt_InputLen_0=Len(Str_Input), uInt_InputLen, i, u, t
    Dim As uByte ubRevocated(100)
    While ListeQC.Up : Wend
    For t=1 to uInt_InputLen_0 Step 50
        str_tmp_1= Mid(Str_Input, t, 100)
        uInt_InputLen=Len(str_tmp_1)
        z_Input= StrPtr(str_tmp_1)
        For i=1 to uInt_InputLen
            ubRevocated(i)=0
            For u=1 to i
                If ubRevocated(u)=0 Then 
                    ListeQC.Recover(u) 
                    If ListeQC.HasTag(Chr((*z_Input)[0])) Then                        
                        If ListeQC.Check Then
                            If uBStopFirstFound=1 Then : Return ListeQC.HashTag : End If
                            If Instr(Str_Result, Str_Sep + ListeQC.HashTag + Str_Sep )=0 Then
                                Str_Result+=ListeQC.HashTag + Str_Sep
                                If ListeQC.Down Then : ListeQC.Aside(u)
                                Else : ubRevocated(u)=1 : End If : ListeQC.Root : 'While ListeQC.Up : Wend ' 
                            Else : While ListeQC.Up : Wend : ubRevocated(u)=0 '
                            End If
                        Else
                            If ListeQC.Down Then : ListeQC.Aside(u) : ubRevocated(u)=0 : Else : ubRevocated(u)=1 : End If 
                            While ListeQC.Up : Wend
                        End If
                    Else : ubRevocated(u)=1 : ListeQC.Root : ' While ListeQC.Up : Wend : 
                        ListeQC.Aside(u)  '
                    End If
                End If
            Next u
            z_Input+=1
        Next i 
    Next t
    If uBNoFalsePositive=0 Then : Return Str_Result : End If
    u=1 : i=1
    While i<>0
        i = Instr( u, Str_Result, Str_Sep ) : str_tmp_1 = Right(Left(Str_Result, i-1 ), i-u)
        u=i+1 : If Instr(Str_Input, str_tmp_1)<>0 Then : str_tmp_2+= str_tmp_1+ Str_Sep : End If
    Wend        
    Return str_tmp_2
End Function 

Dim ListeQC As List
Dim As string Str_Input = "78poissonchatTGHVBNI98virgulepointavionordinateurchienboite7654titiimprimantesourisAzertyuiopqsdfghjklmwxcvbn" 'Len Max=200
'Dim As string Str_Input = "*NePYT98qsazerfdcxwAzertyuiopAzertylmwxcvbnAzertyuiopqsdfghjklmwxcvbnpoissonchatvirgulepointavionordinateurchienboite7654titiZimprimantesouris"
'Dim As string Str_Input = "GTCCYCTRCTTXTXTRCTTXTXT7654EXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNEXTRRCKYNI?INYDRESXeYTYOPNFGHFsourisAzertyREZAWTYTYUIOPMLPOUNBGNOFDH*NedfghjklmwxcvqsazerfdcxwAzertyuiopAzertylmwxcvbnAzertyuiopqsdfghjklmwxcvbnpoissonchat"
Dim As string Str_Res_1, Str_Res_2
Dim As uByte uBStopFirstFound=0
Dim as double T1, T2
Dim As Integer i, TestMaxi = 100000

ListeQC.HashTag("virgule")
ListeQC.HashTag("poissonchatTGHVBNI")
ListeQC.HashTag("chienboite")
ListeQC.HashTag("Bar")
ListeQC.HashTag("ordi")
ListeQC.HashTag("poiss")
ListeQC.HashTag("isso")
ListeQC.HashTag("imprimante")
ListeQC.HashTag("oisson")
ListeQC.HashTag("oissonch")
ListeQC.HashTag("titi")

? "Loading values to FT search"
For i=5 to TestMaxi
    ListeQC.HashTag(str(i)  )
Next i


While ListeQC.Up : Wend : ListeQC.FastAside(1)
 ? "Loaded =" & ListeQC.NodeCount
    ' uBStopFirstFound=1
    
    dim as double t=timer
'ListeQC.SeekMethod(0)
'ListeQC.Root
Str_Res_1 = "Result=" & InstrMulti(Str_Input, ListeQC, uBStopFirstFound)
    
ListeQC.SeekMethod(1)
T1= timer-t : 
t=timer
For i=5 to TestMaxi
    If Instr(Str_Input, str(i)) <>0 Then : Str_Res_2+=str(i)+";"  : End If
Next i
T2= timer-t

ListeQC.Root
While ListeQC.KeyStep
    If Cint(ListeQC.Tag)=0 Then
        If Instr(Str_Input, ListeQC.HashTag) <>0 Then : Str_Res_2+=ListeQC.HashTag+ ";"  : 
        End If
    End If    
Wend
'? InstrMulti("vbnoissonchtiti", ListeQC, uBStopFirstFound)
? Str_Res_1
?  "Result=" & InstrMulti(Str_Input, ListeQC, uBStopFirstFound)
?  "Result=" & InstrMulti(Str_Input, ListeQC, uBStopFirstFound)
?  "Result=" & InstrMulti(Str_Input, ListeQC, uBStopFirstFound)

'?  "Result=" & InstrMulti(Str_Input, ListeQC, uBStopFirstFound)
'?  "Result=" & InstrMulti(Str_Input, ListeQC, uBStopFirstFound)
'?  "Result=" & InstrMulti(Str_Input, ListeQC, uBStopFirstFound)
? "Result=" & Str_Res_2


'ListeQC.Root
ListeQC.Recycle
ListeQC.HashTag("virgule")
ListeQC.HashTag("poissonchatTGHVBNI")
ListeQC.HashTag("chienboite")
ListeQC.HashTag("Bar")
ListeQC.HashTag("ordi")
ListeQC.HashTag("poiss")
ListeQC.HashTag("isso")
ListeQC.HashTag("imprimante")
ListeQC.HashTag("oisson")
ListeQC.HashTag("oissonch")
ListeQC.HashTag("titi")
ListeQC.Root
'? "Loading values to FT search"
For i=5 to TestMaxi
    ListeQC.HashTag(str(i)  )
Next i
While ListeQC.Up : Wend :
  '   ? "Loaded =" & ListeQC.NodeCount
     
?  "Result=" & InstrMulti(Str_Input, ListeQC, uBStopFirstFound) & " *"

? T1
? T2
? "InstrMulti Warp Factor=" & Cint(Cdbl(T2/T1)*100)/100


ListeQC.Destroy : gCollector.Destroy
Print "??=" & AllocateDeallocateCounter & " nodecount=" & ListeQC.NodeCount 
sleep : system

Lost Zergling
Posts: 634
Joined: Dec 02, 2011 22:51
Location: France

Re: Idea or challenge ?!

Post by Lost Zergling »

Okay, so I went after the 30-40% speed loss. There might still be some false positive use cases; the uBNoFalsePositive parameter is set to 0 by default, but it seems okay, at least in my tests. We could perhaps gain another 20 or 30%, but it's starting to get complicated. So unless there's a huge bug, I think I'll stop there. :|
The stability issues on the lzle side were due to two causes:
- A differentiated context issue between .root and .up (could be useful for optimizing the .Root property, we'll see)
- The relevance of using AsideReset
On the fbc side, two questions:
- Dim As String Str_Result=";" instead of Dim As String Str_Result="" causes results to be lost
- The cause of string length thresholds (100 and 200 or 125 and 250)

Code: Select all

#Include once "D:\Basic\LZLE_.bi"  
' WITH CONST MAX_ASIDE=250 & fbc64.exe
Function InstrMulti(Str_Input As String, ListeQC As List, uBStopFirstFound As uByte=0, uBNoFalsePositive As uByte=0, Str_Sep As String=";") As String
    Dim As String Str_Result="", str_tmp_1 , str_tmp_2, str_HashTag
    Dim as zString Ptr z_Input
    Dim As uInteger uInt_InputLen_0=Len(Str_Input), uInt_InputLen, i, u, t
    Dim As uByte ubRevocated(300)
    If ListeQC.Tag<>Chr(18) Then : ListeQC.Root : Else : While ListeQC.Up : Wend : End If    
    For t=1 to uInt_InputLen_0 Step 125 ' 100 =>32 bits fbc32
        str_tmp_1= Mid(Str_Input, t, 250)  ' 200 => 32 bits fbc32
        uInt_InputLen=Len(str_tmp_1)
        z_Input= StrPtr(str_tmp_1)
        For i=1 to uInt_InputLen
            ubRevocated(i)=0 : ListeQC.AsideReset(i) 
            For u=1 to i
                If ubRevocated(u)=0 Then 
                    ListeQC.Recover(u) 
                    If ListeQC.HasTag(Chr((*z_Input)[0])) Then                               
                        If ListeQC.Check Then
                            If uBStopFirstFound=1 Then : Return ListeQC.HashTag : End If          
                            str_HashTag = ListeQC.HashTag
                            If Instr(Str_Result, Str_Sep + str_HashTag + Str_Sep )=0 Then
                                Str_Result+=str_HashTag + Str_Sep
                                If ListeQC.Down Then : ListeQC.Aside(u) : Else : ubRevocated(u)=1 : End If
                            Else : ubRevocated(u)=1
                            End If
                            If ListeQC.Tag<>Chr(18) Then : ListeQC.Root : Else : While ListeQC.Up : Wend : End If    
                        Else
                            If ListeQC.Down Then : ListeQC.Aside(u) : ubRevocated(u)=0 : Else : ubRevocated(u)=1 : End If 
                            If ListeQC.Tag<>Chr(18) Then : ListeQC.Root : Else : While ListeQC.Up : Wend : End If    
                        End If
                    Else : ubRevocated(u)=1 : 
                        If ListeQC.Tag<>Chr(18) Then : ListeQC.Root : Else : While ListeQC.Up : Wend : End If    
                        ListeQC.Aside(u)
                    End If
                End If
            Next u
            z_Input+=1
        Next i 
    Next t
    If uBNoFalsePositive=0 Then : Return Str_Result : End If
    u=1 : i=1
    While i<>0
        i = Instr( u, Str_Result, Str_Sep ) : str_tmp_1 = Right(Left(Str_Result, i-1 ), i-u)
        u=i+1 : If Instr(Str_Input, str_tmp_1)<>0 Then : str_tmp_2+= str_tmp_1+ Str_Sep : End If
    Wend        
    Return str_tmp_2
End Function 

Dim ListeQC As List
Dim As string Str_Input = "78poissonchatTGHVBNI98virgulepointavionordinateurchienboite7654titiimprimantesourisAzertyuiopqsdfghjklmwxcvbn" 'Len Max=200
'Dim As string Str_Input = "*NePYT98qsazerfdcxwAzertyuiopAzertylmwxcvbnAzertyuiopqsdfghjklmwxcvbnpoissonchatvirgulepointavionordinateurchienboite7654titiZimprimantesouris"
'Dim As string Str_Input = "GTCCYCTRCGTCCYCTRCTTXTXTRCTTXTXT7654EXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGTCCYCTRCTTXTXTRCTTXTXT7654EXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGTCCYCTRCTTXTXTRCTTXTXT7654EXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXTTXTXTRCTTXTXT7654EXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNEXTRRCKYNI?INYDRESXeYTYOPNFGHFsourisAzertyREZAWTYTYUIOPMLPOUNBGNOFDH*NedfghjklmwxcvqsazerfdcxwAzertyuiopAzertylmwxcvbnAzertyuiopqsdfghjklmwxcvbnpoissonchat"
Dim As string Str_Res_1, Str_Res_2
Dim As uByte uBStopFirstFound=0
Dim as double T1, T2
Dim As Integer i, TestMaxi = 100000

ListeQC.HashTag("virgule")
ListeQC.HashTag("poissonchatTGHVBNI")
ListeQC.HashTag("chienboite")
ListeQC.HashTag("Bar")
ListeQC.HashTag("ordi")
ListeQC.HashTag("poiss")
ListeQC.HashTag("isso")
ListeQC.HashTag("imprimante")
ListeQC.HashTag("oisson")
ListeQC.HashTag("oissonch")
ListeQC.HashTag("titi")

? "Loading values to FT search"
For i=5 to TestMaxi
    ListeQC.HashTag(str(i)  )
Next i


While ListeQC.Up : Wend : ListeQC.FastAside(1)
 ? "Loaded =" & ListeQC.NodeCount
  '   uBStopFirstFound=1
    
    dim as double t=timer
'ListeQC.SeekMethod(0)
'ListeQC.Root
Str_Res_1 = "Result=" & InstrMulti(Str_Input, ListeQC, uBStopFirstFound)
    
ListeQC.SeekMethod(1)
T1= timer-t : 
t=timer
For i=5 to TestMaxi
    If Instr(Str_Input, str(i)) <>0 Then : Str_Res_2+=str(i)+";"  : End If
Next i
T2= timer-t

ListeQC.Root
While ListeQC.KeyStep
    If Cint(ListeQC.Tag)=0 Then
        If Instr(Str_Input, ListeQC.HashTag) <>0 Then : Str_Res_2+=ListeQC.HashTag+ ";"  : 
        End If
    End If    
Wend
'? InstrMulti("vbnoissonchtiti", ListeQC, uBStopFirstFound)
? Str_Res_1
?  "Result=" & InstrMulti(Str_Input, ListeQC, uBStopFirstFound)
?  "Result=" & InstrMulti(Str_Input, ListeQC, uBStopFirstFound)
?  "Result=" & InstrMulti(Str_Input, ListeQC, uBStopFirstFound)
?  "Result=" & InstrMulti(Str_Input, ListeQC, uBStopFirstFound)
?  "Result=" & InstrMulti(Str_Input, ListeQC, uBStopFirstFound)

'?  "Result=" & InstrMulti(Str_Input, ListeQC, uBStopFirstFound)
'?  "Result=" & InstrMulti(Str_Input, ListeQC, uBStopFirstFound)
'?  "Result=" & InstrMulti(Str_Input, ListeQC, uBStopFirstFound)
? "Result=" & Str_Res_2


'ListeQC.Root
ListeQC.Recycle
? "Garbage=" & ListeQC.GarbageCount
'Dim ListeQC_2 As List

ListeQC.HashTag("virgule")
ListeQC.HashTag("poissonchatTGHVBNI")
ListeQC.HashTag("chienboite")
ListeQC.HashTag("Bar")
ListeQC.HashTag("ordi")
ListeQC.HashTag("poiss")
ListeQC.HashTag("isso")
ListeQC.HashTag("imprimante")
ListeQC.HashTag("oisson")
ListeQC.HashTag("oissonch")
ListeQC.HashTag("titi")
ListeQC.Root
'? "Loading values to FT search"
For i=5 to TestMaxi
    ListeQC.HashTag(str(i)  )
Next i
While ListeQC.Up : Wend :
  '   ? "Loaded =" & ListeQC.NodeCount
     

?  "Result=" & InstrMulti(Str_Input, ListeQC, uBStopFirstFound) & " *"
?  "Result=" & InstrMulti(Str_Input, ListeQC, uBStopFirstFound) & " *"
?  "Result=" & InstrMulti(Str_Input, ListeQC, uBStopFirstFound) & " *"

? T1
? T2
? "InstrMulti Warp Factor=" & Cint(Cdbl(T2/T1)*100)/100


ListeQC.Destroy : gCollector.Destroy
Print "??=" & AllocateDeallocateCounter & " nodecount=" & ListeQC.NodeCount 
sleep : system

Edited
The cause of string length thresholds (100 and 200 or 125 and 250) SOLVED : change Byte to Integer in .Aside, .Recover & .AsideReset properties
New release +5-10% speed

Code: Select all

#Include once "D:\Basic\LZLE_.bi"  
' WITH CONST MAX_ASIDE=500 & change Byte to Integer in .Aside, .Recover & .AsideReset properties
Function InstrMulti(Str_Input As String, ListeQC As List, uBStopFirstFound As uByte=0, uBNoFalsePositive As uByte=0, Str_Sep As String=";") As String
    Dim As String Str_Result="", str_tmp_1 , str_tmp_2, str_HashTag
    Dim as zString Ptr z_Input
    Dim As uInteger uInt_InputLen_0=Len(Str_Input), uInt_InputLen, i, u, t
    Dim As uByte ubRevocated(500)
    If ListeQC.Tag<>Chr(18) Then : ListeQC.Root : Else : While ListeQC.Up : Wend : End If    
    For t=1 to uInt_InputLen_0 Step 250 ' 100 =>32 bits fbc32
        str_tmp_1= Mid(Str_Input, t, 500)  ' 200 => 32 bits fbc32
        uInt_InputLen=Len(str_tmp_1)
        z_Input= StrPtr(str_tmp_1)
        For i=1 to uInt_InputLen
            ubRevocated(i)=0 : ListeQC.AsideReset(i) 
            For u=1 to i
                If ubRevocated(u)=0 Then 
                    ListeQC.Recover(u) 
                    If ListeQC.HasTag(Chr((*z_Input)[0])) Then   
                        ListeQC.SeekMethod(1)
                        If ListeQC.Check Then
                            If uBStopFirstFound=1 Then : Return ListeQC.HashTag : End If          
                            str_HashTag = ListeQC.HashTag
                            If Instr(Str_Result, Str_Sep + str_HashTag + Str_Sep )=0 Then
                                Str_Result+=str_HashTag + Str_Sep
                                If ListeQC.Down Then : ListeQC.Aside(u) : Else : ubRevocated(u)=1 : ListeQC.SeekMethod(0)  : End If
                            Else : ubRevocated(u)=1 : ListeQC.SeekMethod(0)
                            End If
                            If ListeQC.Tag<>Chr(18) Then : ListeQC.Root : ListeQC.SeekMethod(0) : Else : While ListeQC.Up : Wend : End If 
                        Else
                            If ListeQC.Down Then : ListeQC.Aside(u) : ubRevocated(u)=0 : Else : ubRevocated(u)=1 : ListeQC.SeekMethod(0)  : End If 
                            If ListeQC.Tag<>Chr(18) Then : ListeQC.Root : ListeQC.SeekMethod(0)  : Else : While ListeQC.Up : Wend : End If 
                        End If
                    Else : ubRevocated(u)=1 :  
                        If ListeQC.Tag<>Chr(18) Then : ListeQC.Root : Else : While ListeQC.Up : Wend : End If    
                        ListeQC.Aside(u) : ListeQC.SeekMethod(0)   
                    End If
                End If
            Next u
            z_Input+=1
        Next i 
    Next t
    If uBNoFalsePositive=0 Then : Return Str_Result : End If
    u=1 : i=1
    While i<>0
        i = Instr( u, Str_Result, Str_Sep ) : str_tmp_1 = Right(Left(Str_Result, i-1 ), i-u)
        u=i+1 : If Instr(Str_Input, str_tmp_1)<>0 Then : str_tmp_2+= str_tmp_1+ Str_Sep : End If
    Wend        
    Return str_tmp_2
End Function 

Lost Zergling
Posts: 634
Joined: Dec 02, 2011 22:51
Location: France

Re: Idea or challenge ?!

Post by Lost Zergling »

For 100,000 strings to search in test #2, I get Mr. La Forge's previous warp factor of 200, but without the bugs or patches—thanks, Weasley!
The Instr emotion chip from Mr. Data is no longer required, and the results are more precise and complete by giving all multiple occurrences.
To do this, the .Private property RootPrivate had to be changed to Public. The operation is modified; the function expects two arrays passed as references: one for the found strings, and a second for the position indices.
In example #2 (no duplicate occurrences), InstrMulti competes with Instr till a subset of 350 search keywords in my configuration. Of course, the metrics are a bit arbitrary because the two functions Instr and InstrMulti don't do exactly the same thing, and these metrics depend heavily on the structure of the test set, but it nevertheless gives orders of magnitude.
InstrMulti will not be suitable for one-off operations because the time to fill the list (index construction) must also be taken into account overall, but it could be relevant in the case of browsing the contents of a file, for example.
In terms of evolution, the For t=1 Next t loop would be replaced by a table of correspondence between the variable (u) and the tokens available for Aside/Recover, so as not to have a performance loss on input strings of more than 250 characters.
So the final syntax would be as follows: 8)
Locates every occurrence of a substring or character set within a string
Function InStrMulti ( ByRef str As Const String, ByRef substring set As List, ByRef varlen StringArray() as String, ByRef varlen uIntegerArray() As uInteger, [ByRef SearchMode as uByte,] [ByRef uIntStart As uInteger ] ) As uInteger

Code: Select all

#Include once "D:\Basic\LZLE_.bi"  
' WITH CONST MAX_ASIDE=500 & change Byte to Integer in .Aside, .Recover & .AsideReset properties
Function InstrMulti(Str_Input As String, ListeQC As List, Str_Results() As String, uIntIndice() As uInteger, uBStopFirstFound As uByte=0, uIntStart As uInteger=0) As uInteger
    Dim As String str_tmp_1
    Dim as zString Ptr z_Input
    Dim As uInteger uInt_InputLen_0=Len(Str_Input), uInt_InputLen, i, u, t, IndiceRes
    Dim As uByte ubRevocated(500)
    ListeQC.RootPrivate : ListeQC.SeekMethod(0)
    For t=1 to uInt_InputLen_0 Step 250 ' 100 =>32 bits fbc32
        str_tmp_1= Mid(Str_Input, t, 500)  ' 200 => 32 bits fbc32
        uInt_InputLen=Len(str_tmp_1)
        z_Input= StrPtr(str_tmp_1) : z_Input+=uIntStart
        For i=1 to uInt_InputLen
            ubRevocated(i)=0 : ListeQC.AsideReset(i) 
            For u=1 to i
                If ubRevocated(u)=0 Then 
                    ListeQC.Recover(u) 
                    If ListeQC.HasTag(Chr((*z_Input)[0])) Then   
                        ListeQC.SeekMethod(1)
                        If ListeQC.Check Then
                            If uBStopFirstFound=1 Then : Return u : End If
                            If IndiceRes>=Ubound(Str_Results) Then : Redim Preserve Str_Results(Ubound(Str_Results)+100) : End If
                            If IndiceRes>=Ubound(uIntIndice) Then : Redim Preserve uIntIndice(Ubound(uIntIndice)+100) : End If
                            Str_Results(IndiceRes)=ListeQC.HashTag : uIntIndice(IndiceRes)=u : IndiceRes+=1
                            If ListeQC.Down Then : ListeQC.Aside(u) : Else : ubRevocated(u)=1 : ListeQC.SeekMethod(0)  : End If
                            ListeQC.RootPrivate : ListeQC.SeekMethod(0)
                        Else
                            If ListeQC.Down Then : ListeQC.Aside(u) : ubRevocated(u)=0 : Else : ubRevocated(u)=1 : ListeQC.SeekMethod(0)  : End If 
                            ListeQC.RootPrivate : ListeQC.SeekMethod(0)
                        End If
                    Else : ubRevocated(u)=1 
                        ListeQC.RootPrivate : ListeQC.Aside(u) : ListeQC.SeekMethod(0)   
                    End If
                End If
            Next u
            z_Input+=1
        Next i 
    Next t
    Return IndiceRes-1
End Function 

'    If uBNoFalsePositive=0 Then : Return Str_Result : End If
'    u=1 : i=1
'    While i<>0
'        i = Instr( u, Str_Result, Str_Sep ) : str_tmp_1 = Right(Left(Str_Result, i-1 ), i-u)
'        u=i+1 : If Instr(Str_Input, str_tmp_1)<>0 Then : str_tmp_2+= str_tmp_1+ Str_Sep : End If
'    Wend        
'    Return str_tmp_2


Dim ListeQC As List
Dim Str_Results() As string
Dim uIntIndice() As uInteger
Dim uIntIndiceClassic(100) As uInteger
Redim Str_Results(10) : Redim uIntIndice(100)
'Dim As string Str_Input = "78poissonchatTGHVBNI98virgulepointavionordinateurchienboite7654titiimprimantesourisAzertyuiopqsdfghjklmwxcvbn" 'Len Max=200
Dim As string Str_Input = "*NePYT98qsazerfdcxwAzertyuiopAzertylmwxcvbnAzertyuiopqsdfghjklmwxcvbnpoissonchatvirgulepointavionordinateurchienboite7654titiZimprimantesouris"
'Dim As string Str_Input = "GTCCYCTRCGTCCYCTRCTTXTXTRCTTXTXT7654EXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGTCCYCTRCTTXTXTRCTTXTXT7654EXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGTCCYCTRCTTXTXTRCTTXTXT7654EXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXTTXTXTRCTTXTXT7654EXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNEXTRRCKYNI?INYDRESXeYTYOPNFGHFsourisAzertyREZAWTYTYUIOPMLPOUNBGNOFDH*NedfghjklmwxcvqsazerfdcxwAzertyuiopAzertylmwxcvbnAzertyuiopqsdfghjklmwxcvbnpoissonchat"
Dim As string Str_Res_1, Str_Res_2
Dim As uByte uBStopFirstFound=0
Dim as double T1, T2
Dim As Integer i, w, x, IndiceInstrMulti, TestMaxi = 100000

    TestMaxi = 350

ListeQC.HashTag("virgule")
ListeQC.HashTag("poissonchatTGHVBNI")
ListeQC.HashTag("chienboite")
ListeQC.HashTag("Bar")
ListeQC.HashTag("ordi")
ListeQC.HashTag("poiss")
ListeQC.HashTag("isso")
ListeQC.HashTag("imprimante")
ListeQC.HashTag("oisson")
ListeQC.HashTag("oissonch")
ListeQC.HashTag("titi")

? "Loading values to FT search"
For i=5 to TestMaxi
    ListeQC.HashTag(str(i)  )
Next i

While ListeQC.Up : Wend : ListeQC.FastAside(1)
 ? "Loaded =" & ListeQC.NodeCount
    
dim as double t=timer
IndiceInstrMulti = InstrMulti(Str_Input, ListeQC, Str_Results(), uIntIndice(), uBStopFirstFound )
For i=0 To IndiceInstrMulti
    Str_Res_1 += Str_Results(i)+";"
Next i
T1= timer-t : 

x=0
t=timer
For i=5 to TestMaxi
    w=Instr(Str_Input, str(i)) 
    If w<>0 Then 
        Str_Res_2+=str(i)+";"  
        uIntIndiceClassic(x)=w : x+=1
    End If
Next i
T2= timer-t

ListeQC.Root
While ListeQC.KeyStep
    If Cint(ListeQC.Tag)=0 Then
        w=Instr(Str_Input, ListeQC.HashTag) ': 
        If w<>0 Then 
            Str_Res_2+=ListeQC.HashTag+ ";"  
            uIntIndiceClassic(x)=w : x+=1
        End If
    End If    
Wend
?
? "Using InstrMulti :"
? "Result=" & Str_Res_1
Str_Res_1=""
For i=0 to IndiceInstrMulti
    Str_Res_1+= Str(uIntIndice(i)) + ";"
Next i
? "Positions=" &  Str_Res_1
?
? "Using Instr inside a loop :"
? "Result=" & Str_Res_2
Str_Res_2=""
For i=0 to IndiceInstrMulti
    Str_Res_2+= Str(uIntIndiceClassic(i)) + ";"
Next i
? "Positions=" &  Str_Res_2
?
? T1
? T2
? "InstrMulti Warp Factor=" & Cint(Cdbl(T2/T1)*100)/100
?

' Just to check list's inegrity
ListeQC.Recycle
? "Garbage=" & ListeQC.GarbageCount
ListeQC.HashTag("virgule")
ListeQC.HashTag("poissonchatTGHVBNI")
ListeQC.HashTag("chienboite")
ListeQC.HashTag("Bar")
ListeQC.HashTag("ordi")
ListeQC.HashTag("poiss")
ListeQC.HashTag("isso")
ListeQC.HashTag("imprimante")
ListeQC.HashTag("oisson")
ListeQC.HashTag("oissonch")
ListeQC.HashTag("titi")
ListeQC.Root
'? "Loading values to FT search"
For i=5 to TestMaxi
    ListeQC.HashTag(str(i)  )
Next i

ListeQC.Destroy : gCollector.Destroy
Print "??=" & AllocateDeallocateCounter & " nodecount=" & ListeQC.NodeCount 
sleep : system

Lost Zergling
Posts: 634
Joined: Dec 02, 2011 22:51
Location: France

Re: Idea or challenge ?!

Post by Lost Zergling »

This latest version of InstrMulti fixes two (minor) bugs: duplicate occurrence detections due to unmanaged 250/500 overlaps, and the untested Start parameter.
The performance degradation issue on long strings has been addressed in several ways: a Borg collective submission mentions that ByPass would be much simpler and at least as efficient as a queue handling Aside/Recover indices. The minor drawback is that the strings to be detected must not exceed 250 characters, and in any case, lzle is not intended to handle excessively long keys.
str_tmp_1/Mid is replaced by pointers; the difference is imperceptible and rather in favor of the old code using Mid. However, there is no need to copy Str_Input into a variable, which is or should be cleaner and more memory-efficient. A change and new feature concerns the uBStopFirstFound parameter, which acts as a limit on the number of detected parameters, thus allowing for optimal detection. If this parameter is set to 255, there is no identification in the subset of the string concerned by the detection (faster), and the returned value is the position of the found occurrence, as with Instr, to meet the objective of a textual join.
In other cases, the returned value is the logical bound for the modified values ​​in the return result Arrays.
The performance is virtually unchanged; the result finally seems suitable for professional use. Thanks, Seven.

Code: Select all

#Include once "D:\Basic\LZLE_.bi"  
' WITH CONST MAX_ASIDE=500 & change Byte to Integer in .Aside, .Recover & .AsideReset properties
Function InstrMulti(Str_Input As String, ListeQC As List, Str_Results() As String, uIntIndice() As uInteger, uBStopFirstFound As uByte=0, uIntStart As uInteger=0) As uInteger
   ' Dim As String str_tmp_1
    Dim as zString Ptr z_Input
    Dim As uInteger uInt_InputLen_0=Len(Str_Input)-uIntStart, uInt_InputLen, i, u, t, IndiceRes
    Dim As uByte ubRevocated(500), ByPass=1
    ListeQC.RootPrivate : ListeQC.SeekMethod(0) : Str_Results(0)="" : uInt_InputLen=500
    For t=1 to uInt_InputLen_0 Step 250 
        If ByPass Then
            'Using Mid
    '        str_tmp_1= Mid(Str_Input, t, 500) 
    '        uInt_InputLen=Len(str_tmp_1)
    '        z_Input= StrPtr(str_tmp_1) : z_Input+=uIntStart            
            'Using Ptr
            If uInt_InputLen_0-t <250 Then : uInt_InputLen=uInt_InputLen_0-t+1 : End If
            z_Input= StrPtr(Str_Input) : z_Input+=t-1+uIntStart 
            
            For i=1 to uInt_InputLen
                ubRevocated(i)=0 : ListeQC.AsideReset(i) 
                For u=1 to i
                    If ubRevocated(u)=0 Then
                        ListeQC.Recover(u) 
                        If ListeQC.HasTag(Chr((*z_Input)[0])) Then   
                            ListeQC.SeekMethod(1)
                            If ListeQC.Check Then
                                If uBStopFirstFound=255 Then : Return u : End If
                                If IndiceRes>=Ubound(Str_Results) Then : Redim Preserve Str_Results(Ubound(Str_Results)+100) : End If
                                If IndiceRes>=Ubound(uIntIndice) Then : Redim Preserve uIntIndice(Ubound(uIntIndice)+100) : End If
                                If uBStopFirstFound<>0 AndAlso uBStopFirstFound<=IndiceRes Then : Return IndiceRes-1 : End If
                                Str_Results(IndiceRes)=ListeQC.HashTag : uIntIndice(IndiceRes)=u+t-1 : IndiceRes+=1
                                If ListeQC.Down Then : ListeQC.Aside(u) : Else : ubRevocated(u)=1 : ListeQC.SeekMethod(0)  : End If
                                ListeQC.RootPrivate : ListeQC.SeekMethod(0)
                            Else
                                If ListeQC.Down Then : ListeQC.Aside(u) : ubRevocated(u)=0 : Else : ubRevocated(u)=1 : ListeQC.SeekMethod(0)  : End If 
                                ListeQC.RootPrivate : ListeQC.SeekMethod(0)
                            End If
                        Else : ubRevocated(u)=1 
                            ListeQC.RootPrivate : ListeQC.Aside(u) : ListeQC.SeekMethod(0)   
                        End If
                    End If
                Next u
                z_Input+=1
            Next i 
        End If
        If ByPass=0 Then : ByPass=1 : Else : ByPass=0 : End If
    Next t
    Return IndiceRes-1
End Function 

Dim ListeQC As List
Dim Str_Results() As string
Dim uIntIndice() As uInteger
Dim uIntIndiceClassic(100) As uInteger
Redim Str_Results(10) : Redim uIntIndice(100)
'Dim As string Str_Input = "78poissonchatTGHVBNI98virgulepointavionordinateurchienboite7654titiimprimantesourisAzertyuiopqsdfghjklmwxcvbn" 'Len Max=200
Dim As string Str_Input = "*NePYT98qsazerfdcxwAzertyuiopAzertylmwxcvbnAzertyuiopqsdfghjklmwxcvbnpoissonchatvirgulepointavionordinateurchienboite7654titiZimprimantesouris"
'Dim As string Str_Input = "GTCCYCTRCGTCCYCTRCTTXTXTRCTTXTXT7654EXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGTCCYCTRCTTXTXTRCTTXTXT7654EXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGTCCYCTRCTTXTXTRCTTXTXT7654EXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXTTXTXTRCTTXTXT7654EXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNEXTRRCKYNI?INYDRESXeYTYOPNFGHFsourisAzertyREZAWTYTYUIOPMLPOUNBGNOFDH*NedfghjklmwxcvqsazerfdcxwAzertyuiopAzertylmwxcvbnAzertyuiopqsdfghjklmwxcvbnpoissonchat"
Dim As string Str_Res_1, Str_Res_2
Dim As uByte uBStopFirstFound=0
Dim as double T1, T2
Dim As Integer i, w, x, IndiceInstrMulti, TestMaxi = 100000

 '   TestMaxi = 350

ListeQC.HashTag("virgule")
ListeQC.HashTag("poissonchatTGHVBNI")
ListeQC.HashTag("chienboite")
ListeQC.HashTag("Bar")
ListeQC.HashTag("ordi")
ListeQC.HashTag("poiss")
ListeQC.HashTag("isso")
ListeQC.HashTag("imprimante")
ListeQC.HashTag("oisson")
ListeQC.HashTag("oissonch")
ListeQC.HashTag("titi")

? "Loading values to FT search"
For i=5 to TestMaxi
    ListeQC.HashTag(str(i)  )
Next i

While ListeQC.Up : Wend : ListeQC.FastAside(1)
 ? "Loaded =" & ListeQC.NodeCount
    
dim as double t=timer
IndiceInstrMulti = InstrMulti(Str_Input, ListeQC, Str_Results(), uIntIndice(), uBStopFirstFound )
For i=0 To IndiceInstrMulti
    Str_Res_1 += Str_Results(i)+";"
Next i
T1= timer-t : 

x=0
t=timer
For i=5 to TestMaxi
    w=Instr(Str_Input, str(i)) 
    If w<>0 Then 
        Str_Res_2+=str(i)+";"  
        uIntIndiceClassic(x)=w : x+=1
    End If
Next i
T2= timer-t

ListeQC.Root
While ListeQC.KeyStep
    If Cint(ListeQC.Tag)=0 Then
        w=Instr(Str_Input, ListeQC.HashTag) ': 
        If w<>0 Then 
            Str_Res_2+=ListeQC.HashTag+ ";"  
            uIntIndiceClassic(x)=w : x+=1
        End If
    End If    
Wend
?
? "Using InstrMulti :"
? "Result=" & Str_Res_1
Str_Res_1=""
For i=0 to IndiceInstrMulti
    Str_Res_1+= Str(uIntIndice(i)) + ";"
Next i
? "Positions=" &  Str_Res_1
?
? "Using Instr inside a loop :"
? "Result=" & Str_Res_2
Str_Res_2=""
For i=0 to IndiceInstrMulti
    Str_Res_2+= Str(uIntIndiceClassic(i)) + ";"
Next i
? "Positions=" &  Str_Res_2
?
? T1
? T2
? "InstrMulti Warp Factor=" & Cint(Cdbl(T2/T1)*100)/100
?

' Just to check list's inegrity
ListeQC.Recycle
? "Garbage=" & ListeQC.GarbageCount
ListeQC.HashTag("virgule")
ListeQC.HashTag("poissonchatTGHVBNI")
ListeQC.HashTag("chienboite")
ListeQC.HashTag("Bar")
ListeQC.HashTag("ordi")
ListeQC.HashTag("poiss")
ListeQC.HashTag("isso")
ListeQC.HashTag("imprimante")
ListeQC.HashTag("oisson")
ListeQC.HashTag("oissonch")
ListeQC.HashTag("titi")
ListeQC.Root
'? "Loading values to FT search"
For i=5 to TestMaxi
    ListeQC.HashTag(str(i)  )
Next i

ListeQC.Destroy : gCollector.Destroy
Print "??=" & AllocateDeallocateCounter & " nodecount=" & ListeQC.NodeCount 
sleep : system

Edited : issue on uInt_InputLen=>500

To make it a bit seriously, a short documentation in the taste of FB.
InstrMulti

Locates every occurrence of a substring or character of a set (List) within a string Or
Locates 'uBStopFirstFound' first occurrences of a substring or character of a set (List) within a string Or
Locates first/if occurrence of a substring or character of a set (List) within a string (without identify the hit in the set uBStopFirstFound=255)

Syntax
Function InStrMulti ( ByRef str As Const String, ByRef substring set As List, ByRef varlen StringArray() as String, ByRef varlen uIntegerArray() As uInteger, [ByRef SearchMode as uByte,] [ByRef uIntStart As uInteger ] ) As uInteger

Usage
LogicalUbound = InStrMulti( str, List, StrArray(), uIntegerArray(), [ Mode (<>255) ], [ start, ] ) Or
First = InStrMulti( str, List, StrArray(), uIntegerArray(), 255, [ start, ] )

Parameters
str : string to search
List : list of strings to search for in str
StrArray() : string array passed byref for results
uIntegerArray() : uInteger array passed byref for results positions
Mode : 0=All hits, 1-254=max hits threashold, 255=Just locate first hit
Start : starting position

Return Value
Mode 0-254 : LogicalUbound for reading values in StrArray and uIntegerArray
ByRef : StrArray() and uIntegerArray()
Mode 255 : the position of the first occurrence of one of the substring of the substring list in str

Limitations
  • Substrings size in list =< 250 characters per substring
  • Number of substrings to search for should be >500 (vs Instr in a loop)
  • Require a specific tool
  • Not deeply tested - As is
  • Usually not efficient for one-off operations
Addendum :
In lzle .DropAll property, please replace :
If this.IsDestroyed=1 Then : Return 0 : End If : this.NodeRecycle : this.NodeRecycle2
By
If this.IsDestroyed=1 Then : Return 0 : End If : this.NodeRecycle : this.NodeRecycle2 : this.TrackCompute : this.Recycle ' PATCHED
hope it will works better
Lost Zergling
Posts: 634
Joined: Dec 02, 2011 22:51
Location: France

Re: Idea or challenge ?!

Post by Lost Zergling »

Cause : 1 fix and one precision
Precision : A Recycled list can become circular, this is NOT a malfunction.
Hard to find a bug wich was not a 'true' one :mrgreen: :)
When "hacking" the index directly to use each character as key, the parse use is not controled by the default instruction set (.root, .KeyStep and so on), so we have to check not to parse out of string scope.
Fix : uIntCompute=u+t-1 : If uIntCompute >= uInt_InputL Then : Return IndiceRes-1 : End If
Passing StrPtr(string)+Len(string) is a thick faster than passing the string itself, indeed !
But the difference tend to increase with the string size and the string len variability !
So, using a strptr sounds the proper choice : the gain is 2-10% and above, plus lesser memory fragmentation risks.
My tests tend to point out that there is quasi imperceptible speed difference evolution between Instr and InstrMulti as the string size increase.
Perhaps this new version of InstrMulti should be rename InstrPtrMulti : .this will probably depend on a decision by the Admiralty, but after the outcome of the vote of the United Federation of Planets Assembly..

Code: Select all

#Include once "D:\Basic\LZLE_.bi"  
' WITH CONST MAX_ASIDE=500 & change Byte to Integer in .Aside, .Recover & .AsideReset properties
Function InstrMulti(ByVal z_Input_ As zString Ptr, Byval uInt_InputL As uInteger, ListeQC As List, Str_Results() As String, uIntIndice() As uInteger, uBStopFirstFound As uByte=0, uIntStart As uInteger=0) As uInteger
  '  Dim As String str_tmp_1
    If Ubound(Str_Results) =-1 Then : Redim Str_Results(1) : End If
    If Ubound(uIntIndice) =-1 Then : Redim uIntIndice(1) : End If
    Dim as zString Ptr z_Input
 '   Dim As uInteger uInt_InputLen_0=Len(Str_Input)-uIntStart, uInt_InputLen, i, u, t, IndiceRes
    Dim As uInteger uInt_InputLen_0=uInt_InputL-uIntStart, uInt_InputLen, i, u, t, IndiceRes, uIntCompute
    Dim As uByte ubRevocated(500), ByPass=1, uBEndGame=0
    ListeQC.RootPrivate : ListeQC.SeekMethod(0) : Str_Results(0)="" : uInt_InputLen=500
    For t=1 to uInt_InputLen_0 Step 250 
        If ByPass Then
            'Using Mid
         '   str_tmp_1= Mid(Str_Input, t, 500) 
         '   uInt_InputLen=Len(str_tmp_1)
         '   z_Input= StrPtr(str_tmp_1) : z_Input+=uIntStart            
            'Using Ptr
            If uInt_InputLen_0-t <250 Then : uInt_InputLen=uInt_InputLen_0-t+1 : End If
          '  z_Input= StrPtr(Str_Input) : 
            z_Input = z_Input_
            z_Input+=t-1+uIntStart             
            For i=1 to uInt_InputLen
                ubRevocated(i)=0 : ListeQC.AsideReset(i) 
                For u=1 to i
                    If ubRevocated(u)=0 Then
                        ListeQC.Recover(u) 
                        If ListeQC.HasTag(Chr((*z_Input)[0])) Then   
                            ListeQC.SeekMethod(1)
                            If ListeQC.Check Then
                                If uBStopFirstFound=255 Then : Return u ': End If
                                ElseIf uBStopFirstFound<>0 AndAlso uBStopFirstFound<=IndiceRes Then : Return IndiceRes-1 
                                End If
                                If IndiceRes>=Ubound(Str_Results) Then : Redim Preserve Str_Results(Ubound(Str_Results)+100) : If IndiceRes>=Ubound(Str_Results) Then : Return IndiceRes-1 : End If : End If
                                If IndiceRes>=Ubound(uIntIndice) Then : Redim Preserve uIntIndice(Ubound(uIntIndice)+100) :  If IndiceRes>=Ubound(uIntIndice) Then : Return IndiceRes-1 : End If : End If                            
                                uIntCompute=u+t-1 : If uIntCompute >= uInt_InputL Then : Return IndiceRes-1 : End If
                                Str_Results(IndiceRes)=ListeQC.HashTag : uIntIndice(IndiceRes)=uIntCompute : IndiceRes+=1
                                If ListeQC.Down Then : ListeQC.Aside(u) : Else : ubRevocated(u)=1 : ListeQC.SeekMethod(0)  : End If
                                ListeQC.RootPrivate : ListeQC.SeekMethod(0)
                            Else
                                If ListeQC.Down Then : ListeQC.Aside(u) : ubRevocated(u)=0 : Else : ubRevocated(u)=1 : ListeQC.SeekMethod(0) : End If 
                                ListeQC.RootPrivate : ListeQC.SeekMethod(0)
                            End If
                        Else : ubRevocated(u)=1
                            ListeQC.RootPrivate : ListeQC.Aside(u) : ListeQC.SeekMethod(0)
                        End If
                    End If
                Next u
                z_Input+=1
            Next i 
        End If
        If ByPass=0 Then : ByPass=1 : Else : ByPass=0 : End If
    Next t
    Return IndiceRes-1
End Function 

Dim ListeQC As List
Dim Str_Results() As string
Dim uIntIndice() As uInteger
Dim uIntIndiceClassic(100) As uInteger
'? "*" & Ubound(Str_Results) : sleep
'Redim Str_Results(10) : Redim uIntIndice(100)
'Dim As string Str_Input = "78poissonchatTGHVBNI98virgulepointavionordinateurchienboite7654titiimprimantesourisAzertyuiopqsdfghjklmwxcvbn" 'Len Max=200
Dim As string Str_Input = "*NePYT98qsazerfdcxwAzertyuiopAzertylmwxcvbnAzertyuiopqsdfghjklmwxcvbnpoissonchatvirgulepointavionordinateurchienboite7654titiZimprimantesouris"
'Dim As string Str_Input = "GTCCYCTRCGTCCYCTRCTTXTXTRCTTXTXT76GTCCYCTRCGTCCYCTRCTTXTXTRCTTXTXT7654EXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGTCCYCTRCTTXTXTRCGTCCYCTRCGTCCYCTRCTTXTXTRCTTXTXT7654EXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGTCCYCTRCTTXTXTRCTTXTXT7654EXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGTCCYCTRCTTXTXTRCTTXTXT7654EXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXTTXTXTRCTTXTXT7654EXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNEXTRRCKYNI?INYDRESXeYTYOPNFGHFsourisAzertyREZAWTYTYUIOPMLPOUNBGNOFDH*NedfghjklmwxcvqsazerfdcxwAzertyuiopAzertylmwxcvbnAzertyuiopqsdfghjklmwxcvbnpoissonchat"
'Dim As string Str_Input = "TXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXTTXTXTRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNEXTRRCKYNI?INYDRESXeYTYOPNFGHFsourisAzertyREZAWTYTYUIOPMLPOUNBGNOFDH*NedfghjklmwxcvqsazerfdcxwAzertyuiopAzertylmwxcvbnAzertyuiopqsdfghjklmwxcvbnpoissonchat"
 '   Dim As string Str_Input = "GTCCYCTRCGTCCYCTRCTTXTXTRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGTCCYCTRCTTXTXTRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGTCCYCTRCTTXTXTRCTTXGTCCYCTRCGTCCYCTRCTTXTXTRCTTXTXT76GTCCYCTRCGTCCYCTRCTTXTXTRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGT" &_
 '   "CCYCTRCTTXTXTRCGTCCYCTRCGTCCYCTRCTTXTXTRCTTXTXT76GTCCYCTRCGTCCYCTRCTTXTXTRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGTCCYCTRCTTXTXTRCGTCCYCTRCGTCCYCTRCTTXTXTRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGTCCYCTRCTTXTXTRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGTCCYCT" &_
 '   "RCTTXTXTRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXTTXTXTRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNEXTRRCKYNI?INYDRESXeYTYOPNFGHFsourisAzertyREZAWTYTYUIOPMLPOUNBGNOFDH*NedfghjklmwxcvqsazerfdcxwAzertyuiopAzertylmwxcvbnAzertyuiopqsdfghjklmwxcvbnpoissonchat"


Dim As string Str_Res_1, Str_Res_2
Dim As uByte uBStopFirstFound=0
Dim as double T1, T2
Dim As Integer i, w, x, IndiceInstrMulti, TestMaxi = 100000

    TestMaxi = 500
'ListeQC.HashTag("Bouh")
'ListeQC.Recycle

ListeQC.HashTag("virgule") 
ListeQC.HashTag("poissonchatTGHVBNI")
ListeQC.HashTag("chienboite")
ListeQC.HashTag("Bar")
ListeQC.HashTag("ordi")
ListeQC.HashTag("poiss")
ListeQC.HashTag("isso")
ListeQC.HashTag("imprimante")
ListeQC.HashTag("oisson")
ListeQC.HashTag("oissonch")
ListeQC.HashTag("titi")
ListeQC.Root
? "Loading values to FT search"
For i=5 to TestMaxi
    ListeQC.HashTag(str(i)  )
Next i


i=0
ListeQC.Root
While ListeQC.KeyStep : i+=1 :  wend
? "i=" & i

While ListeQC.Up : Wend : ListeQC.FastAside(1)
 ? "Loaded =" & ListeQC.NodeCount
    
dim as double t=timer
IndiceInstrMulti = InstrMulti(StrPtr(Str_Input), Len(Str_Input), ListeQC, Str_Results(), uIntIndice(), uBStopFirstFound )
For i=0 To IndiceInstrMulti
    Str_Res_1 += Str_Results(i)+";"
Next i
T1= timer-t : 

x=0
t=timer
For i=5 to TestMaxi
    w=Instr(Str_Input, str(i)) 
    If w<>0 Then 
        Str_Res_2+=str(i)+";"  
        uIntIndiceClassic(x)=w : x+=1
    End If
Next i
T2= timer-t

ListeQC.Root
While ListeQC.KeyStep
    If Cint(ListeQC.Tag)=0 Then
        w=Instr(Str_Input, ListeQC.HashTag) ': 
        If w<>0 Then 
            Str_Res_2+=ListeQC.HashTag+ ";"  
            uIntIndiceClassic(x)=w : x+=1
        End If
    End If    
Wend
?
? "Using InstrMulti :"
? "Result=" & Str_Res_1
Str_Res_1=""
For i=0 to IndiceInstrMulti
    Str_Res_1+= Str(uIntIndice(i)) + ";"
Next i
? "Positions=" &  Str_Res_1
?
? "Using Instr inside a loop :"
? "Result=" & Str_Res_2
Str_Res_2=""
For i=0 to IndiceInstrMulti
    Str_Res_2+= Str(uIntIndiceClassic(i)) + ";"
Next i
? "Positions=" &  Str_Res_2
?
? T1
? T2
? "InstrMulti Warp Factor=" & Cint(Cdbl(T2/T1)*100)/100
?


'ListeQC.Destroy : gCollector.Destroy
'Print "??=" & AllocateDeallocateCounter & " nodecount=" & ListeQC.NodeCount 
'sleep : system

' Just to check list's integrity
ListeQC.Recycle
? "Garbage=" & ListeQC.GarbageCount
ListeQC.HashTag("virgule")
ListeQC.HashTag("poissonchatTGHVBNI")
ListeQC.HashTag("chienboite")
ListeQC.HashTag("Bar")
ListeQC.HashTag("ordi")
ListeQC.HashTag("poiss")
ListeQC.HashTag("isso")
ListeQC.HashTag("imprimante")
ListeQC.HashTag("oisson")
ListeQC.HashTag("oissonch")
ListeQC.HashTag("titi")
ListeQC.Root
'? "Loading values to FT search"
For i=5 to TestMaxi
    ListeQC.HashTag(str(i)  )
Next i

For i=0 To Ubound(Str_Results) : Str_Results(i)="" : Next i
For i=0 To Ubound(uIntIndice) : uIntIndice(i)=0 : Next i

ListeQC.Root
Str_Res_1=""
IndiceInstrMulti = InstrMulti(StrPtr(Str_Input), Len(Str_Input), ListeQC, Str_Results(), uIntIndice(), uBStopFirstFound )
For i=0 To IndiceInstrMulti
    Str_Res_1 += Str_Results(i)+";"
Next i
? "Using InstrMulti :"
? "Result=" & Str_Res_1
i=0
While ListeQC.KeyStep : i+=1 :  wend
? "i=" & i

ListeQC.Destroy : gCollector.Destroy
Print "??=" & AllocateDeallocateCounter & " nodecount=" & ListeQC.NodeCount 
sleep : system

Lost Zergling
Posts: 634
Joined: Dec 02, 2011 22:51
Location: France

Re: Idea or challenge ?!

Post by Lost Zergling »

Reason : code cleaning and performance improvments : Warp 300 on ex#2 (100000 val) and challenging Instr till 255 values 8)
It is done gradually, but it is difficult to move too quickly because of the engine.
Important : new properties as well as RootPrivate are not designed to be 'multi-context', their usefullness is mostly to gain speed in some circumstances. HasTag should have been compatible with .Recycle propertie, perhaps bug or a deficiency on this issue. :?

Code: Select all

#Include once "D:\Basic\LZLE_.bi"  
' WITH CONST MAX_ASIDE=500 & change Byte to Integer in .Aside, .Recover & .AsideReset properties
Function InstrMulti(ByVal z_Input_ As zString Ptr, Byval uInt_InputL As uInteger, ListeQC As List, Str_Results() As String, uIntIndice() As uInteger, uBStopFirstFound As uByte=0, uIntStart As uInteger=0) As uInteger
    Dim as zString Ptr z_Input
    Dim As uInteger uInt_InputLen_0=uInt_InputL-uIntStart, uInt_InputLen, i, u, t, IndiceRes, uIntCompute
    Dim As uByte ubRevocated(500), ByPass=1
    ListeQC.RootPrivate : uInt_InputLen=500
    For t=1 to uInt_InputLen_0 Step 250 
        If ByPass Then
            If uInt_InputLen_0-t <250 Then : uInt_InputLen=uInt_InputLen_0-t+1 : End If
            z_Input = z_Input_
            z_Input+=t-1+uIntStart             
            For i=1 to uInt_InputLen
                ubRevocated(i)=0 : ListeQC.AsideReset(i) 
                For u=1 to i
                    If ubRevocated(u)=0 Then
                        uIntCompute=u+t-1 : If uIntCompute > uInt_InputL Then : Return IndiceRes-1 :  End If
                        ListeQC.Recover(u) 
                        If ListeQC.HasTagFast(Chr((*z_Input)[0])) Then   
                            If ListeQC.Check Then
                                If uBStopFirstFound=255 Then : Return u 
                                ElseIf uBStopFirstFound<>0 AndAlso uBStopFirstFound<=IndiceRes Then : Return IndiceRes-1 
                                End If
                                If IndiceRes=Ubound(Str_Results)+1 Then : Redim Preserve Str_Results(Ubound(Str_Results)+100) : If IndiceRes=Ubound(Str_Results) Then : Return IndiceRes-1 : End If : End If
                                If IndiceRes=Ubound(uIntIndice)+1 Then : Redim Preserve uIntIndice(Ubound(uIntIndice)+100) : If IndiceRes=Ubound(uIntIndice) Then : Return IndiceRes-1 : End If : End If                            
                                Str_Results(IndiceRes)=ListeQC.HashTag : uIntIndice(IndiceRes)=uIntCompute : IndiceRes+=1
                                If ListeQC.Down Then : ListeQC.Aside(u) : Else : ubRevocated(u)=1 : End If
                            Else
                                If ListeQC.Down Then : ListeQC.Aside(u) : ubRevocated(u)=0 : Else : ubRevocated(u)=1  : End If
                            End If                            
                        Else 
                            ubRevocated(u)=1
                        End If
                        ListeQC.RootPrivate
                    End If                    
                Next u
                z_Input+=1
            Next i 
        End If
        If ByPass=0 Then : ByPass=1 : Else : ByPass=0 : End If
    Next t
    Return IndiceRes-1
End Function 

Dim ListeQC As List
Dim Str_Results() As string
Dim uIntIndice() As uInteger
Dim uIntIndiceClassic(100) As uInteger
'? "*" & Ubound(Str_Results) : sleep
'Redim Str_Results(10) : Redim uIntIndice(100)
'Dim As string Str_Input = "78poissonchatTGHVBNI98virgulepointavionordinateurchienboite7654titiimprimantesourisAzertyuiopqsdfghjklmwxcvbn" 'Len Max=200
'Dim As string Str_Input = "*NePYT98qsazerfdcxwAzertyuiopAzertylmwxcvbnAzertyuiopqsdfghjklmwxcvbnpoissonchatvirgulepointavionordinateurchienboite7654titiZimprimantesouris"
'Dim As string Str_Input = "GTCCYCTRCGTCCYCTRCTTXTXTRCTTXTXT76GTCCYCTRCGTCCYCTRCTTXTXTRCTTXTXT7654EXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGTCCYCTRCTTXTXTRCGTCCYCTRCGTCCYCTRCTTXTXTRCTTXTXT7654EXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGTCCYCTRCTTXTXTRCTTXTXT7654EXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGTCCYCTRCTTXTXTRCTTXTXT7654EXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXTTXTXTRCTTXTXT7654EXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNEXTRRCKYNI?INYDRESXeYTYOPNFGHFsourisAzertyREZAWTYTYUIOPMLPOUNBGNOFDH*NedfghjklmwxcvqsazerfdcxwAzertyuiopAzertylmwxcvbnAzertyuiopqsdfghjklmwxcvbnpoissonchat"
'Dim As string Str_Input = "TXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXTTXTXTRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNEXTRRCKYNI?INYDRESXeYTYOPNFGHFsourisAzertyREZAWTYTYUIOPMLPOUNBGNOFDH*NedfghjklmwxcvqsazerfdcxwAzertyuiopAzertylmwxcvbnAzertyuiopqsdfghjklmwxcvbnpoissonchat"
    Dim As string Str_Input = "GTCCYCTRCGTCCYCTRCTTXTXTRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGTCCYCTRCTTXTXTRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGTCCYCTRCTTXTXTRCTTXGTCCYCTRCGTCCYCTRCTTXTXTRCTTXTXT76GTCCYCTRCGTCCYCTRCTTXTXTRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGT" &_
    "CCYCTRCTTXTXTRCGTCCYCTRCGTCCYCTRCTTXTXTRCTTXTXT76GTCCYCTRCGTCCYCTRCTTXTXTRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGTCCYCTRCTTXTXTRCGTCCYCTRCGTCCYCTRCTTXTXTRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGTCCYCTRCTTXTXTRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGTCCYCT" &_
    "RCTTXTXTRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXTTXTXTRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNEXTRRCKYNI?INYDRESXeYTYOPNFGHFsourisAzertyREZAWTYTYUIOPMLPOUNBGNOFDH*NedfghjklmwxcvqsazerfdcxwAzertyuiopAzertylmwxcvbnAzertyuiopqsdfghjklmwxcvbnpoissonchat"


Dim As string Str_Res_1, Str_Res_2
Dim As uByte uBStopFirstFound=0
Dim as double T1, T2
Dim As Integer i, w, x, IndiceInstrMulti, TestMaxi = 100000

    TestMaxi = 255
'ListeQC.HashTag("Bouh")
'ListeQC.Recycle

ListeQC.HashTag("virgule") 
ListeQC.HashTag("poissonchatTGHVBNI")
ListeQC.HashTag("chienboite")
ListeQC.HashTag("Bar")
ListeQC.HashTag("ordi")
ListeQC.HashTag("poiss")
ListeQC.HashTag("isso")
ListeQC.HashTag("imprimante")
ListeQC.HashTag("oisson")
ListeQC.HashTag("oissonch")
ListeQC.HashTag("titi")
ListeQC.Root
? "Loading values to FT search"
For i=5 to TestMaxi
    ListeQC.HashTag(str(i)  )
Next i


i=0
ListeQC.Root
While ListeQC.KeyStep : i+=1 :  wend
? "i=" & i
ListeQC.RootPrivate
'While ListeQC.Up : Wend : 
'ListeQC.FastAside(1)
 ? "Loaded =" & ListeQC.NodeCount
    
dim as double t=timer
IndiceInstrMulti = InstrMulti(StrPtr(Str_Input), Len(Str_Input), ListeQC, Str_Results(), uIntIndice(), uBStopFirstFound )
For i=0 To IndiceInstrMulti
    Str_Res_1 += Str_Results(i)+";"
Next i
T1= timer-t : 

x=0
t=timer
For i=5 to TestMaxi
    w=Instr(Str_Input, str(i)) 
    If w<>0 Then 
        Str_Res_2+=str(i)+";"  
        uIntIndiceClassic(x)=w : x+=1
    End If
Next i
T2= timer-t

ListeQC.Root
While ListeQC.KeyStep
    If Cint(ListeQC.Tag)=0 Then
        w=Instr(Str_Input, ListeQC.HashTag) ': 
        If w<>0 Then 
            Str_Res_2+=ListeQC.HashTag+ ";"  
            uIntIndiceClassic(x)=w : x+=1
        End If
    End If    
Wend
?
? "Using InstrMulti :"
? "Result=" & Str_Res_1
Str_Res_1=""
For i=0 to IndiceInstrMulti
    Str_Res_1+= Str(uIntIndice(i)) + ";"
Next i
? "Positions=" &  Str_Res_1
?
? "Using Instr inside a loop :"
? "Result=" & Str_Res_2
Str_Res_2=""
For i=0 to IndiceInstrMulti
    Str_Res_2+= Str(uIntIndiceClassic(i)) + ";"
Next i
? "Positions=" &  Str_Res_2
?
? T1
? T2
? "InstrMulti Warp Factor=" & Cint(Cdbl(T2/T1)*100)/100
?


'ListeQC.Destroy : gCollector.Destroy
'Print "??=" & AllocateDeallocateCounter & " nodecount=" & ListeQC.NodeCount 
'sleep : system

' Just to check list's integrity
ListeQC.Recycle
? "Garbage=" & ListeQC.GarbageCount
ListeQC.HashTag("virgule")
ListeQC.HashTag("poissonchatTGHVBNI")
ListeQC.HashTag("chienboite")
ListeQC.HashTag("Bar")
ListeQC.HashTag("ordi")
ListeQC.HashTag("poiss")
ListeQC.HashTag("isso")
ListeQC.HashTag("imprimante")
ListeQC.HashTag("oisson")
ListeQC.HashTag("oissonch")
ListeQC.HashTag("titi")
ListeQC.Root
'? "Loading values to FT search"
For i=5 to TestMaxi
    ListeQC.HashTag(str(i)  )
Next i

For i=0 To Ubound(Str_Results) : Str_Results(i)="" : Next i
For i=0 To Ubound(uIntIndice) : uIntIndice(i)=0 : Next i

ListeQC.Root
Str_Res_1=""
IndiceInstrMulti = InstrMulti(StrPtr(Str_Input), Len(Str_Input), ListeQC, Str_Results(), uIntIndice(), uBStopFirstFound )
For i=0 To IndiceInstrMulti
    Str_Res_1 += Str_Results(i)+";"
Next i
? "Using InstrMulti :"
? "Result=" & Str_Res_1
i=0
While ListeQC.KeyStep : i+=1 :  wend
? "i=" & i

ListeQC.Destroy : gCollector.Destroy
Print "??=" & AllocateDeallocateCounter & " nodecount=" & ListeQC.NodeCount 
sleep : system
Summarize of modiifcations to lzle :
- RootPrivate Propertie send to Public properties
- New Public Propertie AsideReset : Property List.AsideReset(by As Integer) As Byte : Lcontext(by).pNode=0 : Return 1 : End Property
- New Public Propertie HasTagFast :

Code: Select all

Property List.HasTagFast(str_Tag As String) As Byte
    Dim As  ListNode Ptr  pTemp = this.pLastNode
    If pTemp->Tag0=str_Tag Then : uB_tmp=1 : Else : uB_tmp=0 : End If
    While (pTemp->pPrev <> pFirstNode->pPrev And  uB_tmp=0  AND pTemp <> this.pGarbage )
        pTemp = pTemp->pPrev : If pTemp->Tag0=str_Tag Then : uB_tmp=1 : End If 
    Wend         
    If uB_tmp=1  Then : this.pSearchNode=pTemp : pNode=pTemp  : Return 1 : Else :  Return 0 : End If    
End Property
- New Public Properties FastAside :
Property List.FastAside As Byte : Return this.uB_FastAside : End Property
Property List.FastAside(by As Byte) As Byte : this.uB_FastAside=by : Return 1 : End Property
& "this.TrackSecure" is replaced by : If this.uB_FastAside=0 Then : this.TrackSecure : End If in Aside & Recover Properties
Lost Zergling
Posts: 634
Joined: Dec 02, 2011 22:51
Location: France

Re: Idea or challenge ?!

Post by Lost Zergling »

The idea is to continue de-prototyping the InstrMulti function. In the same way we replaced Instr and Mid with pointers, in the same way the less "low level" functions of lzle, that is to say those which use loops can usefully be replaced by pointers, in particular the .RootPrivate and .HashTag properties, the goal being to free ourselves as much as possible from the context control functionalities (context-agnostic = resource-consuming) to directly control the parsing of the list from the main algorithm of InstrMulti. Of course, the .HasTag (or .HasTagFast) property cannot avoid a search loop, because the matching of the values ​​must be checked at some point. .RootPrivate is replaced by Aside/Recover(501) (Aside(0)/Recover(0) is reserved), and .HashTag is replaced by retrieving the keyword from the found string (via hierarchical list search parsing) via pointers rather than from the list. This is possible as long as the matching between the string and list parsing is synchronized. We were able to verify this in the previous prototype. Additionally, .HastTagFast has been slightly reworked, but the impact seems minimal.
The speed gain is real: 8-12%, but it is only noticeable on strings that are not too long. InstrMulti competes with Instr starting at 230-240 search substrings (instead of 250-260) and goes up to Warp 336 (test #2, 100,000 kwds). With a large number of function calls, a lower memory fragmentation impact is also expected.
For the development of InstrMulti, lzle ultimately appears relevant as an underlying hierarchical indexing engine (scalable and recyclable), but also as a prototyping tool (instruction set scalability).

Cons : InstrMulti is not tested/restricted on:
- High-volume search strings
- 250-character limit for each search substring
- Does Not support Chr(0) and some Chr() reserved (lzle), these specific should be filtered using legacy Instr loop

Adv :
- Much more efficient as the subset of substring to search for in string is big
- More efficient as we need to find every occurences and as there are some
- The maximum number of occurences to search for can be specified
- Code is perhaps more clear and readable using InstrMulti vs imbricated loops using Instr
- Source code of InstrMulti can be adapted for specific purpose optimization

Code: Select all

#Include once "D:\Basic\LZLE_.bi"  
' WITH CONST MAX_ASIDE=500 & change Byte to Integer in .Aside, .Recover & .AsideReset properties
Function InstrMulti(ByVal z_Input_ As zString Ptr, Byval uInt_InputL As uInteger, ListeQC As List, Str_Results() As String, uIntIndice() As uInteger, uBStopFirstFound As uByte=0, uIntStart As uInteger=0) As uInteger
    Dim as zString Ptr z_Input, y_Trace, x_Pos
    Dim As uInteger uInt_InputLen_0=uInt_InputL-uIntStart, uInt_InputLen, i, u, t, IndiceRes, uIntCompute, RPcumul
    Dim As uByte ubRevocated(500), ByPass=1, uBkeywdLen, ubtest
    ListeQC.RootPrivate : uInt_InputLen=500 : ListeQC.Aside(501)
    For t=1 to uInt_InputLen_0 Step 250 
        If ByPass Then
            If uInt_InputLen_0-t <250 Then : uInt_InputLen=uInt_InputLen_0-t+1 : End If
            z_Input = z_Input_
            z_Input+=t-1+uIntStart
            For i=1 to uInt_InputLen
                ubRevocated(i)=0 : ListeQC.AsideReset(i) 
                For u=1 to i
                    If ubRevocated(u)=0 Then
                        uIntCompute=u+t-1 : If uIntCompute > uInt_InputL Then : Return IndiceRes-1 : End If
                        ListeQC.Recover(u) 
                        If ListeQC.HasTagFast(Chr((*z_Input)[0])) Then   
                            uBkeywdLen=i-u+1
                            If ListeQC.Check Then                                
                                If uBStopFirstFound=255 Then : Return u 
                                ElseIf uBStopFirstFound<>0 AndAlso uBStopFirstFound<=IndiceRes Then : Return IndiceRes-1 
                                End If
                                If IndiceRes=Ubound(Str_Results)+1 Then : Redim Preserve Str_Results(Ubound(Str_Results)+100) : If IndiceRes=Ubound(Str_Results) Then : Return IndiceRes-1 : End If : End If
                                If IndiceRes=Ubound(uIntIndice)+1 Then : Redim Preserve uIntIndice(Ubound(uIntIndice)+100) : If IndiceRes=Ubound(uIntIndice) Then : Return IndiceRes-1 : End If : End If   
                            '   ******** Using pointers directly on parsed string
                                y_Trace=z_Input_+u-2+t
                                x_Pos=y_Trace+uBkeywdLen ' Str_Results(IndiceRes)=Left(*y_Trace, uBkeywdLen)
                                ubtest=x_Pos[0][0] : x_Pos[0][0]=0                               
                                Str_Results(IndiceRes)=*y_Trace
                                x_Pos[0][0]=ubtest
                            '   ******** Or .. Using List HashTag  Property
                    '           Str_Results(IndiceRes)=ListeQC.HashTag 
                            '   ******** 
                                uIntIndice(IndiceRes)=uIntCompute : IndiceRes+=1 : uBkeywdLen=1
                                If ListeQC.Down Then : ListeQC.Aside(u) : Else : ubRevocated(u)=1 : End If 
                            Else 
                                If ListeQC.Down Then : ListeQC.Aside(u) : ubRevocated(u)=0 : Else : ubRevocated(u)=1 : End If
                            End If                            
                        Else 
                            ubRevocated(u)=1 
                        End If
                        ListeQC.Recover(501)
                    End If   
                Next u
                z_Input+=1
            Next i 
            
        End If
        If ByPass=0 Then : ByPass=1 : Else : ByPass=0 : End If
    Next t
    Return IndiceRes-1
End Function 

Code: Select all

Property List.HasTagFast(str_Tag As String) As Byte
    Dim As  ListNode Ptr  pTemp = this.pLastNode
    While (pTemp->pPrev<>pFirstNode->pPrev AND pTemp<>this.pGarbage And pTemp->Tag0<>str_Tag ) : pTemp = pTemp->pPrev : Wend
    If pTemp->Tag0=str_Tag Then : this.pSearchNode=pTemp : pNode=pTemp  : Return 1 :  Else : Return 0 : End If
End Property
Lost Zergling
Posts: 634
Joined: Dec 02, 2011 22:51
Location: France

Re: Idea or challenge ?!

Post by Lost Zergling »

Warp 360-380 and up to Warp 400, challenging till 195-205 substring to test for in string on exemple #2
(instead of w300-310 up to 335 and 230-240 kwds)
I think this time I'm finally reaching the end of what I could do with the tool to try to improve speed on an InstrMulti. Perhaps some code with multi-dimensional arrays or something else could handle the scalability at low volumes (<200 kwds or more) to challenge the legacy Instr function.
Nevertheless, considering 1mn vs 6 hours (using std algo) for 100 000 entries my goal of usable left join ft search is reached.

Code: Select all

#Include once "D:\Basic\LZLE_.bi"  
' WITH CONST MAX_ASIDE=500 & change Byte to Integer in .Aside, .Recover & .AsideReset properties
Function InstrMulti(ByVal z_Input_ As zString Ptr, Byval uInt_InputL As uInteger, ListeQC As List, Str_Results() As String, uIntIndice() As uInteger, uBStopFirstFound As uByte=0, uIntStart As uInteger=0) As uInteger
    Dim as zString Ptr z_Input, y_Trace, x_Pos
    Dim As uInteger uInt_InputLen_0=uInt_InputL-uIntStart, uInt_InputLen, i, u, t, IndiceRes, uIntCompute, RPcumul
    Dim As uByte ubRevocated(500), ByPass=1, uBkeywdLen, ubtest
    ListeQC.RootPrivate : uInt_InputLen=500 : ListeQC.Aside(501)
    For t=1 to uInt_InputLen_0 Step 250 
        If ByPass Then
            If uInt_InputLen_0-t <250 Then : uInt_InputLen=uInt_InputLen_0-t+1 : End If
            z_Input = z_Input_
            z_Input+=t-1+uIntStart
            For i=1 to uInt_InputLen
                ubRevocated(i)=0 : ListeQC.AsideReset(i) 
                For u=1 to i
                    If ubRevocated(u)=0 Then
                        uIntCompute=u+t-1 : If uIntCompute > uInt_InputL Then : ListeQC.Recover(501) : Return IndiceRes-1 : End If
                        ListeQC.RecoverFast(u) 
                        If ListeQC.HasTagFast(Chr((*z_Input)[0])) Then   
                            uBkeywdLen=i-u+1
                            If ListeQC.Check Then                                
                                If uBStopFirstFound=255 Then : Return u 
                                ElseIf uBStopFirstFound<>0 AndAlso uBStopFirstFound<=IndiceRes Then : Return IndiceRes-1 
                                End If
                                If IndiceRes=Ubound(Str_Results)+1 Then : Redim Preserve Str_Results(Ubound(Str_Results)+100) : If IndiceRes=Ubound(Str_Results) Then : Return IndiceRes-1 : End If : End If
                                If IndiceRes=Ubound(uIntIndice)+1 Then : Redim Preserve uIntIndice(Ubound(uIntIndice)+100) : If IndiceRes=Ubound(uIntIndice) Then : Return IndiceRes-1 : End If : End If   
                            '   ******** Using pointers directly on parsed string
                                y_Trace=z_Input_+u-2+t
                                x_Pos=y_Trace+uBkeywdLen ' Str_Results(IndiceRes)=Left(*y_Trace, uBkeywdLen)
                                ubtest=x_Pos[0][0] : x_Pos[0][0]=0                               
                                Str_Results(IndiceRes)=*y_Trace
                                x_Pos[0][0]=ubtest
                            '   ******** Or .. Using List HashTag  Property
                    '           Str_Results(IndiceRes)=ListeQC.HashTag 
                            '   ******** 
                                uIntIndice(IndiceRes)=uIntCompute : IndiceRes+=1 : uBkeywdLen=1
                                If ListeQC.DownFast Then : ListeQC.AsideFast(u) : Else : ubRevocated(u)=1 : End If 
                            Else 
                                If ListeQC.DownFast Then : ListeQC.AsideFast(u) : ubRevocated(u)=0 : Else : ubRevocated(u)=1 : End If
                            End If                            
                        Else 
                            ubRevocated(u)=1 
                        End If
                        ListeQC.RecoverFast(501) 
                    End If   
                Next u
                z_Input+=1
            Next i             
        End If
        If ByPass=0 Then : ByPass=1 : Else : ByPass=0 : End If
    Next t
    ListeQC.Recover(501) : Return IndiceRes-1
End Function 

Dim ListeQC As List
Dim Str_Results() As string
Dim uIntIndice() As uInteger
Dim uIntIndiceClassic(100) As uInteger
'? "*" & Ubound(Str_Results) : sleep
Redim Str_Results(10) : Redim uIntIndice(100)
'Dim As string Str_Input = "78poissonchatTGHVBNI98virgulepointavionordinateurchienboite7654titiimprimantesourisAzertyuiopqsdfghjklmwxcvbn" 'Len Max=200
Dim As string Str_Input = "*NePYT98qsazerfdcxwAzertyuiopAzertylmwxcvbnAzertyuiopqsdfghjklmwxcvbnpoissonchatvirgulepointavionordinateurchienboite7654titiZimprimantesouris"
'Dim As string Str_Input = "GTCCYCTRCGTCCYCTRCTTXTXTRCTTXTXT76GTCCYCTRCGTCCYCTRCTTXTXTRCTTXTXT7654EXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGTCCYCTRCTTXTXTRCGTCCYCTRCGTCCYCTRCTTXTXTRCTTXTXT7654EXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGTCCYCTRCTTXTXTRCTTXTXT7654EXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGTCCYCTRCTTXTXTRCTTXTXT7654EXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXTTXTXTRCTTXTXT7654EXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNEXTRRCKYNI?INYDRESXeYTYOPNFGHFsourisAzertyREZAWTYTYUIOPMLPOUNBGNOFDH*NedfghjklmwxcvqsazerfdcxwAzertyuiopAzertylmwxcvbnAzertyuiopqsdfghjklmwxcvbnpoissonchat"
'Dim As string Str_Input = "TXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXTTXTXTRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNEXTRRCKYNI?INYDRESXeYTYOPNFGHFsourisAzertyREZAWTYTYUIOPMLPOUNBGNOFDH*NedfghjklmwxcvqsazerfdcxwAzertyuiopAzertylmwxcvbnAzertyuiopqsdfghjklmwxcvbnpoissonchat"

'Static As string Str_Input 
/'
   ' Dim As string 
    Dim As string Str_Input = "GTCCYCTRCGTCCYCTRCTTXTXTRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGTCCYCTRCTTXTXTRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGTCCYCTRCTTXTXTRCTTXGTCCYCTRCGTCCYCTRCTTXTXTRCTTXTXTGTCCYCTRCGTCCYCTRCTTXTXTRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGT" &_
    "CCYCTRCTTXTXTRCGTCCYCTRCGTCCYCTRCTTXTXTRCTTXTXTGTCCYCTRCGTCCYCTRCTTXTXTRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGTCCYCTRCTTXTXTRCGTCCYCTRCGTCCYCTRCTTXTXTRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGTCCYCTRCTTXTXTRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGTCCYCT" &_
    "CCYCTRCTTXTXTRCGTCCYCTRCGTCCYCTRCTTXTXTRCTTXTXTGTCCYCTRCGTCCYCTRCTTXTXTRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGTCCYCTRCTTXTXTRCGTCCYCTRCGTCCYCTRCTTXTXTRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGTCCYCTRCTTXTXTRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGTCCYCT" &_
    "CCYCTRCTTXTXTRCGTCCYCTRCGTCCYCTRCTTXTXTRCTTXTXTGTCCYCTRCGTCCYCTRCTTXTXTRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGTCCYCTRCTTXTXTRCGTCCYCTRCGTCCYCTRCTTXTXTRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGTCCYCTRCTTXTXTRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGTCCYCT" &_
    "CCYCTRCTTXTXTRCGTCCYCTRCGTCCYCTRCTTXTXTRCTTXTXTGTCCYCTRCGTCCYCTRCTTXTXTRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGTCCYCTRCTTXTXTRCGTCCYCTRCGTCCYCTRCTTXTXTRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGTCCYCTRCTTXTXTRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGTCCYCT" &_
    "CCYCTRCTTXTXTRCGTCCYCTRCGTCCYCTRCTTXTXTRCTTXTXTGTCCYCTRCGTCCYCTRCTTXTXTRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGTCCYCTRCTTXTXTRCGTCCYCTRCGTCCYCTRCTTXTXTRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGTCCYCTRCTTXTXTRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGTCCYCT" &_
    "CCYCTRCTTXTXTRCGTCCYCTRCGTCCYCTRCTTXTXTRCTTXTXTGTCCYCTRCGTCCYCTRCTTXTXTRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGTCCYCTRCTTXTXTRCGTCCYCTRCGTCCYCTRCTTXTXTRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGTCCYCTRCTTXTXTRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGTCCYCT" &_
    "CCYCTRCTTXTXTRCGTCCYCTRCGTCCYCTRCTTXTXTRCTTXTXTGTCCYCTRCGTCCYCTRCTTXTXTRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGTCCYCTRCTTXTXTRCGTCCYCTRCGTCCYCTRCTTXTXTRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGTCCYCTRCTTXTXTRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGTCCYCT" &_
    "CCYCTRCTTXTXTRCGTCCYCTRCGTCCYCTRCTTXTXTRCTTXTXTGTCCYCTRCGTCCYCTRCTTXTXTRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGTCCYCTRCTTXTXTRCGTCCYCTRCGTCCYCTRCTTXTXTRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGTCCYCTRCTTXTXTRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGTCCYCT" &_
    "CCYCTRCTTXTXTRCGTCCYCTRCGTCCYCTRCTTXTXTRCTTXTXTGTCCYCTRCGTCCYCTRCTTXTXTRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGTCCYCTRCTTXTXTRCGTCCYCTRCGTCCYCTRCTTXTXTRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGTCCYCTRCTTXTXTRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGTCCYCT" &_
    "CCYCTRCTTXTXTRCGTCCYCTRCGTCCYCTRCTTXTXTRCTTXTXTGTCCYCTRCGTCCYCTRCTTXTXTRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGTCCYCTRCTTXTXTRCGTCCYCTRCGTCCYCTRCTTXTXTRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGTCCYCTRCTTXTXTRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGTCCYCT" &_
    "RCTTXTXTRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXTTXTXTRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNEXTRRCKYNI?INYDRESXeYTYOPNFGHFsourisAzertyREZAWTYTYUIOPMLPOUNBGNOFDH*NedfghjklmwxcvqsazerfdcxwAzertyuiopAzertylmwxcvbnAzertyuiopqsdfghjklmwxcvbnpoissonchat"
'/

Dim As string Str_Res_1, Str_Res_2
Dim As uByte uBStopFirstFound=0
Dim as double T1, T2
Dim As Integer i, w, x, IndiceInstrMulti, TestMaxi = 100000

  '  TestMaxi = 195
'ListeQC.HashTag("Bouh")
'ListeQC.Recycle

ListeQC.HashTag("virgule") 
ListeQC.HashTag("poissonchatTGHVBNI")
ListeQC.HashTag("chienboite")
ListeQC.HashTag("Bar")
ListeQC.HashTag("ordi")
ListeQC.HashTag("poiss")
ListeQC.HashTag("isso")
ListeQC.HashTag("imprimante")
ListeQC.HashTag("oisson")
ListeQC.HashTag("oissonch")
ListeQC.HashTag("titi")
ListeQC.Root
? "Loading values to FT search"
For i=5 to TestMaxi
    ListeQC.HashTag(str(i)  )
Next i


i=0
ListeQC.Root
While ListeQC.KeyStep : i+=1 :  wend
? "i=" & i
ListeQC.RootPrivate
'While ListeQC.Up : Wend : 
'ListeQC.FastAside(1)
 ? "Loaded =" & ListeQC.NodeCount
    
dim as double t=timer
IndiceInstrMulti = InstrMulti(StrPtr(Str_Input), Len(Str_Input), ListeQC, Str_Results(), uIntIndice(), uBStopFirstFound )
For i=0 To IndiceInstrMulti
    Str_Res_1 += Str_Results(i)+";"
Next i
T1= timer-t : 

x=0
t=timer
For i=5 to TestMaxi
    w=Instr(Str_Input, str(i)) 
    If w<>0 Then 
        Str_Res_2+=str(i)+";"  
        uIntIndiceClassic(x)=w : x+=1
    End If
Next i
T2= timer-t

ListeQC.Root
While ListeQC.KeyStep
    If Cint(ListeQC.Tag)=0 Then
        w=Instr(Str_Input, ListeQC.HashTag) ': 
        If w<>0 Then 
            Str_Res_2+=ListeQC.HashTag+ ";"  
            uIntIndiceClassic(x)=w : x+=1
        End If
    End If    
Wend
?
? "Using InstrMulti :"
? "Result=" & Str_Res_1
Str_Res_1=""
For i=0 to IndiceInstrMulti
    Str_Res_1+= Str(uIntIndice(i)) + ";"
Next i
? "Positions=" &  Str_Res_1
?
? "Using Instr inside a loop :"
? "Result=" & Str_Res_2
Str_Res_2=""
For i=0 to IndiceInstrMulti
    Str_Res_2+= Str(uIntIndiceClassic(i)) + ";"
Next i
? "Positions=" &  Str_Res_2
?
? T1
? T2
? "InstrMulti Warp Factor=" & Cint(Cdbl(T2/T1)*100)/100
?


'ListeQC.Destroy : gCollector.Destroy
'Print "??=" & AllocateDeallocateCounter & " nodecount=" & ListeQC.NodeCount 
'sleep : system

' Just to check list's integrity
    ListeQC.Recycle
  '  ListeQC.Root
  '  While ListeQC.KeyStep
  '      ListeQC.NodeFlat
  '  Wend



? "Garbage=" & ListeQC.GarbageCount
ListeQC.HashTag("virgule")
ListeQC.HashTag("poissonchatTGHVBNI")
ListeQC.HashTag("chienboite")
ListeQC.HashTag("Bar")
ListeQC.HashTag("ordi")
ListeQC.HashTag("poiss")
ListeQC.HashTag("isso")
ListeQC.HashTag("imprimante")
ListeQC.HashTag("oisson")
ListeQC.HashTag("oissonch")
ListeQC.HashTag("titi")
ListeQC.Root
'? "Loading values to FT search"
For i=5 to TestMaxi
    ListeQC.HashTag(str(i)  )
Next i

For i=0 To Ubound(Str_Results) : Str_Results(i)="" : Next i
For i=0 To Ubound(uIntIndice) : uIntIndice(i)=0 : Next i

ListeQC.Root
Str_Res_1=""
IndiceInstrMulti = InstrMulti(StrPtr(Str_Input), Len(Str_Input), ListeQC, Str_Results(), uIntIndice(), uBStopFirstFound )
For i=0 To IndiceInstrMulti
    Str_Res_1 += Str_Results(i)+";"
Next i
? "Using InstrMulti :"
? "Result=" & Str_Res_1
i=0
While ListeQC.KeyStep : i+=1 :  wend
? "i=" & i

ListeQC.Destroy : gCollector.Destroy
Print "??=" & AllocateDeallocateCounter & " nodecount=" & ListeQC.NodeCount 
sleep : system

Lzle new porperties

Code: Select all

Property List.AsideFast(by As Integer) As Byte
    Lcontext(by).pNode=pNode : Lcontext(by).pFirstNode=pFirstNode : Lcontext(by).pLastNode=pLastNode ': Lcontext(by).LcHashTag=pNode->Tag0 ': Lcontext(by).bLcHashLen=1
    Return 1
End Property

Property List.RecoverFast(by As Integer) As Byte
    If Lcontext(by).pNode=0 Then : Return 0 : End If
    pNode=Lcontext(by).pNode : pFirstNode=Lcontext(by).pFirstNode: pLastNode=Lcontext(by).pLastNode ': this.bHashLen=1
    Return 1
End Property

Lost Zergling
Posts: 634
Joined: Dec 02, 2011 22:51
Location: France

Re: Idea or challenge ?!

Post by Lost Zergling »

It would probably be possible to gain another 4-5% by tweaking the index structure to optimize the search, but this could lead to stability issues.
This approach seems very cumbersome.
A little code cleaning allowed for a slight gain.
"Warp 380-390 and up to warp 410" and challenging from 190 kWds on example #2. Depending on the length of the string to be searched and the number of hits, performance will vary significantly but seems to stabilize in the least favorable cases at warp 150 and a performance threshold of 350 kWds.
The following points should be noted:
1. The variable threshold (190-350) for the number of substrings to search will depend on the structure and length of the string to be searched.
2. There is a leverage effect: above the threshold, performance is better, but the opposite effect is observed below it: Instr regains the advantage and can be 5 to 10 times faster for 30 or 40 substrings and several hundred or thousands of times faster for a single value to search.
3. A second effect (besides the leverage on the threshold mentioned above) will be the length of the substrings searched: the longer they are, the more the Boyer-Moore algorithm will widen the gap.

Compared to the Boyer-Moore algorithm, the algorithm used is of the so-called 'naive' (or brute-force) type. InstrMulti does not take into account the length of the substrings searched.
  • InstrMulti will therefore not be suitable if the number of substrings to be tested is too small. Likewise, if the string to be searched is large and the substrings to be searched are also large.
  • InstrMulti will be much less suitable if the string to search into is always same because it'll be already in cache for Instr.
  • InstrMulti will be suitable if you have a significant number of substrings to search within the main string, if these substrings are short, and if you have to perform this search on a large number of main strings (i.e., iterating over a label in a file).
  • InstrMulti will be suitable if you need to find all hits and if there shall be a lot of hits : the function would be called one time (vs 1 time per hit), lowering string copy or cache load operations, thus code might be more readable.
  • InstrMulti will be suitable also if you just need to identify if a substring from a subset is inside a string or not (ie a join). Using Instr in a loop, matching is random while InstrMulti is checking all substring in one pass. In this configuration, you can consider a threshold of 40-60 substrings (if matching value, otherwise threshold unchanged), less memory intensive so it shall be appropriate for file or databases relate tasks.
One possible improvement would therefore be to introduce some of this information into the index (length of each substring and last character) to pool the optimization (as an hybrid model). We would not achieve the performance of Boyer Moore on long substrings, but it should be possible perhaps to reduce the gap in a large number of use cases on long strings and substrings, although we should be careful not to degrade performance on short substrings.

nb : small fix uBStopFirstFound top value=254 (and not 255)

Code: Select all

#Include once "D:\Basic\LZLE_.bi"  
' WITH CONST MAX_ASIDE=500 & change Byte to Integer in .Aside, .Recover & .AsideReset properties
Function InstrMulti(ByVal z_Input_ As zString Ptr, Byval uInt_InputL As uInteger, ListeQC As List, Str_Results() As String, uIntIndice() As uInteger, uBStopFirstFound As uByte=0, uIntStart As uInteger=0) As uInteger
    Dim as zString Ptr z_Input, y_Trace, x_Pos
    Dim As uInteger uInt_InputLen_0=uInt_InputL-uIntStart, uInt_InputLen, i, u, t, IndiceRes
    Dim As uByte ubRevocated(500), ByPass=1, uBkeywdLen, ubtest
    ListeQC.RootPrivate : uInt_InputLen=500 : ListeQC.Aside(501)
    For t=1 to uInt_InputLen_0 Step 250 
        If ByPass Then
            If uInt_InputLen_0-t <250 Then : uInt_InputLen=uInt_InputLen_0-t+1 : End If
            z_Input = z_Input_
            z_Input+=t-1+uIntStart
            For i=1 to uInt_InputLen
                ubRevocated(i)=0 : ListeQC.AsideReset(i) 
                For u=1 to i 
                    If ubRevocated(u)=0 Then
                   '     uIntCompute=u+t-1 : If uIntCompute > uInt_InputL Then : ListeQC.Recover(501) : Return IndiceRes : End If
                        ListeQC.RecoverFast(u)
                        If ListeQC.HasTagFast(Chr((*z_Input)[0])) Then
                            If ListeQC.Check Then                 
                                If uBStopFirstFound=254 Then : Return u+t-1
                                ElseIf uBStopFirstFound<>0 AndAlso uBStopFirstFound<=IndiceRes Then : Return IndiceRes-1 
                                End If
                                If IndiceRes=Ubound(Str_Results)+1 Then : Redim Preserve Str_Results(Ubound(Str_Results)+100) : If IndiceRes=Ubound(Str_Results) Then : Str_Results(IndiceRes)="String Array Redim failed" : uIntIndice(IndiceRes)=1 : Return IndiceRes-1 : End If : End If
                                If IndiceRes=Ubound(uIntIndice)+1 Then : Redim Preserve uIntIndice(Ubound(uIntIndice)+100) : If IndiceRes=Ubound(uIntIndice) Then : Str_Results(IndiceRes)="Indice Array Redim failed" : uIntIndice(IndiceRes)=1 : Return IndiceRes-1 : End If : End If  
                            '   ******** Using pointers directly on parsed string
                                uBkeywdLen=i-u+1 ': uIntCompute=u+t-1
                                y_Trace=z_Input_+u-2+t
                                x_Pos=y_Trace+uBkeywdLen ' Str_Results(IndiceRes)=Left(*y_Trace, uBkeywdLen)
                                ubtest=x_Pos[0][0] : x_Pos[0][0]=0                               
                                Str_Results(IndiceRes)=*y_Trace
                                x_Pos[0][0]=ubtest
                            '   ******** Or .. Using List HashTag  Property
                    '           Str_Results(IndiceRes)=ListeQC.HashTag 
                            '   ******** 
                                uIntIndice(IndiceRes)=u+t-1 : IndiceRes+=1 
                                If ListeQC.DownFast Then : ListeQC.AsideFast(u) :  Else : ubRevocated(u)=1 : End If 
                            Else 
                                If ListeQC.DownFast Then : ListeQC.AsideFast(u) : Else : ubRevocated(u)=1 : End If
                            End If                            
                        Else 
                            ubRevocated(u)=1 
                        End If
                        ListeQC.RecoverFast(501) 
                    End If   
                Next u
                z_Input+=1
            Next i             
        End If
        If ByPass=0 Then : ByPass=1 : Else : ByPass=0 : End If
    Next t
    ListeQC.Recover(501) : Return IndiceRes-1
End Function 
lzle related :

Code: Select all

Property List.DownFast As Byte
    If pnode->pBranch=0 Then  : Return 0 
    Else 
        pFirstNode=pNode->pBranch : 
        pLastNode=pNode->pBranch->pBranchLastNode : 
        pNode=pNode->pBranch :  
        Return 1
    End If
End Property

Property List.HasTagFast(str_Tag As String) As Byte
    Dim As  ListNode Ptr  pTemp = this.pLastNode
    While (pTemp<>pFirstNode AND pTemp<>this.pGarbage And pTemp->Tag0<>str_Tag ) : pTemp = pTemp->pPrev : Wend
    If pTemp->Tag0=str_Tag Then : this.pSearchNode=pTemp : pNode=pTemp  : Return 1 :  Else : Return 0 : End If
End Property

Property List.AsideFast(by As Integer) As Byte
    Lcontext(by).pNode=pNode : Lcontext(by).pFirstNode=pFirstNode : Lcontext(by).pLastNode=pLastNode ': Lcontext(by).LcHashTag=pNode->Tag0 ': Lcontext(by).bLcHashLen=1
    Return 1
End Property

Property List.AsideReset(by As Integer) As Byte : Lcontext(by).pNode=0 : Return 1 : End Property

Property List.RecoverFast(by As Integer) As Byte
    If Lcontext(by).pNode=0 Then : Return 0 : End If
    pNode=Lcontext(by).pNode : pFirstNode=Lcontext(by).pFirstNode: pLastNode=Lcontext(by).pLastNode ': this.bHashLen=1
    Return 1
End Property
Nb: If anyone is testing and can achieve improvements, I'm interested :? . In my recent tests, I encountered some strange results, not in terms of results but rather in terms of performance measurements. Elements that seem completely innocuous or logically neutral in terms of performance can surprisingly have a significant and reproductible impact, and the explanation seems anything but trivial. Thus, the "optimization" performed is sometimes very empirical.
It is therefore possible that one or more potential gains remain.
Lost Zergling
Posts: 634
Joined: Dec 02, 2011 22:51
Location: France

Re: Idea or challenge ?!

Post by Lost Zergling »

Warp 400-410 and up to warp 430, threshold at 180 keywords just using Continue. Note: Changed the fill of result tables that start with index 1, to have a function return at 0 if no match in mode 254.

Code: Select all

#Include once "D:\Basic\LZLE_.bi"  
' WITH CONST MAX_ASIDE=500 & change Byte to Integer in .Aside, .Recover & .AsideReset properties
Function InstrMulti(ByVal z_Input_ As zString Ptr, Byval uInt_InputL As uInteger, ListeQC As List, Str_Results() As String, uIntIndice() As uInteger, uBStopFirstFound As uByte=0, uIntStart As uInteger=0) As uInteger
    Dim as zString Ptr z_Input, y_Trace, x_Pos
    Dim As uInteger uInt_InputLen_0=uInt_InputL-uIntStart, uInt_InputLen, i, u, t, IndiceRes=1
    Dim As uByte ubRevocated(500), ByPass=1, uBkeywdLen, ubtest
    ListeQC.RootPrivate : uInt_InputLen=500 : ListeQC.Aside(501)
    For t=1 to uInt_InputLen_0 Step 250 
        If ByPass Then
            If uInt_InputLen_0-t <250 Then : uInt_InputLen=uInt_InputLen_0-t+1 : End If
            z_Input = z_Input_
            z_Input+=t-1+uIntStart
            For i=1 to uInt_InputLen
                ubRevocated(i)=0 : ListeQC.AsideReset(i) 
                For u=1 to i 
                    If ubRevocated(u)=0 Then
                        ListeQC.RecoverFast(u)
                        If ListeQC.HasTagFast(Chr((*z_Input)[0])) Then
                            If ListeQC.Check Then
                                If uBStopFirstFound=254 Then : Return u+t-1
                                ElseIf uBStopFirstFound<>0 AndAlso uBStopFirstFound<=IndiceRes Then : Return IndiceRes-1 
                                End If
                                If IndiceRes=Ubound(Str_Results)+1 Then : Redim Preserve Str_Results(Ubound(Str_Results)+100) : If IndiceRes=Ubound(Str_Results) Then : Str_Results(IndiceRes)="String Array Redim failed" : uIntIndice(IndiceRes)=1 : Return IndiceRes-1 : End If : End If
                                If IndiceRes=Ubound(uIntIndice)+1 Then : Redim Preserve uIntIndice(Ubound(uIntIndice)+100) : If IndiceRes=Ubound(uIntIndice) Then : Str_Results(IndiceRes)="Indice Array Redim failed" : uIntIndice(IndiceRes)=1 : Return IndiceRes-1 : End If : End If  
                            '   ******** Using pointers directly on parsed string
                                uBkeywdLen=i-u+1 ': uIntCompute=u+t-1
                                y_Trace=z_Input_+u-2+t
                                x_Pos=y_Trace+uBkeywdLen ' Str_Results(IndiceRes)=Left(*y_Trace, uBkeywdLen)
                                ubtest=x_Pos[0][0] : x_Pos[0][0]=0                               
                                Str_Results(IndiceRes)=*y_Trace
                                x_Pos[0][0]=ubtest
                            '   ******** Or .. Using List HashTag  Property
                    '           Str_Results(IndiceRes)=ListeQC.HashTag 
                            '   ******** 
                                uIntIndice(IndiceRes)=u+t-1 : IndiceRes+=1 
                                If ListeQC.DownFast Then : ListeQC.AsideFast(u) : Else : ubRevocated(u)=1 : End If  : ListeQC.RecoverFast(501) : Continue For
                            Else 
                                If ListeQC.DownFast Then : ListeQC.AsideFast(u) : Else : ubRevocated(u)=1 : End If  : ListeQC.RecoverFast(501) : Continue For
                            End If                            
                        Else 
                            ubRevocated(u)=1 : ListeQC.RecoverFast(501) : Continue For
                        End If
                    End If   
                Next u
                z_Input+=1
            Next i             
        End If
        If ByPass=0 Then : ByPass=1 : Else : ByPass=0 : End If
    Next t
    ListeQC.Recover(501) : Return IndiceRes-1
End Function 

Dim ListeQC As List
Dim Str_Results() As string
Dim uIntIndice() As uInteger
Dim uIntIndiceClassic(200) As uInteger
Dim As uInteger i, w, x, IndiceInstrMulti, TestMaxi = 100000
Dim As string Str_Res_1, Str_Res_2
Dim As uByte uBStopFirstFound=0
Dim as double T1, T2
'? "*" & Ubound(Str_Results) : sleep
Redim Str_Results(100) : Redim uIntIndice(100)
'Dim As string Str_Input = "78poissonchatTGHVBNI98virgulepointavionordinateurchienboite7654titiimprimantesourisAzertyuiopqsdfghjklmwxcvbn" 'Len Max=200
'Dim As string Str_Input = "*NePYTqsazerfdcxwAzertyuiopAzertylmwxcvbnAzertyuiopqsdfghjklmwxcvbnpuiopqsdfghjklmwxcuiopqsdfghjklwxcavio" 'nlmwxcvbnAzertyuioplmwxcvbnAzer*NePYTqsazerfdcxwAzertyuiopAzertylmwxcvbnAzertyuiopqsdfghjklmwxcvbnpuiopqsdfghjklmwxcuiopqsdfghjklw"
Dim As string Str_Input = "*NePYT98qsazerfdcxwAzertyuiopAzertylmwxcvbnAzertyuiopqsdfghjklmwxcvbnpoissonchatvirgulepointavionordinateurchienboite7654titiZimprimantesouris"
'Dim As string Str_Input = "GTCCYCTRCGTCCYCTRCTTXTXTRCTTXTXT76GTCCYCTRCGTCCYCTRCTTXTXTRCTTXTXT7654EXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGTCCYCTRCTTXTXTRCGTCCYCTRCGTCCYCTRCTTXTXTRCTTXTXT7654EXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGTCCYCTRCTTXTXTRCTTXTXT7654EXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGTCCYCTRCTTXTXTRCTTXTXT7654EXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXTTXTXTRCTTXTXT7654EXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNEXTRRCKYNI?INYDRESXeYTYOPNFGHFsourisAzertyREZAWTYTYUIOPMLPOUNBGNOFDH*NedfghjklmwxcvqsazerfdcxwAzertyuiopAzertylmwxcvbnAzertyuiopqsdfghjklmwxcvbnpoissonchat"
'Dim As string Str_Input = "TXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXTTXTXTRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNEXTRRCKYNI?INYDRESXeYTYOPNFGHFsourisAzertyREZAWTYTYUIOPMLPOUNBGNOFDH*NedfghjklmwxcvqsazerfdcxwAzertyuiopAzertylmwxcvbnAzertyuiopqsdfghjklmwxcvbnpoissonchat*"

'Static As string Str_Input 
/'
   ' Dim As string 
    Dim As string Str_Input = "GTCCYCTRCGTCCYCTRCTTXTXTRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGTCCYCTRCTTXTXTRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGTCCYCTRCTTXTXTRCTTXGTCCYCTRCGTCCYCTRCTTXTXTRCTTXTXTGTCCYCTRCGTCCYCTRCTTXTXTRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGT" &_
    "CCYCTRCTTXTXTRCGTCCYCTRCGTCCYCTRCTTXTXTRCTTXTXTGTCCYCTRCGTCCYCTRCTTXTXTRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGTCCYCTRCTTXTXTRCGTCCYCTRCGTCCYCTRCTTXTXTRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGTCCYCTRCTTXTXTRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGTCCYCT" 
'/

Dim as string Str_tmp="CCYCTRCTTXTXTRCGTCCYCTRCGTCCYCTRCTTXTXTRCTTXTXTGTCCYCTRCGTCCYCTRCTTXTXTRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGTCCYCTRCTTXTXTRCGTCCYCTRCGTCCYCTRCTTXTXTRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGTCCYCTRCTTXTXTRCTTXTXTEXTRRCKYNRCTTXTXTEXTRRCKYNRCTTXGTCCYCT"

    For i=1 To 10
  '      Str_Input+=Str_tmp
    Next i
  '  Str_Input+="sourisAzertyREZAWTYTYUIOPMLPOUNBGNOFDH*NedfghjklmwxcvqsazerfdcxwAzertyuiopAzertylmwxcvbnAzertyuiopqsdfghjklmwxcvbnpoissonchat"
   ' TestMaxi = 350
'/

    TestMaxi = 180

ListeQC.HashTag("virgule") 
ListeQC.HashTag("poissonchatTGHVBNI")
ListeQC.HashTag("chienboite")
ListeQC.HashTag("Bar")
ListeQC.HashTag("ordi")
ListeQC.HashTag("poiss")
ListeQC.HashTag("isso")
ListeQC.HashTag("imprimante")
ListeQC.HashTag("oisson")
ListeQC.HashTag("oissonch")
ListeQC.HashTag("titi")
'ListeQC.HashTag("*")
ListeQC.Root
? "Loading values to FT search"
For i=5 to TestMaxi
    ListeQC.HashTag(str(i)  )
Next i


i=0
ListeQC.Root
While ListeQC.KeyStep : i+=1 :  wend
? "i=" & i
ListeQC.RootPrivate
'While ListeQC.Up : Wend : 
'ListeQC.FastAside(1)
 ? "Loaded =" & ListeQC.NodeCount
  '  uBStopFirstFound=254

dim as double t=timer
IndiceInstrMulti = InstrMulti(StrPtr(Str_Input), Len(Str_Input), ListeQC, Str_Results(), uIntIndice(), uBStopFirstFound )
'? IndiceInstrMulti
'sleep

T1= timer-t : 
If uBStopFirstFound<>254 Then
    For i=1 To IndiceInstrMulti
        Str_Res_1 += Str_Results(i)+";"
    Next i    
Else
    ? "IndiceInstrMulti=" & IndiceInstrMulti
    'uBStopFirstFound=0 : IndiceInstrMulti=0
End If



x=0
t=timer
For i=5 to TestMaxi
    w=Instr(Str_Input, str(i)) 
    If w<>0 Then 
        Str_Res_2+=str(i)+";"  
        uIntIndiceClassic(x)=w : x+=1
    End If
Next i
T2= timer-t

ListeQC.Root
While ListeQC.KeyStep
    If Cint(ListeQC.Tag)=0 Then
        w=Instr(Str_Input, ListeQC.HashTag) ': 
        If w<>0 Then 
            Str_Res_2+=ListeQC.HashTag+ ";"  
            uIntIndiceClassic(x)=w : x+=1
        End If
    End If    
Wend
?


? "Using InstrMulti :"
? "Result=" & Str_Res_1
Str_Res_1=""
If uBStopFirstFound<>254 Then
    For i=1 to IndiceInstrMulti
        Str_Res_1+= Str(uIntIndice(i)) + ";"
    Next i
    ? "Positions=" &  Str_Res_1
Else
    ? "IndiceInstrMulti=" & IndiceInstrMulti
    
End If
?
? "Using Instr inside a loop :"
? "Result=" & Str_Res_2
Str_Res_2=""
If uBStopFirstFound<>254 Then
    For i=0 to IndiceInstrMulti-1
        Str_Res_2+= Str(uIntIndiceClassic(i)) + ";"
    Next i
    ? "Positions=" &  Str_Res_2
End If
?
? T1
? T2
? "InstrMulti Warp Factor=" & Cint(Cdbl(T2/T1)*100)/100
? "InstrMulti Slow Factor=" & Cint(Cint(Cdbl(T1/T2)*100)/100)
?

'ListeQC.Destroy : gCollector.Destroy
'Print "??=" & AllocateDeallocateCounter & " nodecount=" & ListeQC.NodeCount 
'sleep : system

' Just to check list's integrity
    ListeQC.Recycle
  '  ListeQC.Root
  '  While ListeQC.KeyStep
  '      ListeQC.NodeFlat
  '  Wend



? "Garbage=" & ListeQC.GarbageCount
ListeQC.HashTag("virgule")
ListeQC.HashTag("poissonchatTGHVBNI")
ListeQC.HashTag("chienboite")
ListeQC.HashTag("Bar")
ListeQC.HashTag("ordi")
ListeQC.HashTag("poiss")
ListeQC.HashTag("isso")
ListeQC.HashTag("imprimante")
ListeQC.HashTag("oisson")
ListeQC.HashTag("oissonch")
ListeQC.HashTag("titi")

'ListeQC.Root

'? "Loading values to FT search"
For i=5 to TestMaxi
    ListeQC.HashTag(str(i)  )
Next i


For i=0 To Ubound(Str_Results) : Str_Results(i)="" : Next i
For i=0 To Ubound(uIntIndice) : uIntIndice(i)=0 : Next i

ListeQC.Root
uBStopFirstFound=0
Str_Res_1=""
IndiceInstrMulti = InstrMulti(StrPtr(Str_Input), Len(Str_Input), ListeQC, Str_Results(), uIntIndice(), uBStopFirstFound )
'? IndiceInstrMulti & " <> " & Ubound(Str_Results)
'? "3" : sleep

If uBStopFirstFound<>254 Then
    For i=0 To IndiceInstrMulti
        Str_Res_1 += Str_Results(i)+";"
    Next i
    ? "Using InstrMulti :"
    ? "Result=" & Str_Res_1
End If
i=0
While ListeQC.KeyStep : i+=1 :  wend
? "i=" & i

ListeQC.Destroy : gCollector.Destroy
Print "??=" & AllocateDeallocateCounter & " nodecount=" & ListeQC.NodeCount 
sleep :
'? Str_Input
'sleep

system


Someone had put the teleporter in a CONTINUE loop, and the self-diagnostic mode was also in a CONTINUE loop: a brilliant trick from the chief engineer to preserve bottles of Scotch! :) :wink:

Parameters
str : StrPtr(string) String Pointer to string to search into
Len : Len(string) to search into
List : list of strings to search for in str
StrArray() : string array passed byref for results
uIntegerArray() : uInteger array passed byref for results positions
Mode : 0=All hits, 1-253=max hits threashold, 254=Just locate first hit
Start : starting position

Return Value
Mode 0-253 : LogicalUbound for reading values in StrArray and uIntegerArray
ByRef : StrArray() and uIntegerArray()
Mode 254 : the position of the first occurrence of one of the substring of the substring list in str, or 0
Post Reply