# Matthew van Eerde's web log

• #### Grabbing large amounts of text from STDIN in O(n) time

Last time I blogged about an O(n log n) solution to finding the longest duplicated substring in a given piece of text; I have since found an O(n) algorithm, which I linked to in the comments.

But my blog post used an O(n2) algorithm to read the text from STDIN! It looked something like this:

while (!done) {
grab 2 KB of text
allocate a new buffer which is 2 KB bigger
copy the old text and the new text together into the new buffer
free the old buffer
}

There are two better algorithms:

while (!done) {
grab an amount of text equal to the amount we've grabbed so far
allocate a new buffer which is twice as large as the last buffer
copy the old text and the new text together into the new buffer
free the old buffer
}

And:

while (!done) {
grab 2 KB of text
add this to the end of a linked list of text chunks
}

allocate a buffer whose size is the total size of all the chunks added together
walk the linked list and copy the text of each chunk into the buffer

Both "better" algorithms are O(n) but the latter wastes less space.

Here's the improved code:

struct Chunk {
WCHAR text[1024];
Chunk *next;

Chunk() : next(nullptr) { text[0] = L'\0'; }
};

class DeleteChunksOnExit {
public:
DeleteChunksOnExit() : m_p(nullptr) {}
~DeleteChunksOnExit() {
Chunk *here = m_p;
while (here) {
Chunk *next = here->next;
delete here;
here = next;
}
}
void Set(Chunk *p) { m_p = p; }

private:
Chunk *m_p;
};

...

Chunk *tail = nullptr;

DeleteChunksOnExit dcoe;

size_t total_length = 0;

bool done = false;
while (!done) {
Chunk *buffer = new Chunk();
if (nullptr == buffer) {
LOG(L"Could not allocate memory for buffer");
return nullptr;
}

// this runs on the first pass only
tail = buffer;
dcoe.Set(buffer);
} else {
tail->next = buffer;
tail = buffer;
}

if (fgetws(buffer->text, ARRAYSIZE(buffer->text), stdin)) {
total_length += wcslen(buffer->text);
} else if (feof(stdin)) {
done = true;
} else {
return nullptr;
}
}

// gather all the allocations into a single string
size_t size = total_length + 1;
WCHAR *text = new WCHAR[size];
if (nullptr == text) {
LOG(L"Could not allocate memory for text");
return nullptr;
}
DeleteArrayOnExit<WCHAR> deleteText(text);

WCHAR *temp = text;
for (Chunk *here = head; here; here = here->next) {
if (wcscpy_s(temp, size, here->text)) {
LOG(L"wcscpy_s returned an error");
return nullptr;
}

size_t len = wcslen(here->text);
temp += len;
size -= len;
}

deleteText.Cancel();
return text;
}

• #### Finding the longest substring which occurs twice in a given string

I'm reading Jon Bentley's Programming Pearls and one of the interesting exercises was to find the longest substring which occurs twice in a given string of length n.

There's a naïve solution where you look at every pair of (distinct) indexes (i, j), and calculate the length of the common prefix of the substrings starting at those locations; this is O(n2) assuming that the length of the eventual maximum substring is O(1) (that is, << n.)

Jon shows that there is an O(n log n) algorithm, which is a significant savings if n is large (e.g., War and Peace.)  This involves building an array of all substrings, then sorting them lexically, then walking the sorted array; the trick is that now we only need to check pairs of indexes that correspond to adjacent entries in the array.  That step can be done in O(n) time; the O(n log n) comes from the sorting step.

I wrote up a quick app that implements his suggested algorithm.  Source follows.

// main.cpp

#include <windows.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <search.h>

#define LOG(format, ...) wprintf(format L"\n", __VA_ARGS__)

template<typename T>
class DeleteArrayOnExit {
public:
DeleteArrayOnExit(T *p) : m_p(p), m_canceled(false) {}
~DeleteArrayOnExit() { if (!m_canceled) { delete [] m_p; } }
void Cancel() { m_canceled = true; }
void Swap(T *p) { m_p = p; }
private:
T *m_p;
bool m_canceled;
};

// caller must delete [] the buffer when done with it
int __cdecl pwcscmp(const void *pstra, const void *pstrb) {
return wcscmp(*(LPWSTR*)pstra, *(LPWSTR*)pstrb);
}

// returns length of longest common substring
// don't include the null terminator if both strings are identical
// e.g., comlen(banana, bananas) == comlen(banana, banana) == 6
int comlen(LPCWSTR a, LPCWSTR b) {
int i = 0;

// keep going while the strings are the same and not ended
while (a[i] && (a[i] == b[i])) { i++; }

return i;
}

int _cdecl wmain() {
if (nullptr == text) {
return -__LINE__;
}
DeleteArrayOnExit<WCHAR> deleteText(text);

size_t size = wcslen(text) + 1;
LPWSTR *suffixes = new LPWSTR[size];
if (nullptr == suffixes) {
LOG(L"Could not allocate memory for suffixes");
return -__LINE__;
}
DeleteArrayOnExit<LPWSTR> deleteSuffixes(suffixes);

for (size_t i = 0; i < size; i++) {
suffixes[i] = &text[i];
}

qsort(suffixes, size, sizeof(LPWSTR), pwcscmp);

// find the longest common adjacent pair
LPWSTR szMax = suffixes[0];
size_t lenMax = 0;
for (size_t i = 0; i < size - 1; i++) {
size_t len = comlen(suffixes[i], suffixes[i + 1]);
if (len > lenMax) {
lenMax = len;
szMax = suffixes[i];
}
}

WCHAR *substring = new WCHAR[lenMax + 1];
if (nullptr == substring) {
LOG(L"Could not allocate memory for substring");
return -__LINE__;
}
DeleteArrayOnExit<WCHAR> deleteSubstring(substring);
if (0 != wcsncpy_s(substring, lenMax + 1, szMax, lenMax)) {
LOG(L"wcsncpy_s failed");
return -__LINE__;
}
substring[lenMax] = L'\0';

// intentionally not using LOG to avoid trailing newline
wprintf(L"%s", substring);

return 0;
}

WCHAR *text = new WCHAR[1];
if (nullptr == text) {
LOG(L"Could not allocate memory for text");
return nullptr;
}
DeleteArrayOnExit<WCHAR> deleteText(text);
text[0] = L'\0';

// read a 2 KB chunk
const size_t buffer_size = 1024;
WCHAR *buffer = new WCHAR[buffer_size];
if (nullptr == buffer) {
LOG(L"Could not allocate memory for buffer");
return nullptr;
}
DeleteArrayOnExit<WCHAR> deleteBuffer(buffer);

bool done = false;
do {
if (fgetws(buffer, buffer_size, stdin)) {
size_t size = wcslen(text) + wcslen(buffer) + 1;
WCHAR *new_text = new WCHAR[size];
if (nullptr == new_text) {
LOG(L"Could not allocate memory for new text");
return nullptr;
}
DeleteArrayOnExit<WCHAR> deleteNewText(new_text);

WCHAR *dest = new_text;

if (0 != wcscpy_s(dest, size, text)) {
LOG(L"wcscpy_s failed");
return nullptr;
}
dest += wcslen(text);
size -= wcslen(text);

if (0 != wcscpy_s(dest, size, buffer)) {
LOG(L"wcscpy_s failed");
return nullptr;
}

// that should do it for copying
// now swap new_text => text

delete [] text;
text = new_text;

deleteText.Swap(new_text);
deleteNewText.Cancel();

} else if (feof(stdin)) {
done = true;
} else {
return nullptr;
}
} while (!done);

deleteText.Cancel();
return text;
}

• #### Enumerating mixer devices, mixer lines, and mixer controls

The WinMM multimedia APIs include an API for enumerating and controlling all the paths through the audio device; things like bass boost, treble control, pass-through audio from your CD player to your headphones, etc.  This is called the "mixer" API and is the forerunner of the IDeviceTopology API.

I wrote a quick app to enumerate all the mixer devices on the system; for each mixer device, enumerate each mixer line (that is, each source and destination); for each mixer line, enumerate all the controls (volume, mute, equalization, etc.); and for each control, query the associated text (if any) and the current value.

Source and binaries attached.

Pseudocode:

mixerGetNumDevs()
for each mixer device
mixerGetDevCaps(dev)
for each destination (line) on the device
mixerGetLineInfo(dest)
mixerGetLineControls(dest)
for each control on the line
if the control supports per-item description
mixerGetControlDetails(control, MIXER_GETCONTROLDETAILSF_LISTTEXT)
log the per-item description
mixerGetControlDetails(control, MIXER_GETCONTROLDETAILSF_VALUE)
log the value(s)

Usage:

>mixerenum.exe
Mixer devices: 5
Device ID: 0
Manufacturer identifier: 1
Product identifier: 104
Driver version: 6.2
Support: 0x0
Destinations: 1
-- Destination 0: Master Volume --
Destination: 0
Source: -1
Line ID: 0xffff0000
Status: MIXERLINE_LINEF_ACTIVE (1)
User: 0x00000000
Channels: 1
Connections: 2
Controls: 2
Short name: Volume
Long name: Master Volume
-- Target:  --
Type: MIXERLINE_TARGETTYPE_UNDEFINED (0)
Device ID: 0
Manufacturer identifier: 65535
Product identifier: 65535
Driver version: 0.0
Product name:
-- Control 1: Mute --
Type: MIXERCONTROL_CONTROLTYPE_MUTE (0x20010002)
Status: MIXERCONTROL_CONTROLF_UNIFORM (0x1)
Item count: 0
Short name: Mute
Long name: Mute
-- Values --
FALSE
-- Control 2: Volume --
Type: MIXERCONTROL_CONTROLTYPE_VOLUME (0x50030001)
Status: MIXERCONTROL_CONTROLF_UNIFORM (0x1)
Item count: 0
Short name: Volume
Long name: Volume
-- Values --
0xffff on a scale of 0x0 to 0xffff
-- 1: HDMI Audio (Contoso --
Device ID: 1
Manufacturer identifier: 1
Product identifier: 104
Driver version: 6.2
Product name: HDMI Audio (Contoso
Support: 0x0
Destinations: 1
-- Destination 0: Master Volume --
Destination: 0
Source: -1
Line ID: 0xffff0000
Status: MIXERLINE_LINEF_ACTIVE (1)
User: 0x00000000
Component Type: MIXERLINE_COMPONENTTYPE_DST_DIGITAL (1)
Channels: 1
Connections: 2
Controls: 2
Short name: Volume
Long name: Master Volume
-- Target:  --
Type: MIXERLINE_TARGETTYPE_UNDEFINED (0)
Device ID: 0
Manufacturer identifier: 65535
Product identifier: 65535
Driver version: 0.0
Product name:
-- Control 1: Mute --
Type: MIXERCONTROL_CONTROLTYPE_MUTE (0x20010002)
Status: MIXERCONTROL_CONTROLF_UNIFORM (0x1)
Item count: 0
Short name: Mute
Long name: Mute
-- Values --
FALSE
-- Control 2: Volume --
Type: MIXERCONTROL_CONTROLTYPE_VOLUME (0x50030001)
Status: MIXERCONTROL_CONTROLF_UNIFORM (0x1)
Item count: 0
Short name: Volume
Long name: Volume
-- Values --
0xffff on a scale of 0x0 to 0xffff
-- 2: Speakers (Contoso --
Device ID: 2
Manufacturer identifier: 1
Product identifier: 104
Driver version: 6.2
Product name: Speakers (Contoso
Support: 0x0
Destinations: 1
-- Destination 0: Master Volume --
Destination: 0
Source: -1
Line ID: 0xffff0000
Status: MIXERLINE_LINEF_ACTIVE (1)
User: 0x00000000
Component Type: MIXERLINE_COMPONENTTYPE_DST_SPEAKERS (4)
Channels: 1
Connections: 2
Controls: 2
Short name: Volume
Long name: Master Volume
-- Target:  --
Type: MIXERLINE_TARGETTYPE_UNDEFINED (0)
Device ID: 0
Manufacturer identifier: 65535
Product identifier: 65535
Driver version: 0.0
Product name:
-- Control 1: Mute --
Type: MIXERCONTROL_CONTROLTYPE_MUTE (0x20010002)
Status: MIXERCONTROL_CONTROLF_UNIFORM (0x1)
Item count: 0
Short name: Mute
Long name: Mute
-- Values --
FALSE
-- Control 2: Volume --
Type: MIXERCONTROL_CONTROLTYPE_VOLUME (0x50030001)
Status: MIXERCONTROL_CONTROLF_UNIFORM (0x1)
Item count: 0
Short name: Volume
Long name: Volume
-- Values --
0xffff on a scale of 0x0 to 0xffff
Device ID: 3
Manufacturer identifier: 1
Product identifier: 104
Driver version: 6.2
Support: 0x0
Destinations: 1
-- Destination 0: Master Volume --
Destination: 0
Source: -1
Line ID: 0xffff0000
Status: MIXERLINE_LINEF_ACTIVE (1)
User: 0x00000000
Component Type: MIXERLINE_COMPONENTTYPE_DST_WAVEIN (7)
Channels: 1
Connections: 1
Controls: 2
Short name: Volume
Long name: Master Volume
Type: MIXERLINE_TARGETTYPE_WAVEIN (2)
Device ID: 0
Manufacturer identifier: 1
Product identifier: 101
Driver version: 6.2
-- Control 1: Mute --
Type: MIXERCONTROL_CONTROLTYPE_MUTE (0x20010002)
Status: MIXERCONTROL_CONTROLF_UNIFORM (0x1)
Item count: 0
Short name: Mute
Long name: Mute
-- Values --
FALSE
-- Control 2: Volume --
Type: MIXERCONTROL_CONTROLTYPE_VOLUME (0x50030001)
Status: MIXERCONTROL_CONTROLF_UNIFORM (0x1)
Item count: 0
Short name: Volume
Long name: Volume
-- Values --
0xf332 on a scale of 0x0 to 0xffff
-- 4: Microphone (Contoso --
Device ID: 4
Manufacturer identifier: 1
Product identifier: 104
Driver version: 6.2
Product name: Microphone (Contoso
Support: 0x0
Destinations: 1
-- Destination 0: Master Volume --
Destination: 0
Source: -1
Line ID: 0xffff0000
Status: MIXERLINE_LINEF_ACTIVE (1)
User: 0x00000000
Component Type: MIXERLINE_COMPONENTTYPE_DST_WAVEIN (7)
Channels: 1
Connections: 1
Controls: 2
Short name: Volume
Long name: Master Volume
-- Target: Microphone (Contoso --
Type: MIXERLINE_TARGETTYPE_WAVEIN (2)
Device ID: 1
Manufacturer identifier: 1
Product identifier: 101
Driver version: 6.2
Product name: Microphone (Contoso
-- Control 1: Mute --
Type: MIXERCONTROL_CONTROLTYPE_MUTE (0x20010002)
Status: MIXERCONTROL_CONTROLF_UNIFORM (0x1)
Item count: 0
Short name: Mute
Long name: Mute
-- Values --
FALSE
-- Control 2: Volume --
Type: MIXERCONTROL_CONTROLTYPE_VOLUME (0x50030001)
Status: MIXERCONTROL_CONTROLF_UNIFORM (0x1)
Item count: 0
Short name: Volume
Long name: Volume
-- Values --
0xf332 on a scale of 0x0 to 0xffff

• #### Enumerating MIDI devices

In addition to audio playback and recording, Windows Multimedia (WinMM) provides a Musical Instrument Digital Interface (MIDI) API.

Here's how to make a list of all the MIDI devices on the system, their capabilities, and the hardware device interface associated with each of them.

Source and binaries attached.

Pseudocode:

midiInGetNumDevs or midiOutGetNumDevs
for each device
midiInGetDevCaps or midiOutGetDevCaps
log device capabilities
midiInMessage or midiOutMessage
with DRV_QUERYDEVICEINTERFACESIZE
and DRV_QUERYDEVICEINTERFACE
log the device interface

Output:

>midienum.exe
midiIn devices: 1
-- 0: USB2.0 MIDI Device --
Device ID: 0
Manufacturer identifier: 65535
Product identifier: 65535
Driver version: 1.6
Product name: USB2.0 MIDI Device
Support: 0x0
Device interface: "\\?\usb#vid_xxxx&pid_yyyy&..."
midiOut devices: 2
-- 0: Microsoft GS Wavetable Synth --
Device ID: 0
Manufacturer identifier: 1
Product identifier: 27
Driver version: 1.0
Product name: Microsoft GS Wavetable Synth
Technology: 7 (MOD_SWSYNTH)
Voices: 32
Notes: 32
Support: 0x1
MIDICAPS_VOLUME
Device interface: ""
-- 1: USB2.0 MIDI Device --
Device ID: 1
Manufacturer identifier: 65535
Product identifier: 65535
Driver version: 1.6
Product name: USB2.0 MIDI Device
Technology: 1 (MOD_MIDIPORT)
Voices: 0
Notes: 0
Support: 0x0
Device interface: "\\?\usb#vid_xxxx&pid_yyyy&..."

(Actual device interface string suppressed.)

Note the Microsoft GS Wavetable Synth device, which is always present.

Why would you want to know the device interface? In our case, because we want to test all the audio-related interfaces of a particular device on the system.

• #### Implementing a "listen" command using ISpRecoContext from the Microsoft Speech API

Earlier today I posted a quick "say.exe" sample app which you give text and it speaks the text aloud using the text-to-speech part of the Windows Speech API.  It was very straightforward - only 67 lines of C++ code.

It took me a little longer to figure out how to do this "listen.exe" sample app; you run it, speak into the microphone, and it uses the speech-to-text part of the Windows Speech API to print what you're saying to the console.  This is a little more involved: 202 lines of C++ code.

Pseudocode:

CoInitialize()
CoCreateInstance(ISpRecoContext)
pSpRecoContext->SetInterest(recognition events only, thanks)
pSpRecoContext->CreateGrammar()
pSpRecoGrammar->SetDictationState(active)
while(...) {
wait for a speech event (or the user to press Enter)
pSpRecoContext->GetEvents()
for each speech event {
make sure SPEVENT.eEventId is SPEI_RECOGNITION
event.lParam is an ISpRecoResult
pSpRecoResult->GetText()
print the text
}
}

Usage:

>listen.exe
Speak into the microphone naturally; I will print what I understand.
Press ENTER to quit.
(At this point you start talking into the microphone. Text shows up here shortly after you say it.)

Source and binaries attached.

• #### Implementing a "say" command using ISpVoice from the Microsoft Speech API

I've known for a while that Microsoft Windows comes with text-to-speech and speech-to-text APIs, which power the Narrator and Speech Recognition features respectively.

This forum post prompted me to mess around with them a little.

I came up with this implementation of a say.exe command which takes a single argument as text, and then uses the ISpVoice text-to-speech API to have the computer speak it aloud.

Source and binaries attached.

Pseudocode:

CoInitialize(nullptr);
CoCreateInstance(ISpVoice)
pSpVoice->Speak(text);

Usage:

• #### Muting all audio outputs with IAudioEndpointVolume

I have a selfhost tool that I use to mute all audio outputs programmatically.

Pseudocode:

IMMDeviceEnumerator::EnumAudioEndpoints
for each device:
IMMDevice::Activate(IAudioEndpointVolume)
IAudioEndpointVolume::SetMute(TRUE)

Source and binaries attached.

• #### Getting audio peak meter values for all active audio sessions

The Windows Vista volume mixer shows a peak meter for the device.  In Windows 7 we added a peak meter for each application.

The audio interface for both is IAudioMeterInformation; I've used this before in my post about the linearity of Windows volume APIs.  This post showed how an application can get the peak meter reading for the device meter.

In Windows 7 we also added APIs to allow applications to get a list of audio sessions.  I wrote up a quick app which shows how to get a list of all audio sessions, filter it down to the active ones (sessions which have an active audio client), and then get a peak meter reading.

>meters.exe
{0.0.0.00000000}.{c05f2f54-7294-422a-bb0d-8d690c365b73}|#%b{917F8618-9C57-4720-9B7C-88CA45FC983B}: 0.215705
Active sessions: 1

{0.0.0.00000000}.{c05f2f54-7294-422a-bb0d-8d690c365b73}|#%b{917F8618-9C57-4720-9B7C-88CA45FC983B} is the session identifier, which should be considered opaque.  0.215705 is the value of the peak meter, ranging from 0 to 1 linearly in amplitude.  If you are populating a visual peak meter with this information, you will need to apply a curve.

Source and binaries attached.  Pseudocode follows:

CoCreate(IMMDeviceEnumerator);
IMMDeviceEnumerator::GetDefaultAudioEndpoint;
IMMDevice::Activate(IAudioSessionManager2);
IAudioSessionManager2::GetSessionEnumerator;
for (each session) {
IAudioSessionEnumerator::GetSession
IAudioSessionControl::GetState
if the state is anything but "active", skip to the next session
QI IAudioSessionControl to IAudioSessionControl2
IAudioSessionControl2::GetSessionIdentifier
QI IAudioSessionControl to IAudioMeterInformation
IAudioMeterInformation::GetPeakValue
Log the session identifier and the peak value
}

• #### Windows Sound test team rowing morale event

Last Friday the Windows Sound test team went kayaking.  We went to the Agua Verde paddle club and kayaked around Union Bay for a while.

Here's the route we took:

More detail:

http://connect.garmin.com/activity/179545084

• #### Programmatically rearranging displays

Most of my test machines and my laptop have a single display; but I have two dev machines which are each connected to two displays.

When I clean install Windows, I sometimes need to rearrange the displays:

Since I clean install Windows frequently, I wrote myself a little C++ app which does this programmatically using EnumDisplayDevices / EnumDisplaySettings / ChangeDisplaySettingsEx.

Source and binaries attached.

Pseudocode:

for (each device returned by EnumDisplayDevices) {

grab the position and the height/width using EnumDisplaySettings

}

calculate the desired position of the secondary monitor

Set it using ChangeDisplaySettingsEx with DM_POSITION

Call as:

>swapmonitors
Moved secondary monitor to (1920, 0)

EDIT: Oops: 0 should be CDS_GLOBAL | CDS_UPDATEREGISTRY, to make the settings apply to all users, and to persist across display resets / reboots

LONG status = ChangeDisplaySettingsEx(
nameSecondary,
&mode,
nullptr, // reserved
CDS_GLOBAL | CDS_UPDATEREGISTRY, // was 0
nullptr // no video parameter
);

• #### Weighing the Sun and the Moon

In an earlier post I mentioned how the Cavendish experiment allowed us to weigh the Earth - to determine the mass of the Earth mE.  Newton knew the acceleration due to gravity on the surface of the Earth and was able to use that to find the product G mE; Cavendish determined G directly, and was thus able to solve for mE.  He would also have been able to find the mass of the sun as follows:

mE aE = G mE mS / rE2

G, rE, and aE = vE2 / rE are known, so we can solve for mS.

But calculating the mass of the moon is trickier.

Once we were able to put a satellite around the moon we could measure its orbital radius and speed, deduce the acceleration, and use that plus the known G to calculate the mass of the moon.  But prior to that we were limited to techniques like:

The moon does not exactly orbit the Earth, but instead orbits the center of mass of the moon/Earth system.  By careful observation we can determine where this center of mass is.  We can then measure the distance between the center of mass and the Earth's center.  This plus the known mass of the Earth and the distance of the Earth from the Moon allows us to determine the mass of the Moon.

If we're lucky enough to see a foreign object come close to the moon, we can determine how much it is accelerated by the Moon.  This will allow us to determine the mass of the Moon using the technique above.  (We won't be able to determine the mass of the foreign object, but we don't need it.)

When the USSR launched Sputnik, American scientists really wanted to know what its mass was.  But because none of the techniques above were useful, they were unable to determine it.

• #### unattend.xml: turning on Remote Desktop automatically

Here are the portions of my unattend.xml file which are needed to turn on Remote Desktop automatically.

This piece flips the "no remote desktop" kill switch to "allow."

<settings pass="specialize">
...
<component name="Microsoft-Windows-TerminalServices-LocalSessionManager" ...>
<fDenyTSConnections>false</fDenyTSConnections>

That's not enough; it is also necessary to poke a hole in the firewall to allow inbound connections.  I use an indirect string for the Group name, to allow for installing localized builds.  This points to the "Remote Desktop" feature group.

<settings pass="specialize">
...
<component name="Networking-MPSSVC-Svc" ...>
<FirewallGroups>
<Active>true</Active>
<Profile>all</Profile>
<Group>@FirewallAPI.dll,-28752</Group>

If your user account is a member of the "Administrators" group, you're done:

<settings pass="oobeSystem">
<component name="Microsoft-Windows-Shell-Setup" ...>
<UserAccounts>
<LocalAccounts>
<PlainText>true</PlainText>

But if you're like me and you don't want to live in the Administrators group, you need to join the Remote Desktop Users group to be able to log in remotely:

<settings pass="oobeSystem">
<component name="Microsoft-Windows-Shell-Setup" ...>
<UserAccounts>
<DomainAccounts>
<Domain>redmond.corp.microsoft.com</Domain>
<Group>RemoteDesktopUsers</Group>
<Name>MatEer</Name>

• #### Sample: how to enumerate waveIn and waveOut devices on your system

This shows how to call waveInGetNumDevs, waveInGetDevCaps, waveOutGetNumDevs, and waveOutGetDevCaps.

// main.cpp

#include <windows.h>
#include <mmsystem.h>
#include <stdio.h>

#define LOG(format, ...) wprintf(format L"\n", __VA_ARGS__)

int _cdecl wmain() {

UINT devs = waveInGetNumDevs();
LOG(L"waveIn devices: %u", devs);
for (UINT dev = 0; dev < devs; dev++) {
WAVEINCAPS caps = {};
MMRESULT mmr = waveInGetDevCaps(dev, &caps, sizeof(caps));

if (MMSYSERR_NOERROR != mmr) {
LOG(L"waveInGetDevCaps failed: mmr = 0x%08x", mmr);
return mmr;
}

LOG(
L"-- waveIn device #%u --\n"
L"Manufacturer ID: %u\n"
L"Product ID: %u\n"
L"Version: %u.%u\n"
L"Product Name: %s\n"
L"Formats: 0x%x\n"
L"Channels: %u\n"
L"Reserved: %u\n"
,
dev,
caps.wMid,
caps.wPid,
caps.vDriverVersion / 256, caps.vDriverVersion % 256,
caps.szPname,
caps.dwFormats,
caps.wChannels,
caps.wReserved1
);
}

devs = waveOutGetNumDevs();
LOG(L"waveOut devices: %u", devs);
for (UINT dev = 0; dev < devs; dev++) {
WAVEOUTCAPS caps = {};
MMRESULT mmr = waveOutGetDevCaps(dev, &caps, sizeof(caps));

if (MMSYSERR_NOERROR != mmr) {
LOG(L"waveOutGetDevCaps failed: mmr = 0x%08x", mmr);
return mmr;
}

LOG(
L"-- waveOut device #%u --\n"
L"Manufacturer ID: %u\n"
L"Product ID: %u\n"
L"Version: %u.%u\n"
L"Product Name: %s\n"
L"Formats: 0x%x\n"
L"Channels: %u\n"
L"Reserved: %u\n"
L"Support: 0x%x\n"
L"%s%s%s%s%s"
,
dev,
caps.wMid,
caps.wPid,
caps.vDriverVersion / 256, caps.vDriverVersion % 256,
caps.szPname,
caps.dwFormats,
caps.wChannels,
caps.wReserved1,
caps.dwSupport,
((caps.dwSupport & WAVECAPS_LRVOLUME) ?       L"\tWAVECAPS_LRVOLUME\n" :       L""),
((caps.dwSupport & WAVECAPS_PITCH) ?          L"\tWAVECAPS_PITCH\n" :          L""),
((caps.dwSupport & WAVECAPS_PLAYBACKRATE) ?   L"\tWAVECAPS_PLAYBACKRATE\n" :   L""),
((caps.dwSupport & WAVECAPS_VOLUME) ?         L"\tWAVECAPS_VOLUME\n" :         L""),
((caps.dwSupport & WAVECAPS_SAMPLEACCURATE) ? L"\tWAVECAPS_SAMPLEACCURATE\n" : L"")
);
}

return 0;
}

On my system this outputs:

waveIn devices: 3
-- waveIn device #0 --
Manufacturer ID: 1
Product ID: 65535
Version: 0.0
Product Name: Microphone (High Definition Aud
Formats: 0xfffff
Channels: 2
Reserved: 0

-- waveIn device #1 --
Manufacturer ID: 1
Product ID: 65535
Version: 0.0
Product Name: Digital Audio (S/PDIF) (High De
Formats: 0xfffff
Channels: 2
Reserved: 0

-- waveIn device #2 --
Manufacturer ID: 1
Product ID: 65535
Version: 0.0
Product Name: CD Audio (High Definition Audio
Formats: 0xfffff
Channels: 2
Reserved: 0

waveOut devices: 2
-- waveOut device #0 --
Manufacturer ID: 1
Product ID: 65535
Version: 0.0
Product Name: Headphones (High Definition Aud
Formats: 0xfffff
Channels: 2
Reserved: 0
Support: 0x2e
WAVECAPS_LRVOLUME
WAVECAPS_PLAYBACKRATE
WAVECAPS_VOLUME
WAVECAPS_SAMPLEACCURATE

-- waveOut device #1 --
Manufacturer ID: 1
Product ID: 65535
Version: 0.0
Product Name: Digital Audio (S/PDIF) (High De
Formats: 0xfffff
Channels: 2
Reserved: 0
Support: 0x2e
WAVECAPS_LRVOLUME
WAVECAPS_PLAYBACKRATE
WAVECAPS_VOLUME
WAVECAPS_SAMPLEACCURATE

Source and binaries attached.

• #### How many numbers are straights?

As many people, I have to deal with a lot of numbers all the time (bug numbers, changelist numbers, tracking numbers, ...) and as a mathematician I sometimes notice when numbers have peculiar properties.

For example, I recently ran into 152463, which is a straight (made up of consecutive digits, not necessarily in order.)  I then ran into 35426, which is also a straight.  Is this odd?

To put it another way - what proportion of numbers are straights?

We consider only non-negative integer numbers {0, 1, 2, ... }.  No duplicate digits are allowed, so the very last straight is 9,876,543,210.

Break it down by the length of the number in digits.  For example, how many four-digit numbers are there?  We can choose any number from 1-9 for the first digit, and any number from 0-9 for each of the other three digits; so there are 9 * 103 numbers of length 4.

How many of these are straights?  There are seven ways to choose a set of four consecutive digits to make up a four-digit straight: {0,1, 2, 3}, {1, 2, 3, 4}, {2, 3, 4, 5}, {3, 4, 5, 6}, {4, 5, 6, 7}, {5, 6, 7, 8}, {6, 7, 8, 9}.

Each of these seven ways can then be shuffled to make the straights themselves.  For six of these seven sets of digits, all possible shufflings are allowed; there are 4! shufflings for each set.  This gives 6 * 4! straights.

But for {0, 1, 2, 3}, we have to be careful not to end up with 0 as a leading digit.  We choose the leading digit first out of the set {1, 2, 3} (there are three ways to do this) and then we have complete freedom to shuffle the three remaining digits (including 0.)  This gives 3 * 3! straights.

So there are 6 * 4! + 3 * 3! straights which are four digits long.

Considering all possible lengths from 1 to 10 (we'll consider 0 as a number of length 1) gives the following table:

 Length Numbers Straights % Straights 1 10 10 100.000% 2 9 * 101 17 18.889% 3 9 * 102 46 5.111% 4 9 * 103 162 1.800% 5 9 * 104 696 0.773% 6 9 * 105 3480 0.387% 7 9 * 106 19,440 0.216% 8 9 * 107 115,920 0.129% 9 9 * 108 685,440 0.076% 10 9 * 109 3,265,920 0.036% Total 1010 40,91,131 0.041%

Since roughly 1% of five-digit numbers are straights, it is not surprising that I see them that often.

• #### My unattend.xml file

As a tester on Windows 8 I install Windows on my dev machine very frequently.

I use F12 to boot from the network into WinPE, then I run a .bat file which looks something like this:

@echo off
setlocal enabledelayedexpansion

set FANCYLANG=qps-plocm

del unattend-processed.xml
for /f "usebackq delims=" %%l in (type unattend-template.xml) do (
set line=%%l
set line=!line:#FANCY_LANG#=%FANCY_LANG%!
echo !line! >> unattend-processed.xml
)

setup.exe /unattend:unattend-processed.xml

This takes my template unattend.xml file, injects various parameters, and calls setup.exe.

The template unattend.xml follows in all of its glory.  We'll take a closer look at some of the things in future posts.

<?xml version="1.0" encoding="utf-8"?>
<unattend xmlns="urn:schemas-microsoft-com:unattend">
<settings pass="windowsPE">
<component name="Microsoft-Windows-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<UserData>
<ProductKey>
<WillShowUI>OnError</WillShowUI>
</ProductKey>
<AcceptEula>true</AcceptEula>
<FullName>Matthew van Eerde</FullName>
<Organization>Microsoft</Organization>
</UserData>
<DiskConfiguration>
<CreatePartitions>
<Order>1</Order>
<Extend>true</Extend>
<Type>Primary</Type>
</CreatePartition>
</CreatePartitions>
<WillWipeDisk>true</WillWipeDisk>
<DiskID>0</DiskID>
</Disk>
</DiskConfiguration>
<ImageInstall>
<OSImage>
<WillShowUI>OnError</WillShowUI>
<InstallToAvailablePartition>true</InstallToAvailablePartition>
</OSImage>
</ImageInstall>
</component>
<component name="Microsoft-Windows-International-Core-WinPE" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<SetupUILanguage>
<UILanguage>#FANCY_LANG#</UILanguage>
</SetupUILanguage>
<InputLocale>0409:00000409</InputLocale>
<UserLocale>en-US</UserLocale>
<SystemLocale>en-US</SystemLocale>
<UILanguage>#FANCY_LANG#</UILanguage>
</component>
</settings>
<settings pass="specialize">
<component name="Microsoft-Windows-UnattendedJoin" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Identification>
<Credentials>
<Domain>redmond.corp.microsoft.com</Domain>
</Credentials>
<JoinDomain>ntdev.corp.microsoft.com</JoinDomain>
</Identification>
</component>
<component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ComputerName>MATEER-Q</ComputerName>
<RegisteredOwner>Matthew van Eerde</RegisteredOwner>
<RegisteredOrganization>Microsoft</RegisteredOrganization>
</component>
<component name="Microsoft-Windows-TerminalServices-LocalSessionManager" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<fDenyTSConnections>false</fDenyTSConnections>
</component>
<component name="Networking-MPSSVC-Svc" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<FirewallGroups>
<Active>true</Active>
<Profile>all</Profile>
<Group>@FirewallAPI.dll,-28752</Group>
</FirewallGroup>
</FirewallGroups>
</component>
</settings>
<settings pass="oobeSystem">
<component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<OOBE>
<HideEULAPage>true</HideEULAPage>
<NetworkLocation>Work</NetworkLocation>
<ProtectYourPC>3</ProtectYourPC>
<HideOnlineAccountScreens>true</HideOnlineAccountScreens>
<HideLocalAccountScreen>true</HideLocalAccountScreen>
<HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE>
</OOBE>
<UserAccounts>
<LocalAccounts>
<PlainText>true</PlainText>
</LocalAccount>
</LocalAccounts>
<DomainAccounts>
<Domain>redmond.corp.microsoft.com</Domain>
<Group>RemoteDesktopUsers</Group>
<Name>MatEer</Name>
</DomainAccount>
</DomainAccountList>
</DomainAccounts>
</UserAccounts>
<TimeZone>Pacific Standard Time</TimeZone>
<AutoLogon>
<PlainText>true</PlainText>
<Domain>redmond.corp.microsoft.com</Domain>
<Enabled>true</Enabled>
<LogonCount>1</LogonCount>
</AutoLogon>
</component>
</settings>
</unattend>

• #### Programmatically setting a local user account to never expire its password

As a Windows tester, I install Windows on my own machines a lot (this is known internally as "selfhosting", or "dogfooding", or "ice cream-ing".)

One of my little idiosyncracies is I like to run as a non-administrative user.  That is, I don't add my domain account to the local Administrators group.

Instead, I create a local "Admin" account with a known (to me) password; every time I need to elevate, I get a prompt that asks for credentials rather than just "Yes/No".  To this prompt I pass the credentials of the local "Admin" account.

Although I usually install fresh builds regularly (on my multiple machines), sometimes one machine gets a little stale.  In fact, it happened once that my local .\Admin account got so stale that I had to change the password!  This was annoying enough that I devoted some energy into figuring out how to check the "Password never expires" box on the local account properties programmatically.

The result was the following script: call as cscript.exe never-expire-admin-password.wsf  This version hardcodes the username "Admin"; a production version would probably allow passing a username in via the command line.

' LDAP doesn't work for controlling local users
' (unless you're a domain controller, of course)
'
' have to use WinNT provider instead

' Save
End If

• #### Teaching someone to fish and the AKS primality test

This morning my wife (whom I love and adore) woke me up at 3:00 AM with an urgent question.

"Hey!" she said, shaking me awake.

"Is 19 prime?"

...

Like a fool, I answered her.  "Yes.  Yes it is."  Off she went.

This is a true response, but not a correct response.  I realized shortly afterwards that a correct response would look more like:

I'm glad you asked me that. dear.  Eratosthenes, the Greek mathematician, discovered a very efficient way to list primes in about 200 BC that is still in use today.  You start by writing out all the numbers from 1 to 19: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19.  1 is a very special number (it's the multiplicative identity, or what algebraists would call a unit) so we put a square around it.  The first number we didn't consider was 2, so we circle it - that means it's prime - and then cross out every multiple of 2 after that.  Going back, the first number we didn't consider was 3... and so on until we get 1 2 3 X 5 X 7 X X X 11 X 13 X X X 17 X 19.  A common optimization is to realize that after circling a prime p, the first number you cross out (that wasn't crossed out before) is always p2, which means that after circling a number you can immediately jump to its square, and also means you can stop crossing out altogether once you hit p> N...

This would allow her to fish rather than waking me up when she wanted a fish.

An even better response would have been:

It's funny you've asked me that.  Number theorists and cryptanalysts have considered this question for thousands of years.  Eratosthenes' method (see above) is a very simple way to find all the primes below a given number, but an efficient way to determine whether a given number is prime was found only very recently.

In practice, the test that is usually used is the randomized version of the Miller-Rabin test.  Although this is nondeterministic, it is very fast indeed, and will tell you to a very high degree of certainty whether the given number is prime.  This usually suffices.

There is a deterministic version of the Miller-Rabin test too, which is guaranteed to tell you with perfect certainty whether the given number is prime.  But it only works if you believe in the generalized Riemann hypothesis.  Most mathematicians nowadays believe the hypothesis, but no-one has (yet) been able to prove it.

Amazingly, in 2002 three mathematicians named Manindra Agrawal, Neeraj Kayal, and Nitin Saxena came up with a deterministic, proven, polynomial-time (specifically, polynomial in the number of digits in the input) method for telling whether a given number is prime.   This is known as the AKS primality test.  The most striking thing about this test is its simplicity - if something this straightforward can be found after thousands of years of looking, what else out there remains to be found?

Such a response would probably prevent her from waking me again for any mathematical problem at all.  Boo-ya.

My own Perl implementation follows:

use strict;
# use bignum;

print <<USAGE and exit 0 unless @ARGV;
$0 [-v] n Use the AKS primality test to check whether n is prime -v adds verbose log spew USAGE sub is_power($$); sub ceil_log2(); sub first_r($$); sub check_gcds($$); sub check_polynomials($$$);
sub gcd($$); sub totient(); sub polypow($$\@);
sub polymult($$\@\@); sub polyeq(\@\@); my verbose = ARGV[0] eq "-v"; shift @ARGV if verbose; die "Expected only one argument" unless 1 == @ARGV; my n = shift; # step 0: restrict to integers >= 2 print "n is not an integer and so is NEITHER PRIME NOR COMPOSITE\n" and exit 0 unless int(n) == n; print "n < 2 and so is NEITHER PRIME OR COMPOSITE\n" and exit 0 unless n >= 2; # step 1: check if the number is a power of some lower number. # this can be done quickly by iterating over the exponent (2, 3, ...) # and doing a binary search on the base. # we start at the top and work down for performance reasons; # several subroutines need to know ceil(log2(n)) so we calculate it once and pass it around. my log2_n = ceil_log2(n); is_power(n, log2_n) and exit 0; print "Not a power.\n"; # step 2: find the smallest r such that o_r(n) > (log2 n)^2 # where o_r(n) is the multiplicative order of n mod r # that is, the smallest k such that n^k == 1 mod r my r = first_r(n, log2_n); print "r = r\n"; # step 3: for all a between 2 and r inclusive, check whether gcd(a, n) > 1 check_gcds(n, r) or exit 0; # step 4: if r >= n, we're done if (r >= n) { print "r >= n so n is PRIME\n"; exit 0; } # step 5: for all a between 1 and floor( sqrt(phi(r)) log2(n) ) # check whether (x + a)^n = x^n + a mod x^r - 1, n check_polynomials(n, r, log2_n) or exit 0; # step 6: if we got this far, n is prime print "n is PRIME\n"; sub is_power($$) {
my $n = shift; my$log2_n = shift; # actually ceil(log2(n))

print "Checking for power-ness...\n";

# we consider numbers of the form b^i
# we iterate over the exponent i
# starting at i = ceil(log2(n)) and working down to i = 2
#
# for each exponent we do a binary search on the base
# the lowest the base can be is 2
# and the highest the base can be (initially) is 2
#
# we set up bounds on the base that are guaranteed to
# surround the actual base
my $b_low = 1; # 1 ^ ceil(log2(n)) = 1 < n my$b_high = 3; # 3 ^ ceil(log2(n)) > 2 ^ log2(n) = n

for (my $i =$log2_n; $i >= 2;$i--) {
print "\tb^$i\n" if$verbose;

# let's check that the bounds are really correct
die "$b_low ^$i is not < $n" unless$b_low ** $i <$n;
die "$b_high ^$i is not > $n" unless$b_high ** $i >$n;

# do a binary search to find b such that b ^ i = n
while ($b_high -$b_low > 1) {
print "\t\tb^$i: b is between$b_low and $b_high\n" if$verbose;
my $b = int(($b_low + $b_high)/2); my$t = $b **$i;
if ($t ==$n) {
print "$n =$b^$i;$n is COMPOSITE\n";
return 1;
}

($t >$n ? $b_high :$b_low) = $b; } # as we pass from the exponent (say, 5) # to the exponent below (say, 4) # we need to reconsider our bounds # # b_low can remain the same because b ^ (i - 1) is even less than b ^ i # OPEN ISSUE: can we even raise b_low? # # but we need to raise b_high since b ^ i > n does NOT imply b ^ (i - 1) > n # # we'll square b_high; b ^ i > n => (b ^ 2) ^ (i - 1) = b ^ (2 i - 2) > n # since i >= 2 # # OPEN ISSUE: is there a better way to raise this higher bound? Does this help much?$b_high *= $b_high; } # nope, not a power return 0; } sub ceil_log2($) {
my $n = shift; my$i = 0;
my $t = 1; until ($t >= $n) {$i++;
$t *= 2; } return$i;
}

sub first_r($$) { my n = shift; my log2_n = shift; # actually ceil(log2(n)) my s = log2_n ** 2; print "Looking for the first r where o_r(n) > s...\n"; # for each r we want to find the smallest k such that # n^k == 1 mod r my r; for (r = 2; ; r++) { # print "\tTrying r...\n"; # find the multiplicative order of n mod r my k = 1; my t = n % r; until (1 == t or k > s) { t = (t * n) % r; k++; } if (k > s) { # print "\to_r(n) is at least k\n"; last; } else { # print "\to_r(n) = k\n"; } } return r; } sub check_gcds($$) {
my ($n,$r) = @_;

print "Checking GCD($n, a) for a = 2 to$r...\n";

for (my $a = 2;$a <= $r;$a++) {
my $g = gcd($n, $a); next if ($g == $n); # this is OK if (1 !=$g) {
print "gcd($n,$a) = $g;$n is COMPOSITE\n";
return 0;
}
}

print "All GCDs are 1 or $n\n"; return 1; } sub gcd($$) { my (x, y) = @_; (x, y) = (y, x) unless x > y; while (y) { (x, y) = (y, x % y); } return x; } sub check_polynomials($$$) {
my $n = shift; my$r = shift;
my $log2_n = shift; # actually ceil(log2(n)) # iterate over a from 1 to floor( sqrt(phi(r)) log2(n) ) # for each a, check whether the polynomial equality holds: # (x + a)^n = x^n + a mod (x^r - 1, n) # if it fails to hold, the number is composite # # first we need to evaluate phi(r) so we can determine the upper bound # OPEN ISSUE: this seems to be a potential weakness in the algorithm # because the usual way to evaluate phi(r) is to find the prime factorization of r # and then form the product r*PI(1 - 1/p) where the product ranges over all primes # which divide r my$phi = totient($r); # a < sqrt(phi(r)) * log2(n) => a^2 < phi(r) * (log2(n))^2 my$a2_max = $phi *$log2_n * $log2_n; print "Checking polynomials up to roughly ", int sqrt($a2_max), "...\n";

for (my $a = 1;$a * $a <=$a2_max; $a++) { print "\ta =$a...\n" if $verbose; # polynomials are of the form (c0, c1, c2, ..., ci, ...) # which corresponds to c0 + c1 x + c2 x^2 + ... + ci x^i + ...) my @x = (0, 1); my @x_plus_a = ($a % $n, 1); my @lhs = polypow($n, $r, @x_plus_a); # POTENTIAL OPTIMIZATION: # x^n + a mod (x^r - 1) is just x^(n % r) + a # and we know n % r != 0 my @rhs = polypow($n, $r, @x); # x^n$rhs[0] = ($rhs[0] +$a) % $n; # + a next if polyeq(@lhs, @rhs); print "(x +$a)^$n is not equal to x^$n + $a mod(x^$r - 1, $n)\n"; print "So$n is COMPOSITE\n";
return 0;
}

return 1;
}

sub totient($) { my$r = shift;

print "Finding the Euler totient of $r\n"; # we'll do a trial division to find the totient # there are faster ways that use a sieve # but we don't know how big r is my$t = $r; # by construction p will always be prime when it is used # OPEN ISSUE: this might be slow for (my$p = 2; $r > 1;$p++) {
next if $r %$p;

print "\t$p is a factor\n" if$verbose;
# decrease the totient
$t /=$p;
$t *=$p - 1;

# decrease r
$r /=$p; # we know there's at least one factor of p
$r /=$p until $r %$p; # there might be more
}

print "Totient is $t\n"; return$t;
}

sub polypow($$\@) { my n = shift; # this is both the mod and the exponent my r = shift; my @base = @{ +shift }; my exp = n; my @result = (1); # 1 # print "\t(", join(" ", @base), ")^exp mod (x^r - 1, n)\n" if verbose; # basic modpow routine, but with polynomials while (exp) { if (exp % 2) { @result = polymult(n, r, @result, @base); } exp = int (exp / 2); @base = polymult(n, r, @base, @base); } # print "\t= (", join(" ", @result), ")\n" if verbose; return @result; } sub polymult($$\@\@) {
my $n = shift; my$r = shift;
my @first = @{ +shift };
my @second = @{ +shift };

# print "\t\t(", join(" ", @first), ") * (", join(" ", @second), ") mod (x^$r - 1,$n)\n" if $verbose; my @result = (); # first do a straight multiplication first * second my$s = @second - 1;
for (my $i = @first - 1;$i >= 0; $i--) { for (my$j = $s;$j >= 0; $j--) { my$k = $i +$j;
$result[$k] += $first[$i] * $second[$j];
$result[$k] %= $n; } } # then do a straight mod x^r - 1 # consider a polynomial # c0 + ... + ck x^k # with k >= r # we can subtract ck (x^r - 1) # without changing the mod value # the net effect is to eliminate the x^k term # and add ck to the x^(k - r) term for (my$i = @result - 1; $i >=$r; $i--) { my$j = $i -$r;
$result[$j] += $result[$i];
$result[$j] %= $n; pop @result; } # eliminate any leading zero terms for (my$i = @result - 1; 0 == $result[$i]; $i--) { pop @result; } # print "\t\t= (", join(" ", @result), ")\n" if$verbose;
return @result;
}

sub polyeq(\@\@) {
my @lhs = @{ +shift };
my @rhs = @{ +shift };

# print "(", join(" ", @lhs), ") = (", join(" ", @rhs), ")?\n" if $verbose; return 0 unless @lhs == @rhs; for (my$i = @lhs - 1; $i >= 0;$i--) {
return 0 unless $lhs[$i] == $rhs[$i];
}

return 1;
}

Here's the output when I run it on 19:

>perl -w aks.pl 19
Checking for power-ness...
Not a power.
Looking for the first r where o_r(19) > 25...
r = 19
Checking GCD(19, a) for a = 2 to 19...
All GCDs are 1 or 19
19 >= 19 so 19 is PRIME

And here's the output with a bigger input:

>perl -w aks.pl 99
Checking for power-ness...
Not a power.
Looking for the first r where o_r(997) > 100...
r = 103
Checking GCD(997, a) for a = 2 to 103...
All GCDs are 1 or 997
Finding the Euler totient of 103
Totient is 102
Checking polynomials up to roughly 100...
997 is PRIME
• #### Excruciating rhymes

I was watching How the Grinch Stole Christmas and I was struck by this rhyme (from the song  "You're a Mean One, Mr. Grinch:")

You're a nauseous super naus!
You're a dirty crooked jockey, and you drive a crooked hoss

-- Dr. Seuss, How the Grinch Stole Christmas

I tried to see what other particularly excruciating rhymes I could remember.  I came up with two:

You know, that little guy, he's got me feeling all contempt-ey:
He takes his boat out loaded up and brings it back in empty.

-- Phil Vischer, Lyle the Kindly Viking

And then of course:

In short, when I've a smattering of elemental strategy,
You'll say a better Major-General had never sat a-gee!

-- W. S. Gilbert, The Pirates of Penzance

• #### What is a perfect score in Yahtzee?

The dice game Yahtzee takes thirteen turns. Each turn involves rolling a set of five dice (with two possible rerolls, the player having the option to reroll only a subset of the dice), then marking one of thirteen boxes and scoring points according to whether the numbers on the dice meet the rules of the box. After each box is marked, the game is over.

The thirteen boxes and their rules are:

1. ONES: score is the sum of those dice showing a one (could be zero.)
2. TWOS: score is the sum of those dice showing a two (could be zero.)
3. THREES: score is the sum of those dice showing a three (could be zero.)
4. FOURS: score is the sum of those dice showing a four (could be zero.)
5. FIVES: score is the sum of those dice showing a five (could be zero.)
6. SIXES: score is the sum of those dice showing a six (could be zero.)
7. THREE OF A KIND: if three of the dice show the same number, the score is the sum of all five dice; otherwise, zero.
8. FOUR OF A KIND: if four of the dice show the same number, the score is the sum of all five dice; otherwise, zero.
9. FULL HOUSE: if three of the dice show the same number as each other, and the other two show the same number as each other, score 25; otherwise, zero.1
10. SMALL STRAIGHT: if there are four dice which show (1 2 3 4), or (2 3 4 5), or (3 4 5 6), score 30; otherwise, zero. (There is no defined order for the rolled dice, so order is not important.)
11. LARGE STRAIGHT: if the five dice show (1 2 3 4 5), or (2 3 4 5 6), score 40; otherwise, zero. (Order is not important.)
12. CHANCE: score the total of the five dice (minimum possible score is 5.)2
13. YAHTZEE (FIVE OF A KIND:) if all five dice show the same number, score 50; otherwise, zero.

There are two bonuses possible:

1. At the end of the game, if the point total for boxes 1-6 (ONES through SIXES) is at least 63, the player gets a bonus of 35. (63 corresponds to getting three ones + three twos + three threes + ... + three sixes, but there are other possibilities. I don't know where 35 came from.)
2. At any time the player checks a box, if...
• the five dice all have the same number as each other
• AND the Yahtzee box is already checked
• AND this was not a result of "taking a zero in Yahtzee"
... then the player gets a bonus 100 points. This bonus can take effect multiple times per game.

So a perfect Yahtzee game looks like this. On the first turn...

 Roll Box Score (x x x x x) any Yahtzee YAHTZEE 50 Subtotal 50

... then on the following turns, in no particular order (but see below:)

 Roll Box Score (1 1 1 1 1) ONES 5 (2 2 2 2 2) TWOS 10 (3 3 3 3 3) THREES 15 (4 4 4 4 4) FOURS 20 (5 5 5 5 5) FIVES 25 (6 6 6 6 6) SIXES 30 (6 6 6 6 6) THREE OF A KIND 30 (6 6 6 6 6) FOUR OF A KIND 30 (x x x x x) any Yahtzee FULL HOUSE 0 or 25 (see below)1 (x x x x x) any Yahtzee SMALL STRAIGHT 0 (but see below) (x x x x x) any Yahtzee LARGE STRAIGHT 0 (but see below) (6 6 6 6 6) CHANCE 30 Subtotal 195 or 220

Now we look at the bonuses and add it all up:

 Category Score Base score 245 or 270 Bonus for scoring >= 63 in ONES through SIXES 35 Bonus for scoring twelve additional YAHTZEEs after scoring 50 in YAHTZEE 1200 Total 1480 or 1505

So a perfect score in Yahtzee is 1480 or 1505.

Well...

... not quite. There's another rule about Yahtzees (the "Joker" rule) which I didn't tell you about yet, because it's kind of complicated. If...

• You roll a Yahtzee
• AND the YAHTZEE box is already checked (a zero score is OK; you don't get the 100 point bonus, but this rule still applies)
• ... then you MUST score this in the ONES-through-SIXES box for the number you rolled (say, 3)...
• ... UNLESS that box has also already been checked (a zero score is OK), in which case...

... you can score this roll in any unchecked box and it automatically meets the scoring criterion (wow!) So you would get 30 points in SMALL STRAIGHT or 40 points in LARGE STRAIGHT. (Or 25 points in FULL HOUSE.)1

This gives an additional 70 or 95 points, bring the total to 1575. (It also creates additional order dependencies; the boxes for FULL HOUSE, SMALL STRAIGHT, and LARGE STRAIGHT must be checked after the ONES through SIXES box for the corresponding number is checked.)

1 Does five-of-a-kind count as a full house? I'm not sure. Mathematically it would make sense. It turns out not to matter for this calculation because of the Joker rule, but it may matter in an actual game. The official rules say "Score in this box only if the dice show three of one number and two of another", so I guess not, but I would be comfortable adopting a "house rule" which allowed a Yahtzee to count as a "native" full house. Normally a house rule should be agreed upon by all players before the start of the game, but I'm pretty sure it would be possible to convince the other players to let you score your (3 3 3 3 3) in FULL HOUSE while YAHTZEE was still open. ("You want to throw away 25 points?  Really?  Um, go right ahead...")

2 The lowest possible Yahtzee score is 5: get (1 1 1 1 1) and score it in Chance, then get zeros in all the other boxes.

• #### Programmatically grabbing a screenshot of the primary display

It's sometimes difficult to explain to people what my job actually is. "I test Windows sound." "Cool.  How does that work?"

A product like Windows has a lot of components that interact with each other.  If everything works, the user doesn't know that most of these components even exist; everything is invisible and seamless.

Most testing involves the connection ("interface") between two components.  "I test APIs."  To the uninitiated, this is just a word.  It sounds like "I test wakalixes."  "You test what, now?"

There are two interfaces which are easier to explain.  There's the software-to-hardware interface, where the driver talks to the hardware.  "I test the HD Audio, USB Audio, and Bluetooth audio class drivers."  "Huh?" "They make the speakers and the microphone work."  "Oh, cool.  So you sit around and use Skype all day?"

But the easiest of all to explain is the user interface.  "I make sure that the Sound Recorder app, the volume slider, and the Sound control panel work." "Oh, that!  I had this annoying problem once where..."

What does the test result for an invisible interface look like? A lot of logging.  "I expected this call to succeed; it returned this HRESULT."  "I poked the hardware like this and got a bluescreen."  "There seems to be an infinite loop here."  Lots of text.

Boring.

UI testing has logging too.  But with UI testing you can also... TAKE PICTURES!  A UI bug is a lot easier to understand (triage, and fix) if there's a screenshot attached (preferably with a big red rectangle highlighting the problem.)

It is therefore valuable to have an automatable utility that can take a screenshot and dump it to a file.  Here's one I cribbed together from the "Capturing an Image" sample code on http://msdn.microsoft.com/en-us/library/dd183402(v=VS.85).aspx.  Source and binaries attached.

This version only captures the main display, and not secondary monitors (if any.)

Pseudocode:

screen_dc = GetDC(nullptr);

memory_dc = CreateCompatibleDC(screen);

rect = GetClientRect(GetDesktopWindow());

hbmp = CreateCompatibleBitmap(screen_dc, rect);

SelectObject(memory_dc, hbmp);

BitBlt(memory_dc, rect, screen_dc);

bmp = GetObject(hbmp);

bytes = allocate enough memory

bytes = GetDIBits(screen_dc, bmp, hbmp)

file = CreateFile();

WriteFile(bytes);

• #### Generating primes using the Sieve of Eratosthenes plus a few optimizations

When solving Project Euler problems I frequently need to iterate over prime numbers less than a given n. A Sieve of Eratosthenes method quickly and easily finds the small prime numbers; there are more complicated methods that find larger prime numbers, but with a couple of tweaks the Sieve of Eratosthenes can get quite high.

A naive implementation for finding the set of primes below n will:

1. Allocate an array of n booleans, initialized to false.
2. Allocate an empty list
3. For each i in the range 2 to n:
1. If the boolean value at this index in the array is true, i is composite. Skip to the next value and check that.
2. If the boolean value at this index in the array is false, i is prime!
3. Add i to the list of primes
4. For each multiple of i in the range 2i to n, set the boolean value at that index in the array to true

There are a handful of simple optimizations that can be made to this naive implementation:

1. Step 3d) will have no effect until the multiple of i reaches i2, so the range can be changed to "i2 to n"
2. As a direct consequence of this, step 3d) can be skipped entirely once i2 passes n.
3. Instead of allocating an array of n booleans, an array of nbits will suffice.
4. All the even-indexed bits are set to true on the first pass. Manually recognize that 2 is prime, and only allocate bits for odd-numbered values. Change the outer loop in 3) to "in the range 3 to n", incrementing by two each time. Change the loop 3d) to increment by 2i each time.
5. Storing the list of primes takes a lot of memory - more than the sieve. Don't bother creating a list of primes, just write an enumerator that travels the sieve directly.

With these optimizations I can enumerate primes from 2 up to 5 billion (5 * 109) in about seven minutes.  Source and binaries attached.

>primes 5000000000
Will enumerate primes <= 5000000000 = 5e+009
Memory for sieve: 298.023 MB
Initialization complete: 983 milliseconds since start
Sieving to 70711
Sieving complete: 4.70292 minutes since start
Picking up the rest to 5000000000
Pickup complete: 6.12252 minutes since start
Primes: 234954223
1: 2
23495423: 442876981
46990845: 920233121
70486267: 1410555607
93981689: 1909272503
117477111: 2414236949
140972533: 2924158169
164467955: 3438252577
187963377: 3955819157
211458799: 4476550979
234954221: 4999999883
Enumerating complete: 7.43683 minutes since start
Freeing CPrimes object

There are more complicated sieves like the Sieve of Atkin which perform better but at the cost of being much more complex. So far I haven't had to resort to any of those.

• #### How to create a shortcut from the command line

Working on Windows, I install Windows a lot.  This means a lot of my customizations have to be re-applied every time I install.  To save myself some time I created a script which applies some of them. Last time I showed how to set the desktop wallpaper from a command-line app.

This time, a script to create a shortcut.  The example usage creates a shortcut to Notepad and puts that in the "SendTo" folder.  I find this very useful because I often need to edit text files that have non-".txt" assocations.  (There are also other shortcuts I create with it.)

Here's the script:

>create-shortcut.vbs

If WScript.Arguments.Count < 2 Or WScript.Arguments.Count > 3 Then
WScript.Echo "Expected two or three arguments; got " & WScript.Arguments.Count
WScript.Echo "First argument is the file to create"
WScript.Echo "Second is the command to link to"
WScript.Echo "Third, if present, is the arguments to pass"
WScript.Quit
End If

Set shell = WScript.CreateObject("WScript.Shell")

If WScript.Arguments.Count = 3 Then
End If

• #### Command-line app to set the desktop wallpaper

Working on Windows, I find myself installing Windows a lot.

I find that I like to change a lot of the settings that Windows offers to non-default values.  (That is, I'm picky.)

I have a script which automates some of these things, which I add to now and again.  Some of the bits of the script are straightforward, but once in a while the tweak itself is of interest.

One of the things I love about my work setup is the many large monitors.  So, one of the things I like to change is the desktop wallpaper image.

Changing the desktop wallpaper required some code, which makes it "of interest."  Here's the code.

// main.cpp

#include <windows.h>
#include <winuser.h>
#include <stdio.h>

int _cdecl wmain(int argc, LPCWSTR argv[]) {
if (1 != argc - 1) {
wprintf(L"expected a single argument, not %d\n", argc);
return -__LINE__;
}

if (!SystemParametersInfo(
SPI_SETDESKWALLPAPER,
0,
const_cast<LPWSTR>(argv[1]),
SPIF_SENDCHANGE
)) {
DWORD dwErr = GetLastError();
wprintf(L"SystemParametersInfo(...) failed with error %d\n", dwErr);
return -__LINE__;
}

wprintf(L"Setting the desktop wallpaper to %s succeeded.\n", argv[1]);
return 0;
}

Binaries attached.

Warning: if you pass a relative path to this tool, it won't qualify it for you, and the SystemParametersInfo call won't either - so the wallpaper you want won't be set, though all the calls will succeed.  Make sure to specify a fully-qualified path.

• #### Welcome Yuk Lai Suen to the blogosphere!

Please help to welcome my colleague fellow Windows Sound team test dev Yuk Lai Suen to the blogosphere!  Yuk Lai's first post discusses the utility of manual testing.

Yuk Lai Suen
http://blogs.msdn.com/b/yuk_lai_suen/

• #### Beep sample

A question came in today about the Beep(...) API1 not being able to set the frequency of the beep that was generated. In order to confirm that it worked I whipped up a quick sample which would take the frequency (and duration) on the command line. Source and binaries attached.

For fun I added the ability to pass in the frequency using Scientific pitch notation. Note that A4 is about 431 Hz using this scale, rather than the more standard 440 Hz2.

for (int i = 1; i + 1 < argc; i += 2) {

ULONG frequency;
HRESULT hr = HertzFromScientificPitchNotation(argv[i], &frequency);
if (FAILED(hr)) { return -__LINE__; }

ULONG duration;
hr = UlongFromString(argv[i + 1], &duration);
if (FAILED(hr)) { return -__LINE__; }

if (!Beep(frequency, duration)) {
LOG(L"Beep(%u, %u) failed: GetLastError() = %u", frequency, duration, GetLastError());
return -__LINE__;
}
}

So, for example, you can play a certain well-known tune via Beep() using this command:

>beep.exe C3 2000 G3 2000 C4 4000 E4 500 Eb4 3500 C3 500 G2 500 C3 500 G2 500 C3 500 G2 500 C3 2000

1 More on the Beep(...) API:

The official Beep(...) documentation

A couple of blog posts from Larry Osterman:
Beep Beep
What’s up with the Beep driver in Windows 7?

2 If you want the more standard pitch, change this line:

double freq = 256.0;

To this:

double freq = 440.0 * pow(semitoneRatio, -9.0); // C4 is 9 semitones below A4

Page 2 of 6 (137 items) 12345»