Wednesday, October 29, 2014

[mLite] 99 bottles of beer

99 bottles of beer is often the first thing I try to do in a (new for me) programming language. Here is an mLite implementation (which can also be found now on RosettaCode.)
val NL = implode [#"newline"]

fun itone 1 = "it" 
        | n = "one"

fun plural (s, 0) = ("no " @ s @ "s") 
                    | (s, 1) = ("1 " @ s) 
                    | (s, n) = (ntos n @ " " @ s @ "s")

fun verse 0 = "no bottles of beer on the wall" @ NL @ 
              "no bottles of beer" @ NL @ 
              "go to the store and buy some more" @ NL @ 
              "99 bottles of beer on the wall" @ NL @ NL 
        | x = plural ("bottle",x) @ " of beer on the wall" @ NL @ 
              plural ("bottle",x) @ " of beer" @ NL @ 
              "take " @ (itone x) @ " down and pass it round" @ NL @ 
              plural ("bottle", (x-1)) @ " of beer on the wall" @ NL @ NL

fun bottles x = map (print o verse) (rev (0 :: iota (1, x)))

fun default (false, y) = y | (x, _) = x

;
bottles (ston (default (argv 0, "99")))
This code allows one to specify how many bottles on the command line (e.g.
mlite -f 99bob.m 4
), defaulting to the usual 99.

© Copyright Bruce M. Axtens, 2014

Tuesday, October 28, 2014

[mLite] Learning an ML dialect

I've tried to get my head into functional languages for some time. This time I'm having a go at mLite, which the author, Nils M Holm, describes as "a lightweight (and slightly odd) inhabitant of the ML universe ... Much like ML, but with dynamic typingguards, and a Haskell-style apply operator."

I've created a presence on RosettaCode for mLite and solved the Ackermann function challenge.

© Bruce M. Axtens, 2014.

Saturday, March 08, 2014

[TCL] Love that Tool Command Language

TCL is just so cool and concise …
cd CSV
set target "snap-[clock format [clock add [clock seconds] -1 hours] -format %Y-%m-%d-%H -gmt 1]"
if {![file exists $target.rar]} then {
    set cmd [list c:/program\ files/winrar/rar.exe m -m5 -s ${target}.rar ${target}-utc.csv]
        exec -- {*}$cmd
}
That goes to the log (called CSV) folder and compresses the previous hour’s worth of log. I run that from a scheduler.

I really like the conciseness of Tcl: clock add [clock seconds] -1 hours, which subtracts an hour from the current time and clock format which takes that and renders it as GMT.

© Copyright Bruce M. Axtens, 2014

Thursday, February 27, 2014

[VBScript] Boosting myself to HIGH process priority

The script gets its own name, then looks through the Win32_Processes for a match with CSCRIPT.EXE and a CommandLine containing that name. Any matches (should be only one, but could be more) are boosted to HIGH process priority.

This demonstrates WMI calls from both VBScript and JScript. Note the need to explicitly define an Enumerator in the JScript version.
Option Explicit

Dim sName
Dim sComputer
Dim oWMI
Dim cProcesses
Dim oProcess

Const HIGH = 256
sComputer = "."
Set oWMI = GetObject("winmgmts:\\" & sComputer & "\root\cimv2")
sName = WScript.ScriptName

Set cProcesses = oWMI.ExecQuery("Select * from Win32_Process Where Name = 'cscript.exe' And CommandLine LIKE '%" & sName & "%'")
For Each oProcess In cProcesses
  oProcess.SetPriority(HIGH) 
  WScript.Echo "Boosted myself"
Next
And in JScript
var HIGH = 256;
var sComputer = ".";
var sName = WScript.ScriptName;    

var query = GetObject("winmgmts:\\\\" + sComputer + "\\root\\cimv2")
    .ExecQuery("Select * from Win32_Process " + 
    "Where Name = 'cscript.exe' And CommandLine LIKE '%" + sName + "%'")

// Enumerate WMI objects
var cProcesses = new Enumerator(query);

for ( ; !cProcesses.atEnd(); cProcesses.moveNext()) { 
    var oProcess = cProcesses.item()
    oProcess.Priority = HIGH
    WScript.Echo( "Boosted myself")
}
© Copyright Bruce M. Axtens, 2014

Tuesday, February 25, 2014

[VBScript] How can I display 64 bit double number using VBScript on 32 Bit OS?

"How can I display 64 bit double number using VBScript on 32 Bit OS?" That was the question on StackOverflow back at the end of 2012. And nobody had answered it. What a challenge!

I assumed a pure-VBScript solution was needed, and set about finding and implementing a Very Large Integer class with which I could then implement a Hex64 to integer function. A workable candidate function was found on Rosetta Code in the
Liberty BASIC solution to the Long Multiplication task.

The code for the class is as follows. It's pretty much the same as the original.

Option Explicit
Class VeryLongInteger
  'http://rosettacode.org/wiki/Long_Multiplication#Liberty_BASIC
  Public Function MULTIPLY(Str_A, Str_B)
    Dim signA, signB, sResult, Str_Shift, i, d, Str_T
    signA = 1
    If Left(Str_A,1) = "-" Then 
      Str_A = Mid(Str_A,2)
      signA = -1
    End If
    signB = 1
    If Left(Str_B,1) = "-" Then 
      Str_B = Mid(Str_B,2)
      signB = -1
    End If
    sResult = vbNullString
    Str_T = vbNullString
    Str_shift = vbNullString
    For i = Len(Str_A) To 1 Step -1
      d = CInt(Mid(Str_A,i,1))
      Str_T = MULTBYDIGIT(Str_B, d)
      sResult = ADD(sResult, Str_T & Str_shift)
      Str_shift = Str_shift & "0"
      'print d, Str_T, sResult 
    Next
    If signA * signB < 0 Then sResult = "-" + sResult
    'print sResult
    MULTIPLY = sResult
  End Function
  
  Private Function MULTBYDIGIT(Str_A, d)
    Dim sResult, carry, i, a, c
    'multiply Str_A by digit d
    sResult = vbNullString
    carry = 0
    For i = Len(Str_A) To 1 Step -1
      a = CInt(Mid(Str_A,i,1))
      c = a * d + carry
      carry = c \ 10
      c = c Mod 10
      'print a, c
      sResult = CStr(c) & sResult 
    Next
    If carry > 0 Then sResult = CStr(carry) & sResult
    'print sResult
    MULTBYDIGIT = sResult
  End Function
  
  Public Function ADD(Str_A, Str_B)
    Dim L, sResult, carry, i, a, b, c
    'add Str_A + Str_B, for now only positive
    l = MAX(Len(Str_A), Len(Str_B))
    Str_A=PAD(Str_A,l)
    Str_B=PAD(Str_B,l)
    sResult = vbNullString 'result
    carry = 0
    For i = l To 1 Step -1
      a = CInt(Mid(Str_A,i,1))
      b = CInt(Mid(Str_B,i,1))
      c = a + b + carry
      carry = Int(c/10)
      c = c Mod 10
      'print a, b, c
      sResult = CStr(c) & sResult
    Next
    If carry>0 Then sResult = CStr(carry) & sResult
    'print sResult
    ADD = sResult
  End Function
  
  Private Function Max(a,b)
    If a > b Then
      Max = a
    Else
      Max = b
    End If
  End Function
  
  Private Function pad(a,n)  'pad from right with 0 to length n
    Dim sResult
    sResult = a
    While Len(sResult) < n
      sResult = "0" & sResult
    Wend
    pad = sResult
  End Function
End Class
With that defined I have now all I need to implement a Hex64 function. This I did in two forms:

* A memoized version which precomputes all the relevant powers of 16

Function PrecomputedFromHex64(sHex)
  Dim VLI
  Set VLI = New VeryLongInteger
  
  Dim Sixteen(16)
  Sixteen(0) = "1"
  Sixteen(1) = "16"
  Sixteen(2) = VLI.MULTIPLY(Sixteen(1),"16")
  Sixteen(3) = VLI.MULTIPLY(Sixteen(2),"16")
  Sixteen(4) = VLI.MULTIPLY(Sixteen(3),"16")
  Sixteen(5) = VLI.MULTIPLY(Sixteen(4),"16")
  Sixteen(6) = VLI.MULTIPLY(Sixteen(5),"16")
  Sixteen(7) = VLI.MULTIPLY(Sixteen(6),"16")
  Sixteen(8) = VLI.MULTIPLY(Sixteen(7),"16")
  Sixteen(9) = VLI.MULTIPLY(Sixteen(8),"16")
  Sixteen(10) = VLI.MULTIPLY(Sixteen(9),"16")
  Sixteen(11) = VLI.MULTIPLY(Sixteen(10),"16")
  Sixteen(12) = VLI.MULTIPLY(Sixteen(11),"16")
  Sixteen(13) = VLI.MULTIPLY(Sixteen(12),"16")
  Sixteen(14) = VLI.MULTIPLY(Sixteen(13),"16")
  Sixteen(15) = VLI.MULTIPLY(Sixteen(14),"16")
  
  Dim theAnswer, i, theDigit, theMultiplier, thePower, aPower
  theAnswer = "0"
  aPower = 0
  For i = Len(sHex) To 1 Step -1
    theDigit = UCase(Mid(sHex,i,1))
    theMultiplier = InStr("0123456789ABCDEF",theDigit)-1
    thePower = Sixteen(aPower)
    thePower = VLI.MULTIPLY(CStr(theMultiplier),thePower)
    theAnswer = VLI.ADD(theAnswer,thePower )
    aPower = aPower + 1
  Next
  PrecomputedFromHex64 = theAnswer
End Function
* A non-memoized version which computes the relevant power when it is needed.
Function FromHex64(sHex)
  Dim VLI
  Set VLI = New VeryLongInteger       
  Dim theAnswer, i, theDigit, theMultiplier, thePower, aPower
  theAnswer = "0"
  thePower = "1"
  For i = Len(sHex) To 1 Step -1
    theDigit = UCase(Mid(sHex,i,1))
    theMultiplier = InStr("0123456789ABCDEF",theDigit)-1
    theAnswer = VLI.ADD(theAnswer,VLI.MULTIPLY(thePower,theMultiplier))
    thePower = VLI.MULTIPLY(thePower,"16")
  Next
  FromHex64 = theAnswer
End Function
The memoized version is slightly faster than the non-memoized, despite the overhead of the memoization. If the class/function pair were to be used a lot in a script, one might consider making both the VLI instantiation and the Sixteen() array global and precomputing the array at the beginning of the script rather than on each invocation.

A rough test of the code follows:

Const test = "FFFFFFFFFFFFFFFF" '"41417724EBA8953E"
Dim t, I, S
t=Timer
For I = 1 To 100
  S = FromHex64(test)
Next
WScript.Echo "No memoization", Timer-t

t=Timer
For I = 1 To 100
  S = PrecomputedFromHex64(test)
Next
WScript.Echo "Memoized each time",Timer-t

Function GlobalMemoFromHex64(sHex)  
  Dim theAnswer, i, theDigit, theMultiplier, thePower, aPower
  theAnswer = "0"
  aPower = 0
  For i = Len(sHex) To 1 Step -1
    theDigit = UCase(Mid(sHex,i,1))
    theMultiplier = InStr("0123456789ABCDEF",theDigit)-1
    thePower = Sixteen(aPower)
    thePower = VLI.MULTIPLY(CStr(theMultiplier),thePower)
    theAnswer = VLI.ADD(theAnswer,thePower )
    aPower = aPower + 1
  Next
  GlobalMemoFromHex64 = theAnswer
End Function

Dim VLI
Set VLI = New VeryLongInteger

Dim Sixteen(16)
Sixteen(0) = "1"
Sixteen(1) = "16"
Sixteen(2) = VLI.MULTIPLY(Sixteen(1),"16")
Sixteen(3) = VLI.MULTIPLY(Sixteen(2),"16")
Sixteen(4) = VLI.MULTIPLY(Sixteen(3),"16")
Sixteen(5) = VLI.MULTIPLY(Sixteen(4),"16")
Sixteen(6) = VLI.MULTIPLY(Sixteen(5),"16")
Sixteen(7) = VLI.MULTIPLY(Sixteen(6),"16")
Sixteen(8) = VLI.MULTIPLY(Sixteen(7),"16")
Sixteen(9) = VLI.MULTIPLY(Sixteen(8),"16")
Sixteen(10) = VLI.MULTIPLY(Sixteen(9),"16")
Sixteen(11) = VLI.MULTIPLY(Sixteen(10),"16")
Sixteen(12) = VLI.MULTIPLY(Sixteen(11),"16")
Sixteen(13) = VLI.MULTIPLY(Sixteen(12),"16")
Sixteen(14) = VLI.MULTIPLY(Sixteen(13),"16")
Sixteen(15) = VLI.MULTIPLY(Sixteen(14),"16")

t=Timer
For I = 1 To 100
  S = GlobalMemoFromHex64(test)
Next
WScript.Echo "Global memo",Timer-t
Running the above in VBSEdit a few times under varying conditions gave the following results:
No memoization 2.632813
Memoized each time 1.023438
Global memo 0.328125

No memoization 1.667969
Memoized each time 0.8867188
Global memo 0.2695313

No memoization 1.488281
Memoized each time 0.9335938
Global memo 0.3046875
The global precomputation of the memo array is fastest technique, but if you're only using the function once you'll get by with the non-memo version.

Further optimisations are possible. Compilation, of course, would make it even faster.

Enjoy!
© Copyright Bruce M. Axtens, 2014