Κυριακή 18 Μαΐου 2025

Cartesian Product

https://rosettacode.miraheze.org/wiki/Cartesian_product_of_two_or_more_lists#M2000_Interpreter

This program perform same cartesian products:

(1,2)x(3,4)=((1,3),(1,4),(2,3),(2,4))

(3,4)x(1,2)=((3,1),(3,2),(4,1),(4,2))

(1,2)x()=()

()x(1,2)=()

(1776,1789)x(7,12)x(4,14,23)x(0,1)=((1776,7,4,0),(1776,7,4,1),(1776,7,14,0),(1776,7,14,1),(1776,7,23,0),(1776,7,23,1),(1776,12,4,0),(1776,12,4,1),(1776,12,14,0),(1776,12,14,1),(1776,12,23,0),(1776,12,23,1),(1789,7,4,0),(1789,7,4,1),(1789,7,14,0),(1789,7,14,1),(1789,7,23,0),(1789,7,23,1),(1789,12,4,0),(1789,12,4,1),(1789,12,14,0),(1789,12,14,1),(1789,12,23,0),(1789,12,23,1))

(1,2,3)x(30)x(500,100)=((1,30,500),(1,30,100),(2,30,500),(2,30,100),(3,30,500),(3,30,100))

(1,2,3)x()x(500,100)=()

(1,2,3)x(500,100)x()=()

 


module checkit {
showTuple = lambda (a as array) -> {
k=lambda m=1 ->{
shift 2
if m=1 then
m=0:drop
push "("+array#str$(",")+")"
else
push letter$+",("+array#str$(",")+")"
end if
}
="("+a#fold$(k)+")"
}
function cartesian_prod(a as array, b as array) {
rest=[]
aa=each(a)
while aa
bb=each(b)
while bb
data (array(aa), array(bb))
end while
end while
ccc=(,)
if len(rest)>0 then
cc=each(rest)
while cc
ccc=stackitem(cc)
d=array([])
dd=each(d)
while dd
ee=each(ccc)
while ee
data array(dd)#end((array(ee),))
end while
end while
end while
end if
=array([])
}
open "out.txt" for output as #f
print #f,"(1,2)x(3,4)=";showTuple(cartesian_prod((1,2), (3,4)))
print #f,"(3,4)x(1,2)=";showTuple(cartesian_prod((3,4), (1,2)))
print #f,"(1,2)x()=";showTuple(cartesian_prod((1,2), (,)))
print #f,"()x(1,2)=";showTuple(cartesian_prod((,), (1,2)))
print #f,"(1776,1789)x(7,12)x(4,14,23)x(0,1)=";showTuple(cartesian_prod((1776,1789), (7,12), (4,14,23), (0,1)))
print #f,"(1,2,3)x(30)x(500,100)=";showTuple(cartesian_prod((1,2,3), (30,), (500,100)))
print #f,"(1,2,3)x()x(500,100)=";showTuple(cartesian_prod((1,2,3), (,), (500,100)))
print #f,"(1,2,3)x(500,100)x()=";showTuple(cartesian_prod((1,2,3), (,), (500,100), (,)))
close #f
win notepad, dir$+"out.txt"
}
checkit

Κυριακή 11 Μαΐου 2025

Revision 49 - Version 13 - Late bound SQLITE3 - EXAMPLE

 Preface

1. Final fix for fonts which have break character number 13 (like Seqoe UI). Now we can give full justification (left and right) using a left margin and a length (so this is like a right margin).


2. Expand DECLARE for using C and return values like LONG LONG and DOUBLE (8bytes). There is a new module SQLITE3, a demo for the use of SQLITE3, download from internet, unzip it, and use of c functions directly from M2000. We make a database with one table, we insert rows and we read the table and display it. Last we unload the library (sqlite3.dll). 

This is what we do with this program (first time the program load the SQLITE3.DLL from internet from the official repository)


The program is self explained;

At the declarations the & means by reference, and the Long means the value converted to LONG (but this isn't the case when we use by reference, we have to use the specific type). Another way to use by reference is to use just Long anyname and before the call stage we make a Long thisVar and then we pass the Varptr(thisVar)  (VarPtr works with array items also, so if A(3) is a long the VarPtr(A(3)) pass the address of A(3), the &A(3) not work for this type of call - modules use &A(3) making a hidden variable which actually passed by reference and at the return we get the value back to A(3) if A(3) exist - because we can reduce the array to zero items)

For downloading we use the URLDownloadToFileW() function from urlmon.  The file is less than 2Mbyte, and we get with no wait. We download it in a tmp file (M2000 erase all tmp files which we get name using TEMPNAME$ - all of these files are in TEMP folder). Then we use an object of M2000 which has this name and link: https://github.com/M2000Interpreter/Environment/blob/main/ZipTool.cls

Just two methods, openZip and ExtractToPath and we have two files (the dll is the one we want).

Although DECLARE look for dll first to APPDIR$ we did something else: We place the dll to user directory (for M2000), So for every DECLARE we provide the exact path (see DIR$ + ).

All the jobs with SQLITE3 after opening the db file comes with the need if preparing the SQL query, compiling it, then binding data, then execute step by step, getting results when rows are available, then for each row we get each value from each column from row, and at the end we release the db file.

We have three strings with Sql statements. One for the creation of the table. No binding used. Another for inserting a row, and we have values to bind, then if we have another row we reset the compiled statement and again bind new data, and so on until no data exist on stack of values. Last string for statement is one to read the table, according the value of ORDER BY, here we sort on name. No bindings here, but we have many steps, the number of the returning rows.

I didn't use Blobs, so if you like find the missing functions from SQLITE documentation, as an exercise.  


We bind strings using TRANSIENT (SQLITE3 take a copy of string). Also we read strings getting a copy from the provided pointer, and the provided length in bytes. Firs we ask about the length, we make a buffer, then we get the pointer and do the copy in the buffer and we return a string as a copy from buffer (buffer removed - is local) the string from function are not copied again, M2000 swap string pointers. So we have exactly two copies, one to buffer (which use CopyBytes internally). And then we get a BSTR string from buffer (as a copy), using Eval$(Buffer_pointer) which get all bytes and place them in a string without missing/altering a bit.

Add the reading of fields, and I use :memory: to make the table in memory (we can change it for a file name with path and extension db).


IF VERSION<13 THEN EXIT
IF VERSION=13 THEN IF REVISION<49 THEN EXIT
WINDOW 6, WINDOW
FORM 60
LOCALE 1033
DIR USER
IF NOT EXIST("sqlite3.dll") THEN
PRINT "FILE sqlite3.dll NOT FOUND - START DOWNLOAD FROM INTERNET"
CONST BINDF_GETNEWESTVERSION = 0x10&
STRING ZIPFILE="https://www.sqlite.org/2025/sqlite-dll-win-x86-3490200.zip"
DECLARE URLDownloadToFile LIB "urlmon.URLDownloadToFileW" {
LONG p, szUrl$, sxFname$, LONG dwRrvd, LONG cback
}
STRING tempZIP=TEMPNAME$ // this file erased automatic when we exit M2000.
IF URLDownloadToFile(0,ZIPFILE, tempZIP, BINDF_GETNEWESTVERSION, 0)=0 THEN
PRINT "ZIP FILE LOADED"
DECLARE zip COMPRESSOR
METHOD zip, "openZip", tempZIP
METHOD zip, "ExtractToPath", DIR$
PRINT "ZIP FILE EXTRACTED TO:";DIR$
END IF
REMOVE "urlmon"  ' unload library
END IF
IF NOT EXIST("sqlite3.dll") THEN EXIT
CONST SQLITE_OK AS LONG = 0
CONST SQLITE_STATIC AS LONG = 0, SQLITE_TRANSIENT AS LONG =-1
CONST SQLITE_ROW AS LONG = 100, SQLITE_DONE AS LONG = 101
CONST RET_STATUS=3
ENUM Types {
dbl=1,
int,
int64,
txt
}
DECLARE sqlite3_libversion_number LIB c DIR$+"sqlite3.sqlite3_libversion_number" {} AS LONG
DECLARE sqlite3_libversion LIB c DIR$+"sqlite3.sqlite3_libversion" {} AS LONG
DECLARE sqlite3_open16 LIB c DIR$+"sqlite3.sqlite3_open16" {
Filename$, LONG &handler
} AS RET_STATUS
DECLARE sqlite3_close_v2 LIB c DIR$+"sqlite3.sqlite3_close_v2" { LONG handler}
DECLARE sqlite3_prepare16_v2 LIB c DIR$+"sqlite3.sqlite3_prepare16_v2" {
LONG handler, LONG bufferPtr, LONG Byte_size,
LONG &handlerStatement, LONG &pNotUsed
} AS RET_STATUS
DECLARE sqlite3_step LIB c DIR$+"sqlite3.sqlite3_step" {
LONG handlerStatement
} AS RET_STATUS
DECLARE sqlite3_reset LIB c DIR$+"sqlite3.sqlite3_reset" {
LONG handlerStatement
} AS RET_STATUS
DECLARE sqlite3_finalize LIB c DIR$+"sqlite3.sqlite3_finalize" {
LONG handlerStatement
} AS RET_STATUS
DECLARE sqlite3_bind_value LIB c DIR$+"sqlite3.sqlite3_bind_value" {
LONG handlerStatement, LONG Nth, LONG ValueHandler
} AS RET_STATUS
DECLARE sqlite3_value_double LIB c DIR$+"sqlite3.sqlite3_value_double" {
LONG ValueHandler
} AS DOUBLE
DECLARE sqlite3_column_double LIB c DIR$+"sqlite3.sqlite3_column_double" {
LONG handlerStatement, LONG Nth
} AS DOUBLE
DECLARE sqlite3_column_int64 LIB c DIR$+"sqlite3.sqlite3_column_int64" {
LONG handlerStatement, LONG Nth
} AS LONG LONG
DECLARE sqlite3_column_bytes16 LIB c DIR$+"sqlite3.sqlite3_column_bytes16" {
LONG handlerStatement, LONG Nth
} AS LONG
DECLARE sqlite3_column_text16 LIB c DIR$+"sqlite3.sqlite3_column_text16" {
LONG handlerStatement, LONG Nth
} AS LONG
DECLARE sqlite3_column_int LIB c DIR$+"sqlite3.sqlite3_column_int" {
LONG handlerStatement, LONG Nth
} AS LONG
DECLARE sqlite3_bind_double LIB c DIR$+"sqlite3.sqlite3_bind_double" {
LONG handlerStatement, LONG Nth, Dbl
} AS RET_STATUS
DECLARE sqlite3_bind_text16 LIB c DIR$+"sqlite3.sqlite3_bind_text16" {
LONG handlerStatement, LONG Nth, a$, LONG len_bytes, SQLITE_TRANSIENT
} AS RET_STATUS
DECLARE sqlite3_bind_text LIB c DIR$+"sqlite3.sqlite3_bind_text" {
LONG handlerStatement, LONG Nth, a$, LONG len_bytes, SQLITE_TRANSIENT
} AS RET_STATUS
DECLARE sqlite3_bind_int64 LIB c DIR$+"sqlite3.sqlite3_bind_int64" {
LONG handlerStatement, LONG Nth, int64
} AS RET_STATUS
DECLARE sqlite3_bind_int LIB c DIR$+"sqlite3.sqlite3_bind_int" {
LONG handlerStatement, LONG Nth, LONG int
} AS RET_STATUS


ret= sqlite3_libversion_number()
PRINT "SQLITE VERSION = ";(ret div 1000000)+"."+((ret mod 1000000) div 1000)+"."+(ret mod 1000)
rem {// press enter key just after rem to use these lines
BUFFER readthis AS BYTE*80
copyfromthere=sqlite3_libversion()
METHOD readthis, "FillDataFromMem", copyfromthere
PRINT "Version=";LEFTPART$(CHR$(EVALl$(readthis)), CHR$(0))
}
LONG db, where, compiled_statement, compiled_statement
create$={
CREATE TABLE COMPANY(
ID INT64 PRIMARY KEY NOT NULL,
NAME TEXT NOT NULL,
AGE INT NOT NULL,
ADDRESS   CHAR(50),
SALARY   REAL
);
}
insertRow$={
INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) VALUES (?1, ?2, ?3, ?4, ?5);
}
ReadFieldNamesAndTypes$={
PRAGMA TABLE_INFO(COMPANY)
}
ReadRows$={
SELECT * FROM COMPANY ORDER BY NAME
}
rem {ret=sqlite3_open16(DIR$+"check.db", &db)}


ret=sqlite3_open16(":memory:", &db)
BUFFER preparemem AS INTEGER*10000 ' for 10000 characters
compiled_statement=@process(db, preparemem, create$)
IF compiled_statement<>0 THEN
ret=sqlite3_step(compiled_statement)
IF ret=SQLITE_DONE THEN PRINT "TABLE CREATED"
ret=sqlite3_finalize(compiled_statement)
END IF
compiled_statement=@process(db, preparemem,insertRow$)
IF compiled_statement<>0 THEN
FLUSH
Data 1,"Paul", 32, "California", 20000.45
Data 2, "Allen", 25, "Texas", 15000.00
Data 3, "Teddy", 23, "Norway", 20000.00
Data 4, "Mark", 25, "Rich-Mond ", 65000.50
Data 5, "David", 27, "Texas", 85000.00
Data 6, "Kim", 22, "South-Hall", 45000.00
rowTypes=(int64, txt, int, txt, dbl)
WHILE @bind_stack(compiled_statement, rowTypes)
ret=sqlite3_step(compiled_statement)
IF ret=SQLITE_DONE ELSE CANCEL=TRUE: EXIT
IF EMPTY THEN EXIT
ret=sqlite3_reset(compiled_statement)
END WHILE
ret=sqlite3_finalize(compiled_statement)
END IF
IF VALID(CANCEL) THEN GOTO 100
PRINT "DATA INSERTED TO DATABASE AT TABLE COMPANY"
VARIANT var0, var1, var2, var3, var4
compiled_statement=@process(db, preparemem, ReadFieldNamesAndTypes$)
IF compiled_statement<>0 THEN
ret=SQLITE_ROW
rows_counter=0
FLUSH
WHILE ret=SQLITE_ROW
ret=sqlite3_step(compiled_statement)
IF ret=SQLITE_ROW THEN
rows_counter++
var0=@sqlite_column_string(compiled_statement, 0&)
var1=@sqlite_column_string(compiled_statement, 1&)
var2=@sqlite_column_string(compiled_statement, 2&)
' 1 not null 0 can be null
var3=@sqlite_column_string(compiled_statement, 3&)
? var0, var1, var2, var3
END IF
END WHILE
PRINT "FOUND NUMBER OF FIELDS = ";rows_counter
ret=sqlite3_finalize(compiled_statement)
END IF


compiled_statement=@process(db, preparemem, ReadRows$)
IF compiled_statement<>0 THEN
ret=SQLITE_ROW
rows_counter=0
WHILE ret=SQLITE_ROW
ret=sqlite3_step(compiled_statement)
IF ret=SQLITE_ROW THEN rows_counter++
END WHILE
PRINT "FOUND NUMBER OF ROWS = ";rows_counter
ret=sqlite3_reset(compiled_statement)
ret=SQLITE_ROW
// WORKS WITH VARIANTSOR WITH SPECIFIC TYPES
// THESE ARE THE VARIABLES FILLING FROM DATABASE
STRING headers="{0:8}|{1:10}|{2:3}|{3:20}|{4:10}"
STRING rowformat="{0:-8}|{1:10}|{2:-3}|{3:20}|{4:2:-10}"
STRING bar="{0:8}+{1:10}+{2:3}+{3:20}+{4:10}"
// FROM HERE WE RENDER THE TABLE COMPANY
PRINT FORMAT$(headers, "ID", "NAMES","AGE", "ADDRESS", "SALARY")
S=STRING$("-",WIDTH)
PRINT PART $(0), FORMAT$(bar, S, S, S, S, S )
PRINT
WHILE ret=SQLITE_ROW
ret=sqlite3_step(compiled_statement)
IF ret=SQLITE_ROW THEN
// THIS IS ZERO BASED INDEX FOR COLUMNS
var0=sqlite3_column_int64(compiled_statement, 0&)
var1=@sqlite_column_string(compiled_statement, 1&)
var2=sqlite3_column_int(compiled_statement, 2&)
var3=@sqlite_column_string(compiled_statement, 3&)
var4=sqlite3_column_double(compiled_statement, 4&)
PRINT FORMAT$(rowformat, var0, var1, var2, var3, var4)
END IF
END WHILE
PRINT PART $(0), FORMAT$(bar, S, S, S, S, S )
ret=sqlite3_finalize(compiled_statement)
END IF
100 ret=sqlite3_close_v2(db)
REMOVE "sqlite3"  ' unload library
PRINT ret=SQLITE_OK
FUNCTION sqlite_column_string(stmt AS LONG, icol AS LONG)
LOCAL buffer_size=sqlite3_column_bytes16(stmt, icol)
LOCAL buf : BUFFER buf AS BYTE*buffer_size
METHOD buf, "FillDataFromMem", sqlite3_column_text16(stmt, icol)
=EVAL$(buf)
END FUNCTION
FUNCTION process(this_db, prep, a$)
prep[0]=a$
LOCAL LONG z=LEN(a$)*2, ret, compiled_statement
LOCAL LONG where=prep(0), where0, last=z+where
DO where0=where
z=last-where
ret=sqlite3_prepare16_v2(this_db, where0, z, &compiled_statement, &where)
UNTIL ret=0 or z=last-where
IF ret=0 THEN =compiled_statement
END FUNCTION
FUNCTION bind_stack(that_stmt, Rtypes)
IF STACK.SIZE<LEN(Rtypes) THEN FLUSH : =FALSE: EXIT FUNCTION
LOCAL int64val AS LONG LONG, dbl1 AS DOUBLE, int1 AS LONG
LOCAL m=EACH(Rtypes), ret AS LONG
// BINDING USE BASE ONE FOR INDEX
WHILE m
SELECT CASE array(m)
CASE txt
READ a$
ret+=sqlite3_bind_text16(that_stmt, m^+1, a$, LEN(a$)*2, SQLITE_TRANSIENT)
CASE dbl
READ dbl1
ret+=sqlite3_bind_double(that_stmt, m^+1, dbl1)
CASE int64
READ int64val
ret+=sqlite3_bind_int64(that_stmt, m^+1, int64val)
CASE int
READ int1
ret+=sqlite3_bind_int(that_stmt, m^+1, int1)
END SELECT
END WHILE
=ret=SQLITE_OK
END FUNCTION



Τετάρτη 7 Μαΐου 2025

Using Interface on VB6 objects (first example)

Here we declare a function for any object of specific interface, based on GUID or IID the Interface ID.

For using this type of function we have to pass as first argument the object which have the specific interface.

We don't need to use the IUnknown.QueryInterface, to get the specific object, because the delcaration has the string representation of GUID or IID (or RIID).  Here for the example we just use an object with dual interfaces, the IUnkwon and the dispatch. We see how we get the dispatch from IUnkown, using IUknown.QueryInterface passing the specific GUI and the VarPtr(Ptr) which is the pointer how points to memory to place the result. When a new pointer added internal an IUnknown.Addref add one to the accumulator for the number of references. When on reference deleted then the IUnknown.Release called which decrement the number of references and if this number turn to zero then that means no pointer point to object, so the object has to remove from memory, which the code of the object do, erasing the resource which hold (the status of the object).

So the second and third function used to handle the life of the object.


idispatch$="{00020400-0000-0000-C000-000000000046}"
enum IUnknown {
QueryInterface=0
AddRef
Release
}
enum Idispatch {
IUnknown
GetTypeInfoCount
GetTypeInfo
GetIDsOfNames
Invoke
}
DECLARE IUnknown.QueryInterface INTERFACE idispatch$, QueryInterface {long riid, long myptr}
K=List:=1,2,3,4
object Ptr
buffer clear riid as Long*4
return riid, 0:=0x0002_0400, 1:=0, 2:=0xc0, 3:=0x4600_0000
buffer clear myobj as long
? "0x"+hex$(uint(IUnknown.QueryInterface(K, riid(0), varptr(ptr))), 4)="0x00000000"
? type$(ptr)="Inventory"
? len(ptr)=4
? ptr is K = true


Κυριακή 4 Μαΐου 2025

Unicode name variables

 This is from m2000 editor. Run m2000.exe and write edit unicode then paste this code 

When we copy the code from editor, we get html formated based on a selected color collection dark or bright text colors. This is a page from Writer (LibreOffice), and the same for Word. At the second half of this page I change the background with magenta (the same as for the default background for M2000).


Write Settings or press ctrl+U from m2000 command line (which is the one we see when we run m2000.exe).

From INFO.gsb we can run mEditor which we can paste the code:



As you see, this editor use white background.

You can load it using this two statements the first time:

Dir Appdir$ : Load Info

Then after loading press F1 to save it to user folder (the folder change by the info which run when we load it). Next time we use Load Info. If we have a program (with one or more modules) we can write New before the Loaf Info. We can open explorer using Win Dir$

cls,0

Δ=1
Δ++
Print Δ
=3
Print  **2=^2, ^2-1=8
Τύπωσε **2=^2, ^2-1=8 ' this is Print statement too
Print =3
जावास्क्रिप्ट=100
जावास्क्रिप्ट++
Print "जावास्क्रिप्ट=";जावास्क्रिप्ट
ĦĔĽĻŎ$="hello"
Print ĦĔĽĻŎ$+ħĕľļŏ$="hellohello"
〱〱〱〱$="too less"
Print Left$(〱〱〱〱$, 3)="too"
Print


c͓͈̃͂̋̈̆̽h̥̪͕ͣ͛̊aͨͣ̍͞ơ̱͔̖͖̑̽ș̻̥ͬ̃̈ͩ =100 : Print "c͓͈̃͂̋̈̆̽h̥̪͕ͣ͛̊aͨͣ̍͞ơ̱͔̖͖̑̽ș̻̥ͬ̃̈ͩ ="; c͓͈̃͂̋̈̆̽h̥̪͕ͣ͛̊aͨͣ̍͞ơ̱͔̖͖̑̽ș̻̥ͬ̃̈ͩ


print


Revision 46 Version 13 - BARCODES

 Some fixes done for Revision 46, of version 13 of M2000 Interpreter.

Also a new object, the m2000.cBarcode we can make Barcodes (We can make QR code from another point: https://georgekarras.blogspot.com/2022/10/revision-10-version-11-qrcode.html)



The object cBarcode found at git:  https://github.com/wqweto/ClipBar from Vladimir Vissoultchev, from Bulgaria.


WINDOW 14, 16200, 14200;
mode 14
CLS,0
report 2, "BARCODE"
declare bar "m2000.cBarcode"
rem print type$(bar)
Enum UcsBarCodeTypeEnum {
ucsBctAuto = 0
ucsBctEan13
ucsBctEan8
ucsBctEan128
ucsBctUpcA
ucsBctUpcE
}
Long ink_bleed_in_percents=10
boolean true=1=1, false=1=0
method bar, "init", ink_bleed_in_percents, bHangSeparators:= TRUE, bShowDigits:=TRUE as ok
method bar, "GetBarCode", "123456789012:12345", ucsBctEan13 as Pic
With Pic, "Width" as pic.width, "Height" as pic.height
move  5000, 6000
// Pic is a Picture object
mem$=""
// Now Image can render Pic to string
Image Pic, 7500 to mem$
rem ? image.x(mem$)=7500
rem ? len(mem$)
// so now we place a bitmap
image mem$ ', 7500
// We can render Picture as emf directly
Step 0, -4000
Image Pic, 7500*1.2
// We can make a file in memory using Drawing.
// We can make another emf where we draw the picture
drawing {
image Pic, 3000
} as Mem
rem ? len(Mem)
step -3000, 6000
// we can rotate the image now
image Mem, 10000, ,-90
method bar, "GetBarCode", "abcdefABCDEFG23432432432432432", ucsBctEan128 as Pic
move 5000, 9000
Image Pic, 11000


method bar, "GetBarCode", "123456789012abcdefABCDEFG23432432432432432", ucsBctEan128 as Pic
move 5000, 12000
Image Pic, 11000
push key$
drop
sub BigTitle(this$, there, percent)
local w, n
mm=width
w=scale.x
drawing {
Linespace 0
mode 8
pen #FFEEFf
bold 0
legend chr$(160)+this$+chr$(160),"Lucida Handwriting", 8,0,3,1,-20


} as w
cursor 0, there
move !
n=scale.x/image.x(w)*percent/100
step (scale.x-image.x(w)*n)/2
image w, image.x(w)*n
end sub