— Указатели в СтарБейсике видишь?
— …
— А они там есть.

Вступление

Динамические массивы повышают интеграционную адаптивность программ.

Под динамическими массивами здесь и далее понимаем множественные данные, структура и число элементов которых становятся известны и могут меняться на этапе решения вычислительной задачи.

Без претензии на толкование динамических массивов, далее по тексту изложены:

  • операторы языка StarBasic из Apache OpenOffice для работы с динамическими массивами;

  • демонстрационные примеры с динамическими массивами на языке StarBasic.

Операторы языка StarBasic

Для практического применения динамических массивов необходимо и достаточно:

  • создавать собственные структуры данных;

  • обращаться к данным по‑значению и по‑адресу (ссылке);

  • произвольно выделять и освобождать блоки памяти в куче.

Язык программирования StarBasic содержит необходимый и достаточный набор операторов:

  1. Оператор объявления пользовательского типа данных:

Type TUserType
	field_1 as EmbeddedType_1
	field_2 as EmbeddedType_2(*)
End Type

(*) Для полиморфных данных применим встроенный тип Variant.

  1. Оператор выделения блока памяти из кучи:

Dim userVar as New TUserType
  1. Оператор возврата блока памяти в кучу:

userVar = Nothing
  1. Оператор присваивания значения:

userVar_1 = userVar_2
  1. Оператор присваивания ссылки:

Set userVar_1 = userVar_2
  1. Передача параметров по-ссылке (ByRef) и по‑значению (ByVal).

  2. Функции пользовательского типа возвращают результат по‑ссылке.

Далее на примерах.

Пример 1. Односвязный список

Главный структурный элемент односвязного списка — это указатель на следующий элемент списка.

Описание элемента односвязного списка на языке «С»:

struct sListItem
{
	int 		item;	//полезная информация элемента
	sListItem	*succ;	//указатель на следующий элемент
};

Структурная схема односвязного списка:

Пример демонстрирует по‑элементное создание и удаление односвязного списка.

Описание структуры односвязного списка на языке StarBasic:

Type TListItem
	n as Integer
	succ as Variant
End Type

Точка запуска демонстрации односвязного списка:

'***************************************************************************
' Тест односвязного списка

Sub list_Test()

Детали и подробности в исходном тексте.
Исходный текст демонстрационного примера далее по тексту.

Пример 2. Двоичное дерево

В структуре элемента двоичного дерева содержится два указателя на предшествующий и последующий элементы.

Описание элемента бинарного дерева на языке «С»:

struct sBTreeItem
{
	int 		item;	//полезная информация элемента
	sBTreeItem	*pred;	//указатель на предыдущий элемент
	sBTreeItem	*succ;	//указатель на следующий элемент
};

Структурная схема двоичного дерева:

Пример демонстрирует адаптивное создание, реструктуризацию, частичное и полное удаление произвольного двоичного дерева согласно набора внутренних и внешних правил.

Описание структуры двоичного дерева на языке StarBasic:

Type BTreeNodeType
	Expression as String
	Left as Variant
	Right as Variant
End Type

Точки запуска демонстрации двоичного дерева

'***************************************************************************
' Функция для формул листа

Public Function BOOLCALC(formula as String) as String

'***************************************************************************
' Тест динамического двоичного дерева

Sub btree_test()

Прикладная часть примера с двоичным деревом — простой логический вычислитель.

На вход вычислителя подаётся строка с логической формулой.

Вычислитель возвращает решение (упрощение) логической формулы, если это возможно. Например:

0 ^ 1 = 1

b | a & b | c = B | C

Словарь вычислителя:
• 0 — FALSE
• 1 — TRUE
• A, D, C, D, E, F, G — произвольные логические функции (всего 7)
• | — логический оператор OR
• & — логический оператор AND
• ^ — логический оператор XOR
• ~ — логический оператор NOT
• () — оператор повышения приоритета

Высший приоритет у оператора отрицания «~».

Другие операторы имеют равный приоритет, выполняются последовательно слева направо.

Оператор повышения приоритета «()» меняет порядок выполнения операторов по правилам арифметики.

Детали и подробности в исходном тексте.

Демонстрационный пример

Демонстрационный пример требует средний уровень безопасности макросов.

Во время открытия файла примера «включить» макросы.

В демонстрационном примере два листа с формулами и два модуля с макросами.

На первом листе таблица основных логических преобразований. Первый лист служит для проверки программы.

На втором листе форма проверки произвольной логической формулы.

Первый модуль макросов содержит демонстрационный пример работы со связным списком.
Во-втором модуле макросов — демонстрационный пример работы с двоичным деревом.

Демонстрационный файл доступен по ссылке:
ApacheOpenOfficeDynarr.ods.

Вместо заключения

Дополнительная информация в завершение темы динамических массивов как средства повышения интеграционной адаптивности программ:

  • примеры подготовлены в Apache OpenOffice 4.1.11;

  • допускается применение других офисных пакетов;

  • условия распространения кода — GNU LGPL v3.

Добавленный текст

Исходный код для Примера 1 - "Связный список"
'******************************************************************************
' Dynamic array example for Apache OpenOffice StarBasic 
' based on singly linked list
'
' Copyright (с) 2022, "Nikolay E. Garbuz" <nik_garbuz@list.ru> 
'
' This program is free software: you can redistribute it and/or modify
' it under the terms of the GNU Lesser General Public License version 3 as
' published by the Free Software Foundation.
'
' This program is distributed in the hope that it will be useful,
' but WITHOUT ANY WARRANTY; without even the implied warranty of
' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
' GNU General Public License for more details.
'
' You should have received a copy of the GNU Lesser General Public License
' along with this program.  If not, see <http://www.gnu.org/licenses/>.
'
' Authored by Nikolay Garbuz <nik_garbuz@list.ru>
' Modified by
'
' TAB Size .EQ 4
'******************************************************************************

Option Explicit

'***************************************************************************
' Структура элемента односвязного списка

Type TListItem
	n as Integer
	succ as Variant
End Type

'***************************************************************************
' Тест односвязного динамического списка

Sub list_Test()
	Dim list as TListItem
	Dim i as Integer
	
	list = Nothing
	
	For i = 1 TO 3 
		Set list = newListItem(list)
	Next i
	
	Dim li as TListItem
	
	While Not IsNull(list)
		li = list

		print getItemNumber(li)

		freeListItem(list)
'		Set list = freeListItem(list) ' so it is also possible

	Wend
End Sub

'***************************************************************************
' Добавляет новый элемент в начало

Function newListItem(ByRef Head as TListItem) as TListItem
	Dim newItem as New TListItem
	
	Set newItem.succ = Head
	
	If IsNull (newItem.succ) Then
		newItem.n = 1 
	Else
		newItem.n = newItem.succ.n + 1
	End If
	Set Head = newItem
	newListItem = Head
End Function

'***************************************************************************
' Удаляет первый элемент в списке

Function freeListItem(ByRef Head as TListItem) as TListItem
	Dim Item as TListItem
	
	Set Item = Head
	
	If Not IsNull(Item) Then
		Set Head = Head.succ
		Item = Nothing
	End If
	
	freeListItem = Head
End Function

'***************************************************************************
' Возвращает данные элемента в списке

Function getItemNumber(ByRef Head as TListItem) as Integer
	getItemNumber = 0
	If Not IsNull(Head) Then
		getItemNumber = Head.n
	End If
End Function

Исходный код для Примера 2 - "Двоичное дерево"
'******************************************************************************
' Boolean calculator example for Apache OpenOffice StarBasic,
' based on dynamic B-Tree
'
' Copyright (с) 2010, 2022, "Nikolay E. Garbuz" <nik_garbuz@list.ru> 
'
' This program is free software: you can redistribute it and/or modify
' it under the terms of the GNU Lesser General Public License version 3 as
' published by the Free Software Foundation.
'
' This program is distributed in the hope that it will be useful,
' but WITHOUT ANY WARRANTY; without even the implied warranty of
' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
' GNU General Public License for more details.
'
' You should have received a copy of the GNU Lesser General Public License
' along with this program.  If not, see <http://www.gnu.org/licenses/>.
'
' Authored by Nikolay Garbuz <nik_garbuz@list.ru>
' Modified by
'
' TAB Size .EQ 4
'******************************************************************************

'******************************************************************************
'******************************************************************************
' Logical operations
'
' associativity:	a or (b or c) = (a or b) or c				a and (b and c) = (a and b) and c
' commutativity:	a or b = b or a								a and b = b and a
' absorption:		a or (a and b) = a 							a and (a or b) = a
' distributivity:	a or (b and c) = (a or b) and (a or c)		a and (b or c) = (a and b) or (a and c)
' complements:		a or not a = 1 								a and not a = 0
' idempotency:		a or a = a 									a and a = a 	
' boundedness:		a or 0 = a 									a and 1 = a
' 					a or 1 = 1 									a and 0 = 0
'
' 0 and 1 are complements:		not 0 = 1								not 1 = 0
' are not complements:			not a = not a 							not b = not b
' de Morgan's laws:				not (a or b) = not a and not b 			not (a and b) = not a or not b
'
' involution:					not not a = a
'
' exclusive disjunction, XOR:	a xor b = not (a and b) and (a or b)
'								a xor 1 = not (a and 1) and (a or 1)
'										= not a or 1 = not a
'								a xor 0 = not (a and 0) and (a or 0)
'										= not 0 and a = 1 and a = a
'								a xor a = not (a and a) and (a or a)
'										= not a and a = 0
'
'								a xor not a = not (a and not a) and (a or not a)
'												not 0 and a
'												1 and a = a
'
'								not a xor not b = not (not a and not b) and (not a or not b)
'												= (not not a or not not b) and (not a or not b)
'												= (a or b) and not (a and b)
'												= a xor b
'******************************************************************************

Option Explicit
Option Base 0

'***************************************************************************
' Const & Globals

Const OP_OR = "|"
Const OP_AND = "&"
Const OP_XOR = "^"
Const OP_NOT = "~"

Const OP_TRUE = "1"
Const OP_FALSE = "0"

Const OP_A = "A"
Const OP_B = "B"
Const OP_C = "C"
Const OP_D = "D"
Const OP_E = "E"
Const OP_F = "F"
Const OP_G = "G"

Const OP_LBR = "("
Const OP_RBR = ")"

Const OP_SPACE = " "

Dim bOpsArray() as String
Dim bComplementsArray() as String
Dim bConditionsArray() as String

'******************************************************************************
'*** B-Tree structure

Type BTreeNodeType
	Expression as String
	Left as Variant
	Right as Variant
End Type

'******************************************************************************
'******************************************************************************
' small B-Tree test
'******************************************************************************

Sub btree_test()
	Dim formula as String
	Dim Caption as String
	
	btEvaluate_init()

	formula = "1^0"
	Caption = "Condition: " & formula
	
	MsgBox (btEvaluate(formula), 64, Caption) ' 1

	formula = "A&B|B&C"
	Caption = "Condition: " & formula

	MsgBox (btEvaluate(formula), 64,  Caption) ' B|C

	formula = "(A&B)|(B&C)"
	Caption = "Condition: " & formula

	MsgBox (btEvaluate(formula), 64,  Caption) ' B&(A|C)
	
End Sub

'******************************************************************************
'******************************************************************************
' Boolean calculator for worksheet
'******************************************************************************

Public Function BOOLCALC(formula as String) as String
	btEvaluate_init()
	formula = UCase(formula)
	boolCalc = btEvaluate(formula)
End Function

'******************************************************************************
'******************************************************************************
'*** Boolean calculator for StarBasic
'******************************************************************************

Function btEvaluate(ByRef sFormula as String) as String
	Dim bTree as BTreeNodeType

	bTree = Nothing
	
	Dim err_pos as Integer
	err_pos = formula_chkSyntax(sFormula)
	
	If err_pos > 0 Then
		Dim l as String
		Dim r as String
		l = Left(sFormula, err_pos)
		r = Mid(sFormula, err_pos + 1)
		btEvaluate = l & "<!>" & r
		Exit Function
	End If
	
	bTree = btMakeTree(sFormula)
	
	bTree = btOptimizeTree(bTree)
	
	bTree = btCalcTree(bTree)
	
	btEvaluate = btDrawTree(bTree)
	
	btDeleteTree(bTree)
	
End Function

'***************************************************************************
' preparatory subroutine

Sub btEvaluate_init()
	Static ImInit as Boolean
	If NOT ImInit Then
		bOpsArray = Array(OP_NOT, OP_OR, OP_AND, OP_XOR)
		bComplementsArray = Array(OP_TRUE, OP_FALSE)
		bConditionsArray = Array(OP_A, OP_B, OP_C, OP_D, OP_E, OP_F, OP_G)
		ImInit = TRUE
	End If
End Sub

'******************************************************************************
' calculates a boolean formula in a B-Tree view

Function btCalcTree(ByRef bTree as BTreeNodeType) as BTreeNodeType
	Dim treeNode as BTreeNodeType
	Dim rightNode as BTreeNodeType
	Dim leftNode as BTreeNodeType

	Set treeNode = bTree

	If NOT btIsNodeLast(treeNode) Then
		Set leftNode = btCalcTree(treeNode.Left)
		Set rightNode = btCalcTree(treeNode.Right)

		Select Case treeNode.Expression
			Case OP_NOT

				If rightNode.Expression = OP_NOT Then
					Set treeNode = rightNode.Right
				Else
					If rightNode.Expression = OP_TRUE Then
						rightNode.Expression = OP_FALSE

						Set treeNode = rightNode
					Else
						If rightNode.Expression = OP_FALSE Then
							rightNode.Expression = OP_TRUE

							Set treeNode = rightNode
						Else
							Set treeNode.Right = rightNode
						End If
					End If
				End If
					
			Case OP_OR

				If leftNode.Expression = OP_TRUE OR rightNode.Expression = OP_TRUE Then
					treeNode = Nothing
					leftNode = Nothing
					rightNode  = Nothing

					Set treeNode = btMakeNode(OP_TRUE)
				Else
					If leftNode.Expression = OP_FALSE Then
						treeNode = Nothing
						leftNode = Nothing

						Set treeNode = rightNode
					Else
						If rightNode.Expression = OP_FALSE Then
							treeNode = Nothing
							rightNode  = Nothing

							Set treeNode = leftNode
						Else
							If btAreNodesSame(leftNode, rightNode) Then
								treeNode = Nothing
								rightNode  = Nothing

								Set treeNode = leftNode
							Else
								If btAreNodesComplement(leftNode, rightNode) Then
									treeNode = Nothing
									leftNode = Nothing
									rightNode  = Nothing

									Set treeNode = btMakeNode(OP_TRUE)
								Else
									Set treeNode = btMakeNode(treeNode.Expression, leftNode, rightNode)
								End If
							End If
						End If
					End If
				End If

			Case OP_AND

				If leftNode.Expression = OP_FALSE OR rightNode.Expression = OP_FALSE Then
					treeNode = Nothing
					leftNode = Nothing
					rightNode  = Nothing

					Set treeNode = btMakeNode(OP_FALSE)
				Else
					If leftNode.Expression = OP_TRUE Then
						treeNode = Nothing
						leftNode = Nothing

						Set treeNode = rightNode
					Else
						If rightNode.Expression = OP_TRUE Then
							treeNode = Nothing
							rightNode  = Nothing

							Set treeNode = leftNode
						Else
							If btAreNodesSame(leftNode, rightNode) Then
								treeNode = Nothing
								rightNode  = Nothing

								Set treeNode = leftNode
							Else
								If btAreNodesComplement(leftNode, rightNode) Then
									treeNode = Nothing
									leftNode = Nothing
									rightNode  = Nothing

									Set treeNode = btMakeNode(OP_FALSE)
								Else
									Set treeNode = btMakeNode(treeNode.Expression, leftNode, rightNode)
								End If
							End If
						End If
					End If
				End If

			End Select
	End If

	btCalcTree = treeNode
End Function

'******************************************************************************
'*** subroutines for boolean optimization
'******************************************************************************

'******************************************************************************
' optimizes the logical tree

Function btOptimizeTree(ByRef bTree as BTreeNodeType) as BTreeNodeType
	Dim treeNode as New BTreeNodeType
	Dim ChCount as Integer

	Set treeNode = bTree

	Do
		ChCount = 0

		treeNode = btOptimizeAreNot(treeNode, ChCount)
		treeNode = btOptimizeMorgans(treeNode, ChCount)
		treeNode = btOptimizeDistribution(treeNode, ChCount)
		treeNode = btOptimizeAbsorption(treeNode, ChCount)

	Loop Until ChCount = 0

	btOptimizeTree = treeNode
End Function

'******************************************************************************
' optimization for absorption
'	a or (a and b) = a
'	a and (a or b) = a

Function btOptimizeAbsorption(ByRef bTree as BTreeNodeType, ByRef ChCount as Integer) as BTreeNodeType
	Dim freeNode as BTreeNodeType
	Dim treeNode as New BTreeNodeType
	Dim stOR as Boolean
	Dim stAND as Boolean
	Dim lsOR as Boolean
	Dim lsAND as Boolean
	Dim Absorpt as Boolean

	Set treeNode = bTree

	If NOT btIsNodeLast(treeNode) Then
		stOR = treeNode.Expression = OP_OR
		stAND = treeNode.Expression = OP_AND
		If stOR OR stAND Then

			If NOT btIsNodeLast(treeNode.Right) Then
				lsAND = treeNode.Right.Expression = OP_AND
				lsOR = treeNode.Right.Expression = OP_OR

				If (stOR AND lsAND) OR (stAND AND lsOR) Then
					Absorpt = btAreNodesSame(treeNode.Left, treeNode.Right.Left) OR _
								btAreNodesSame(treeNode.Left, treeNode.Right.Right)

					If Absorpt Then
						Set freeNode = treeNode
						Set treeNode = treeNode.Left
						freeNode = Nothing
						
						ChCount = ChCount + 1
					End If
				End If
			End If

			If NOT btIsNodeLast(treeNode.Left) Then
				lsAND = treeNode.Left.Expression = OP_AND
				lsOR = treeNode.Left.Expression = OP_OR

				If (stOR AND lsAND) OR (stAND AND lsOR) Then
					Absorpt = btAreNodesSame(treeNode.Right, treeNode.Left.Left) OR _
								btAreNodesSame(treeNode.Right, treeNode.Left.Right)

					If Absorpt Then
						Set freeNode = treeNode
						Set treeNode = treeNode.Right
						freeNode = Nothing
						
						ChCount = ChCount + 1
					End If
				End If
			End If

		End If
		Set freeNode = treeNode.Left
		Set treeNode.Left = btOptimizeAbsorption(treeNode.Left, ChCount)
		freeNode = Nothing
		
		Set freeNode = treeNode.Right
		Set treeNode.Right = btOptimizeAbsorption(treeNode.Right, ChCount)
		freeNode = Nothing
	End If

	btOptimizeAbsorption = treeNode
End Function

'******************************************************************************
' optimization for distribution
'	(a or b) and (a or c) = a or (b and c)
'	(a and b) or (a and c) = a and (b or c)

Function btOptimizeDistribution(ByRef bTree as BTreeNodeType, ByRef ChCount as Integer) as BTreeNodeType
	Dim freeNode as BTreeNodeType
	Dim treeNode as New BTreeNodeType
	Dim stOR as Boolean
	Dim stAND as Boolean
	Dim lsOR as Boolean
	Dim lsAND as Boolean

	Set treeNode = bTree

	If NOT btIsNodeLast(treeNode) Then
		stOR = treeNode.Expression = OP_OR
		stAND = treeNode.Expression = OP_AND
		If stOR OR stAND Then
			lsOR = treeNode.Left.Expression = OP_OR AND treeNode.Right.Expression = OP_OR
			lsAND = treeNode.Left.Expression = OP_AND AND treeNode.Right.Expression = OP_AND
			If (stOR AND lsAND) OR (stAND AND lsOR) Then
				Dim idx as Integer
				idx = 0
				idx = idx + IIf(btAreNodesSame(treeNode.Left.Left, treeNode.Right.Left), 1, 0)
				idx = idx + IIf(btAreNodesSame(treeNode.Left.Left, treeNode.Right.Right), 2, 0)
				idx = idx + IIf(btAreNodesSame(treeNode.Left.Right, treeNode.Right.Left), 4, 0)
				idx = idx + IIf(btAreNodesSame(treeNode.Left.Right, treeNode.Right.Right), 8, 0)

				Select Case idx
					Case 1: ' left - left
						Set treeNode.Right.Left = treeNode.Left.Right
						Set treeNode.Left = treeNode.Left.Left
					Case 2: ' left - right
						Set treeNode.Right.Right = treeNode.Left.Right
						Set treeNode.Left = treeNode.Left.Left
					Case 4: ' right - left
						Set treeNode.Right.Left = treeNode.Left.Left
						Set treeNode.Left = treeNode.Left.Right
					Case 8: ' right - right
						Set treeNode.Right.Right = treeNode.Left.Left
						Set treeNode.Left = treeNode.Left.Right
					Case Else:
						idx = -1
				End Select
				If idx > 0 Then
					ChCount = ChCount + 1
					If stOR Then
						treeNode.Expression = OP_AND
						treeNode.Right.Expression = OP_OR
					Else
						treeNode.Expression = OP_OR
						treeNode.Right.Expression = OP_AND
					End If
				End If
			End If
		End If

		Set freeNode = treeNode.Left
		Set treeNode.Left = btOptimizeDistribution(treeNode.Left, ChCount)
		freeNode = Nothing
		
		Set freeNode = treeNode.Right
		Set treeNode.Right = btOptimizeDistribution(treeNode.Right, ChCount)
		freeNode = Nothing
		
	End If
	btOptimizeDistribution = treeNode
End Function

'******************************************************************************
' optimization for double 'not' by Morgan
'	not a or not b = not (a and b)
'	not a and not b = not (a or b)
'	not a xor not b = a xor b

Function btOptimizeMorgans(ByRef bTree as BTreeNodeType, ByRef ChCount as Integer) as BTreeNodeType
	Dim freeNode as BTreeNodeType
	Dim treeNode as New BTreeNodeType
	Dim bOR as Boolean
	Dim bAND as Boolean
	Dim bXOR as Boolean
	Dim bNOT as Boolean

	Set treeNode = bTree

	If NOT btIsNodeLast(treeNode) Then

		bOR = treeNode.Expression = OP_OR
		bAND = treeNode.Expression = OP_AND
		bXOR = treeNode.Expression = OP_XOR
		bNOT = NOT (btIsNodeLast(treeNode.Left) OR btIsNodeLast(treeNode.Left))
		If bNOT Then
			bNOT = treeNode.Left.Expression = OP_NOT AND treeNode.Right.Expression = OP_NOT
			If (bOR OR bAND) AND bNOT Then
				Set treeNode.Right.Left = treeNode.Left.Right
				treeNode.Left = Nothing
				If bOR Then
					treeNode.Right.Expression = OP_AND
				Else
					treeNode.Right.Expression = OP_OR
				End If
				treeNode.Expression = OP_NOT
				ChCount = ChCount + 1
			End If
			If bXOR AND bNOT Then
				Set freeNode = treeNode.Left
				Set treeNode.Left = treeNode.Left.Right
				freeNode = Nothing
				
				Set freeNode = treeNode.Right
				Set treeNode.Right = treeNode.Right.Right
				freeNode = Nothing
				
				ChCount = ChCount + 1
			End If
		End If
		Set freeNode = treeNode.Left
		Set treeNode.Left = btOptimizeMorgans(treeNode.Left, ChCount)
		freeNode = Nothing
		
		Set freeNode = treeNode.Right
		Set treeNode.Right = btOptimizeMorgans(treeNode.Right, ChCount)
		freeNode = Nothing
		
	End If
	btOptimizeMorgans = treeNode
End Function

'******************************************************************************
' deleting a simple double "not"
'	not not a = a

Function btOptimizeAreNot(ByRef bTree as BTreeNodeType, ByRef ChCount as Integer) as BTreeNodeType
	Dim freeNode as BTreeNodeType
	Dim treeNode as New BTreeNodeType

	Set treeNode = bTree

	If NOT btIsNodeLast(treeNode) Then
		If treeNode.Expression = OP_NOT Then
			If treeNode.Right.Expression = OP_NOT Then
				Set freeNode = treeNode
				Set treeNode = treeNode.Right.Right
				freeNode = Nothing
				
				ChCount = ChCount + 1
			End If
		End If
		Set freeNode = treeNode.Left
		Set treeNode.Left = btOptimizeAreNot(treeNode.Left, ChCount)
		freeNode = Nothing
		
		Set freeNode = treeNode.Right
		Set treeNode.Right = btOptimizeAreNot(treeNode.Right, ChCount)
		freeNode = Nothing
		
	End If
	btOptimizeAreNot = treeNode
End Function

'******************************************************************************
'*** returns TRUE for the last node in the B-tree

Function btIsNodeLast(Node as BTreeNodeType) as Boolean
	If IsNull(Node) Then
		btIsNodeLast = TRUE
	Else
		btIsNodeLast = IsNull(Node.Left) AND IsNull(Node.Right)
	End If
End Function

'******************************************************************************
' returns TRUE if nodes match

Function btAreNodesSame(FirstNode as BTreeNodeType, SecondNode as BTreeNodeType) as Boolean
	If IsNull(FirstNode) Then
		btAreNodesSame = IsNull(SecondNode)
	Else
		If IsNull(SecondNode) then
			btAreNodesSame = FALSE
		Else
			Dim express as Boolean
			Dim projec as Boolean
			Dim mirror as Boolean

			express = FirstNode.Expression = SecondNode.Expression

			projec = btAreNodesSame(FirstNode.Left, SecondNode.Left) _
				AND btAreNodesSame(FirstNode.Right, SecondNode.Right)

			mirror = btAreNodesSame(FirstNode.Left, SecondNode.Right) _
				AND btAreNodesSame(FirstNode.Right, SecondNode.Left)

			btAreNodesSame =  express AND (projec OR mirror)

		End If
	End If

End Function

'******************************************************************************
' returns TRUE if nodes are complement

Function btAreNodesComplement(FirstNode as BTreeNodeType, SecondNode as BTreeNodeType) as Boolean

	Dim areComplements as Boolean

	areComplements = FALSE

	If NOT (IsNull(FirstNode) AND IsNull(SecondNode)) Then
		If btIsNodeLast(FirstNode) AND btIsNodeLast(SecondNode) Then
			areComplements = btIsComplement(FirstNode.Expression) _
							AND btIsComplement(SecondNode.Expression) _
							AND (FirstNode.Expression <> SecondNode.Expression)
		Else
			areComplements = (FirstNode.Expression = OP_NOT) XOR (SecondNode.Expression = OP_NOT)
			If areComplements Then
				Dim tempF as New BTreeNodeType
				Dim tempS as New BTreeNodeType

				If FirstNode.Expression = OP_NOT Then
					Set tempF = FirstNode
				Else
					Set tempF = btMakeNode(OP_NOT, Nothing, FirstNode)
				End If

				If SecondNode.Expression = OP_NOT Then
					Set tempS = SecondNode
				Else
					Set tempS = btMakeNode(OP_NOT, Nothing, SecondNode)
				End If

				areComplements = btAreNodesSame(tempF, tempS)
			End If
		End If
	End If

	btAreNodesComplement = areComplements
End Function

'******************************************************************************
'*** B-tree building subroutines
'******************************************************************************

'******************************************************************************
' creates a B-Tree from a formula

Function btMakeTree(sFormula as String) as BTreeNodeType
	Dim logicElement as String
	Dim simpleElement as String
	Dim bTree as BTreeNodeType
	
	Do While sFormula <> ""
		 logicElement = formula_logicStructure(sFormula)
		
		Do While logicElement <> ""
			simpleElement = formula_boolElement(logicElement)

			If btIsAtom(simpleElement) Then
				Set bTree = btMakeNode(simpleElement)
			Else
				If btIsOperator(simpleElement) Then
					If simpleElement = OP_NOT Then
						Set bTree = btMakeNode( _
													simpleElement, _
													Nothing, _
													btMakeTree ( _
																	formula_logicStructure(logicElement) _
																) _
												)
					Else
						If simpleElement = OP_XOR Then '  not (a and b) and (a or b)
							Dim leftSubTreeA as BTreeNodeType
							Dim leftSubTreeB as BTreeNodeType
							Dim rightSubTreeA as BTreeNodeType
							Dim rightSubTreeB as BTreeNodeType
								
							Set LeftSubTreeA = btCopyTree(bTree)
							Set LeftSubTreeB = btCopyTree(LeftSubTreeA)
	
							Set rightSubTreeA =  btMakeTree(formula_logicStructure(sFormula))
								
							Set rightSubTreeB =  btCopyTree(rightSubTreeA)
								
							bTree = btDeleteTree(bTree)
								
							Set LeftSubTreeA = btMakeNode( OP_AND, LeftSubTreeA, RightSubTreeA )
							Set LeftSubTreeA = btMakeNode( OP_NOT, Nothing, LeftSubTreeA )
								
							Set RightSubTreeA = btMakeNode( OP_OR, LeftSubTreeB, RightSubTreeB )
								
							Set bTree = btMakeNode( OP_AND, LeftSubTreeA, RightSubTreeA )
						Else
							Set bTree = btMakeNode( _
														simpleElement, _
														bTree, _
														btMakeTree( _
																	formula_logicStructure(sFormula) _
																) _
													)
						End If
					End If
				Else ' btIsOperator(simpleElement) 
					Set bTree = btMakeTree(simpleElement)
				End If
			End If	
		Loop
	Loop
	
	btMakeTree = bTree
End Function

'******************************************************************************
' creates a B-Tree node from a formula

Function btMakeNode(sNode as String, Optional ByVal leftNode as BTreeNodeType, Optional ByVal rightNode as BTreeNodeType) as BTreeNodeType
	Dim treeNode as New BTreeNodeType

	With treeNode
		.Expression = sNode

		If IsMissing(leftNode) Then
			Set .Left = Nothing
		Else
			Set .Left = leftNode
		End If

		If IsMissing(rightNode)	Then
			Set .Right = Nothing
		Else
			Set .Right = rightNode
		End If

	End With

	btMakeNode = treeNode
End Function

'******************************************************************************
' creates a B-Tree copy

Function btCopyTree(ByVal bTree as BTreeNodeType) as BTreeNodeType
	Dim treeNode as New BTreeNodeType
	
	If IsNull(bTree) Then
		btCopyTree = bTree
	Else	
		With treeNode
			.Expression = bTree.Expression
			.Left = btCopyTree(bTree.Left)
			.Right = btCopyTree(bTree.Right)
		End With
		btCopyTree = treeNode
	End If
End Function

'******************************************************************************
' deletes a B-Tree

Function btDeleteTree(ByRef bTree as BTreeNodeType) as BTreeNodeType
	If btIsNodeLast(bTree) Then
		bTree = Nothing
	Else
		bTree.Left = btDeleteTree(bTree.Left)
		bTree.Right = btDeleteTree(bTree.Right)
	End If
	btDeleteTree = bTree
End Function

'******************************************************************************
'*** text parsing subroutines
'******************************************************************************

'******************************************************************************
' returns the logical structure from the formula

Function formula_logicStructure(ByRef sFormula as String) as String
	Dim Element as String
	Dim LogicElement as String

	LogicElement  = ""
	Do
		Element = formula_boolElement(sFormula)
		If Len(Element) > 1 Then
			LogicElement = LogicElement & OP_SPACE & OP_LBR & Element & OP_RBR
		Else
			LogicElement = LogicElement & OP_SPACE & Element
		EndIf
	Loop Until Element <> OP_NOT

	formula_logicStructure = Trim(LogicElement)
End Function

'******************************************************************************
' returns the next boolean from a formula

Function formula_boolElement(ByRef sFormula as String) as String
	Dim i as Integer

	While sFormula <> "" AND Left(sFormula, 1) <= OP_SPACE
		sFormula = Mid(sFormula, 2)
	Wend

	formula_boolElement = ""

	If Len(sFormula) > 0 Then
		Dim splitPos as Integer
		Dim splitNext as Integer
		Dim Element as String

		If Left(sFormula, 1) = OP_LBR Then
			splitPos = formula_pairBracket(sFormula, OP_LBR, OP_RBR)
			Element = Mid(sFormula, 2, splitPos - 2)
			sFormula = Mid(sFormula, splitPos + 1)
		Else
			splitPos = formula_delimPos(sFormula)
			Element = formula_Element(sFormula, splitPos)
		End If

		formula_boolElement = Trim(Element)
	End If

End Function

'******************************************************************************
' returns the position of the first delimiter
' returns 0 if delimiter does not exist

Function formula_delimPos(sFormula as String) as Integer
	Dim i as Integer
	Dim j as Integer
	Dim l as Integer
	
	formula_delimPos = 0
	
	While Left(sFormula, 1) <= OP_SPACE
		sFormula = Mid(sFormula, 2)
	Wend
	
	For i = 1 To Len(sFormula)
		For j = LBound(bOpsArray) To UBound(bOpsArray)
			l = Len(bOpsArray(j))
			If Mid(sFormula, i, l) = bOpsArray(j) Then
				formula_delimPos = i + l - 1
				Exit Function
			End If
		Next j
	Next i
End Function


'******************************************************************************
' check formula syntax
' returns position of syntax error or zero

Function formula_chkSyntax(ByRef sFormula as String) as Integer
	Dim Pos as Integer
	
	sFormula = Trim(sFormula)
	
	' lost Atom
	If NOT (btIsAtom(Right(sFormula, 1)) OR Right(sFormula, 1) = OP_RBR) Then
		formula_chkSyntax = Len(sFormula)
		Exit Function
	End If
	
	' lost brackets
	Pos = formula_chkBrackets(sFormula, OP_LBR, OP_RBR)
	
	If Pos > 0 Then
		formula_chkSyntax = Pos
		Exit Function
	End If
	
	' opetators harmony
	Dim nextOp as Boolean
	Dim ch as String
	nextOp = FALSE
	
	For Pos = 1 To Len(sFormula)
		ch = Mid(sFormula, Pos, 1)
		If ch <> OP_LBR AND ch <> OP_RBR Then
			If nextOp Then
				If btIsOperator(ch) AND ch <> OP_NOT Then
					nextOp = NOT nextOp
				Else
					formula_chkSyntax = Pos
					Exit Function
				End If
			Else
				If ch <> OP_NOT Then
					If btIsAtom(ch) Then
						nextOp = NOT nextOp
					Else
						formula_chkSyntax = Pos
						Exit Function
					End If
				End If
			End If
		Else
			If nextOp AND ch <> OP_RBR AND NOT (btIsOperator(ch) AND ch <> OP_NOT) Then
				formula_chkSyntax = Pos
				Exit Function
			End If		
		End If
	Next Pos
	formula_chkSyntax = 0
End Function

'******************************************************************************
' check formula syntax
' returns position of syntax error or zero

Function formula_chkBrackets(sFormula as String, L_Bra as String, R_Bra as String) as Integer

	Dim balancer as Integer
	Dim i as Integer
	Dim op as String
	balancer = 0

	For i = 1 To Len(sFormula)
		op = Mid(sFormula, i, 1)
		If op = L_Bra Then
			balancer = balancer + 1
		End If
		If op = R_Bra Then
			balancer = balancer - 1
		End If
		If balancer < 0 Then
			formula_chkBrackets = i
			Exit Function
		End If
	Next i

	If balancer > 0 then
		formula_chkBrackets = i
	Else
		formula_chkBrackets = 0
	End If
End Function

'******************************************************************************
' returns the first element

Function formula_Element(ByRef sFormula as String, splitPos as Integer) as String
	Dim Element as String
	Element = ""
	
	If splitPos = 1 Then
		Element = Left(sFormula, splitPos)
		sFormula = Mid(sFormula, splitPos + 1)
	Else
		If splitPos = 0 Then
			Element = sFormula
			sFormula = ""
		Else
			Element = Left(sFormula, splitPos - 1)
			sFormula = Mid(sFormula, splitPos)
		End If
	End If
	formula_Element = Trim(Element)
End Function

'******************************************************************************
' returns the position of the parenthesis

Function formula_pairBracket(ByVal sFormula as String, L_Bra as String, R_Bra as String) as Integer
	Dim lbra_Count as Integer
	Dim rbra_Count as Integer
	Dim pos as Integer

	formula_pairBracket = 0

	For pos = 1 To Len(sFormula)
		lbra_Count = lbra_Count + IIf(Mid(sFormula, pos, 1) = L_Bra, 1, 0)
		rbra_Count = rbra_Count + IIf(Mid(sFormula, pos, 1) = R_Bra, 1, 0)
		If lbra_Count = rbra_Count Then
			formula_pairBracket = pos
			Exit Function
		End If
	Next pos
End Function

'******************************************************************************
'*** routines for data analysis
'******************************************************************************

'******************************************************************************
' returns TRUE if operator

Function btIsOperator(ByVal sElement as String) as Boolean
	Dim i as Integer
	
	btIsOperator  = FALSE
	
	For i = LBound(bOpsArray) To UBound(bOpsArray)
		If sElement = bOpsArray(i) Then
			btIsOperator = TRUE
			Exit Function
		End If
	Next i
End Function

'******************************************************************************
' returns TRUE if atom

Function btIsAtom(ByVal sElement as String) as Boolean

	btIsAtom = btIsComplement(sElement) OR btIsCondition(sElement)

End Function

'******************************************************************************
' returns TRUE if complement

Function btIsComplement(ByVal sElement as String) as Boolean
	Dim i as Integer
	
	btIsComplement  = FALSE
	
	For i = LBound(bComplementsArray) To UBound(bComplementsArray)
		If sElement = bComplementsArray(i) Then
			btIsComplement = TRUE
			Exit Function
		End If
	Next i
End Function

'******************************************************************************
' returns TRUE if condition

Function btIsCondition(ByVal sElement as String) as Boolean
	Dim i as Integer
	
	btIsCondition  = FALSE
	
	For i = LBound(bConditionsArray) To UBound(bConditionsArray)
		If sElement = bConditionsArray(i) Then
			btIsCondition = TRUE
			Exit Function
		End If
	Next i
End Function

'******************************************************************************
'*** other functions
'******************************************************************************

Function btDrawTree(Tree as BTreeNodeType) as String
	Static rec as Integer

	Dim sout as String
	sout = ""

	rec = rec + 1

	If IsNull(Tree) Then
		sout = sout & "()"
	Else
		If btIsNodeLast(Tree) Then
			sout = sout & Tree.Expression
		Else
			If Tree.Expression = OP_NOT Then
				sout = sout & OP_NOT
				sout = sout & btDrawTree(Tree.Right)
			Else
				If rec > 1 Then
					sout = sout & OP_LBR
				End If
				If NOT IsNull(Tree.Left) Then
					sout = sout & btDrawTree(Tree.Left)
				End If
				sout = sout & Tree.Expression
				If NOT IsNull(Tree.Right) Then
					sout = sout & btDrawTree(Tree.Right)
				End If
				If rec > 1 Then
					sout = sout & OP_RBR
				End If
			End If
		End If
	End If
	btDrawTree = Trim(sout)
	rec = rec - 1
End Function

Sub trapExept()
	Dim j as Integer
	j = 0
	j = j / j
End Sub

Комментарии (0)