How to recognize alphaMeric characters

Just starting out? Need help? Post your questions and find answers here.
AZJIO
Addict
Addict
Posts: 2193
Joined: Sun May 14, 2017 1:48 am

Re: How to recognize alphaMeric characters

Post by AZJIO »

Here's an example with a regular expression

Code: Select all

EnableExplicit

Procedure SplitRegExpByWords(String$, List WordsList.s(), NoCaseSensitive = #PB_String_NoCase)
	Protected re, Prev$
	ClearList(WordsList())
	re = CreateRegularExpression(#PB_Any, "[a-zA-Z]+")
	If re
		If ExamineRegularExpression(re, String$)
			While NextRegularExpressionMatch(re)
				If AddElement(WordsList())
					WordsList() = RegularExpressionMatchString(re)
				EndIf
			Wend
		EndIf
		FreeRegularExpression(re)
	Else
		Debug RegularExpressionError()
	EndIf
	SortList(WordsList(), #PB_Sort_Ascending | #PB_Sort_NoCase)

	ResetList(WordsList())
	While NextElement(WordsList())
		If NoCaseSensitive
			If ReplaceString(WordsList(), Prev$, "", #PB_String_NoCase, 1, 1) = ""
				DeleteElement(WordsList())
				Continue
			EndIf
		Else
			If WordsList() = Prev$
				DeleteElement(WordsList())
				Continue
			EndIf
		EndIf
		Prev$ = WordsList()
	Wend
EndProcedure

Define NewList WordsList.s()
Define String$ = "This is see see see is see see see is this"
SplitRegExpByWords(String$, WordsList(), #PB_String_NoCase)

ForEach WordsList()
	Debug WordsList()
Next
Marc56us
Addict
Addict
Posts: 1600
Joined: Sat Feb 08, 2014 3:26 pm

Re: How to recognize alphaMeric characters

Post by Marc56us »

Good job!
Just need to ad some chars (i.e: hyphen (-), apostrophe (') etc)

Code: Select all

"[a-zA-Z'-]+"

Code: Select all

"This is see see see is see see see is this but I can't check-in"

Code: Select all

but
can't
check-in
I
is
see
This
:wink:
User avatar
idle
Always Here
Always Here
Posts: 5929
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: How to recognize alphaMeric characters

Post by idle »

you can try this down load the zip and look in example
https://github.com/idle-PB/Squint3

it will download "Genesis.txt"
url.s = "https://archive.org/download/kjv-text-files/Genesis.txt"

Code: Select all


;an example of scaning a text file to build a dictionary 
;to display it in sorted order and search for an item 

XIncludeFile "..\squint3.pbi"

UseModule SQUINT

EnableExplicit 

Global sq.isquint = SquintNew()

Procedure CBSearch(*key,*value,*userData) ;Can add items to a list if wanted 
  PrintN(PeekS(*key,-1,#PB_UTF8) + " :frequency " + Str(*value))  
EndProcedure   

Procedure CBList(*key,*value,*userData) ;Can add items to a list if wanted 
  Static ct 
  PrintN(Str(ct) + " : " + PeekS(*key,-1,#PB_UTF8) + " :frequency " + Str(*value))  
  ct+1
EndProcedure 

OpenConsole()  

Global key.s,url.s,file.s,fn,size,*st,*chr.unicode 
Global len,ct,path.s,line.s

url.s = "https://archive.org/download/kjv-text-files/Genesis.txt"

path.s = GetUserDirectory(#PB_Directory_Downloads)  + GetFilePart(url)
If FileSize(path) < 0 
  ReceiveHTTPFile(url,path)
EndIf 

fn = OpenFile(#PB_Any,path) 
If fn 
  Repeat  
    line.s = ReadString(fn,#PB_UTF8)
    ct+1 
    *chr=@line 
    If line <> "" 
      *st=*chr 
      Repeat
        While (*chr\u > 0 And *chr\u <= 32)  
          *chr+2
          *st=*chr
        Wend  
        While ((*chr\u >= '0' And *chr\u <= '9') Or (*chr\u >= 'A' And *chr\u <= 'Z') Or (*chr\u >= 'a' And *chr\u <= 'z') Or *chr\u=39) ;'  
          len+1
          *chr+2
        Wend       
        If len 
          key = LCase(PeekS(*st,len))
          ct = sq\Get(0,@key) + 1     ;look up the word and increment its count    
          sq\Set(0,@key,ct)           ;set the word in the dictionary  
          len = 0 
          *st=*chr+2  
        EndIf   
        If *chr\u = 0 
          Break 
        EndIf 
        *chr+2
        *st=*chr
      Until *chr\u = 0    
    EndIf 
  Until Eof(fn) 
  
  sq\Walk(0,@CBList())            ;dumps the dictionary      
  PrintN("Search for ba") 
  
  Repeat 
    PrintN("enter a key to search for of type quit to end")   
    key = LCase(Input())    
    If key <> "quit" Or key <> "end" 
       sq\Enum(@key,@CBSearch())        ;search the dictionary 
    Else 
      Break 
    EndIf   
  ForEver   
    
EndIf 

sq\Free()

CloseConsole() 


result
0 : 1 :frequency 52
1 : 2 :frequency 51
2 : 3 :frequency 51
3 : 4 :frequency 51
4 : 5 :frequency 51
5 : 6 :frequency 51
6 : 7 :frequency 51
7 : 8 :frequency 51
8 : 9 :frequency 51
9 : A :frequency 2
10 : BOOK :frequency 1
11 : Baal :frequency 2
12 : Babel :frequency 2
13 : Bashemath :frequency 6
....
1464 : wrestled :frequency 3
1465 : wrestlings :frequency 1
1466 : wrong :frequency 1
1467 : wroth :frequency 6
1468 : wrought :frequency 1
1469 : ye :frequency 87
1470 : yield :frequency 2
1471 : yoke :frequency 1
1472 : yonder :frequency 1
1473 : you :frequency 82
Search for Ba
Baal :frequency 2
Babel :frequency 2
Bashemath :frequency 6
vmars316
Enthusiast
Enthusiast
Posts: 474
Joined: Fri Jun 29, 2012 12:24 am
Contact:

Re: How to recognize alphaMeric characters

Post by vmars316 »

Hey idle ,

OOPS!

Sorry to say , Looks like I messed up your beautiful code .

The change I made was to replace this:
; url.s = "https://archive.org/download/kjv-text-files/Genesis.txt"
;
; path.s = GetUserDirectory(#PB_Directory_Downloads) + GetFilePart(url)
; If FileSize(path) < 0
; ReceiveHTTPFile(url,path)
; EndIf

with this:
Global FileName$ = "File-to-be-Indexed.txt"
fn = OpenFile(#PB_Any,FileName$)

So that I could read a local file instead of file off web .

I don't see why that would mess up the program .

What happens is
The program starts opens the Console and Debug .
Console says:

0 : :frequency 0
Search for ba
enter a key to search for of type quit to end

No Matter what I enter (even for quit) it repeats:

0 : :frequency 0
Search for ba
enter a key to search for of type quit to end

Pls , what am I doing wrong .

Your code with no changes runs perfectly ,
and it doesn't ask for a search key .

I would hope make my code do the exact same thing .

Thanks for your Help...

Code: Select all

; IDLE_ORIGINAL_Try_Again_02.pb
;an example of scaning a text file to build a dictionary 
;to display it in sorted order and search for an item 

Global CurrentDir$ = GetCurrentDirectory() 
XIncludeFile "C:\PureBasic-myApps\vmProjects\BUTTONS\Line-By-Line\Line-By-Line-Folder\Create-INDEX\squint3.pbi"

UseModule SQUINT

EnableExplicit 

Global sq.isquint = SquintNew()

Procedure CBSearch(*key,*value,*userData) ;Can add items to a list if wanted 
  PrintN(PeekS(*key,-1,#PB_UTF8) + " :frequency " + Str(*value))  
EndProcedure   

Procedure CBList(*key,*value,*userData) ;Can add items to a list if wanted 
  Static ct 
  PrintN(Str(ct) + " : " + PeekS(*key,-1,#PB_UTF8) + " :frequency " + Str(*value))  
  ct+1
EndProcedure 

OpenConsole()  

Global key.s,url.s,file.s,fn,size,*st,*chr.unicode 
Global len,ct,path.s,line.s

; url.s = "https://archive.org/download/kjv-text-files/Genesis.txt"
; 
; path.s = GetUserDirectory(#PB_Directory_Downloads)  + GetFilePart(url)
; If FileSize(path) < 0 
;   ReceiveHTTPFile(url,path)
; EndIf 

Global FileName$ = "File-to-be-Indexed.txt"

fn = OpenFile(#PB_Any,FileName$) 
Debug "Open Success = " + fn 

If fn 
  Repeat  
    line.s = ReadString(fn,#PB_UTF8)
Debug line 
    ct+1 
    *chr=@line 
    If line <> "" 
      *st=*chr 
      Repeat
        While (*chr\u > 0 And *chr\u <= 32)  
          *chr+2
          *st=*chr
        Wend  
        While ((*chr\u >= '0' And *chr\u <= '9') Or (*chr\u >= 'A' And *chr\u <= 'Z') Or (*chr\u >= 'a' And *chr\u <= 'z') Or *chr\u=39) ;'  
          len+1
          *chr+2
        Wend       
        If len 
          key = LCase(PeekS(*st,len))
          ct = sq\Get(0,@key) + 1     ;look up the word and increment its count    
          sq\Set(0,@key,ct)           ;set the word in the dictionary  
          len = 0 
          *st=*chr+2  
        EndIf   
        If *chr\u = 0 
          Break 
        EndIf 
        *chr+2
        *st=*chr
      Until *chr\u = 0    
    EndIf 
  Until Eof(fn) 
  
  sq\Walk(0,@CBList())            ;dumps the dictionary      
  PrintN("Search for ba") 
  
   Repeat 
     PrintN("enter a key to search for of type quit to end")   
     key = LCase(Input())    
     If key <> "quit" Or key <> "end" 
        sq\Enum(@key,@CBSearch())        ;search the dictionary 
     Else 
       Break 
     EndIf   
   ForEver   
    
EndIf 

sq\Free()

CloseConsole() 

vmars.us Win11 x64 , Martin Guitar 000-16 (1995)
"All things in moderation , except for love and forgiveness."
User avatar
idle
Always Here
Always Here
Posts: 5929
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: How to recognize alphaMeric characters

Post by idle »

it was just a brain fart, replace the line

Code: Select all

If key <> "quit" Or key <> "end" 
with

Code: Select all

If key <> "quit" and key <> "end" 
and that will stop the looping

I also needed to add ProcedureReturn 1 in the call backs,
I'm finding it really hard to code at the moment with the brain fog

this should work for you now

Code: Select all

; IDLE_ORIGINAL_Try_Again_02.pb
;an example of scaning a text file to build a dictionary 
;to display it in sorted order and search for an item 

Global CurrentDir$ = GetCurrentDirectory() 
XIncludeFile "C:\PureBasic-myApps\vmProjects\BUTTONS\Line-By-Line\Line-By-Line-Folder\Create-INDEX\squint3.pbi"

UseModule SQUINT

EnableExplicit 

Global sq.isquint = SquintNew()

Procedure CBSearch(*key,*value,*userData) ;Can add items to a list if wanted 
  PrintN(PeekS(*key,-1,#PB_UTF8) + " :frequency " + Str(*value))  
  ProcedureReturn 1 ;keep enumerating 
EndProcedure   

Procedure CBList(*key,*value,*userData) ;Can add items to a list if wanted 
  Static ct 
  PrintN(Str(ct) + " : " + PeekS(*key,-1,#PB_UTF8) + " :frequency " + Str(*value))  
  ct+1
  ProcedureReturn 1 ; keep enumerating  
EndProcedure 

OpenConsole()  

Global key.s,url.s,file.s,fn,size,*st,*chr.unicode 
Global len,ct,path.s,line.s

;  url.s = "https://archive.org/download/kjv-text-files/Genesis.txt"
; ; 
;  path.s = GetUserDirectory(#PB_Directory_Downloads)  + GetFilePart(url)
;  If FileSize(path) < 0 
;    ReceiveHTTPFile(url,path)
;  EndIf 

Global FileName$ = "File-to-be-Indexed.txt"

fn = OpenFile(#PB_Any,FileName$) 
Debug "Open Success = " + fn 

If fn 
  Repeat  
    line.s = ReadString(fn,#PB_UTF8)
Debug line 
    ct+1 
    *chr=@line 
    If line <> "" 
      *st=*chr 
      Repeat
        While (*chr\u > 0 And *chr\u <= 32)  
          *chr+2
          *st=*chr
        Wend  
        While ((*chr\u >= '0' And *chr\u <= '9') Or (*chr\u >= 'A' And *chr\u <= 'Z') Or (*chr\u >= 'a' And *chr\u <= 'z') Or *chr\u=39) ;'  
          len+1
          *chr+2
        Wend       
        If len 
          key = LCase(PeekS(*st,len))
          ct = sq\Get(0,@key) + 1     ;look up the word and increment its count    
          sq\Set(0,@key,ct)           ;set the word in the dictionary  
          len = 0 
          *st=*chr+2  
        EndIf   
        If *chr\u = 0 
          Break 
        EndIf 
        *chr+2
        *st=*chr
      Until *chr\u = 0    
    EndIf 
  Until Eof(fn) 
  
  sq\Walk(0,@CBList())            ;dumps the dictionary      
 
  
   Repeat 
     PrintN("enter a key to search for or type quit to end")   
     key = LCase(Input())    
     If (key <> "quit" And key <> "end")  
        sq\Enum(@key,@CBSearch())        ;search the dictionary 
     Else 
       Break 
     EndIf   
   ForEver   
    
EndIf 

sq\Free()

CloseConsole() 


vmars316
Enthusiast
Enthusiast
Posts: 474
Joined: Fri Jun 29, 2012 12:24 am
Contact:

Re: How to recognize alphaMeric characters

Post by vmars316 »

Thanks for your Help idle :
Sorry I didn't understand your code .
AND I didn't understand my problem .
; =================================================
; I would like to run with a Local file (C:\) ,
; but whenever I try it , the Console shows Empty .
; Pls , how can I fix this ?
; I even tried "file:///C:/PureBasic-myApps/vmProjects/BUTTONS/Line-By-Line/Line-By-Line-Folder/Create-INDEX/File-to-be-Indexed.txt" , but nogo .
; =================================================
;fn = OpenFile(#PB_Any ,"File-To-be-Indexed.txt")
;If fn
; Repeat
; line.s = ReadString(fn,#PB_UTF8)
; =================================================

Code: Select all


; IDLE_ORIGINAL-Restart_DEBUG.pb
;an example of scaning a text file to build a dictionary 
;to display it in sorted order and search for an item 

Global CurrentDir$ = GetCurrentDirectory() 
XIncludeFile "C:\PureBasic-myApps\vmProjects\BUTTONS\Line-By-Line\Line-By-Line-Folder\Create-INDEX\squint3.pbi"

UseModule SQUINT

EnableExplicit 

Global sq.isquint = SquintNew()

Procedure CBSearch(*key,*value,*userData) ;Can add items to a list if wanted 
  PrintN(PeekS(*key,-1,#PB_UTF8) + " :frequency " + Str(*value))  
EndProcedure   

Procedure CBList(*key,*value,*userData) ;Can add items to a list if wanted 
  Static ct 
  PrintN(Str(ct) + " : " + PeekS(*key,-1,#PB_UTF8) + " :frequency " + Str(*value)) 
  Debug (Str(ct) + " : " + PeekS(*key,-1,#PB_UTF8) + " :frequency " + Str(*value)) 
  
  ct+1
EndProcedure 

OpenConsole()  

Global key.s,url.s,file.s,fn,size,*st,*chr.unicode 
Global len,ct,path.s,line.s

url.s = "https://archive.org/download/kjv-text-files/Genesis.txt"

path.s = GetUserDirectory(#PB_Directory_Downloads)  + GetFilePart(url)
If FileSize(path) < 0 
  ReceiveHTTPFile(url,path)
EndIf 
; =================================================
;   I would like to run with a Local file (C:\) ,
;   but whenever I try it , the Console shows Empty .
;   Pls , how can I fix this ?
; =================================================
;fn = OpenFile(#PB_Any,"File-To-be-Indexed.txt") 
;If fn 
;  Repeat  
;    line.s = ReadString(fn,#PB_UTF8)
; =================================================

fn = OpenFile(#PB_Any,path) 
If fn 
  Repeat  
    line.s = ReadString(fn,#PB_UTF8)
    ct+1 
    *chr=@line 
    If line <> "" 
      *st=*chr 
      Repeat
        While (*chr\u > 0 And *chr\u <= 32)  
          *chr+2
          *st=*chr
        Wend  
        While ((*chr\u >= '0' And *chr\u <= '9') Or (*chr\u >= 'A' And *chr\u <= 'Z') Or (*chr\u >= 'a' And *chr\u <= 'z') Or *chr\u=39) ;'  
          len+1
          *chr+2
        Wend       
        If len 
          key = LCase(PeekS(*st,len))
          ct = sq\Get(0,@key) + 1     ;look up the word and increment its count    
          sq\Set(0,@key,ct)           ;set the word in the dictionary  
          len = 0 
          *st=*chr+2  
        EndIf   
        If *chr\u = 0 
          Break 
        EndIf 
        *chr+2
        *st=*chr
      Until *chr\u = 0    
    EndIf 
  Until Eof(fn) 
  
  sq\Walk(0,@CBList())            ;dumps the dictionary      
  PrintN("Search for ba") 
  
  Repeat 
    PrintN("enter a key to search for of type quit to end")   
    key = LCase(Input())    
;    If key <> "quit" Or key <> "end" 
    If (key <> "quit" And key <> "end")  
      sq\Enum(@key,@CBSearch())        ;search the dictionary 
    Else 
      Break 
    EndIf   
  ForEver   
    
EndIf 

sq\Free()

CloseConsole() 

vmars.us Win11 x64 , Martin Guitar 000-16 (1995)
"All things in moderation , except for love and forgiveness."
User avatar
idle
Always Here
Always Here
Posts: 5929
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: How to recognize alphaMeric characters

Post by idle »

this should work for you now.

Code: Select all


Macro Comments() 
  ; SQUINT 3, Sparse Quad Union Indexed Nibble Trie
  ; Copyright Andrew Ferguson aka Idle (c) 2020 - 2023 
  ; Version 3.1.3b
  ; PB 5.72-6.02b 32bit/64bit asm and c backends for Windows,Mac OSX,Linux,PI,M1
  ; Thanks Wilbert for the high low insight and utf8 conversion help.
  ; Squint is a compact prefix Trie indexed by nibbles into a sparse array with performance metrics close to a map
  ; It provides O(K) performance with a memory size ~32 times smaller than a 256 node trie
  ; Squint is at worst 2 times slower than a Map for set operations, look ups are closer to 1:1 or faster   
  ; as squint can bail out as soon as a char of a key isn't found unlike a map that has to evaluate the whole key. 
  ; Squint is lexographicaly sorted so sorting is magnitudes faster than what you could achieve with a map list or unsorted array 
  ; Squint also supports collections or subtries, which facilitates tasks like in memory DB's  
  ; The Numeric mode of squint behaves like a map and is closer to 1:1 perfromace with a sized map 
  ; 
  ; see https://en.wikipedia.org/wiki/Trie 
  ;     simillar structures   
  ;     https://dotat.at/prog/qp/blog-2015-10-04.html
  ;     https://cr.yp.to/critbit.html 
  ;
  ; Squint supports Set, Get, Enum, EnumNode , Walk, WalkNode, Delete and Prune with a flag in Delete
  ; String keys can be Unicode, Ascii or UTF8 the type must be specified 
  ; all string keys get mapped to UTF8 
  ;
  ; SquintNumeric supports, SetNumeric GetNumeric DeleteNumeric and WalkNumeric
  ; it's provided as a direct subtitute for a numeric map, keys can be any size upto #SQUINT_MAX_KEY =1024
  ; keys are returned as pointers   
  ;
  ; Note while you can mix string and numeric keys in the same trie it's not recomended unless you only require set and get 
  ;   ;
  ; MIT License
  ; Permission is hereby granted, Free of charge, to any person obtaining a copy
  ; of this software and associated documentation files (the "Software"), to deal
  ; in the Software without restriction, including without limitation the rights
  ; To use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  ; copies of the Software, and to permit persons to whom the Software is
  ; furnished to do so, subject to the following conditions:
  ; 
  ; The above copyright notice and this permission notice shall be included in all
  ; copies or substantial portions of the Software.
  ; 
  ; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS Or
  ; IMPLIED, INCLUDING BUT Not LIMITED To THE WARRANTIES OF MERCHANTABILITY,
  ; FITNESS For A PARTICULAR PURPOSE And NONINFRINGEMENT. IN NO EVENT SHALL THE
  ; AUTHORS Or COPYRIGHT HOLDERS BE LIABLE For ANY CLAIM, DAMAGES Or OTHER
  ; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT Or OTHERWISE, ARISING FROM,
  ; OUT OF Or IN CONNECTION With THE SOFTWARE Or THE USE Or OTHER DEALINGS IN THE
  ; SOFTWARE. 
  
EndMacro 

DeclareModule SQUINT 
  
  #SQUINT_MAX_KEY = 1024
  
  Structure squint_node Align #PB_Structure_AlignC
    *vertex.edge
    StructureUnion
      squint.q
      value.i 
    EndStructureUnion 
  EndStructure   
  
  Structure edge   Align #PB_Structure_AlignC
    e.squint_node[0]
  EndStructure 
  
  Structure squint Align #PB_Structure_AlignC
    *vt
    size.i
    count.i
    gEnum.i
    write.i
    *root.squint_node
    sb.a[#SQUINT_MAX_KEY]
  EndStructure
  
  CompilerIf #PB_Compiler_32Bit 
    #Squint_Pmask = $ffffffff
    #Squint_Integer = 4 
  CompilerElse
    #Squint_Pmask = $ffffffffffff
    #Squint_Integer = 8 
  CompilerEndIf
  
  ;-Squint Callback prototype 
  Prototype Squint_CB(*key,*value=0,*userdata=0)
  Prototype Squint_CBFree(*mem) 
  
  Declare SquintNew()
  Declare SquintFree(*this.Squint,*pfn.Squint_CBFree=0)
  
  Declare SquintSetNode(*this.squint,*subtrie,*key,value.i,mode=#PB_Unicode)
  Declare SquintGetNode(*this.squint,*subtrie,*key,mode=#PB_Unicode,bval=1)
  Declare SquintDeleteNode(*this.squint,*subtrie,*key,prune=0,mode=#PB_Unicode)
  Declare SquintWalkNode(*this.squint,*subtrie,*pfn.squint_CB,*userdata=0) 
  Declare SquintEnum(*this.squint,*key,*pfn.squint_CB,*userdata=0,mode=#PB_Unicode)
  Declare SquintEnumNode(*this.squint,*subtrie,*key,*pfn.squint_CB,*userdata=0,mode=#PB_Unicode)
  
  Declare SquintSetNumeric(*this.squint,*key,value.i,size=#Squint_Integer)
  Declare SquintGetNumeric(*this.squint,*key,size = #Squint_Integer)
  Declare SquintDeleteNumeric(*this.squint,*key,size = #Squint_Integer)
  Declare SquintWalkNumeric(*this.squint,*pfn.squint_CB,*userdata=0)
  
  Declare SquintSize(*this.squint)
  Declare SquintNumKeys(*this.squint)
  
  ;-Squint Inteface iSquint  
  Interface iSquint
    Free(*pfn.Squint_CBFree=0)
    Delete(*subtrie,*key,prune=0,mode=#PB_Unicode)
    Set(*subtrie,*key,value.i,mode=#PB_Unicode)
    Get(*subtrie,*key,mode=#PB_Unicode,bval=1)
    Enum(*key,*pfn.squint_CB,*userdata=0,mode=#PB_Unicode)
    EnumNode(*subtrie,*key,*pfn.squint_CB,*userdata=0,mode=#PB_Unicode)
    Walk(*subtrie,*pfn.squint_CB,*userdata=0)
    SetNumeric(*key,value.i,size=#Squint_Integer) 
    GetNumeric(*key,size= #Squint_Integer) 
    DeleteNumeric(*key,size=#Squint_Integer)
    WalkNumeric(*pfn.Squint_CB,*userdata=0)
    Size()
    NumKeys()
  EndInterface
  
  DataSection: vtSquint:
    Data.i @SquintFree()
    Data.i @SquintDeleteNode() 
    Data.i @SquintSetNode()
    Data.i @SquintGetNode()
    Data.i @SquintEnum()
    Data.i @SquintEnumNode() 
    Data.i @SquintWalkNode()
    Data.i @SquintSetNumeric()
    Data.i @SquintGetNumeric()
    Data.i @SquintDeleteNumeric()
    Data.i @SquintWalkNumeric() 
    Data.i @SquintSize() 
    Data.i @SquintNumKeys()
  EndDataSection   
  
EndDeclareModule

Module SQUINT
  
  EnableExplicit
  
  ;-macros 
  Macro _SETINDEX(in,index,number)
    in = in & ~(15 << (index << 2)) | (number << (index << 2))
  EndMacro
  
  Macro _GETNODECOUNT()
    CompilerIf #PB_Compiler_32Bit 
      nodecount = MemorySize(*node\vertex) / SizeOf(squint_node)
    CompilerElse
      nodecount = (*node\vertex >> 48)
    CompilerEndIf
  EndMacro
  
  Macro _POKENHL(in,Index,Number)
    *Mem.Ascii = in
    *Mem + Index >> 1
    If Index & 1
      *Mem\a = (*Mem\a & $f0) | (Number & $f)
    Else
      *Mem\a = (*Mem\a & $0f) | (Number << 4)
    EndIf
  EndMacro
  
  CompilerIf #PB_Compiler_Processor = #PB_Processor_x86 
    Macro rax : eax : EndMacro 
  CompilerEndIf   
  
  CompilerIf #PB_Compiler_Thread 
    Macro _LockMutex(mut) 
      LockMutex(mut) 
    EndMacro 
    Macro _UnlockMutex(mut)
      UnlockMutex(mut)
    EndMacro   
  CompilerElse 
    Macro _Lockmutex(mut) 
    EndMacro 
    Macro _UnlockMutex(mut)
    EndMacro   
  CompilerEndIf   
  
  Macro _gLockXCHG(var,var1) 
    CompilerIf #PB_Compiler_Backend = #PB_Backend_C 
      !__atomic_exchange_n(&p_node->f_vertex,p_new,__ATOMIC_SEQ_CST) ; 
    CompilerElse 
      CompilerIf #PB_Compiler_Processor = #PB_Processor_x86
        !mov eax , [p.p_#var1]
        !mov edx , [p.p_#var]
        !xchg dword [edx] , eax
      CompilerElse 
        !mov rax , [p.p_#var1]
        !mov rdx , [p.p_#var] 
        !xchg qword [rdx] , rax
      CompilerEndIf 
    CompilerEndIf 
  EndMacro
  
  Global gEnumlock.i  
  
  Macro _gEnumlock(x)
    CompilerIf #PB_Compiler_Backend = #PB_Backend_C 
      !__atomic_exchange_n(&squintXg_genumlock,x,__ATOMIC_SEQ_CST) ; 
    CompilerElse  
      !mov rdx, x  
      !xchg qword [squint.v_gEnumlock] , rdx
    CompilerEndIf 
  EndMacro 
  
  Macro _sfence
    CompilerIf #PB_Compiler_Backend = #PB_Backend_Asm  
      !sfence 
    CompilerElse 
      CompilerIf #PB_Compiler_Processor = #PB_Processor_Arm32 Or #PB_Compiler_Processor = #PB_Processor_Arm64
        !__sync_synchronize();
      CompilerElse   
        !__asm__("sfence" ::: "memory");   
      CompilerEndIf   
    CompilerEndIf   
  EndMacro 
  
  Macro _lfence 
    CompilerIf #PB_Compiler_Backend = #PB_Backend_Asm   
      ;!lfence
    CompilerElse
      CompilerIf #PB_Compiler_Processor = #PB_Processor_Arm32 Or #PB_Compiler_Processor = #PB_Processor_Arm64 
        !__sync_synchronize();
      CompilerElse  
        !__asm__("lfence" ::: "memory"); 
      CompilerEndIf   
    CompilerEndIf    
  EndMacro 
  
  Macro _CONVERTUTF8() 
    
    vchar = PeekU(*key)
    
    If mode = #PB_Unicode  
      CompilerIf #PB_Compiler_Backend = #PB_Backend_C  
        If vchar > $7f
          If vchar > $7ff
            vchar = $8080E0 | (vchar >> 12) | ((vchar << 2) & $3F00) | ((vchar << 16) & $3F0000)
          Else
            vchar = $80C0 | (vchar >> 6) | ((vchar << 8) & $3F00)
          EndIf
        EndIf   
      CompilerElse  
        
        !mov	eax, [p.v_vchar] 
        !cmp eax, 0x80 
        !jb .l2 
        !cmp	eax, 0x0800
        !jae .l1
        !mov edx, eax 
        !sal	edx, 8
        !and	edx, 0x3f00 
        !sar	eax, 6
        !or	edx, eax
        !or  edx, 0x80C0 
        !mov eax,edx
        !jmp	.l2
        !.l1:
        !mov edx, eax 
        !sal	edx, 16
        !and	edx, 0x3f0000
        !mov ecx, eax 
        !sal ecx, 2 
        !and ecx, 0x3f00
        !or  edx,ecx 
        !sar eax, 12 
        !or  edx, eax 
        !or  edx, 0x8080e0 
        !mov eax,edx 
        !.l2:
        !mov	[p.v_vchar], eax  
        
      CompilerEndIf    
      
    EndIf  
    
  EndMacro 
  
  Macro _MODECHECK()
    _CONVERTUTF8()
    If mode <> #PB_Unicode
      If (vchar >> ((count&1)<<4) & $ff = 0)
        Break
      EndIf 
    EndIf
  EndMacro 
  
  Global gwrite = CreateMutex() 
  Global NewList lpointers.i() 
  
  Macro _SETNODE()
    
    If *node\vertex
      
      _GETNODECOUNT()
      If (offset <> 15 Or nodecount = 16)
        
        *this\write = *node  ;<----------------------------------Set before update
        _sfence 
        *node = *node\Vertex\e[offset] & #Squint_Pmask
      Else  
        
        offset = nodecount
        nodecount+1 
        *new = AllocateMemory((nodecount)*SizeOf(squint_node)) 
        *old = *node\vertex & #Squint_Pmask
        CopyMemory(*old,*new,(offset)*SizeOf(squint_node)) 
        
        CompilerIf #PB_Compiler_64Bit; 
          *new | ((nodecount) << 48)
        CompilerEndIf  
        
        _gLockXCHG(node,new) 
        
        _SETINDEX(*node\squint,idx,offset)
        
        *this\write = *node     ;<----------------------------------Set before update
        _sfence 
        *node = *node\Vertex\e[offset] & #Squint_Pmask
        
        FreeMemory(*old) 
        
        *this\size+SizeOf(squint_node) 
        
      EndIf 
      
    Else
      
      *node\vertex = AllocateMemory(SizeOf(squint_Node))
      
      *this\size+SizeOf(squint_node) 
      CompilerIf #PB_Compiler_64Bit; 
        *node\vertex | (1 << 48)
      CompilerEndIf
      *node\squint = -1
      _SETINDEX(*node\squint,idx,0)
      
      *this\write = *node ;<----------------------------------Set before update
      _sfence 
      *node = *node\Vertex\e[0] & #Squint_Pmask
      
    EndIf 
    
    
  EndMacro
  
  ;-General functions 
  
  ;##################################################################################
  ;# Create a new Squint Trie 
  ;#  
  ;# example    
  ;#   global sq.iSquint = SquintNew() use via an interface with isquint           
  ;#   or 
  ;#   global sq = SquintNew() or normal use      
  ;################################################################################## 
  
  Procedure SquintNew()
    
    Protected *this.squint,a
    *this = AllocateMemory(SizeOf(squint))
    If *this
      *this\vt = ?vtSquint
      *this\root = AllocateMemory(SizeOf(squint_node)*16)
      ProcedureReturn *this
    EndIf
  EndProcedure
  
  Procedure ISquintFree(*this.squint,*node.squint_node=0,*pfn.Squint_CBFree=0)
    Protected a,offset,nodecount
    If Not *node
      ProcedureReturn 0
    EndIf
    For a=0 To 15
      offset = (*node\squint >> (a<<2)) & $f
      If *node\vertex
        _GETNODECOUNT()
        If (offset <> 15 Or nodecount = 16)
          *this\size - SizeOf(squint_node)
          ISquintFree(*this,*node\Vertex\e[offset] & #Squint_Pmask)
        EndIf
      EndIf
    Next
    _LockMutex(gwrite)
    If *node\vertex
      _GETNODECOUNT()
      If *pfn 
        *pfn(*node\value) 
      EndIf 
      FreeMemory(*node\Vertex & #Squint_Pmask) 
      *node\vertex=0
    EndIf
    _UnlockMutex(gwrite) 
    ProcedureReturn *node
  EndProcedure
  
  ;##################################################################################
  ;# Free Squint Trie 
  ;# If you need to free your own pointers, Walk the trie 1st to free them  
  ;# then call sq\free() or SquintFree(sq)   
  ;################################################################################## 
  
  Procedure SquintFree(*this.squint,*pfn.Squint_CBFree=0)  
    
    Protected a,offset,*node.squint_node,nodecount
    *node = *this\root
    For a=0 To 15
      offset = (*node\squint >> (a<<2)) & $f
      If *node\vertex
        _GETNODECOUNT()
        If (offset <> 15 Or nodecount = 16)
          ISquintFree(*this,*node,*pfn)
        EndIf
      EndIf
    Next
    FreeMemory(*this\root)
    nodecount = *this\size 
    FreeMemory(*this) 
    ProcedureReturn  nodecount  
  EndProcedure
  ;-string functions 
  
  ;#################################################################################
  ;#    Set a node from the root or from a previously set node   
  ;#    *this.squint instance from SquintNew() 
  ;#    *subtrie 0 Or the addess of a previously stored node retuned from this function 
  ;#   
  ;#    *key   address of a null terminated string can be unicode ascii or UTF8
  ;#    value.i  non zero value or address of something  
  ;#    mode.i  Desired key format #PB_Uniocde, #PB_Ascii, #PB_UTF8  
  ;#    returns *subtrie the node       
  ;# example     
  ;#    *cars = SquintSetNode(sq,0,@"cars:",100)          the key = "cars:"        
  ;#     *toyota = squintSetNode(sq,*cars,@"Toyota:",200) the key = "cars:toyota" 
  ;#     squintSetNode(sq,*toyota,@"Corolla",201)         the key = "cars:toyota:corolla" 
  ;#     squintSetNode(sq,*toyota,@"Cameray",202)         the key = "cars:toyota:Cameray"
  ;# via interface  
  ;#     sq\set(*toyota,@"Cameray",202)                   the key = "cars:toyota:Cameray"   
  ;################################################################################## 
  
  Procedure SquintSetNode(*this.squint,*subtrie,*key,value.i,mode=#PB_Unicode)
    
    Protected *node.squint_node,idx,offset,nodecount,vchar.l,vret.l,count,*out
    Protected *new.squint_node,*old,*adr 
    
    If *subtrie = 0
      *node = *this\root & #Squint_Pmask
    Else 
      *node = *subtrie & #Squint_Pmask
    EndIf 
    _CONVERTUTF8()
    
    _LockMutex(gwrite)
    
    While vchar
      *this\write = *node   ;<----------------------------------Set the write flag with current node    
      _sfence 
      idx = (vchar >> 4) & $f
      offset = (*node\squint >> (idx<<2)) & $f
      _SETNODE()     
      idx = vchar & $0f
      offset = (*node\squint >> (idx<<2)) & $f
      _SETNODE()    
      vchar >> 8
      count+1
      If vchar = 0
        *key+2
        _MODECHECK()
      EndIf
    Wend
    idx=0
    *out = *node 
    offset = *node\squint & $f
    _SETNODE()
    
    *this\write = 0    ;<----------------------------------Clear the write flag     
    
    *this\count+1 
    
    _unLockMutex(gwrite)
    
    If value 
      *node\value = value
    EndIf 
    
    ProcedureReturn *out
    
  EndProcedure
  
  
  ;##################################################################################
  ;#    Get a node from the root or from a previously stored node aka subtrie    
  ;#    *this.squint instance from SquintNew() 
  ;#    *subtrie 0 Or the addess of a previously stored node retuned from this function 
  ;#   *key   address of a null terminated string can be unicode ascii or UTF8
  ;#    mode.i  Desired key format #PB_Uniocde, #PB_Ascii, #PB_UTF8  
  ;#    
  ;#  returns the value or subnode        
  ;#  example    
  ;#     x = squintGetNode(sq,0,@"cars:toyota:")   subtrie = root, the key = "cars:toyota"  
  ;#     x = squintGetNode(sq,*toyota,@"Corolla")  subtrie = *toyota the key evaluates to = "cars:toyota:corolla"   
  ;#     or via interface 
  ;#     x = sq\get(0,@"cars:toyota:")  subtrie = root, the key = "cars:toyota"    
  ;################################################################################## 
  
  Procedure SquintGetNode(*this.squint,*subtrie,*key,mode=#PB_Unicode,bval=1)
    
    Protected *node.squint_Node,idx,offset,nodecount,vchar.l,vret.l,count,*out
    
    If *subtrie = 0
      *node = *this\root & #Squint_Pmask
    Else 
      *node = *subtrie & #Squint_Pmask
    EndIf 
    _CONVERTUTF8()
    
    If *node\vertex
      
      While vchar
        
        If *this\write <> *node   ;dont step on same write node
          _lfence 
          offset = (*node\squint >> ((vchar & $f0) >> 2 )) & $f
          _GETNODECOUNT()
          If offset < nodecount
            *node = (*node\Vertex\e[offset] & #Squint_Pmask)
          Else
            ProcedureReturn 0
          EndIf
        Else 
          Continue 
        EndIf  
        
        If *this\write <> *node  ;dont step on same write node
          _lfence 
          offset = (*node\squint >> ((vchar & $0f) << 2)) & $f
          _GETNODECOUNT()
          If offset < nodecount
            *node = (*node\Vertex\e[offset] & #Squint_Pmask)
          Else
            ProcedureReturn 0
          EndIf
        Else 
          Continue 
        EndIf 
        
        vchar >> 8
        count+1
        If vchar = 0
          *key+2
          _MODECHECK()
        EndIf
        
      Wend
      
      *out = *node 
      offset = *node\squint & $f
      _GETNODECOUNT()
      
      If offset <= nodecount
        *node = (*node\Vertex\e[offset] & #Squint_Pmask)
        If bval 
          ProcedureReturn *node\value
        Else 
          ProcedureReturn *out  
        EndIf   
      Else
        ProcedureReturn 0
      EndIf
      
    EndIf 
    
  EndProcedure 
  
  ;##################################################################################
  ;#    Resets a keys value to 0 or deletes the childen of the node freeing up memory     
  ;#    *this.squint instance from SquintNew() 
  ;#    *subtrie 0 Or the addess of a previously stored node retuned from this function 
  ;#    *key     address of a null terminated string can be unicode ascii or UTF8
  ;#    mode.i   Desired key format 
  ;#    returns  the value or 0
  ;#    example  
  ;#    x = SquintDeleteNode(sq,*cars,@"Toyota:",1)  subnode = *cars, the key evals to "cars:toyota" prune =1 So it deletes the child nodes corrola And camery  
  ;#    x = SquintDeleteNode(sq,0,@"cars:toyota:corolla") subtrie = root, the full key = "cars:toyota:corolla" prune=0 so it set the value to 0  
  ;#    via inteface  
  ;#    sq\delete(0,@"cars:toyota:corolla")     
  ;################################################################################## 
  
  Procedure SquintDeleteNode(*this.squint,*subtrie,*key.Unicode,prune=0,mode=#PB_Unicode)
    
    Protected *node.squint_node,idx,*mem.Character,offset,nodecount,vchar.l,vret.l,count,*out
    If *subtrie = 0
      *node = *this\root & #Squint_Pmask
    Else
      *node = *subtrie  & #Squint_Pmask 
    EndIf 
    _CONVERTUTF8()
    While vchar
      offset = (*node\squint >> ((vchar & $f0) >> 2 )) & $f
      If *node\vertex
        _GETNODECOUNT()
        If (offset <> 15 Or nodecount = 16)
          *node = *node\Vertex\e[offset] & #Squint_Pmask
        EndIf
      Else
        ProcedureReturn 0
      EndIf
      If *node
        offset = (*node\squint >> ((vchar & $0f) << 2)) & $f
        If *node\vertex
          _GETNODECOUNT()
          If (offset <> 15 Or nodecount = 16)
            *node = *node\Vertex\e[offset] & #Squint_Pmask
          EndIf
        Else
          ProcedureReturn 0
        EndIf
      EndIf
      vchar >> 8
      If vchar = 0
        *key+2
        _MODECHECK()
      EndIf
    Wend
    If prune
      ISquintFree(*this,*node)
      If (*node\vertex & #Squint_Pmask) = 0
        *node\squint = 0
      EndIf
    Else
      offset = *node\squint & $f
      _GETNODECOUNT()
      If offset <= nodecount
        *node = (*node\Vertex\e[offset] & #Squint_Pmask)
        If (*node\vertex & #Squint_Pmask) = 0
          *node\squint = 0
        EndIf
      Else
        ProcedureReturn 0
      EndIf
    EndIf
  EndProcedure
  
  Procedure IEnum(*this.squint,*node.squint_Node,depth,*pfn.squint_CB,*outkey,*userdata=0)
    Protected a.i,offset,nodecount,*mem.Ascii 
    
    If Not *node
      ProcedureReturn 0
    EndIf
    
    For a =0 To 15
      
      offset = (*node\squint >> (a<<2)) & $f
      If (*node\vertex And *node\squint)
        _GETNODECOUNT()
        If (offset <> 15 Or nodecount = 16)
          _POKENHL(*outkey,depth,a)
          If IEnum(*this,*node\Vertex\e[offset] & #Squint_Pmask,depth+1,*pfn,*outkey,*userdata) = 0 
            Break 
          EndIf  
        EndIf
        
      EndIf
    Next
    
    If *node\vertex=0
      If *pfn
        PokeA(*outkey+((depth>>1)),0)
        If *pfn(*outkey,*node\value,*userdata) = 0 
          ProcedureReturn 0
        EndIf   
      EndIf
    EndIf
    ProcedureReturn *node
    
  EndProcedure
  
  ;##################################################################################
  ;#  Enumerates the Trie from a given key   
  ;#    *this.squint instance from SquintNew() 
  ;#    *subtrie 0 Or the addess of a previously stored node       
  ;#    *key address of a null terminated string can be unicode ascii Or UTF8
  ;#     mode.i  Desired key format #PB_Uniocde, #PB_Ascii, #PB_UTF8  
  ;#    *pfn.squint_CB address of callback function as Squint_CB(*key,*value=0,*userdata=0) 
  ;#        where *key is pointer to the key *value is pointer to the *value, *userDate      
  ;# example    
  ;#     squintEnum(sq,*subtrie,@"cars:toyota:",@MyCallback())       
  ;#  or via interface 
  ;#     sq\Enum@"cars:toyota:",@MyCallback())   
  ;################################################################################## 
  
  Procedure SquintEnumNode(*this.squint,*subtrie,*key,*pfn.squint_CB,*userdata=0,mode=#PB_Unicode)  
    
    Protected *node.squint_Node,idx,*mem.Ascii,offset,nodecount,depth,vchar.l,vret.l,count,*out
    Protected outkey.s{1024} 
    
    _LockMutex(gwrite) 
    
    If *subtrie = 0
      *node = *this\root
    Else
      *node = *subtrie  & #Squint_Pmask 
    EndIf 
    
    _CONVERTUTF8()
    
    While vchar 
      
      offset = (*node\squint >> ((vchar & $f0) >> 2 )) & $f
      _GETNODECOUNT()
      If offset < nodecount
        *mem = @outkey+(depth>>1) 
        *mem\a = (*mem\a & $0f) | (((vchar >> 4) & $f)<<4)
        depth+1
        *node = (*node\Vertex\e[offset] & #Squint_Pmask)
      Else
        _UnlockMutex(gwrite) 
        ProcedureReturn 0
      EndIf
      
      offset = (*node\squint >> ((vchar & $0f) << 2)) & $f
      _GETNODECOUNT()
      If offset < nodecount
        *mem = @outkey+(depth>>1) 
        *Mem\a = ((*Mem\a & $f0) | (vchar & $f))
        depth+1
        *node = (*node\Vertex\e[offset] & #Squint_Pmask)
      Else
        _UnlockMutex(gwrite) 
        ProcedureReturn 0
      EndIf
      
      vchar >> 8
      count+1
      If vchar = 0
        *key+2
        _MODECHECK()
      EndIf
    Wend
    
    IEnum(*this,*node,depth,*pfn,@outkey,*userdata)
    
    _UnlockMutex(gwrite) 
    
  EndProcedure
  
  ;##################################################################################
  ;#  Enumerates the Trie from a given key   
  ;#    *this.squint instance from SquintNew() 
  ;#    *key   address of a null terminated string can be unicode ascii or UTF8
  ;#     mode.i  Desired key format #PB_Uniocde, #PB_Ascii, #PB_UTF8  
  ;#    *pfn.squint_CB address of callback function as Squint_CB(*key,*value=0,*userdata=0) 
  ;#        where *key is pointer to the key *value is pointer to the *value, *userDate      
  ;# example    
  ;#     squintEnum(sq,@"cars:toyota:",@MyCallback())       
  ;#  or via interface 
  ;#     sq\Enum@"cars:toyota:",@MyCallback())   
  ;################################################################################## 
  
  Procedure SquintEnum(*this.squint,*key,*pfn.squint_CB,*userdata=0,mode=#PB_Unicode)
    
    SquintEnumNode(*this,0,*key,*pfn,*userdata,mode) 
    
  EndProcedure   
  
  ;##################################################################################
  ;# Walks the entire trie    
  ;#    *this.squint instance from SquintNew() 
  ;#    *pfn.squint_CB address of callback function as Squint_CB(*key,*value=0,*userdata=0) 
  ;#       where *key is pointer to the key *value is pointer to the *value, *userDate      
  ;# example    
  ;#     squintWalk(sq,@MyCallback())       
  ;#  or via interface 
  ;#     sq\Walk(@MyCallback())   
  ;################################################################################## 
  
  Procedure SquintWalk(*this.squint,*pfn.squint_CB,*userdata=0) 
    
    Protected outkey.s{#SQUINT_MAX_KEY} 
    
    _LockMutex(gwrite) 
    
    IEnum(*this,*this\root,0,*pfn,@outkey,*userdata)
    
    _unLockMutex(gwrite)
    
  EndProcedure
  
  ;##################################################################################
  ;# Walks from a subtrie    
  ;#    *this.squint instance from SquintNew() 
  ;#    *subtrie 0 Or the addess of a previously stored node  
  ;#    *pfn.squint_CB address of callback function As Squint_CB(*key,*value=0,*userdata=0) 
  ;#          
  ;# example    
  ;#     squintWalkNode(sq,*cars,@MyCallback())       
  ;#  or via interface 
  ;#     sq\Walk(*cars,@MyCallback())   
  ;################################################################################## 
  
  Procedure SquintWalkNode(*this.squint,*subtrie,*pfn.squint_CB,*userdata=0)   
    
    Protected *node, outkey.s{#SQUINT_MAX_KEY}    
    
    _LockMutex(gwrite) 
    
    If *subtrie = 0
      *node = *this\root
    Else
      *node = *subtrie  & #Squint_Pmask 
    EndIf 
    IEnum(*this,*node,0,*pfn,@outkey,*userdata)
    
    _unLockMutex(gwrite)
    
  EndProcedure
  
  Procedure SquintSize(*this.squint) 
    ProcedureReturn *this\size 
  EndProcedure   
  
  Procedure SquintNumKeys(*this.squint)
    ProcedureReturn *this\count 
  EndProcedure   
  
  ;-Numeric functions operate the same as a map, keys can be anything that's serial  
  
  ;#################################################################################
  ;#    Set a numeric key  
  ;#    note you can use both numeric or string keys in the same trie  
  ;#    a numeric key is an address to a variable and the required size in bytes 
  ;#     
  ;#    *this.squint instance from SquintNew() 
  ;#    *key   address of a variable or memory pointer 
  ;#    value.i non zero value or address of something  
  ;#    size.i required size in bytes    
  ;#  example     
  ;#     ikey.l = 12345 
  ;#     SquintSetNumeric(sq,@ikey,1234567,4)  the key is a long  
  ;#     pt.point  
  ;#     pt\x = 100 
  ;#     pt\y = 200   
  ;#     SquintSetNumeric(sq,@pt,1,SizeOf(point))       
  ;#  via interface  
  ;#     sq\setNumeric(@ikey,123435,4)    
  ;################################################################################## 
  
  Procedure SquintSetNumeric(*this.squint,*key,value.i,size=#Squint_Integer)
    
    Protected *node.squint_node,idx,offset,nodecount,vchar.i,vret.i,count 
    Protected *old,*new,*adr,*akey.Ascii 
    *node = *this\root & #Squint_Pmask
    *akey = *key
    
    _LockMutex(gwrite) 
    
    While count <= size  
      *this\write = *node   ;<----------------------------------Set the write flag with current node    
      _sfence 
      idx = (*akey\a >> 4) & $f
      offset = (*node\squint >> (idx<<2)) & $f
      _SetNODE()
      idx = (*akey\a & $f)
      offset = (*node\squint >> (idx<<2)) & $f
      _SetNODE()
      *akey+1 
      count+1
    Wend
    
    _UnlockMutex(gwrite) 
    *this\write = 0    
    
    *node\value = value
    
  EndProcedure
  
  
  ;##################################################################################
  ;#    Get a numeric node     
  ;#    *this.squint instance from SquintNew() 
  ;#    *key   address of a variable or memory pointer 
  ;#    size   number of bytes used to store the key    
  ;#  returns the value         
  ;#  example  
  ;#     key.l = 12345  
  ;#     x = squintGetNumeric(sq,@ikey,4)   
  ;#     or via interface 
  ;#     x = sq\get(@ikey,4)      
  ;################################################################################## 
  
  Procedure SquintGetNumeric(*this.squint,*key,size=#Squint_Integer)
    
    Protected *node.squint_Node,idx,offset,nodecount,vchar.i,vret.i,count,*akey.Ascii 
    *node = *this\root & #Squint_Pmask
    *akey = *key
    
    While count <= size  
      
      If *this\write <> *node   ;test to see if same as write node
        _lfence 
        offset = (*node\squint >> ((*akey\a & $f0) >> 2 )) & $f
        _GETNODECOUNT()
        If offset < nodecount
          *node = (*node\Vertex\e[offset] & #Squint_Pmask)
        Else
          ProcedureReturn 0
        EndIf
      Else 
        Continue 
      EndIf  
      
      If *this\write <> *node   ;test to see if same as write node
        _lfence 
        offset = (*node\squint >> ((*akey\a & $0f) << 2)) & $f
        _GETNODECOUNT()
        If offset < nodecount
          *node = (*node\Vertex\e[offset] & #Squint_Pmask)
        Else
          ProcedureReturn 0
        EndIf
      Else 
        Continue 
      EndIf  
      *akey+1
      count+1
    Wend
    
    ProcedureReturn *node\value
  EndProcedure
  
  ;##################################################################################
  ;#  Delete Numeric resets the keys value to 0      
  ;#    *this.squint instance from SquintNew() 
  ;#    *key   address of a variable or memory pointer 
  ;#    size   number of bytes used to store the key    
  ;#  example  
  ;#     key.l = 12345  
  ;#     x = SquintDeleteNumeric(sq,@ikey,4)   
  ;#     or via interface 
  ;#     x = sq\DeleteNumeric(@ikey,4)      
  ;################################################################################## 
  
  Procedure SquintDeleteNumeric(*this.squint,*key,size=#Squint_Integer)    
    Protected *node.squint_node,idx,*mem.Character,offset,nodecount,vchar.i,vret.i,count,*akey.Ascii 
    *node = *this\root & #Squint_Pmask
    *akey = *key
    
    While count <= size 
      offset = (*node\squint >> ((*akey\a & $f0) >> 2 )) & $f
      _GETNODECOUNT()
      If offset < nodecount
        *node = (*node\Vertex\e[offset] & #Squint_Pmask)
      Else
        ProcedureReturn 0
      EndIf
      offset = (*node\squint >> ((*akey\a & $0f) << 2)) & $f
      _GETNODECOUNT()
      If offset < nodecount
        *node = (*node\Vertex\e[offset] & #Squint_Pmask)
      Else
        ProcedureReturn 0
      EndIf
      *akey+1
      count+1
    Wend
    If (*node\vertex & #Squint_Pmask) = 0
      *node\squint = 0
    EndIf
  EndProcedure
  
  Procedure IEnumNumeric(*this.squint,*node.squint_Node,idx,depth,*pfn.squint_CB,*userdata=0)
    Protected a.i,offset,nodecount,*mem.Ascii,vchar.i,vret.i 
    If Not *node
      ProcedureReturn 0
    EndIf
    For a = 0 To 15 
      offset = (*node\squint >> (a<<2)) & $f
      If (*node\vertex And *node\squint)
        _GETNODECOUNT()
        If (offset <> 15 Or nodecount = 16)
          _POKENHL(@*this\sb,depth,a)
          IEnumNumeric(*this,*node\Vertex\e[offset] & #Squint_Pmask,0,depth+1,*pfn,*userdata)
        EndIf
      EndIf
    Next
    If *node\vertex=0
      vchar = PeekI(@*this\sb)
      If *pfn  
        *pfn(@*this\sb,*node\value,*userdata)
      EndIf
    EndIf
    ProcedureReturn *node
  EndProcedure
  
  ;##################################################################################
  ;# Walks whole trie. note it's not thread safe yet you can only walk one thread at a time with same trie    
  ;#    *this.squint instance from SquintNew() 
  ;#    *pfn.squint_CB address of callback function as Squint_CB(*key,*value=0,*userdata=0) 
  ;#     where *key is pointer to the key *value is pointer to the *value, *userDate      
  ;# example    
  ;#     squintWalkNumeric(sq,@MyCallback())       
  ;#  or via interface 
  ;#     sq\WalkNumeric(@MyCallback())   
  ;################################################################################## 
  
  Procedure SquintWalkNumeric(*this.squint,*pfn.squint_CB,*userdata=0)       
    _LockMutex(gwrite) 
    
    IEnumNumeric(*this,*this\root,0,0,*pfn,*userdata)
    
    _UnLockMutex(gwrite)
    
  EndProcedure
  
EndModule 

UseModule SQUINT

EnableExplicit 

Global sq.isquint = SquintNew()

Procedure CBSearch(*key,*value,*userData) ;Can add items to a list if wanted 
  PrintN(PeekS(*key,-1,#PB_UTF8) + " :frequency " + Str(*value))  
  ProcedureReturn 1 
EndProcedure   

Procedure CBList(*key,*value,*userData) ;Can add items to a list if wanted 
  Static ct 
  PrintN(Str(ct) + " : " + PeekS(*key,-1,#PB_UTF8) + " :frequency " + Str(*value))  
  ct+1
  ProcedureReturn 1
EndProcedure 

Procedure ScanFile(file.s) 
  
  Protected fn,line.s,ct,*chr.Unicode,*st,len,key.s
  
  fn = OpenFile(#PB_Any,file) 
  If fn 
    Repeat  
      line.s = ReadString(fn,#PB_UTF8)
      ct+1 
      *chr=@line 
      If line <> "" 
        *st=*chr 
        Repeat
          While (*chr\u > 0 And *chr\u <= 32)  
            *chr+2
            *st=*chr
          Wend  
          While ((*chr\u >= '0' And *chr\u <= '9') Or (*chr\u >= 'A' And *chr\u <= 'Z') Or (*chr\u >= 'a' And *chr\u <= 'z') Or *chr\u=39) ;'  
            len+1
            *chr+2
          Wend       
          If len 
            key = LCase(PeekS(*st,len))
            ct = sq\Get(0,@key) + 1     ;look up the word and increment its count    
            sq\Set(0,@key,ct)           ;set the word in the dictionary  
            len = 0 
            *st=*chr+2  
          EndIf   
          If *chr\u = 0 
            Break 
          EndIf 
          *chr+2
          *st=*chr
        Until *chr\u = 0    
      EndIf 
    Until Eof(fn) 
  EndIf   
  
  ProcedureReturn sq\size() 
  
EndProcedure  

OpenConsole()  

Global file.s,key.s,sz 

File = OpenFileRequester("Select File","*.*","Text (*.txt;*.bat)|*.txt;*.bat|PureBasic (*.pb)|*.pb;*.pbi|All files (*.*)|*.*", 0)

If file <> "" 
  
  sz = ScanFile(file) 
  If sz 
    
    sq\Walk(0,@CBList())  ;dumps the dictionary      
    
    Repeat 
      PrintN("enter a key to search for eg " + #DQUOTE$ + "ba" + #DQUOTE$ + " or type quit To end")   
      key = LCase(Input())    
      If (key <> "quit" And key <> "end") 
        sq\Enum(@key,@CBSearch())  ;search the dictionary 
      Else 
        Break 
      EndIf   
    ForEver   
    
  EndIf 
  
EndIf 

sq\Free()

CloseConsole() 


vmars316
Enthusiast
Enthusiast
Posts: 474
Joined: Fri Jun 29, 2012 12:24 am
Contact:

Re: How to recognize alphaMeric characters

Post by vmars316 »

Thank you very much idle ,
Yep reads File in from "C:\" perfectly .

Sorry to be bugging during 'foggy brain time' .
I'm 1943 vintage , and I know what you mean .
It can last for weeks .
This might Help...

https://vmars.us/ShowMe/Summer-2021.html


Thanks again...
vmars.us Win11 x64 , Martin Guitar 000-16 (1995)
"All things in moderation , except for love and forgiveness."
User avatar
idle
Always Here
Always Here
Posts: 5929
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: How to recognize alphaMeric characters

Post by idle »

I Think your allowed to have brain fog, me not so much, I hope the link I found for the example is useful.
vmars316
Enthusiast
Enthusiast
Posts: 474
Joined: Fri Jun 29, 2012 12:24 am
Contact:

Re: How to recognize alphaMeric characters

Post by vmars316 »

Yes indeed .
Thank you so very much for your Help , Andrew...
vmars.us Win11 x64 , Martin Guitar 000-16 (1995)
"All things in moderation , except for love and forgiveness."
Post Reply