understanding this VB code - vb6

Can someone try wrap their brain around this one? I thought it was simply ensuring there were 2 bytes in hex byte and ensuring the values were between 0 -9 and A-F but no.
A snippet of a program that is for an infrared controller/blaster. This subroutine will send the actual signals (or other codes) out the serial port to the controller for it to finish the job.
Sample call:
SendCode ("04241001")
The VB6 code says:
Public Sub SendCode(ByVal strOut As String)
' ****************************
' This sub sends the hex codes
' ****************************
Dim numb1 As Integer, numb2 As Integer
Dim strRS As String
Dim i As Long
Dim newline(200) As String, outline(200) As String
Debug.Print "Sending IR - " & strOut
strRS = vbNullString
For i = 1 To Len(strOut)
newline(i) = Mid(strOut, i, 1)
Next
For i = 1 To Len(strOut) Step 2
If Asc(newline(i)) < 64 Then
numb1 = (Asc(newline(i)) - 48) * 16
strRS = strRS + Format(Hex(numb1 / 16), "0")
Else
numb1 = (Asc(newline(i)) - 55) * 16
strRS = strRS + Format(Hex(numb1 / 16), "0")
End If
If Asc(newline(i + 1)) < 64 Then
numb2 = (Asc(newline(i + 1)) - 48)
strRS = strRS + Format(Hex(numb2), "0")
Else
numb2 = (Asc(newline(i + 1)) - 55)
strRS = strRS + Format(Hex(numb2), "0")
End If
numb1 = numb1 + numb2
outline((i + 1) \ 2) = CByte(numb1)
strRS = strRS + " "
Next
With MSComm1
.RTSEnable = True
Sleep (20)
.OutBufferCount = 0
For i = 1 To (Len(strOut) / 2)
.Output = Chr(outline(i))
Next
Sleep (20)
.RTSEnable = False
End With
End Sub
The question is based around the second For/Next loop with Step 2 and the embedded IF statements. What is going on inside the loop?
numb1 and numb2
What is the purpose of this loop?

It converts a hex string into a binary byte string, then sends the binary byte string.
It also converts the binary bytes back into hex (strRS) so that you can check the conversion and the output. The check/debug string is not used for anything, but if you put a break point in there you can check the values.

Related

Split int array in to two arrays even and odd VBscript error

I have written code for an array of numbers which prints out. I'm now writing code to split the array into even and off numbers. I've started off with an if statement to separate the numbers but i'm struggling to find a solution on how to do it. My code below is failing as it's unable to split the numbers.
Sub main()
a=Array(5,10,15,20)
for each x in a
Msgbox(x)
If MyArray(I) / 2 = MyArray(I)
List1.AddItem MyArray(I) ' Even Integers
Else
List2.AddItem MyArray(I) ' Odd Integers
End if
next
End Sub
As Lankymart suggests, the simplest approach would be to use Mod() and check if the remainder is 1 or 0, but you can also do it with the approach you seemed to be working towards:
If MyArray(index)/2 = Int(MyArray(index)/2) Then
' Even number
Else
' Odd number
End If
Mod() approach:
If MyArray(index) Mod 2 = 0 Then
' Even number
Else
' Odd number
End If
Here's a complete subroutine that demonstrates what you are trying to do:
Dim arr(4) As Integer
Dim arrEven() As Integer
Dim iEvenValues As Integer
Dim arrOdd() As Integer
Dim iOddValues As Integer
Dim iCounter As Integer
' Initialize array
arr(0) = 5
arr(1) = 10
arr(2) = 15
arr(3) = 20
For iCounter = 1 To UBound(arr)
If arr(iCounter - 1) Mod 2 = 0 Then
iEvenValues = iEvenValues + 1
ReDim Preserve arrEven(iEvenValues)
arrEven(iEvenValues - 1) = arr(iCounter - 1)
Else
iOddValues = iOddValues + 1
ReDim Preserve arrOdd(iOddValues)
arrOdd(iOddValues - 1) = arr(iCounter - 1)
End If
Next
Dim sValues As String
sValues = "Even values (" & iEvenValues & "):"
For iCounter = 1 To UBound(arrEven)
sValues = sValues & " " & arrEven(iCounter - 1)
Next
MsgBox sValues
sValues = "Odd values (" & iOddValues & "):"
For iCounter = 1 To UBound(arrOdd)
sValues = sValues & " " & arrOdd(iCounter - 1)
Next
MsgBox sValues

Read the quantity of carriage return used in Word

I am calculating accuracy from a word document by calculating the total number of changes made once track review is on. Incorrect use of punctuation is calculated as 1/4 mark, while for contextual or grammar errors a full 1 mark is deducted.
Right now all carriage returns are being calculated as 1 full mark. I want this either to be removed completely or can pass it along as 1/4 mark deduction. I am using the following for counting . ; and , as 1/4 mark deduction.
For Each myRevision In ActiveDocument.Revisions
myRevision.Range.Select
If myRevision.Type = wdRevisionInsert Then
lngRevisions = Len(Selection.Text)
For i = 1 To lngRevisions
If Mid(Selection.Text, i, 1) = "," Then
punct = punct + 1
Else
End If
If Mid(Selection.Text, i, 1) = "." Then
punct = punct + 1
Else
End If
If Mid(Selection.Text, i, 1) = ";" Then
punct = punct + 1
Else
End If
If Mid(Selection.Text, i, 1) = "" Then
punct = punct + 1
Else
End If
Next i
Count = Count + 1
Else
End If
Next
tCorrections = Count + punct * 0.25 - punct
Accuracy = ((tWords - tCorrections) / tWords) * 100
Accuracy = Round(Accuracy, 1)
Use an array of types names (aLabels) and a string of the types occurring in your data (sC) via a mapping (aMap) of types to counting slots for a flexible way to classify your string(s). As in this demo:
Option Explicit
Dim aLabels : aLabels = Split("Vowels Consants Digits Punctuations EOLs Unclassified")
ReDim aCounts(UBound(aLabels))
Dim sC : sC = "abce1,2." & vbCr
Dim aMap : aMap = Array(0, 1, 1, 0, 2, 3, 2, 3, 4)
Dim sD : sD = sC & "d" & sC & "bb111."
Dim p, i
For p = 1 To Len(sD)
i = Instr(sC, Mid(sD, p, 1))
If 0 = i Then
i = UBound(aLabels)
Else
i = aMap(i - 1)
End If
aCounts(i) = aCounts(i) + 1
Next
For i = 0 To UBound(aLabels)
WScript.Echo Right(" " & aCounts(i), 3), aLabels(i)
Next
output:
cscript 42505210.vbs
4 Vowels
6 Consants
7 Digits
5 Punctuations
2 EOLs
1 Unclassified
Based on such raw data (frequencies of types) you an add specials weights.
Update wrt comment:
As I said: Add weights after calculating the raw frequencies:
... as above ...
Dim nSum
' Std - all weights = 1
nSum = 0 : For Each i In aCounts : nSum = nSum + i : Next
WScript.Echo "all pigs are equal:", nSum
' No EOLs
nSum = 0 : For Each i In aCounts : nSum = nSum + i : Next : nSum = nSum - aCounts(4)
WScript.Echo "EOLs don't count:", nSum
nSum = 0 : aCounts(0) = aCounts(0) * 4 : For Each i In aCounts : nSum = nSum + i : Next
WScript.Echo "vowels count * 4:", nSum
additional output:
all pigs are equal: 25
EOLs don't count: 23
vowels count * 4: 37

Fast Fourier Transform Using Excel's VBA

I'm trying to implement a Fast Fourier Transform (Radix-2) in MS's Excel VBA. The code I'm using pulls data from a range in the worksheet, does the calculations, then dumps the results in the adjacent columns. What I'm having trouble with is 1) know what to do with the resulting X[k] arrays, and 2) matching these results with the results from Excel's built in FFT (they do not currently match). The code is shown below. Thanks in advance for your help.
Sub Enforce_DecimationInTime()
On Error GoTo ERROR_HANDLING
Dim SubName As String
SubName = "Enforce_DecimationInTime()"
Dim WS As Worksheet
Dim n As Long, v As Long, LR As Long, x As Long
Set WS = Worksheets("FFT")
LR = WS.Range("A" & Rows.Count).End(xlUp).Row
n = LR - 1
Do Until 2 ^ x <= n And 2 ^ (x + 1) > n 'locates largest power of 2 from size of input array
x = x + 1
Loop
n = n - (n - 2 ^ x) 'calculates n using the largest power of 2
If n + 1 <> WS.Range("A" & Rows.Count).End(xlUp).Row Then
WS.Range("A" & 2 ^ x + 2 & ":A" & LR).Delete xlUp 'deletes extra input data
End If
v = WorksheetFunction.Log(n, 2) 'calculates number of decimations necessary
Application.ScreenUpdating = False
For x = 1 To v
Call Called_Core.DecimationInTime(WS, n, 2 ^ x, x) 'calls decimation in time subroutine
Next x
Application.ScreenUpdating = True
Exit Sub
ERROR_HANDLING:
MsgBox "Error encountered in " & SubName & ": exiting subroutine." _
& vbNewLine _
& vbNewLine & "Error description: " & Err.Description _
& vbNewLine & "Error number: " & Err.Number, vbCritical, Title:="Error!"
End
End Sub
The above subroutine calls the below subroutine through a For/Next loop to the count of "v".
Sub DecimationInTime(WS As Worksheet, n As Long, Factor As Integer, x As Long)
On Error GoTo ERROR_HANDLING
Dim SubName As String
SubName = "DecimationInTime()"
Dim f_1() As Single, f_2() As Single
Dim i As Long, m As Long, k As Long
Dim TFactor_N1 As String, TFactor_N2 As String, X_k() As String
Dim G_1() As Variant, G_2() As Variant
ReDim f_1(0 To n / Factor - 1) As Single
ReDim f_2(0 To n / Factor - 1) As Single
ReDim G_1(0 To n / 1 - 1) As Variant
ReDim G_2(0 To n / 1 - 1) As Variant
ReDim X_k(0 To n - 1) As String
TFactor_N1 = WorksheetFunction.Complex(0, -2 * WorksheetFunction.Pi / (n / 1)) 'twiddle factor for N
TFactor_N2 = WorksheetFunction.Complex(0, -2 * WorksheetFunction.Pi / (n / 2)) 'twiddle factor for N/2
For i = 0 To n / Factor - 1
f_1(i) = WS.Range("A" & 2 * i + 2).Value 'assign input data
f_2(i) = WS.Range("A" & 2 * i + 3).Value 'assign input data
Next i
WS.Cells(1, 1 + x).Value = "X[" & x & "]" 'labels X[k] column with k number
For k = 0 To n / 2 - 1
For m = 0 To n / Factor - 1
G_1(m) = WorksheetFunction.ImProduct(WorksheetFunction.ImPower(TFactor_N2, k * m), WorksheetFunction.Complex(f_1(m), 0)) 'defines G_1[m]
G_2(m) = WorksheetFunction.ImProduct(WorksheetFunction.ImPower(TFactor_N2, k * m), WorksheetFunction.Complex(f_2(m), 0)) 'defines G_2[m]
Next m
X_k(k) = WorksheetFunction.ImSum(WorksheetFunction.ImSum(G_1), WorksheetFunction.ImProduct(WorksheetFunction.ImSum(G_2), WorksheetFunction.ImPower(TFactor_N1, k))) 'defines X[k] for k
If k <= n / 2 Then X_k(k + n / 2) = WorksheetFunction.ImSum(WorksheetFunction.ImSum(G_1), WorksheetFunction.ImProduct(WorksheetFunction.ImSum(G_2), WorksheetFunction.ImPower(TFactor_N1, k), WorksheetFunction.Complex(-1, 0))) 'defines X[k] for k + n/2
WS.Cells(k + 2, 1 + x).Value = X_k(k)
WS.Cells(k + 2 + n / 2, 1 + x).Value = X_k(k + n / 2)
Next k
Exit Sub
ERROR_HANDLING:
MsgBox "Error encountered in " & SubName & ": exiting subroutine." _
& vbNewLine _
& vbNewLine & "Error description: " & Err.Description _
& vbNewLine & "Error number: " & Err.Number, vbCritical, Title:="Error!"
End
End Sub
I went back through the process and determined my problem was that I had assigned the wrong values to the twiddle factors, TFactor_N1 and TFactor_N2. After fixing this problem and adjusting which values are displayed, I was able to get the same results as Excel's built in FFT. The fixed code is show below.
Sub Enforce_DecimationInTime()
On Error GoTo ERROR_HANDLING
Dim SubName As String
SubName = "Enforce_DecimationInTime()"
Dim WS As Worksheet
Dim n As Long, v As Long, LR As Long, x As Long
Dim TFactor_N1 As String, TFactor_N2 As String
Set WS = Worksheets("FFT")
LR = WS.Range("A" & Rows.Count).End(xlUp).Row
n = LR - 1
Do Until 2 ^ x <= n And 2 ^ (x + 1) > n 'locates largest power of 2 from size of input array
x = x + 1
Loop
n = n - (n - 2 ^ x) 'calculates n using the largest power of 2
If n + 1 <> WS.Range("A" & Rows.Count).End(xlUp).Row Then
WS.Range("A" & 2 ^ x + 2 & ":A" & LR).Delete xlUp 'deletes extra input data
End If
v = WorksheetFunction.Log(n, 2) 'calculates number of decimations necessary
TFactor_N1 = WorksheetFunction.ImExp(WorksheetFunction.Complex(0, -2 * WorksheetFunction.Pi / (n / 1))) 'twiddle factor for N
TFactor_N2 = WorksheetFunction.ImExp(WorksheetFunction.Complex(0, -2 * WorksheetFunction.Pi / (n / 2))) 'twiddle factor for N/2
Application.ScreenUpdating = False
For x = 1 To v
Call Called_Core.DecimationInTime(WS, n, 2 ^ x, x, TFactor_N1, TFactor_N2) 'calls decimation in time subroutine
Next x
Application.ScreenUpdating = True
Exit Sub
ERROR_HANDLING:
MsgBox "Error encountered in " & SubName & ": exiting subroutine." _
& vbNewLine _
& vbNewLine & "Error description: " & Err.Description _
& vbNewLine & "Error number: " & Err.Number, vbCritical, Title:="Error!"
End
End Sub
Sub DecimationInTime(WS As Worksheet, n As Long, Factor As Integer, x As Long, TFactor_N1 As String, TFactor_N2 As String)
On Error GoTo ERROR_HANDLING
Dim SubName As String
SubName = "DecimationInTime()"
Dim f_1() As String, f_2() As String
Dim i As Long, m As Long, k As Long
Dim X_k() As String
Dim G_1() As Variant, G_2() As Variant
ReDim f_1(0 To n / Factor - 1) As String
ReDim f_2(0 To n / Factor - 1) As String
ReDim G_1(0 To n / 1 - 1) As Variant
ReDim G_2(0 To n / 1 - 1) As Variant
ReDim X_k(0 To n - 1) As String
For i = 0 To n / Factor - 1
f_1(i) = WS.Cells(2 * i + 2, 1).Value 'assign input data
f_2(i) = WS.Cells(2 * i + 3, 1).Value 'assign input data
Next i
For k = 0 To n / 2 - 1
For m = 0 To n / Factor - 1 'defines G_1[m] and G_2[m]
G_1(m) = WorksheetFunction.ImProduct(WorksheetFunction.ImPower(TFactor_N2, k * m), f_1(m))
G_2(m) = WorksheetFunction.ImProduct(WorksheetFunction.ImPower(TFactor_N2, k * m), f_2(m))
Next m 'defines X[k] for k and k + n/2
X_k(k) = WorksheetFunction.ImSum(WorksheetFunction.ImSum(G_1), WorksheetFunction.ImProduct(WorksheetFunction.ImSum(G_2), WorksheetFunction.ImPower(TFactor_N1, k)))
If k <= n / 2 Then X_k(k + n / 2) = WorksheetFunction.ImSub(WorksheetFunction.ImSum(G_1), WorksheetFunction.ImProduct(WorksheetFunction.ImSum(G_2), WorksheetFunction.ImPower(TFactor_N1, k)))
If x = 1 Then
WS.Cells(k + 2, 1 + x).Value = X_k(k)
WS.Cells(k + 2 + n / 2, 1 + x).Value = X_k(k + n / 2)
End If
Next k
Exit Sub
ERROR_HANDLING:
MsgBox "Error encountered in " & SubName & ": exiting subroutine." _
& vbNewLine _
& vbNewLine & "Error description: " & Err.Description _
& vbNewLine & "Error number: " & Err.Number, vbCritical, Title:="Error!"
End
End Sub
The function call is not good
Call Called_Core.DecimationInTime(WS, n, 2 ^ x, x, TFactor_N1, TFactor_N2)
It should be:
Call DecimationInTime(WS, n, 2 ^ x, x, TFactor_N1, TFactor_N2)
Implementing FFT in ExcelVBA is kind of involved, but not too bad. For typical applications, the input signal is usually real-valued, not coplex-valued. This would be the case if you were measuring a dynamic signal from a velocity or acceleration transducer, or microphone.
Shown here is a DFT that will convert any number of input pairs, (eg. time and velocity). They do not have to be 2^N number of data (required for FFT). Usually the time is evenly divided so that all you need is DeltaTime (the time interval between your data). Let me drop in the code here:
Sub dft()
Dim ytime(0 To 18000) As Double 'Time history values such as velocity or acceleration
Dim omega(0 To 8096) As Double 'Discreet frequency values used in transform
Dim yfreqr(0 To 8096) As Double 'Real valued component of transform
Dim yfreqi(0 To 8096) As Double 'Imaginary component of transform
Dim t As Double, sumr As Double, sumi As Double, sum As Double 'Cumulative sums
Dim omegadt As Double, omegat As Double, deltime As Double 'More constants self explanitory
Dim wksInData As Worksheet 'This is the Excel worksheet where the data is read from and written to
Dim s As Integer, i As Integer 'Counters for the transform loops
Dim transdim As Integer 'Dimension of the transform
'Read number of values to read, delta time
'Read in dimension of transform
Set wksInData = Worksheets("DFT Input") 'This is what I named the worksheet
numval = wksInData.Cells(5, 2)
deltime = wksInData.Cells(6, 2)
transdim = wksInData.Cells(5, 4)
For i = 0 To numval - 1 'Read in all the input data, its just a long column
ytime(i) = wksInData.Cells(i + 8, 2) 'So the input starts on row 8 column 2 (time values on column 1 for plotting)
Next i 'Loop until you have all the numbers you need
'Start the transform outer loop...for each discreet frequency
'Value s is the counter from 0 to 1/2 transform dimension
'So if you have 2000 numbers to convert, transdim is 2000
For s = 0 To transdim / 2 'Since transform is complex valued, use only 1/2 the number of transdim
sumr = 0# 'Set the sum of real values to zero
sumi = 0# 'Set the sum of imaginary values to zero
omega(s) = 2# * 3.14159265 * s / (transdim * deltime) 'These are the discreet frequencies
omegadt = omega(s) * deltime 'Just a number used in computations
' Start the inner loop for DFT
For i = 0 To numval - 1
sumr = sumr + ytime(i) * Cos(omegadt * i) 'This is the real valued sum
sumi = sumi + ytime(i) * Sin(omegadt * i) 'This is the complex valued sum
Next i ' and back for more
yfreqr(s) = sumr * 2# / transdim 'This is what is called the twiddle factor, just a constant
yfreqi(s) = -sumi * 2# / transdim 'Imaginary component is negative
Next s
'One last adjustment for the first and last transform values
'They are only 1/2 of the rest, but it is easiest to do this now after the inner loop is done
yfreqr(0) = yfreqr(0) / 2# 'Beginning factor
yfreqi(0) = yfreqi(0) / 2#
yfreqr(transdim / 2) = yfreqr(transdim / 2) / 2# 'End factor
yfreqi(transdim / 2) = yfreqi(transdim / 2) / 2#
wksInData.Cells(2, 8) = "Output" 'Just a column text header
For s = 0 To transdim / 2 'And write the output to columns 3, 4, 5 to the worksheet
wksInData.Cells(s + 8, 3) = omega(s) 'remember that magnitude is sqrt(real ^2 + imaginary ^2 )
wksInData.Cells(s + 8, 4) = yfreqr(s) 'but you can do this with an Excel formula on the worksheet
wksInData.Cells(s + 8, 5) = yfreqi(s) 'same with phase angle = arctan(Imaginary/Real)
Next s 'End of writeout loop.
'This is the inverse DFT
'I like to check my calculation,
'Should get the original time series back
For i = 0 To numval - 1
sum = 0
t = deltime * i
For s = 0 To transdim / 2
omegat = omega(s) * t
sum = sum + yfreqr(s) * Cos(omegat) - yfreqi(s) * Sin(omegat)
Next s
ytime(i) = sum
Next i
In alternative to the VBA solutions already posted, recent versions of Excel allow to implement the FFT as a pure formula with LAMBDA functions (i.e. without any VBA code).
One such implementation is https://github.com/altomani/XL-FFT.
For power of two length it uses a recursive radix-2 Cooley-Tukey algorithm
and for other length a version of Bluestein's algorithm that reduces the calculation to a power of two case.

Convert hex string (image) to base64 (for browser rendering) in VBScript

I have a script that outputs a .bmp captcha image.
The image is built in hexadecimal, and converted to binary and sent to the browser via response.binaryWrite chrB(CByte(myHexImage)) (as an image mime type = bmp)
I want the option to move away from that (changing mime type, etc) and toward just sending something to the output like this:
data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2 ...
(except that my images are BMP)
Is there a quick and easy way to convert that hex or binary to base64 in vbscript? Here is a snippet of what I have implmented now as described above.
how can I change this so I output, to the screen, valid hex format (which i can then easily convert to base64) or base64 directly?
Dim sBmpEndLine, sBmpInfoHeader, sBmpHeader, sTmpHex
If (m_iBmpWidth Mod 4) <> 0 Then
sBmpEndLine = string((4 - (m_iBmpWidth Mod 4)) * 2, "0")
Else
sBmpEndLine = vbNullString
End If
sBmpInfoHeader = array("28000000", "00000000", "00000000", "0100", "0800", "00000000", "00000000", "120B0000", "120B0000", "00000000", "00000000")
sBmpInfoHeader(1) = formatHex(hex(m_iBmpWidth), 4, 0, True)
sBmpInfoHeader(2) = formatHex(hex(m_iBmpHeight), 4, 0, True)
sBmpInfoHeader(6) = formatHex(hex((m_iBmpHeight * m_iBmpWidth) + (m_iBmpHeight * (len(sBmpEndLine) / 2))), 4, 0, True)
sBmpInfoHeader(9) = formatHex(hex(len(m_sBmpColorMap) / 8), 4, 0, True)
sBmpInfoHeader(10) = sBmpInfoHeader(9)
sBmpHeader = array("424D", "00000000", "0000", "0000", "00000000")
sBmpHeader(1) = formatHex(hex((len(join(sBmpHeader, "")) / 2) + (len(join(sBmpInfoHeader, "")) / 2) + (len(m_sBmpColorMap) / 2) + (m_iBmpHeight * m_iBmpWidth) + (m_iBmpHeight * (len(sBmpEndLine) / 2))), 4, 0, True)
sBmpHeader(4) = formatHex(hex((len(join(sBmpHeader, "")) / 2) + (len(join(sBmpInfoHeader, "")) / 2) + (len(m_sBmpColorMap) / 2)), 4, 0, True)
sendHex(join(sBmpHeader, ""))
sendHex(join(sBmpInfoHeader, ""))
sendHex(m_sBmpColorMap)
For y = m_iBmpHeight To 1 Step -1
For x = 1 To m_iBmpWidth
sTmpHex = m_aBitmap(y, x)
If sTmpHex = vbNullString Then
sendHex(m_sBgColor)
Else
sendHex(sTmpHex)
End If
Next
sendHex(sBmpEndLine)
Next
Response.Flush
And here is the sendHex() function:
Private Sub sendHex(valHex)
Dim iCntHex
For iCntHex = 1 To len(valHex) Step 2
'Response.BinaryWrite chrB(CByte("&H" & mid(valHex, iCntHex, 2)))
response.Write "&H" & mid(valHex, iCntHex, 2)
Next
End Sub
The Microsoft.XMLDOM has built in converters for bin.base64 and bin.hex. I wrote functions that demonstrate how to use this:
Function TextToBinary(text, dataType)
Dim dom
Set dom = CreateObject("Microsoft.XMLDOM")
dom.loadXML("<HELLO/>")
dom.documentElement.nodeTypedValue = text
dom.documentElement.dataType = dataType
TextToBinary = dom.documentElement.nodeTypedValue
End Function
Function BinaryToText(binary, dataType)
Dim dom
Set dom = CreateObject("Microsoft.XMLDOM")
dom.loadXML("<HELLO/>")
dom.documentElement.dataType = dataType
dom.documentElement.nodeTypedValue = binary
dom.documentElement.removeAttribute("dt:dt")
BinaryToText = dom.documentElement.nodeTypedValue
End Function
Function HexToBase64(strHex)
HexToBase64 = BinaryToText(TextToBinary(strHex, "bin.hex"), "bin.base64")
End Function
Function Base64ToHex(strBase64)
Base64ToHex = BinaryToText(TextToBinary(strBase64, "bin.base64"), "bin.hex")
End Function
Here's an example of their usage:
MsgBox HexToBase64("41")
MsgBox Base64ToHex("QQ==")
Also look at the ADODB.Stream as a means of working with binary files. It'll work with these routines.
I was able to get this working. Here is how.
In sendHex, I removed the &H portion, and wrapped my string in hex():
Private Sub sendHex(valHex)
Dim iCntHex
For iCntHex = 1 To len(valHex) Step 2
If len( mid(valHex, iCntHex, 2)) = 1 Then
response.write "0"
end if
response.write mid(valHex, iCntHex, 2)
Next
End Sub
This results in a string output like this (in byte strings of 2 hexidecimal chars):
424d1e050000000000003e00000028000000340000001800000001000
I can then dump that proper hex string into a HEX to base64 function as follows (not written by me, but rather, by Richard Mueller - http://www.rlmueller.net/Base64.htm)
Function HexToBase64(strHex)
' Function to convert a hex string into a base64 encoded string.
' Constant B64 has global scope.
Dim lngValue, lngTemp, lngChar, intLen, k, j, strWord, str64, intTerm
intLen = Len(strHex)
' Pad with zeros to multiple of 3 bytes.
intTerm = intLen Mod 6
If (intTerm = 4) Then
strHex = strHex & "00"
intLen = intLen + 2
End If
If (intTerm = 2) Then
strHex = strHex & "0000"
intLen = intLen + 4
End If
' Parse into groups of 3 hex bytes.
j = 0
strWord = ""
HexToBase64 = ""
For k = 1 To intLen Step 2
j = j + 1
strWord = strWord & Mid(strHex, k, 2)
If (j = 3) Then
' Convert 3 8-bit bytes into 4 6-bit characters.
lngValue = CCur("&H" & strWord)
lngTemp = Fix(lngValue / 64)
lngChar = lngValue - (64 * lngTemp)
str64 = Mid(B64, lngChar + 1, 1)
lngValue = lngTemp
lngTemp = Fix(lngValue / 64)
lngChar = lngValue - (64 * lngTemp)
str64 = Mid(B64, lngChar + 1, 1) & str64
lngValue = lngTemp
lngTemp = Fix(lngValue / 64)
lngChar = lngValue - (64 * lngTemp)
str64 = Mid(B64, lngChar + 1, 1) & str64
str64 = Mid(B64, lngTemp + 1, 1) & str64
HexToBase64 = HexToBase64 & str64
j = 0
strWord = ""
End If
Next
' Account for padding.
If (intTerm = 4) Then
HexToBase64 = Left(HexToBase64, Len(HexToBase64) - 1) & "="
End If
If (intTerm = 2) Then
HexToBase64 = Left(HexToBase64, Len(HexToBase64) - 2) & "=="
End If
End Function
This converts the above to base64, and I can use the output like this (e.g. in a browser url bar) to view it as an image:
data:image/bmp;base64,Qk0eBQAAAAAAAD4AAAAo...

Memory and execution time reduction for algorithms

I have been asked to ask this question again and in a little different context. This is the previous post:
Filtering in VBA after finding combinations
I would like to make this code possible with 100 different variables without having excel run out of memory and reducing the execution time significantly.
The problem with the code below is that if I have 100 boxes, excel will run out of memory in the line "Result(0 To 2 ^ NumFields - 2)" ( The code works for < 10 boxes)
This is my input:
3 A B C D E ...
7.7 3 1 1 1 2 ...
5.5 2 1 2 3 3 ...
This is the code:
Function stackBox()
Dim ws As Worksheet
Dim width As Long
Dim height As Long
Dim numOfBox As Long
Dim optionsA() As Variant
Dim results() As Variant
Dim str As String
Dim outputArray As Variant
Dim i As Long, j As Long
Dim currentSymbol As String
'------------------------------------new part----------------------------------------------
Dim maxHeight As Double
Dim maxWeight As Double
Dim heightarray As Variant
Dim weightarray As Variant
Dim totalHeight As Double
Dim totalWeight As Double
'------------------------------------new part----------------------------------------------
Set ws = Worksheets("Sheet1")
With ws
'clear last time's output
height = .Cells(.Rows.Count, 1).End(xlUp).row
If height > 3 Then
.Range(.Cells(4, 1), .Cells(height, 1)).ClearContents
End If
numOfBox = .Cells(1, 1).Value
width = .Cells(1, .Columns.Count).End(xlToLeft).Column
If width < 2 Then
MsgBox "Error: There's no item, please fill your item in Cell B1,C1,..."
Exit Function
End If
'------------------------------------new part----------------------------------------------
maxHeight = .Cells(2, 1).Value
maxWeight = .Cells(3, 1).Value
ReDim heightarray(1 To 1, 1 To width - 1)
ReDim weightarray(1 To 1, 1 To width - 1)
heightarray = .Range(.Cells(2, 2), .Cells(2, width)).Value
weightarray = .Range(.Cells(3, 2), .Cells(3, width)).Value
'------------------------------------new part----------------------------------------------
ReDim optionsA(0 To width - 2)
For i = 0 To width - 2
optionsA(i) = .Cells(1, i + 2).Value
Next i
GenerateCombinations optionsA, results, numOfBox
' copy the result to sheet only once
ReDim outputArray(1 To UBound(results, 1) - LBound(results, 1) + 1, 1 To 1)
Count = 0
For i = LBound(results, 1) To UBound(results, 1)
If Not IsEmpty(results(i)) Then
'rowNum = rowNum + 1
str = ""
totalHeight = 0#
totalWeight = 0#
For j = LBound(results(i), 1) To UBound(results(i), 1)
currentSymbol = results(i)(j)
str = str & currentSymbol 'results(i)(j) is the SYMBOL e.g. A, B, C
'look up box's height and weight , increment the totalHeight/totalWeight
updateParam currentSymbol, optionsA, heightarray, weightarray, totalHeight, totalWeight
Next j
If totalHeight < maxHeight And totalWeight < maxWeight Then
Count = Count + 1
outputArray(Count, 1) = str
End If
'.Cells(rowNum, 1).Value = str
End If
Next i
.Range(.Cells(4, 1), .Cells(UBound(outputArray, 1) + 3, 1)).Value = outputArray
End With
End Function
Sub updateParam(ByRef targetSymbol As String, ByRef symbolArray As Variant, ByRef heightarray As Variant, ByRef weightarray As Variant, ByRef totalHeight As Double, ByRef totalWeight As Double)
Dim i As Long
Dim index As Long
index = -1
For i = LBound(symbolArray, 1) To UBound(symbolArray, 1)
If targetSymbol = symbolArray(i) Then
index = i
Exit For
End If
Next i
If index <> -1 Then
totalHeight = totalHeight + heightarray(1, index + 1)
totalWeight = totalWeight + weightarray(1, index + 1)
End If
End Sub
Sub GenerateCombinations(ByRef AllFields() As Variant, _
ByRef Result() As Variant, ByVal numOfBox As Long)
Dim InxResultCrnt As Integer
Dim InxField As Integer
Dim InxResult As Integer
Dim i As Integer
Dim NumFields As Integer
Dim Powers() As Integer
Dim ResultCrnt() As String
NumFields = UBound(AllFields) - LBound(AllFields) + 1
ReDim Result(0 To 2 ^ NumFields - 2) ' one entry per combination
ReDim Powers(0 To NumFields - 1) ' one entry per field name
' Generate powers used for extracting bits from InxResult
For InxField = 0 To NumFields - 1
Powers(InxField) = 2 ^ InxField
Next
For InxResult = 0 To 2 ^ NumFields - 2
' Size ResultCrnt to the max number of fields per combination
' Build this loop's combination in ResultCrnt
ReDim ResultCrnt(0 To NumFields - 1)
InxResultCrnt = -1
For InxField = 0 To NumFields - 1
If ((InxResult + 1) And Powers(InxField)) <> 0 Then
' This field required in this combination
InxResultCrnt = InxResultCrnt + 1
ResultCrnt(InxResultCrnt) = AllFields(InxField)
End If
Next
If InxResultCrnt = 0 Then
Debug.Print "testing"
End If
'additional logic here
If InxResultCrnt >= numOfBox Then
Result(InxResult) = Empty
Else
' Discard unused trailing entries
ReDim Preserve ResultCrnt(0 To InxResultCrnt)
' Store this loop's combination in return array
Result(InxResult) = ResultCrnt
End If
Next
End Sub
Here's a version that does all the heavy lifting in variant arrays
(Combinations logic based on this answer for This Answer by Joubarc)
This runs on a sample dataset of 100 boxes with > 40,000 returned, and in < 1 second
Notes:
Execution time rises quickly if the Max number of boxes increases (eg 4 from 100: approx 13s)
If the number of returned results exceeds 65535, the code to tranpose the array into the sheet fails (last line of the sub) If you need to handle this may results, you will need to change the way results are returned to the sheet
Sub Demo()
Dim rNames As Range
Dim rHeights As Range
Dim rWeights As Range
Dim aNames As Variant
Dim aHeights As Variant
Dim aWeights As Variant
Dim MaxNum As Long
Dim MaxHeight As Double
Dim MaxWeight As Double
' *** replace these six line with your data ranges
Set rNames = Range([F5], [F5].End(xlToRight))
Set rHeights = rNames.Offset(1, 0)
Set rWeights = rNames.Offset(2, 0)
MaxNum = [C5]
MaxHeight = [C6]
MaxWeight = [C7]
aNames = rNames
aHeights = rHeights
aWeights = rWeights
Dim Result() As Variant
Dim n As Long, m As Long
Dim i As Long, j As Long
Dim iRes As Long
Dim res As String
Dim TestCombin() As Long
Dim TestWeight As Double
Dim TestHeight As Double
Dim idx() As Long
' Number of boxes
ReDim TestCombin(0 To MaxNum - 1)
n = UBound(aNames, 2) - LBound(aNames, 2) + 1
' estimate size of result array = number of possible combinations
For m = 1 To MaxNum
i = i + Application.WorksheetFunction.Combin(n, m)
Next
ReDim Result(1 To 3, 1 To i)
' allow for from 1 to MaxNum of boxes
iRes = 1
For m = 1 To MaxNum
ReDim idx(0 To m - 1)
For i = 0 To m - 1
idx(i) = i
Next i
Do
'Test current combination
res = ""
TestWeight = 0#
TestHeight = 0#
For j = 0 To m - 1
'Debug.Print aNames(1, idx(j) + 1);
res = res & aNames(1, idx(j) + 1)
TestWeight = TestWeight + aWeights(1, idx(j) + 1)
TestHeight = TestHeight + aHeights(1, idx(j) + 1)
Next j
'Debug.Print
If TestWeight <= MaxWeight And TestHeight <= MaxHeight Then
Result(1, iRes) = res
' optional, include actual Height and Weight in result
Result(2, iRes) = TestHeight
Result(3, iRes) = TestWeight
iRes = iRes + 1
End If
' Locate last non-max index
i = m - 1
While (idx(i) = n - m + i)
i = i - 1
If i < 0 Then
'All indexes have reached their max, so we're done
Exit Do
End If
Wend
'Increase it and populate the following indexes accordingly
idx(i) = idx(i) + 1
For j = i To m - 1
idx(j) = idx(i) + j - i
Next j
Loop
Next
' Return Result to sheet
Dim rng As Range
ReDim Preserve Result(1 To 3, 1 To iRes)
' *** Adjust returnm range to suit
Set rng = [E10].Resize(UBound(Result, 2), UBound(Result, 1))
rng = Application.Transpose(Result)
End Sub

Resources