Παρασκευή 25 Νοεμβρίου 2022

Complex Numbers (part 1)

This part has two final classes, the DoubleComplex() and the FloatComplex. Each class define objects of general type Group, with types DoubleComplex & Complex, and FloatComplex & Complex.

Each class has a constructor/ In each constructor we make a Buffer of two at least numbers, for Double Complex we use double type (8bytes per number), for the other we use Single type (4 bytes per number). Each constructor can take 1 to 3 arguments, the first is the real number, the second is the imaginary number and last is how many complex numbers we need. 

We make Value and Set methods to handle the way we assign values and read values from these objects. After we define an object we can assign a new value either from same type or using the another type but from base class Complex.

Just look the code.

Class Complex {
Private:
k, lim=0
Function copy {
=group(this)
}
Public:
Property Ptr {
value {link parent k to k: value=k(0)}
}
Property Ubound {
value {link parent lim to lim: value=lim}
}
Final Lbound=0&
// we use clear to clear value variable which property define as double by default
// using Property a { }=0& we can define the value as long
// but here we use Real for two different buffers, one with type double and one with type single
Property Real {
value {clear:link parent k to k: value=eval(k,0)}
}
Group Real {
operator "++" {
link parent k to k: Return k, 0:=eval(k, 0)+1
}
}
Property Imaginary {
value {clear:link parent k to k: value=eval(k,1)}
}
Group Imaginary {
operator "++" {
link parent k to k: Return k, 1:=eval(k, 1)+1
}
}
Property toString$ {
value {
Link parent k, lim to k, lim
read ? where=0&
if where<0 or where>lim then error "Index out of bound"
where*=2
m=eval(k,where+1)
if m==0 then
value$=format$("({0})",eval(k, where))
else.if eval(k,0)==0 then
u$="({1}i)"
value$=format$("({0}i)",eval(k,where+ 1))
else
if abs(m)==1 then u$="({0}{2}i)" else u$="({0}{2}{1}i)"
value$=format$(u$,eval(k, where),abs(m), IF$(m<0->"-","+"))
end if
}
}
Function Export$() {
=eval$(.k)
}
Module Import (This$) {
m=len(This$)*2
if m>0 and m<=len(.k) then
Return .k, 0:=This$
end if
}
Module SwapBuffers (&a as Complex){
if .len<> a.len then Error "Not compatible buffers"
swap .k, a.k
swap .lim, a.lim

}
Class:
Module Complex {
Error "Can't define objects"
}
}
Class DoubleComplex as Complex {
Private:
len=16
Function newDoubleComplex(a as DoubleComplex, w) {
buffer z as double*2
return z, 0:=eval$(.k, w*.len!, .len)
let a.k=z, a.lim=0 // k is Private in a
=group(a)
}
Function copyme(a as DoubleComplex) {
buffer z as double*2*(a.lim+1)
return z, 0:=eval$(.k)
let a.k=z
=group(a)
}
Public:
Operator "+"  {
read a as Complex
let this=.newFloatComplex(this, 0)
return .k, 0:=eval(.k,0)+a.real, 1:=eval(.k, 1)+a.imaginary
}
Operator "-"  {
read a as Complex
let this=.newFloatComplex(this, 0)
return .k, 0:=eval(.k,0)-a.real, 1:=eval(.k, 1)-a.imaginary
}
Set () {
read a as Complex
read ? where=0&
if where<0 or where>.lim then error "Index out of bound" else where*=2
if a is type FloatComplex then
return .k, where:=a.real, where+1:=a.imaginary
else.if a is type DoubleComplex then
return .k, where:=eval$(a.k)
else
error "can't assign this group"
end if
}
Module Redim (many as long) {
if many<1 then error "Wrong Dimension"
buffer clear .k as double*2*many: .lim<=many-1
}
Module RedimPreserve (many as long) {
if many<1 then error "Wrong Dimension"
buffer .k as double*2*many: .lim<=many-1
}
Function final copy {
=.copyme(this)
}
value () {
read ? where=0&
if where<0 or where>.lim then error "Index out of bound"

if where=0 and .lim=0 then
=this
else
=.newDoubleComplex(this, where)
end if
}
Class:
Module DoubleComplex (r as double=0, i as double=0, many=1&) {
if many<1 then error "Wrong Dimension"
buffer clear .k as double*2*many: .lim<=many-1
return .k, 0:=r, 1:=i
}
}
Class FloatComplex as Complex{
Private:
len=8
Function newFloatComplex(a as FloatComplex, w) {
buffer z as single*2
return z, 0:=eval$(.k, w*.len!, .len)
let a.k=z, a.lim=0
=group(a)
}
Function copyme(a as FloatComplex) {
buffer z as single*2*(a.lim+1)
return z, 0:=eval$(.k)
let a.k=z
=group(a)
}
Public:
Operator "+"  {
read a as Complex
let this=.newFloatComplex(this, 0)
return .k, 0:=eval(.k,0)+a.real, 1:=eval(.k, 1)+a.imaginary
}
Operator "-"  {
read a as Complex
let this=.newFloatComplex(this, 0)
return .k, 0:=eval(.k,0)-a.real, 1:=eval(.k, 1)-a.imaginary
}
Set () {
read a as Complex
read ? where=0&
if where<0 or where>.lim then error "Index out of bound" else where*=2
if a is type DoubleComplex then
return .k, where:=a.real, where+1:=a.imaginary
else.if a is type FloatComplex then
return .k, where:=eval$(a.k)
else
error "can't assign this group"
end if
}
Module Redim (many as long) {
if many<1 then error "Wrong Dimension"
buffer clear .k as single*2*many: .lim<=many-1
}
Module RedimPreserve (many as long) {
if many<1 then error "Wrong Dimension"
buffer .k as single*2*many: .lim<=many-1
}
Function final copy {
=.copyme(this)
}
value (){
read ? where=0&
if where<0 or where>.lim then error "Index out of bound"
if where=0 and .lim=0 then
=this
else
=.newFloatComplex(this, where)
end if
}
Class:
Module FloatComplex (r as single=0, i as single=0, many=1&) {
if many<1 then error "Wrong Dimension"
buffer clear .k as single*2*many: .lim<=many-1
return .k, 0:=r, 1:=i
}
}
def typename$(x)=type$(x)
z1=FloatComplex(2,2) : print z1.toString$="(2+2i)"
z2=DoubleComplex(-2,3) :print z2.toString$="(-2+3i)"
print typename$(z2.real)="Double"
z5=FloatComplex(10,-5) : print z5.toString$="(10-5i)"
print typename$(z5.real)="Single"
z5=FloatComplex(10) :print z5.toString$="(10)"
z6=DoubleComplex() : print z6.toString$="(0)"
z6=z5 :print z6.toString$="(10)"
print z5 is type FloatComplex = true
print z6 is type DoubleComplex = true
z2.imaginary++
z2.imaginary++
print  z2.toString$="(-2+5i)"
z5=z2
print typename$(z5.real)="Single"
print z5 is type FloatComplex = true
z5.real++
print z5.toString$="(-1+5i)"
zArray=FloatComplex(1,1,10)
zArray(4)=FloatComplex(30,-5)
zArray(5)=FloatComplex(3,-75)
zArray(7)=zArray(4)
for i=zArray.Lbound to zArray.Ubound
print i, zArray.toString$(i)
next
z5=zArray(4)
print z5.toString$="(30-5i)"
print zArray(4).toString$ = zArray.toString$(4)
print  z5.toString$="(30-5i)"
zk=z5+zArray+FloatComplex(5, 100)
print zArray(4).toString$="(30-5i)"
print zk.tostring$()="(36+96i)"
zk=zArray
// print zk.ubound, zk.ptr, zArray.ptr, zArray.Ubound
zA=zArray.Copy() // copy 10 items
zArray(5)=FloatComplex(3, 4)
print zArray(5).toString$="(3+4i)"
print za(5).toString$="(3-75i)"
za(7)=zArray(5)+za(5)
print za(7).toString$="(6-71i)"
za(7)=za(7)-za(5)
print za(7).toString$="(3+4i)"
za(7)=za(7)-zArray(5)
print za(7).toString$="(0)"
z5.import z1.export$()
print z5.tostring$="(2+2i)"
zArray.import za.export$()
print zArray.toString$(7)="(0)"
print zArray.ptr<>za.ptr
print zArray.Ubound=9, z5.Ubound=0
zArray.SwapBuffers &z5 // swap data only
print zArray.Ubound=0, z5.Ubound=9, z5(5).toString$="(3-75i)"


Τετάρτη 23 Νοεμβρίου 2022

About Types in M2000

A new revision uploaded see github.

There are two different aspects for types:  Those types on variables, and those types on parameters. You may thing that a parameter is like a variable. Indeed a parameter is a variable, but these, parameter and variable, have something not common, the type. A variable declared somewhere in the program and maybe passed as an argument for a function, module, or a subroutine, by default using the By Value pass. So the parameter of a function, module or subroutine, get the value of the argument, but what about the type?

A parameter get the value from a stack of values. An argument written to the stack of values, we can say the current stack. The argument get the value from an expression (this is the By Value pass), so passing only a variable is like passing the result of an expression with only a variable. For M2000 there are two major expressions: Numeric and Alphanumeric (String Expression). Boolean expressions consider Numeric (0 for false non zero for true).

For a function parameter, interpreter see the name of the parameter, and if ends to $, then expect to find string value in stack else an error raised. So according to major types, numeric and string we  have a first check, before checking the value.

For a function a(x,y) we can't pass strings as arguments. Although we can do this a="11234.5" and the interpreter do that a=Val("11234.5"), and pass the value 11234.5 to a, we can't pass the "11345.5" where we expect to get a numeric value.

How many numeric types used in M2000?

We can use integer, long, simple, double, currency, decimal and boolean (exist as type, and that type returns a comparison)

How we can force a type for a variable?

By default 1 is a double literal number, is same like 1.0.  A statement like A=10 when A not defined as local previous in the module, make A as double type and this can't change with assigning an new value. So A=10& give to A a double type 10 and not the long type 10&, if A has type double.

We can use Def statement to define types for variables before the first assignment.

Def integer K=100, L=4

make K and L integer type (16bit) with values 100% and 4% (% used for integer literal numbers). M2000 check overflow for variables and return error.

If the Def find K or L isn't new names (in local scope) then an error raised. Def defines variables always local and not exist before as local. We can't use Def to shadowing variables.

We can use this form also:

Def K as integer=100, L as integer=4

In a subroutine we can't use Def because we want something else for shadowing a local variable with a new one with same name.

Local K=100%, L=4%

or

Local K as integer=100, L as integer=4

Every time we use Local from above a new set of K and L shadowing any K and L (if any K and L exist before as local variables, or global variables too). So when we call a subroutine, we have same scope as the caller, but we can use Local variables, and at the exit from routine any variable we define erased, so any K or L which we shadow are available to the caller. Local also define local arrays too

We don't use Local in the main body of a Module or Function (except for simple functions which are like subroutines, with the code written at the end of module's code), because these define a new NameSpace, and any new variable/definition by default is Local. We can use Global to make at some point a global variable or array, and the life for that is the same as for local variables.

Parameter Types

Lets see the program bellow. Function A need two arguments, and the parameter list has two, the x and z, one of them is Long type (32 bit) and the other is Integer (16 bit). So what we get; It depends. Here we pass 2 and 3 (double type) which convert to long and integer, and then we have the return value as the multiplication of those two. We read the type from the variable M. The M variable is new variable so the type for it is the type of the return value from function, and here is Long (long*Integer return long or error). The second try, we pass a big value 300000 which can't fit to Integer and we get Overflow error.

Function Alfa(x as long, z as integer) {
=x*z
}
M=Alfa(2, 3)
Print type$(M)="Long"
try ok {
M=Alfa(2, 300000)
}
Print Error$=" Overflow in function ALFA()"


Now we change the example. We remove the types from the function signature and we make a SayType module. We want that module, because we use M only for the call, and at the end of module's run the M erased, so next time can be another type. So  We pass to Alfa() numeric literals. The first two have double type, the next two have Currency type, and the last two have Decimal type.

Use 2~, 3~ for single type (4byte float).

Module SayType (M) {

	Print type$(M)
Print M
}
Function Alfa(x, z) {
=x*z
}
SayType Alfa(2, 3)
SayType Alfa(2, 300000)
SayType Alfa(2#, 3#)
SayType Alfa(2#, 300000#)
SayType Alfa(2@, 3@)
SayType Alfa(2@, 300000@)

Hexadecimal values are two types: 0xFFFF (65535) and 0xFFFFFFFF (4294967295) are currency type (map to unsigned), 0xFFFF% is Integer signed (-1) and 0xFFFFFFFFF& is long signed  (-1). So there is no type unsigned long, but we can use a double, or  decimal or a currency type. The Binary.And() and other functions return Currency type but they work on 32bit unsinged long. The Binary.add() has no overflow, the overflow bit just not used,

So now we know that using types make functions to have problems with overflow. This isn't bad if we know what to do. Because the M2000 Environment (Interpreter plus internal objects) written for education and playing by programming for kids and pupils, using only the major types Numeric and String we can make the life easier.

There are three type of  identifiers for variables. like A for numeric (and pointers to objects), like A$ for strings and some kind of objects (including Document), and like A% for  numeric with integer value (we may have a double type under the hood, but the value is always integer). This third major type is like numeric but there is a rounding process like the school rounding method, at the half integer value. All of these prints True.

	A%=2.6
Print A%=3
A%=1.6
Print A%=2
A%=-1.6
Print A%=-2
A%=-2.6
Print A%=-3

If we use A=A/(A*2), we get 1 not 0.5

for i=1 to 10
A%=i: A%/=i*2: Print A%=1
next
And now we check these two programs which give different results:


We get  8 numbers: 100, 50, 25, 13, 7, 4, 2, 1
A%=100
Do
Print A%,
A%/=2
When A%>1
Print A%

We get 7 numbers: 100, 50, 25, 12, 6, 3, 1
A=100&
Do
Print A,
A/=2
When A>1
Print A
Why ? Because we have different rounding algorithm. 25 div 2 return 12, that works for Long type. A% from 25/2 get 12.5 and has >=0.5 decimals so convert to 13, so 13/2 give 6.5 so we get 7, then 7/2 give 3.5 so we get 4. Looking the values we see 13 vs 12, 7 vs 6, 4 vs 3, but the bigger problem is the amount of numbers which is 1 more for A%,  and this means one more iteration. The one more iteration can be a fault, or if we see alternative, the one less iteration can be a fault.

So lets see another problem (which is a part of  understanding the M2000 Interpreter), its not about type, but about scope. 

There is one problem with DIM statement. This statement can define arrays and can redim arrays (change the definition for an identifier for array which exist). If we have a global array say A(10) from 0 to 9, and we have a module with the statement DIM A(100) we change the global array.  To prevent this if we know about the global array, or we want the module to included in a library, we use the DIM NEW A(100) which make always a new local identifier (shadowing any other local or global with the same name. If we have to redim the local we use DIM A(50), the A() is the local one. If we want to redim the global one we use SET DIM A() where the SET change for that line the Namespace to global namespace so A() is the global array. Statement GLOBAL A(50) always make a new identifier for global namespace, and LOCAL A(50) make a new identifier for local namespace.  Global and Local can't redim arrays.

If the global A() exist and we have a parameter A() there will be no problem, because the READ statement (this used for reading from stack of values) always use the current namespace to create new identifiers, so the Read A() in a function or in a module make a new local array A().

global a(10)=1000
Print len(a())=10
Module beta {
// we change the global one
let a()=(1,2,3,4)
Print len(a())=4
}
beta
Print len(a())=4
dim a(100)=1
Module beta2(a()) {
// a() is local
Print len(a())=7
let a()=(1,2,3,4)
Print len(a())=4
}
beta2 (1,2,3,4,5,6,7)
Print len(a())=100
Module beta3 {
// a() is local
Push a() // this a() is global
read a() // this a() is local
dim a(4) // redim to 4 items
Print len(a())=4
}
beta3
Print len(a())=100

You can skip the next paragraph, which contain things about array names, pointer interfaces, passing by reference arrays, variables and array items.


Arrays have names like variables but these have to do with the way to assign values to array items, and read array items. We can make references with different type, using Link statement. So we may have A() and link A$() so we can put strings and numbers to same array. Tuple are arrays of one dimension, or zero, like this (,). So A()=(1,2+5, "abc") change A() to one dimension type, and length of three items, from index 0 to 2. See the string at index 2. To create an array with parentheses like A() we can use Dim, Local, Global, Link to make a reference (references can't change the reference item, here the array which hold), and the Read statement (from stack of values). The Read statement can make also a reference if we pass a reference and we ask a reference. The by reference pass works with & from both sides, for argument and for parameter. Actually the &A() is a string which hold the weak reference, so pushing the reference to stack, only the weak reference pass. The read statement see the & before the name, and the string in the stack and look if the weak reference is valid, and if is valid create the real reference, but if isn't valid raise error. The same works for variables passing by reference, but we can't link a numeric variable to a string and vis versa. Last for reference pass, we can pass by reference array items, but the arrays didn' lock. So lets say we pass &A(30) and &A() and we make the A() an empty array. Passing the array item, also as a weak reference, goes to a parameter say &A, not an array. Interpreter use a copy in copy out method. It use the weak reference to put the value to local A and at the exit check if the weak reference us still valid and if it is put back the value of A before erase it. If isn't valid no error raised. Arrays have two interfaces, one with names with parentheses and another one with pointers, names like numeric variables. So if we pass A which hold an array, this is a by value pass of pointer so is like passing by reference. If we pass A() without & before the name we pass actual a pointer from a different interface, so when a Read occur, at the moment of read, we get a copy (shallow) of array. Because we may have objects for array items, he copy process may copy some kind of objects but not those with the pointer interface. Pointer interface have the container types: arrays, lists, stacks and groups. Arrays and Groups are values if we didn't use pointer interface. So A()=B() make a copy of B() to A() without changing the internal pointer of actual array object. A=A() if A is a new variable we make A a pointer to A(). If A is a numeric variable change to pointer. If A is a Group, or lambda function or any other object we get error. Only three objects share a common pointer interface, arrays, stacks and lists. We can constrain the parameter type using AS LIST to get only list, etc. Array items always change to anything, except if they have a Group type which have a Set method so this method run on assignment. Here is an example. Class alfa() make a group which has a method  Value with a parameter and a Set method. We make 10 items in A()  of class alfa(). The a(3)=20 execute the set method of group in a(3). By default value and set are public methods.

class alfa {
private:
k=100
value (n){
=.k*n
}
set {
read .k
}
}
dim a(10)=alfa()
Print a(3)(3)=300
a(3)=20
Print a(3)(3)=60
We can extract a copy of a(3) object using Group() because Z=a(3) raise error, need an argument for the value (we get Variable N can't initialized, we didn't pass a value).
m=group(a(3))
Print m(3)
So m(3) isn't array, is a group which return a value. A group may have a name, so has a life same as a variable, or may have a pointer, and has a life until the last pointer erased. We can get a pointer from a(3):
z=pointer(a(3))
Print eval(z, 3)=60
a(3)=1000
Print eval(z, 3)=3000
As we see, we change value in a(3) running the set statement, and the z pointer now return the value through the eval(z, 3), where 3 is the argument for the value method. Getting a pointer from m can be done, but will be a weak pointer (we didn't see that from the pointer view, is the same like the real pointer about functionality). See now that z point to another group object, the m, which is a named object.  Pointer z internal holds only the reference to m. So we can't use after the death of m (at the end of execution of the module/function or sub/ simple function which may have a local group.
z=pointer(m)
Print eval(z, 3)=60

Last we can put lambda functions in arrays, and execute from there.
Dim L(10)
L(0)=lambda x=0 ->{
x++
=x
}
L(1)=L(0)
L(2)=L(1)
Print L(1)()=1, L(1)()=2
L(3)=L(1)
Print L(2)()=1, L(3)()=3, L(1)()=3
L(1)=L(0)
Print L(1)()=1, L(1)()=2
m=L(0)
Dim L(10)=m()
Print L() ' print 1 2 3 4 5 6 7 8 9 10
Dim L(10)<<m()
Print L() ' print 2 3 4 5 6 7 8 9 10 11
Print m()=12
Using Dim L()  we define or redim an array to empty size. Using L()=(,) we make an empty array too but for an array who defined before this statement.

That all for today...


Universal Turing Machine

 Read https://en.wikipedia.org/wiki/Universal_Turing_machine

We use stack of values as TAPE. We can writ e from both sides, we can read from anywhere, but we can pop only from top. So if we have to change value to 15th element (where no 1 is the first element)  we have first do this shift 15  so the 15th became 1st, then do Drop to drop it from stack, then we push the new value and do shiftback 15 to send it to  position 15. A stack of value is a collection of values, which we can easy move elements, insert or delete. So we have variable HEAD to get the scanned symbol.

These 4 statements change the current symbol (as HEAD point to) with one on inst(0) array item.

shift .head : drop :push inst(0): shiftback .head

Rules have 5 parts, and we have a List where we place them. The key for each element is a combination of state and scanning symbol. The other 3 combined in a tuple and that is the value of the element for the key. So When we run the machine, we have an initial state, we read the symbol at Head, (if no symbols are on tape, we feed the stack with the blank symbol). Then we combine these two to form a key and we read the tuple. We get three values, the new symbol to replace the scanning one (this happen in every step), the action to perform on Head, (right, left, stay) and the new state. When we place stay we have the new state as the Terminating state, so the machine stop. 

When we place Head beyond the bounds of stack we simply add another item, as Blank Symbol, so the Head always points to a Symbol.

Modules in M2000 read/modify parent stack of values. Interpreter call modules placing any parameter on top of stack so if stack has values "A" 112 "B" and we call ModuleOne 1,2 then the stack will be 1,2,"A", 112, "B" (we have a merging situation). The stack of values isn't used as return stack. Functions and lambda functions,  except simple functions (like subrutines, Funstion/End Function) have own stack, if they called in expressions, but same stack through Call statement.

The [] identifier return a stack object replacing the current stack with an empty one. So using this we clear the current stack, but all elements held by the variable which hold the pointer to stack.

so p=[] give the pointer to current stack to p and place a new pointer to an empty stack as the current stack (M2000 never expose the pointer of current stack), The function array() do many things, but with a stack object (a pointer to a stack object) we get an array from the stack, and then stack object  would be empty.

m=stack:=1,2,"abc"
Print len(m)=3
k=array(m)
Print len(m)=0
Print len(k)

So array([]) get the stack and convert to array. In the following example we call a function (with a new stack of values), we get all parameters, make an array and use the special # functions for arrays, we get the sum. The #sum() works for numeric values, so if we pass some strings, there will be no problem for the sum. See the use of ! before a stack object and an array. When we use it, we place all items as parameters. The difference is about the objects, the state after the use. The array not changed, the stack object has no items. To prevent the loss of items we can give a copy of stack object using Stack()

Function Sum() {
=array([])#sum()
}


Print sum(1,2,3,4,5)=15
Print sum(!(1,2,3),!(4,5))=15
Z=stack:=1,2,3,4,5
Print sum(!Z)=15
Print Len(Z)=0

On the tape (stack of values) we can use any value, but here for the Machine we place numbers, the position of the symbol in Symbols array (from 0 as the lower index). So we place symbols as strings, and the Tape module convert these to positions..

Arrays in M2000 are two kinds, the Array and the Buffer. The array has 16bytes per item, and internal the item is type Variant, (string, boolean,  integer, long, single, double, currency, decimal, object). Buffers used for arrays of simple items like double (8 byte per item), byte (1 byte), etc and as arrays of Structures. The Array has two interfaces. One defined with DIM, eg DIM a(-10 to 10)=1 and the other by using any array (or tuple, but no Buffers). So p=a() make a p a pointer to a(). So check these lines of code, and see that the pointer interface always see the array as one dimension from 0. A statement a+=100 add 100 to all numeric elements. But a(-10)+=100 add 100 to that item only.

dim a(-10 to 10)=1
p=a()
a(-10)=2
Print p#val(0)=2

When we have an array with strings and numbers we can use a second name to get the other type, so the line:  dim inst$() : link inst$() to inst()  make an array inst$() (empty) and a link to inst() so these two identifiers are the same array. So when this happen:

inst$()=.Rules(theRule$)

The this.Rules(theRule$) take the key theRule$ and return a tuple. The array with  name with parenthesis use one pointer and never change the pointer. See the example bellow

dim a()
k=(1,2,3,4)
a()=k
a(0)+=10
Print a(0)=11, k#val(0)=1

The a(0) has value 11, so the internal pointer of a() is different from k. We can make a name with parenthesis as a reference of a pointer to a. A reference means that is ONE thing with another name. So when we do this m()=(1,2,3,4,5,6,7) is like we do k=(1,2,3,4,5,6,7). The difference are for the use of operators (for k ++, += and other apply to all elements, for m() only for the item by the index).

k=(1,2,3,4)
Link k to m()
k+=100
Print m(0)=101
k=(500,200,100)
Print m(0)=500


m()=(1,2,3,4,5,6,7)
Print len(k)=7


Enjoy this program:


Module CheckIt {
print "Universal Turing Machine"
print "------------------------"
class Machine {
private:
Head=1, Symbols=(,), States=(,)
Initial_State$, Terminating_state$, Blank_Symbol$
BS=0, Rules=list, caption$
tp$="{0:4} {1} {2} {3:5} {4:4}"
public:
Module States {
.States<=array([])
}
Module Symbols {
.Symbols<=array([])
}
Module Reset (.Initial_State$, .Terminating_state$, .Blank_Symbol$) {
if len(.States)=0 then error "No States defined"
if len(.Symbols)=0 then error "No Symbols defined"
if .States#nothave(.Initial_State$) then error "Initial State Not Exist"
if .States#nothave(.Terminating_state$) then error "Terminating State Not Exist"
it=.Symbols#pos(.Blank_Symbol$) : if it=-1 then error "Blank symbol not exist"
.BS<=it
.Rules<=List
}
Module Init (.caption$) {
flush  // empty stack
print .caption$
}
Module AddRule (state$, read_symbol$, write_symbol$, action$, end_state$) {

if .States#nothave(state$) then Error "State not exist"
if .symbols#nothave(read_symbol$) then Error "Read Symbol not exist"
if .symbols#nothave(write_symbol$) then Error "Read Symbol not exist"
if ("right","left","stay")#nothave(action$) then Error "Action not exist"
if .States#nothave(end_state$) then Error "End state not exist"
try ok {
tuple=(.symbols#pos(write_symbol$), action$, end_state$)
Append .rules, state$+"_"+read_symbol$:=tuple
}
if not ok then error "rule "+ state$+"_"+read_symbol$+" already exist "
Pen 11 {
Print format$(.tp$, state$, read_symbol$, write_symbol$, action$, end_state$)
}
if stack.size>=5 then loop
}
Module TapeOLD {
s=[]
m=each(s)
while m
it= .Symbols#pos(stackitem$(m))
if it=-1 then error "Tape symbol not exist at position ";m^
data it
end while
}
		Module Tape {
m=stack.size
while m
it= .Symbols#pos(letter$)
if it=-1 then error "Tape symbol not exist at position ";101-m
data it
m--
end while
}
		Module Run (steps as long, display as boolean) {
if len(.rules)=0 then error "No rules found"
if .Initial_State$="" or .Terminating_state$="" or .Blank_Symbol$="" then
error "Reset the machine please"
end if
if empty then push .BS
curState$=.Initial_State$
cont=true
.head<=1
dim inst$() : link inst$() to inst()
while curState$<>.Terminating_state$
if display then pen 15 {showstack()}
steps--
theRule$=curState$+"_"+.symbols#val$(stackitem(.head))
if not exist(.Rules, theRule$) then error "Undefined "+theRule$
inst$()=.Rules(theRule$)
shift .head : drop :push inst(0): shiftback .head
select case inst$(1)
case "right"
.head++ : if .head>stack.size then data .BS
case "left"
if .head<=1 then push .BS else .head--
else case
cont=false
end select
// change state
curState$=inst$(2)
// Show Stack
if steps=0 or not cont then exit
end while
if steps=0 then print over
Pen 12 {showstack()}
print "tape length: ";stack.size : flush
Refresh
sub showstack()
local d$=format$("{0:-5} {1::-5} ", curState$, .head)
local i: for i=1 to min.data(stack.size, 60): d$+=.symbols#val$(stackitem(i)):Next
print d$
end sub
}
}
Turing1=Machine()
For Turing1 {
.init "Simple incrementer"
.States "q0", "qf"
.Symbols "B", "1"
.Reset "q0", "qf", "B"    // initial state, terminating state, blank symbol
.AddRule "q0", "1", "1", "right", "q0"
.AddRule "q0", "B", "1", "stay", "qf"
.tape "1", "1", "1"
.Run 100, true
}
Turing2=Machine()
For Turing2 {
.init "Three-state busy beaver"
.States "a", "b", "c", "halt"
.Symbols "0", "1"
.Reset "a", "halt", "0"
.AddRule "a", "0", "1", "right", "b", "a", "1", "1", "left", "c"
.AddRule "b", "0", "1", "left", "a", "b", "1", "1", "right", "b"
.AddRule "c", "0", "1", "left", "b", "c", "1", "1", "stay", "halt"
.Run 1000, true
}

For Turing1 {
.init "Sorter"
.States "A","B","C","D","E","X"
.Symbols "a","b","B","*"
.Reset "A", "X", "*"
.AddRule "A", "a", "a", "right", "A", "A", "b", "B", "right", "B"
.AddRule "A", "*", "*", "left", "E", "B", "a", "a", "right", "B"
.AddRule "B", "b", "b", "right", "B", "B", "*", "*", "left", "C"
.AddRule "C", "a", "b", "left", "D", "C", "b", "b", "left", "C"
.AddRule "C", "B", "b", "left", "E", "D", "a", "a", "left", "D"
.AddRule "D", "b", "b", "left", "D", "D", "B", "a", "right", "A"
.AddRule "E", "a", "a", "left", "E", "E", "*", "*", "right", "X"
.tape "b", "a", "b","b","b","a","a"
.Run 100, false
}
Turing1.tape "b","b","b","a","b","a","b","a","a","a","b","b","a"
Turing1.Run 1000, false

Turing3=Machine()
for Turing3 {
.init "5-state, 2-symbol probable Busy Beaver machine from Wikipedia"
.States "A","B","C","D", "E", "H"
.Symbols "0", "1"
.Reset "A", "H", "0"
.AddRule "A", "0", "1", "right", "B", "A", "1", "1", "left", "C"
.AddRule "B", "0", "1", "right", "C", "B", "1", "1", "right", "B"
.AddRule "C", "0", "1", "right", "D", "C", "1", "1", "left", "E"
.AddRule "D", "0", "1", "left", "A", "D", "1", "1", "left", "D"
.AddRule "E", "0", "1", "stay", "H", "E", "1", "0", "left", "A"
profiler
.Run 470, false //000000, false
Print round(timecount/1000,2);"s"    // estimated 12.5 hours for 47000000 steps
}
}
CheckIt