Uniform Distribution from Integer in C#

When dealing with random numbers, one often needs to get a random floating point number between 0 and 1 (not inclusive). Unfortunately, most random generators only deal with integers and/or bytes. Since bytes can be easily converted to integers, question becomes: “How can I convert integer to double number in [0..1) range?”

Well, assuming you start with 64-bin unsigned integer, you can use something like this:

ulong value = 1234567890; //some random value
byte[] buffer = BitConverter.GetBytes((ulong)0x3FF << 52 | value >> 12);
return BitConverter.ToDouble(buffer, 0) - 1.0;

With that in mind you you can see that 8-byte (64-bit) buffer is filled with double format combined of (almost) all 1's in exponent and the fraction portion containing random number. If we take that raw buffer and convert it into a double, we’ll get a number in [1..2) range. Simply substracting 1.0 will place it in our desired [0..1) range. It’s as good as distribution of a double can be (i.e. the maximum number of bits - 56 - are used).

This is as good as uniform distribution can get using 64-bit double.


PS: If we apply the same principle to the float, equivalent code will be something like this (assuming a 32-bit random uint as input):

uint value = 1234567890; //some random value
byte[] buffer = BitConverter.GetBytes((uint)0x7F << 23 | value >> 9);
return BitConverter.ToSingle(buffer, 0) - 1.0;

PPS: C#'s Random class uses code that’s probably a bit easier to understand:

return value * (1.0 / Int32.MaxValue);

Unfortunately, this will use only 31 bits for distribution (instead of 52 available in double). This will cause statistical anomalies if used later to scale into a large integer range.

Splitting the Baby

For one of my hardware projects, I decided to try doing things a bit differently. Instead using a single repository, I decided to split it into two - one containing Firmware and other containing Hardware.

Since repository already had those as a subdirectories, I though using --subdirectory-filter as recommended on GitHub would solve it all. Unfortunately, that left way too many commits not touching either of those files. So I decided to tweak procedure a bit.

I first removed all the files I didn’t need using --index-filter. On that cleaned-up state I applied --subdirectory-filter just to bring directory to the root. Unfortunately, while preserving tags was possible, it proved to be annoying enough to actually remove them all and manually retag all once done.

As discussed above, on the COPY of original repository we first remove all files/directories that are NOT Hardware and then we essentially move Hardware directory to the root level of newly reorganized repository.

git remote rm origin
git filter-branch --index-filter \
    'git rm -rf --cached --ignore-unmatch .gitignore LICENSE.md PROTOCOL.md README.md Firmware/' \
    --prune-empty --tag-name-filter cat -- --all
rm -Rf .git/logs .git/refs/original .git/refs/remotes .git/refs/tags
git filter-branch --prune-empty --subdirectory-filter Hardware main
rm -Rf .git/logs .git/refs/original
git gc --prune=all --aggressive
git log --pretty --graph

With Hardware repository sorted, I did exactly the same process for Firmware with the new COPY of original repository, only changing the directory names.

git remote rm origin
git filter-branch --index-filter \
    'git rm -rf --cached --ignore-unmatch .gitignore LICENSE.md PROTOCOL.md README.md Hardware/' \
    --prune-empty --tag-name-filter cat -- --all
rm -Rf .git/logs .git/refs/original .git/refs/remotes .git/refs/tags
git filter-branch --prune-empty --subdirectory-filter Firmware main
rm -Rf .git/logs .git/refs/original
git gc --prune=all --aggressive
git log --pretty --graph

Once I got two repositories, it was easy enough to combine them. I personally love using subtrees but submodules have their audience too.

Ubuntu 20.10 on Surface Go

Surface Go is almost a perfect Ubuntu machine. The only reason for “almost” being the lack of camera support. All else works out of box or with minor updates. While you can use the standard installation setup, I like to do it in a bit more involved setup.

Mind you, you will need to have a network adapter plugged during install as debootstrap requires it and enabling wireless is one of things not working out of box. If that’s the problem, stick with the default install instead.

First you of course you need to boot from install USB. After booting into Ubuntu desktop installation one needs a root prompt. All further commands are going to need root credentials anyhow.

sudo -i

Now we can set a few variables - disk, pool, host name, and user name. This way we can use them going forward and avoid accidental mistakes. Just make sure to replace these values with ones appropriate for your system.

DISK=/dev/disk/by-id/^^ata_disk^^
HOST=^^desktop^^
USER=^^user^^

Disk setup is really minimal. If there was a chance of dual-boot, EFI partition would be too small. For multiple kernels, one would need to increase boot partition. However, considering Surface Go has 64 MB or disk space, keeping those partitions small is probably a better choice. And no, you cannot make EFI partition smaller than 32 GB despite not needing more than a few megs.

blkdiscard $DISK

sgdisk --zap-all                        $DISK

sgdisk -n1:1M:+63M  -t1:EF00 -c1:EFI    $DISK
sgdisk -n2:0:+448M  -t2:8300 -c2:Boot   $DISK
sgdisk -n3:0:0      -t3:8309 -c3:Ubuntu $DISK

sgdisk --print                          $DISK

Having boot and EFI partition unencrypted does offer advantages and having standard kernels exposed is not much of a security issue. However, one must encrypt root partition.

cryptsetup luksFormat -q --cipher aes-xts-plain64 --key-size 256 \
    --pbkdf pbkdf2 --hash sha256 $DISK-part3

Since crypt device name is displayed on every startup, for Surface Go I like to use host name here.

cryptsetup luksOpen $DISK-part3 ${HOST^}

Now we can prepare all needed partitions.

yes | mkfs.ext4 /dev/mapper/${HOST^}
mkdir /mnt/install
mount /dev/mapper/${HOST^} /mnt/install/

yes | mkfs.ext4 $DISK-part2
mkdir /mnt/install/boot
mount $DISK-part2 /mnt/install/boot/

mkfs.msdos -F 32 -n EFI $DISK-part1
mkdir /mnt/install/boot/efi
mount $DISK-part1 /mnt/install/boot/efi

To start the fun we need debootstrap package.

apt update ; apt install --yes debootstrap

And then we can get basic OS on the disk. This will take a while.

debootstrap focal /mnt/install/

Our newly copied system is lacking a few files and we should make sure they exist before proceeding.

echo $HOST > /mnt/install/etc/hostname
sed "s/ubuntu/$HOST/" /etc/hosts > /mnt/install/etc/hosts
sed '/cdrom/d' /etc/apt/sources.list > /mnt/install/etc/apt/sources.list
cp /etc/netplan/*.yaml /mnt/install/etc/netplan/

If you are installing via WiFi, you might as well copy your wireless credentials:

mkdir -p /mnt/install/etc/NetworkManager/system-connections/
cp /etc/NetworkManager/system-connections/* /mnt/install/etc/NetworkManager/system-connections/

Finally we’re ready to “chroot” into our new system.

mount --rbind /dev  /mnt/install/dev
mount --rbind /proc /mnt/install/proc
mount --rbind /sys  /mnt/install/sys
chroot /mnt/install \
    /usr/bin/env DISK=$DISK HOST=$HOST USER=$USER \
    bash --login

Let’s not forget to setup locale and time zone.

locale-gen --purge "en_US.UTF-8"
update-locale LANG=en_US.UTF-8 LANGUAGE=en_US
dpkg-reconfigure --frontend noninteractive locales

dpkg-reconfigure tzdata

Now we’re ready to onboard the latest Linux image and the boot environment packages.

apt install --yes --no-install-recommends linux-image-generic linux-headers-generic \
    --yes initramfs-tools cryptsetup keyutils grub-efi-amd64-signed shim-signed tasksel

Since we’re dealing with encrypted data, we should auto mount it via crypttab. If there are multiple encrypted drives or partitions, keyscript really comes in handy to open them all with the same password. As it doesn’t have negative consequences, I just add it even for a single disk setup.

echo "${HOST^}  UUID=$(blkid -s UUID -o value $DISK-part3)  none \
    luks,discard,initramfs,keyscript=decrypt_keyctl" >> /etc/crypttab
cat /etc/crypttab

To mount boot and EFI partition, we need to do some fstab setup too:

echo "UUID=$(blkid -s UUID -o value /dev/mapper/${HOST^}) \
    / ext4 noatime,nofail,x-systemd.device-timeout=5s 0 1" >> /etc/fstab
echo "PARTUUID=$(blkid -s PARTUUID -o value $DISK-part2) \
    /boot ext4 noatime,nofail,x-systemd.device-timeout=5s 0 1" >> /etc/fstab
echo "PARTUUID=$(blkid -s PARTUUID -o value $DISK-part1) \
    /boot/efi vfat noatime,nofail,x-systemd.device-timeout=5s 0 1" >> /etc/fstab
cat /etc/fstab

Now we update our boot environment.

KERNEL=`ls /usr/lib/modules/ | cut -d/ -f1 | sed 's/linux-image-//'`
update-initramfs -u -k $KERNEL

Grub update is what makes EFI tick.

sed -i "s/^GRUB_CMDLINE_LINUX_DEFAULT.*/GRUB_CMDLINE_LINUX_DEFAULT=\"quiet splash mem_sleep_default=deep\"/" /etc/default/grub
update-grub
grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=Ubuntu \
    --recheck --no-floppy

Finally we install out GUI environment. I personally like ubuntu-desktop-minimal but you can opt for ubuntu-desktop. In any case, it’ll take a considerable amount of time.

tasksel install ubuntu-desktop-minimal

Short package upgrade will not hurt.

apt update ; apt dist-upgrade --yes

The only remaining task before restart is to create the user, assign a few extra groups to it, and make sure its home has correct owner.

sudo adduser --disabled-password --gecos '' $USER
usermod -a -G adm,cdrom,dip,lpadmin,plugdev,sudo $USER
echo "$USER ALL=NOPASSWD:ALL" | sudo tee /etc/sudoers.d/$USER >/dev/null
passwd $USER

Before finishing it up, I like to install Surface Go WiFi and backlight tracer packages. This will allow for usage of wireless once we boot into installed system and for remembering light level between plugged/unplugged states.

wget -O /tmp/surface-go-wifi_amd64.deb \
    https://www.medo64.com/download/surface-go-wifi_0.0.5_amd64.deb
apt install --yes /tmp/surface-go-wifi_amd64.deb

wget -O /tmp/backlight-tracer_amd64.deb \
    https://www.medo64.com/download/backlight-tracer_0.1.1_all.deb
apt install --yes /tmp/backlight-tracer_amd64.deb

As install is ready, we can exit our chroot environment.

exit

And cleanup our mount points.

umount /mnt/install/boot/efi
umount /mnt/install/boot
mount | grep '/mnt/install/' | tac | awk '/\/mnt/ {print $3}' | xargs -i{} umount -lf {}
umount /mnt/install

After the reboot you should be able to enjoy your installation. Seeing errors is fine - just reboot manually if stuck.

reboot

Once booted I like to setup suspend to react on power button and and to disable automatic brightness changes.

gsettings set org.gnome.settings-daemon.plugins.power button-power 'suspend'
gsettings set org.gnome.settings-daemon.plugins.power power-button-action 'suspend'
gsettings set org.gnome.settings-daemon.plugins.power ambient-enabled 'false'
gsettings set org.gnome.mutter experimental-features "['x11-randr-fractional-scaling']"

My preferred scale factor is 150% (instead of default 200%) but you’ll need to change that in settings manually.

BareCam

Illustration

I had an interesting problem. I wanted another monitor. But I didn’t want to buy it. For one, I just needed it just temporarily. Secondly, I needed it to be mobile so any properly sized monitor was out of question.

With that in mind, I took inventory of things I had and came upon an idea. I already have HDMI capture USB and I already have Surface Go which surely looks like a mini monitor. If I connect my laptop via HDMI to the capture card and use webcam to show capture output, I essentially have an HDMI-to-monitor connection.

I tried it and it worked wonderfully. Almost.

As my Surface Go runs Ubuntu, I have tried multiple Linux webcam applications and neither of them worked as I wanted. I simply could not find any that would work in full screen. In addition to losing screen real estate, I also noted other issue - darn cursor was visible. None of the webcams would turn off cursor while running.

After going over every webcam application I could find and finding a fault for each, I finally decided to build my own.

This is as simple as webcam software gets. When started, it will display the first webcam while keeping cursor hidden. If you press Space, it will switch to the next webcam. Essentially everything I needed.

And yes, I did complicate it a bit more later. I added a support for windowed mode, going as far to allow alignment to any screen corner (try keys 0-9) so you can have “head-in-the-corner” effect. I also added a few configurable settings and will probably add a few more with time. However, the idea is to keep it as simple as possible.

Application supports both Linux and Windows. If you’re in need for something like that, give it a try.

Building a Gaming PC

This year Christmas project for my son and me became building a PC. He got eager to game a bit more and laptop he has wasn’t sufficient anymore. So, after more than 20 years being a laptop-only household (yes, not counting servers), we went onto building a PC.

Our goal was to build something that would give a better gaming performance than his 940M-based laptop (ok, that wasn’t that hard), allow playing most titles on 1080p, but still not drain the bank. As we went onto component selection, we went back and forth multiple times as we analyzed how things fit, whether they were giving enough bang-for-the-buck, and how available they were at a decent price range.

Literally the first decision was selecting a case. After looking at price s and motherboard support, we decided to go with micro-ATX. Every single manufacturer had bunch of motherboards in this format and we found many cases in that size were both reasonably sized and at a reasonable cost. After watching a lot of YouTube videos (e.g., ScatterVolt and Gamers Nexus) we decided on darkFlash DLM21 with a mesh front.

This case was the right size, the right features, and the right looks. Of course you cannot expect wonders from the case that’s under $100 so there are a lot of things that could be done better. However, it does offer enough airflow and it has ample space for both current PC build and its further expansions.

Unfortunately, this case doesn’t come with any fans so those looks ended up costing a bit more than expected. I don’t consider it a breaking deal as most case fans are really shitty when it comes to noise so we would probably replace them anyway. We longed for Noctua but decided on Artic P12/14 at the end. It’s almost as quiet as Noctua but at a fraction of a cost. Even though case has enough space for 5 fans, we only decided on one push-pull pair. We could always add another front fan later. While using pressure-optimized fans might not be needed in this case, they were a bit cheaper and came in black.

Power supply was an easy decision. The cheapest one from trusty source with 80+ rating would do. We opted for EVGA as it was the cheapest 500 W power supply that had a single 12V rail and a over-temperature protection. We did toy with an idea of grabbing something more powerful for the sake of the upgradability but decided against it. Any future upgrade needing more power will probably also include newer devices and maybe motherboard. With Intel’s 12V standard creeping in, spending too much money on “maybe” seemed unnecessary. Unfortunately, the old-style colored cables (albeit in black mesh) are not the best sight. But we’ll survive. :)

All this was selected before we had to decide if we want to be a team Intel or team AMD. And we easily went with AMD. Intel does have their offering in our desired price range but there was a slightly cheaper or more performant AMD no matter where we looked. While processor availability proved to be an issue with AMD we had enough time for that not to matter much.

We spent hours and hours browsing over all budget motherboards only to decide between two ASRock offers. One was B450M Pro4 due to it’s better than average VRMs and a really reasonable cost. Works perfectly with Ryzen 2 and (after BIOS upgrade) with Ryzen 3 CPUs. Based on recent reviews, BIOS update is pretty much done for any board currently on sale. No wonder it’s part of many budget builds.

But we decided to splurge a few dollars more and go for B550M variant of the same. Major reason was a newer chipset that should give us a bit more lifetime out of it while keeping reasonably good (albeit simplistic) VRM. Since most boards in that price range either don’t include wireless or have just a basic one, we appreciated this one including cutouts for antenna alongside M.2 E-type slot. This essentially allows us to use any laptop M.2 WiFi we chose and upgrade over its lifetime. Unfortunately, this board doesn’t support Ryzen 2 so no 2600 here.

We did spend some time looking at other motherboards too - especially Gigabyte and MSI offering - but they were always more expensive and with less features as compared to ASRock. Yes, build quality was better for many of them and ASRock is known for their “optimism” when it comes to board shutdown protection. In the end we decided that going with slightly higher ASRock was better than going with low-end Gigabyte/MSI for the same price.

We also looked at A520 chipset boards as this would have been a good fit on paper for a budget PC. Alas, time was not right for this as availability was spotty, features were limited, and price was comparable with B550 boards. At the same price point, B550 wins every time.

Processor decision was hard as 3100 was realistically good enough for what we needed. It’s a proven processor that can handle pretty much anything this computer would be used for. We decided to go slightly higher with AMDs 3300X counting that two months would be enough time to get one. Unfortunately, that wasn’t true as the only scalpers had it in stock. In the end we went with 3600 because we could get it at MSRP.

It took us a long time to decide what to do about CPU fan. On one hand, AMD CPUs come with more than capable stock fan. On other hand, that thing is not the most quiet out there. At the end we opted to go with Arctic Freezer 7 X. Realistically, it’s a minimal upgrade when it comes to temperatures. When it comes to the noise level, things are slightly different. If you value reducing noise levels on a budget, this one is a great deal. And yes, for a budget build, this is probably the most dubious choice as we could have as easily gone with the stock fan.

Choice of memory was annoying at best. AMD is a bit picky about memory and any incompatibility is usually accompanied by crashes. My Epyc-based file server crashed a few times a day no matter how I adjusted timings until I finally gave up and bought the new memory. Yes, the newer generations had some improvements but selecting memory is a still task that needs to be considered carefully. While on motherboard pages you can see explicitly validated modules, I found these are both way too expensive for what they are and way too limited in selection. Finding the exact match was an exercise in futility. I felt like goldilocks as my bed was either too soft or too hard. And I wasn’t as fortunate as she was to find it just right.

On micro-ATX boards one also has memory selection restricted by height. While our current CPU fan left quite a bit of space for memory, changing it in future might cause problem with the clearance. To future-proof the system, we self-restricted to modules that were on a shorter side. Also, modules had to come in pairs to make use of a dual channel but also having just two modules was slightly favored as compared to populating all four as most of budget boards daisy-chain their modules and thus there is a latency increase when all DIMMs are occupied.

Finding the memory based on QVL was easier for some brands as compared to other. Crucial was impossible to corelate while ThermalTake was as easy as it gets. Unfortunately, as it often happens with ThermalTake, modules were “almost” good. One kit was annoyingly flashy and other was intended for water cooling.

Final selection wasn’t directly from ASRock QVL but we went close. List contains both HyperX Fury (HX432C16FB3/8) and HyperX Predator (HX436C17PB4/8) in their single module configuration. We narrowed our choice between kit versions of the same. From motherboard perspective, there should be literally no difference so one might say we followed the rules in their spirit. The final selection between Fury (3200 MHz) and Predator (3600 MHz) modules was a difficult one. So we selected neither. :)

After watching a few videos about terminology and overclocking, we decided on G.Skill RipJaws V (3600 MHz). Yes, that wasn’t in the list of officially supported memory on ASRock pages - but B550M Pro4 is listed G.Skill’s page. Yes, that memory is taller than either HyperX (42 mm vs 32 mm) - but our CPU cooler would have enough clearance. Since price (lower than HyperX Predator) and coolness factor were acceptable, we decided to screw our own rules and go for it.

And not. We didn’t install that memory either as NewEgg package got lost in the mail - literally. So, after another research round, we switched to Crucial Ballistix (BL2K8G36C16U4B) kit. And no, this memory is not in motherboard’s QVL list. However, Crucial does claim it’s compatible so we decided to give it a try. And CL16-18-18-38 timings are actually not too bad making it a nice fit with Ryzen out of the box.

For now, 16 GB will be enough and there are 2 empty DIMM slots still remaining for further upgrade at the cost of a 1 clock latency.

For storage we toyed with an idea of 4.0 NVMe as both selected motherboard and CPU would support it. But considering price was doubled as compared to more standard 3.0, we decided to cheap out. We selected a reasonably decent NVMe SSD that was cheapest at the time of purchase. More specifically, we selected Samsung 970 Evo 500 GB in 500 GB capacity. Since we bought it after 970 Evo Plus was already out, we managed to grab it at a reasonable price. As a secondary drive we went with spinning rust and old 2 TB SpinPoint I had lying around. And yes, I didn’t include this in the price. :)

Graphic card selection was essentially just between Radeon RX 580 and GeForce GTX 1650 Super. Both are close in performance and in price. However, at the time of buying prices for RX 580 were going into stratosphere while GTX 1650 Super remained in sub-$200 range. Out of all GeForce cards we ended up going with MSI’s Gaming X as it was one of the most quiet graphic cards available under load and it would even turn off fan completely when not gaming.

In M.2 E-key slot we placed Intel’s 9260NGW. We selected this card purely because we already owned it and thus would save $20 that we would need to pay for a new AX200. We also had Killer 1535 card but Intel had Bluetooth 5.1. For wireless to be complete, we had to buy pigtail reaching M.2 slot. Length of 30 cm was sufficient to reach the rear panel and from there we went with cable to the external antenna.

All in all, from the moment of decision to having computer running, it took us 2 months. A solid week was spent selecting desired components alongside with 1st and 2nd runner-up. And then we just waited for components to drop in price or, in case of Ryzen CPUs, to become available. Not everything was bought at an optimal cost. For example, we overpaid for CPU just because our desired model wasn’t available. Graphic card with the same performance profile was available at lower cost if we went with a bit louder fan configuration. Furthermore, we could have saved another $65 or so by skipping CPU cooler, downgrading motherboard (to B450M), and downgrading memory speed (to DDR4-3200). But we didn’t. :)

All said and done, it was a fun project, a decent machine too boot, and it will hopefully serve well into the future.

Here is the full table of components used:

ComponentSelectedPrice
CasedarkFlash DLM21 MESH$60
Front case fanArctic P14 PWM$15
Rear case fanArctic P12 PWM$10
Power supplyEVGA 500 W1$40
CPUAMD Ryzen 5 3600$200
CPU FanArctic Freezer 7 X$25
MotherboardASRock B550M Pro4$85
MemoryCrucial Ballistix DDR4-3600 (2x8GB)$75
GPUMSI’s GeForce GTX 1650 Super Gaming X$190
Storage (1)Samsung 970 Evo 500 GB$60
Storage (2)Seagate Spinpoint M9T 2TB$0
WirelessIntel 9260NGW$0
Wireless (cable)NGFF antenna with pigtail$5
Wireless (antenna)NGFF antenna with pigtail$15
TOTAL$780