Pizza Day

There’s a nifty number $\theta$ in the following equation $$p_n=\lfloor\theta^{3^n}\rfloor$$ where $p_n$ is a prime number and $n$, as usual, is the sequence of natural numbers (0 doesn’t work—the result is 1, which is not prime). $\theta$ in fact represents the smallest such number, and is approximately equal to 1.3063778838630806904686. This number is known as Mills’ constant, and the prime numbers that it generates are Mills’ primes. The difficulty is, though, that these numbers grow very, very rapidly. The first is $2$, the second $11$, then $1361$, $2521008887$, and each number after that roughly triples in the number of digits it contains.

So how would one go about finding the value of $\theta$? Through some simple rearranging, $$\theta=exp(\frac{\log{p_n}}{3^n})$$, but you have to know the “largest” prime number in this (by definition infinite) sequence to get more decimals of theta.

Okay, so given the sequence, how many digits of $\theta$ are known from each Mills’ prime?

Mills' PrimePrime DigitsMills' ConstantConstant Digits (correct after decimal point)
211.259...0
1121.30529...2
136141.3063778838246...10
2521008887101.306377883863080690468614492575...27
p528~87
p684~255
p7253~763
skip a few...
p1261683~185051
p13185051~555153
p14555153~1665460

So since the 14th Mills’ prime is known with a fairly high degree of confidence, nearly 1665460 digits of Mills’ constant are known. Conservatively, one would chop off a few digits to be sure of the numbers’ accuracy.

Oddly, however, this detail is not included in the OEIS (related sequences A051254, A051021, A224845, A108739).

The last of the related sequences listed above is a useful one for Mills’ primes, because the sequence of [Mills’] primes can be written far more succinctly with these numbers:

$$p_n=p_{n-1}^3+b_{n-1}$$, where $b_n$ is the nth number in A108739.

In other words

$$p_2=p_1^3+b_1$$
$$11=2^3+3$$

The sequence $b_n$ I’ll reproduce here:

$$[3, 30, 6, 80, 12, 450, 894, 3636, 70756, 97220, 66768, 300840, 1623568]$$

I’d like to find $p_{15}$, but since it’s likely 1665460 digits in length, it takes a long time to test compositeness using even one witness with Miller-Rabin, much less the several it would probably take. And forget factoring it! The biggest number I’ve managed to factor on my home computer is a mere 133 digits long, and that took 4 days (it’s now crunching on a 126 digit number, and may have that cracked by the time I get home).

Speaking of which, the sequence I’m working on is the aliquot sequence starting with 499080. My contributions began with entry 2040.

Now I need to figure out why $$\sum_{n=1}^{\infty} n=-\frac{1}{12}$$. I’m still not convinced…

Finally, a Numberphile video on Mills’ constant:

Colonel, Panic!

So the old way of reading and writing to the registry from VBA and VB Scripts is obsolete. Sometimes it still works, but there’s a new way. Unfortunately, it’s a bit more complicated than it used to be.

The old way:

Function RegRead(RegKey As String) As String
Dim ws As Object
    On Error Resume Next
    Set ws = CreateObject("WScript.Shell")
    RegRead = ws.RegRead(RegKey)
End Function

Short, simple, sweet. The above version does have the downside of being limited (as far as I can tell) to REG_SZ and REG_EXPAND_SZ values, but hey, that’s most of what anyone (sane) will mess with.

I, on the other hand, need to not only “mess with”, but modify REG_DWORD and (gasp) REG_BINARY values. Step one, though, is reading them. After much hunting through the Internet’s ancient and not-so-ancient forums and Microsoft’s sometimes appalling (lack of) documentation, here’s the new function:

Public Const HKCR = &H80000000
Public Const HKCU = &H80000001
Public Const HKLM = &H80000002
Public Const HKU = &H80000003
Public Const HKCC = &H80000005
Public Const REG_SZ = 1
Public Const REG_EXPAND_SZ = 2
Public Const REG_BINARY = 3
Public Const REG_DWORD = 4
Public Const REG_MULTI_SZ = 7
Public Const REG_QWORD = 11
Public Function RegRead(hDefKey As Long, sSubKeyName As String, sValueName As String) As Variant
    Dim oReg, sValue, arrValueTypes, i, regtype, arrValueNames
    Dim castValueNames() As String
    Set oReg = GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\default:StdRegProv")
    oReg.EnumValues hDefKey, sSubKeyName, arrValueNames, arrValueTypes
    ReDim castValueNames(UBound(arrValueNames))
    For i = 0 To UBound(arrValueNames)
        castValueNames(i) = arrValueNames(i)
    Next
    regtype = arrValueTypes(InArray(sValueName, castValueNames))
    Select Case regtype
        Case REG_SZ
            oReg.GetStringValue hDefKey, sSubKeyName, sValueName, sValue
        Case REG_EXPAND_SZ
            oReg.GetExpandedStringValue hDefKey, sSubKeyName, sValueName, sValue
        Case REG_BINARY
            oReg.GetBinaryValue hDefKey, sSubKeyName, sValueName, sValue
        Case REG_DWORD
            oReg.GetDWORDValue hDefKey, sSubKeyName, sValueName, sValue
        Case REG_MULTI_SZ
            oReg.GetMultiStringValue hDefKey, sSubKeyName, sValueName, sValue
        Case REG_QWORD
            oReg.GetQWORDValue hDefKey, sSubKeyName, sValueName, sValue
        Case -1
            Set sValue = Nothing
    End Select
    RegRead = sValue
End Function
Function InArray(value As Variant, ByRef arr() As String) As Long
    Dim i As Long
    For i = LBound(arr) To UBound(arr)
        If arr(i) = value Then
            InArray = i
            Exit For
        End If
        InArray = -1
    Next
End Function

The marked lines are important. When one reads a value from the registry, one (typically) wants only that value. Line 16 enumerates all values in a subkey, but I’m only looking for one, so I needed a way to get at that one. So I need to find out at what index of the array arrValueNames  contains sValueName  (if any—this will also tell me, in a way, if the value I’m looking for doesn’t exist).

The reason I have to do this in the first place is that I can’t use the functions given to determine the type of a single entry, only all of them, and only with the EnumValues  method. Work with what you’ve got…

I already had an InArray  function. For that I originaly wanted to have both parameters as Variant s, but VBA doesn’t seem to like to pass Variant  arrays around. But it would still work for my purposes. Unfortunately, I couldn’t pass arrValueNames  directly—I had to “cast” it to a String  array, castValueNames , which, lacking certain tools of more robust languages, requires lines 17-20. Without this step, Word (I’m writing this VBA application in MS Word 2007) crashes reliably on accessing a null string, and those are everywhere in the registry.

The result of all this is that I can finally read basically any value I wish! The value returned from reading REG_BINARY and REG_MULTI_SZ values are arrays, so I have to deal with those appropriately, but this routine gets me everything I need from the registry.

Of course, this is only half the process. The next step is writing—getting everything I need back to the registry. And this is a lot easier, too.

Public Function RegWrite(hDefKey As Long, sSubKeyName As String, sValueName As String, sValue As Variant, regtype As Integer) As Long
    Dim oReg, sValue, arrValueTypes, i, regtype, arrValueNames
    Set oReg = GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\default:StdRegProv")
    Select Case regtype
        Case REG_SZ
            oReg.SetStringValue hDefKey, sSubKeyName, sValueName, sValue
        Case REG_EXPAND_SZ
            oReg.SetExpandedStringValue hDefKey, sSubKeyName, sValueName, sValue
        Case REG_BINARY
            oReg.SetBinaryValue hDefKey, sSubKeyName, sValueName, sValue
        Case REG_DWORD
            oReg.SetDWORDValue hDefKey, sSubKeyName, sValueName, sValue
        Case REG_MULTI_SZ
            oReg.SetMultiStringValue hDefKey, sSubKeyName, sValueName, sValue
        Case REG_QWORD
            oReg.SetQWORDValue hDefKey, sSubKeyName, sValueName, sValue
        Case -1
            Set sValue = Nothing
    End Select
End Function

Unfortunately, another wrinkle enters the picture: permissions. Unless you’re running with administrative permissions, unless UAC is disabled, or unless you’re writing to a key to which the user has permission to modify, you’re out of luck again. And here is where I abandoned my quest, because futzing with permissions in the environment I’m in (work) is a non-starter.

MS needs to make a better interface with the Win 7+ jumplists. I could copy and rewrite the files in %AppData%\Microsoft\Windows\Recent\AutomaticDestinations, and maybe I could even find out which one is the one I’m concerned about, but I really am not a fan of that idea. Unfortunately, the API doesn’t include removing items. All the stuff you can do with the taskbar is cool, even with jumplists, and I may use that in the future, but it’s not useful to me right now. There’s an event if the user removes an item, but I haven’t figured out how to say that I’m programmatically removing an item, while allowing that item to be added to the jumplist later.