13 Commits

23 changed files with 779 additions and 32 deletions

View File

@@ -5,3 +5,9 @@ theme = "vanilla"
pygmentsCodeFences = true
pygmentsStyle = "github"
summaryLength = 20
[markup]
[markup.tableOfContents]
endLevel = 4
ordered = false
startLevel = 3

View File

@@ -1,8 +1,8 @@
---
title: About
---
I'm Daniel, a Computer Science student currently in my third (and final) undergraduate year at Oregon State University.
Due my initial interest in calculators and compilers, I got involved in the Programming Language Theory research
I'm Daniel, a Computer Science student currently working towards my Master's Degree at Oregon State University.
Due to my initial interest in calculators and compilers, I got involved in the Programming Language Theory research
group, gaining same experience in formal verification, domain specific language, and explainable computing.
For work, school, and hobby projects, I use a variety of programming languages, most commonly C/C++,

View File

@@ -0,0 +1,280 @@
---
title: Rendering Mathematics On The Back End
date: 2020-07-15T15:27:19-07:00
draft: true
tags: ["Website", "Nix", "Ruby", "KaTeX", "Hugo"]
---
Due to something of a streak of bad luck when it came to computers, I spent a
significant amount of time using a Linux-based Chromebook, and then a
Pinebook Pro. It was, in some way, enlightening. The things that I used to take
for granted with a 'powerful' machine now became a rare luxury: StackOverflow,
and other relatively static websites, took upwards of ten seconds to finish
loading. On Slack, each of my keypresses could take longer than 500ms to
appear on the screen, and sometimes, it would take several seconds. Some
websites would present me with a white screen, and remain that way for much
longer than I had time to wait. It was awful.
At one point, I installed uMatrix, and made it the default policy to block
all JavaScript. For the most part, this worked well. Of course, I had to
enable JavaScript for applications that needed to be interactive, like
Slack, and Discord. But for the most part, I was able to browse the majority
of the websites I normally browse. This went on until I started working
on the [compiler series]({{< relref "00_compiler_intro.md" >}}) again,
and discovered that the LaTeX math on my page, which was required
for displaying things like inference rules, didn't work without
JavaScript. I was left with two options:
* Allow JavaScript, and continue using MathJax to render my math.
* Make it so that the mathematics is rendered on the back end.
I've [previously written about math rendering]({{< relref "math_rendering_is_wrong.md" >}}),
and made the observation that MathJax's output for LaTeX is __identical__
on every computer. From the MathJax 2.6 change log:
> _Improved CommonHTML output_. The CommonHTML output now provides the same layout quality and MathML support as the HTML-CSS and SVG output. It is on average 40% faster than the other outputs and the markup it produces are identical on all browsers and thus can also be pre-generated on the server via MathJax-node.
It seems absurd, then, to offload this kind of work into the users, to
be done over and over again. As should be clear from the title of
this post, this made me settle for the second option: it was
__obviously within reach__, especially for a statically-generated website
like mine, to render math on the backend.
I settled on the following architecture:
* As before I would generate my pages using Hugo.
* I would use the KaTeX NPM package to rendering math.
* To build the website no matter what computer I was on, I would use Nix.
It so happens that Nix isn't really required for using my approach in general.
I will give my setup here, but feel free to skip ahead.
### Setting Up A Nix Build
My `default.nix` file looks like this:
```Nix {linenos=table}
{ stdenv, hugo, fetchgit, pkgs, nodejs, ruby }:
let
url = "https://dev.danilafe.com/Web-Projects/blog-static.git";
rev = "<commit>";
sha256 = "<hash>";
requiredPackages = import ./required-packages.nix {
inherit pkgs nodejs;
};
in
stdenv.mkDerivation {
name = "blog-static";
version = rev;
src = fetchgit {
inherit url rev sha256;
};
builder = ./builder.sh;
converter = ./convert.rb;
buildInputs = [
hugo
requiredPackages.katex
(ruby.withPackages (ps: [ ps.nokogiri ]))
];
}
```
I'm using `node2nix` to generate the `required-packages.nix` file, which allows me,
even from a sandboxed Nix build, to download and install `npm` packages. This is needed
so that I have access to the `katex` binary at build time. I fed the following JSON file
to `node2nix`:
```JSON {linenos=table}
[
"katex"
]
```
The Ruby script I wrote for this (more on that soon) required the `nokigiri` gem, which
I used for traversing the HTML generated for my site. Hugo was obviously required to
generate the HTML.
### Converting LaTeX To HTML
After my first post complaining about the state of mathematics on the web, I received
the following email (which the author allowed me to share):
> Sorry for having a random stranger email you, but in your blog post
[(link)](https://danilafe.com/blog/math_rendering_is_wrong) you seem to focus on MathJax's
difficulty in rendering things server-side, while quietly ignoring that KaTeX's front
page advertises server-side rendering. Their documentation [(link)](https://katex.org/docs/options.html)
even shows (at least as of the time this email was sent) that it renders both HTML
(to be arranged nicely with their CSS) for visuals and MathML for accessibility.
This is a great point, and KaTeX is indeed usable for server-side rendering. But I've
seen few people who do actually use it. Unfortunately, as I pointed out in my previous post on the subject,
few tools remain that provide the software that actually takes your HTML page and substitutes
LaTeX for math.
> [In MathJax,] The bigger issue, though, was that the `page2html`
program, which rendered all the mathematics in a single HTML page,
was gone. I found `tex2html` and `text2htmlcss`, which could only
render equations without the surrounding HTML. I also found `mjpage`,
which replaced mathematical expressions in a page with their SVG forms.
This is still the case, in both MathJax and KaTeX. The ability
to render math in one step is the main selling point of front-end LaTeX renderers:
all you have to do is drop in a file from a CDN, and voila, you have your
math. There are no such easy answers for back-end rendering.
So what _do_ I do? Well, there are two types on my website: inline math and display math.
On the command line ([here are the docs](https://katex.org/docs/cli.html)),
the distinction is made using the `--display-mode` argument. So, the general algorithm
is to replace the code inside the `$$...$$` with their display-rendered version,
and the code inside the `\(...\)` with the inline-rendered version. I came up with
the following Ruby function:
```Ruby {linenos=table}
def render_cached(cache, command, string, render_comment = nil)
cache.fetch(string) do |new|
puts " Rendering #{render_comment || new}"
cache[string] = Open3.popen3(command) do |i, o, e, t|
i.write new
i.close
o.read.force_encoding(Encoding::UTF_8).strip
end
end
end
```
Here, the `cache` argument is used to prevent re-running the `katex` command
on an equation that was already rendered before (the output is the same, after all).
The `command` is the specific shell command that we want to invoke; this would
be either `katex` or `katex -d`. The `string` is the math equation to render,
and the `render_comment` is the string to print to the console instead of the equation
(so that long, display math equations are not printed out to standard out).
Then, given a substring of the HTML file, we use regular expressions
to find the `\(...\)` and `$$...$$`s, and use the `render_cached` method
on the LaTeX code inside.
```Ruby {linenos=table}
def perform_katex_sub(inline_cache, display_cache, content)
rendered = content.gsub /\\\(((?:[^\\]|\\[^\)])*)\\\)/ do |match|
render_cached(inline_cache, "katex", $~[1])
end
rendered = rendered.gsub /\$\$((?:[^\$]|$[^\$])*)\$\$/ do |match|
render_cached(display_cache, "katex -d", $~[1], "display")
end
return rendered
end
```
There's a bit of a trick to the final layer of this script. We want to be
really careful about where we replace LaTeX, and where we don't. In
particular, we _don't_ want to go into the `code` tags. Otherwise,
it wouldn't be able to talk about LaTeX code! Thus, we can't just
search-and-replace over the entire HTML document; we need to be context
aware. This is where `nokigiri` comes in. We parse the HTML, and iterate
over all of the 'text' nodes, calling `perform_katex_sub` on all
of those that _aren't_ inside code tags.
Fortunately, this is pretty easy to specify thanks to something called XPath.
This was my first time encountering it, but it seems extremely useful: it's
a sort of language for selecting XML nodes. First, you provide an 'axis',
which is used to specify the positions of the nodes you want to look at
relative to the root node. The axis `/` looks at the immediate children
(this would be the `html` tag in a properly formatted document, I would imagine).
The axis `//` looks at all the transitive children. That is, it will look at the
children of the root, then its children, and so on. There's also the `self` axis,
which looks at the node itself.
After you provide an axis, you need to specify the type of node that you want to
select. We can write `code`, for instance, to pick only the `<code>....</code>` tags
from the axis we've chosen. We can also use `*` to select any node, and we can
use `text()` to select text nodes, such as the `Hello` inside of `<b>Hello</b>`.
We can also apply some more conditions to the nodes we pick using `[]`.
For us, the relevant feature here is `not(...)`, which allows us to
select nodes that do __not__ match a particular condition. This is all
we need to know.
We write:
* `//`, starting to search for nodes everywhere, not just the root of the document.
* `*`, to match _any_ node. We want to replace math inside of `div`s, `span`s, `nav`s,
all of the `h`s, and so on.
* `[not(self::code)]` cutting out all the `code` tags.
* `/`, now selecting the nodes that are immediate descendants of the nodes we've selected.
* `text()`, giving us the text contents of all the nodes we've selected.
All in all:
```
//*[not(self::code)]/text()
```
Finally, we use this XPath from `nokigiri`:
```Ruby {linenos=table}
files = ARGV[0..-1]
inline_cache, display_cache = {}, {}
files.each do |file|
puts "Rendering file: #{file}"
document = Nokogiri::HTML.parse(File.open(file))
document.search('//*[not(self::code)]/text()').each do |t|
t.replace(perform_katex_sub(inline_cache, display_cache, t.content))
end
File.write(file, document.to_html)
end
```
I named this script `convert.rb`; it's used from inside of the Nix expression
and its builder, which we will cover below.
### Tying it All Together
Finally, I wanted an end-to-end script to generate HTML pages and render the LaTeX in them.
I used Nix for this, but the below script will largely be compatible with a non-Nix system.
I came up with the following, commenting on Nix-specific commands:
```Bash {linenos=table}
source $stdenv/setup # Nix-specific; set up paths.
# Build site with Hugo
# The cp is Nix-specific; it copies the blog source into the current directory.
cp -r $src/* .
hugo --baseUrl="https://danilafe.com"
# Render math in HTML and XML files.
# $converter is Nix-specific; you can just use convert.rb.
find public/ -regex "public/.*\.html" | xargs ruby $converter
# Output result
# $out is Nix-specific; you can replace it with your destination folder.
mkdir $out
cp -r public/* $out/
```
This is it! Using the two scripts, `convert.rb` and `builder.sh`, I
was able to generate my blog with the math rendered on the back-end.
Please note, though, that I had to add the KaTeX CSS to my website's
`<head>`.
### Caveats
The main caveat of my approach is performance. For every piece of
mathematics that I render, I invoke the `katex` command. This incurs
the penalty of Node's startup time, every time, and makes my approach
take a few dozen seconds to run on my relatively small site. The
better approach would be to use a NodeJS script, rather than a Ruby one,
to perform the conversion. KaTeX also provides an API, so such a NodeJS
script can find the files, parse the HTML, and perform the substitutions.
I did quite like using `nokigiri` here, though, and I hope that an equivalently
pleasant solution exists in JavaScript.
Re-rendering the whole website is also pretty wasteful. I rarely change the
mathematics on more than one page at a time, but every time I do so, I have
to re-run the script, and therefore re-render every page. This makes sense
for me, since I use Nix, and my builds are pretty much always performed
from scratch. On the other hand, for others, this may not be the best solution.
### Conclusion
With the removal of MathJax from my site, it is now completely JavaScript free,
and contains virtually the same HTML that it did beforehand. This, I hope,
makes it work better on devices where computational power is more limited.
I also hope that it illustrates a general principle - it's very possible,
and plausible, to render LaTeX on the back-end for a static site.

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 476 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 204 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

View File

@@ -0,0 +1,384 @@
---
title: DELL Is A Horrible Company And You Should Avoid Them At All Costs
date: 2020-07-17T16:23:34-07:00
draft: true
tags: ["Electronics"]
---
I really do not want this to be a consumer electronics blog. Such things
aren't interesting to me, and nor do I have much knowledge
about them. However, sometimes, ripples from these areas make their way
into my life, and this is one such instance. Let me tell you
{{< sidenote "right" "source-note" "a story" >}}
I originally wrote about this in
<a href="https://www.dell.com/community/XPS/Ridiculously-Bad-Support-Experience/td-p/7554383">a thread on DELL's support website</a>. Some of this post is
going to be adapted from the support website, but some things have happened
since. You will probably notice the change between the terse language I used
in the original post and the fresh text that I'm writing now.
{{< /sidenote >}} of
my experience with DELL and their XPS 2-in-1 laptop, which has gone on since
around January of 2020, and is still going at the time of writing, in July
2020, half a year later.
I was, until recently, an undergraduate student in Computer Science. I will
soon be starting my Masters in Computer Science, too. I say this to make one
thing clear: I need a computer. Not only is it a necessity for my major,
but the majority of my hobbies -- including this blog -- are digital, too.
Since my university is a couple of hours from my home, I travel back and forth
a lot. I also have a cozy little spot in the
{{< sidenote "right" "offices-note" "graduate student offices" >}}
They're a bunch of cubicles in a keycard-protected room, really. Nothing fancy.
{{< /sidenote >}}at my university, but travel by bus, so I find myself spending
roughly equal portions of my work time at home and 'elsewhere'. A laptop
as my primary machine, I thought, made sense. But it had to be a decent one.
Persuaded by one of my instructors, who stressed the importance of vision and
a decent screen, I settled on a DELL XPS, which at the time came with a 4k
display.
As is commonplace, things went great at first. The screen _was_ really nice,
all of my code compiled swiftly, and even the games I occasionally played ran
at a solid 60fps. I was happy with my purchase.
There was one hiccup before things went really downhill, a sort of
foreshadowing of things to come. My trackpad didn't work at peculiar times.
### Prologue: Trackpad Hiccups
While working, booted into Linux, I noticed that my trackpad was having some
trouble. It was stuttering, and occasionally wouldn't work at all for seconds
at a time. I assumed that this was a problem with the trackpad drivers on
Linux, or perhaps the whole system was freezing up. I rebooted, and the
problem went away.
Until it came back.
A few days later, my trackpad was freezing virtually every minute.
It was strange, but fortunately, I'm used to a keyboard-based workflow, and
the malfunctions did not affect me too much. It was just a little troubling.
What soon made it more troubling, was that I noticed this exact same issue
occurring on Windows. To me, this meant one dreadful thing: it was a hardware
issue.
I poked and prodded for a little bit, and finally discovered the cause:
whenever I put my hand on the left palmrest, the trackpad would reliably stop
working. Knowing what the issue was, I called DELL. I spoke to a guy on the
other end, who had me run through diagnostics, driver updates, and BIOS
settings (I imagined this was procedure, so I didn't mind doing the extra
work to make the other guy's job easier). Finally, he scheduled a repair
appointment. A technician came into my house, took off the laptop cover,
and said something along the lines of:
> Now look. They gave me a whole new motherboard and case to replace yours,
but in my personal opinion, this is a bad idea. Things are bound to break
when you do this. See how the replacement case has an insulating piece
of fabric under the left palmrest, and yours doesn't? Why don't we rip
the fabric off the replacement case, and tape it in place on your machine,
without any reassembly?
This man was wiser than any of the other DELL technicians, I now understand.
The repair went without a hitch. He grilled me for going to college instead of
just picking up a trade, which was cheaper and offered more job security.
In the end, I felt a little weird about having a piece of fabric duct taped
inside my computer, but the trackpad had no more issues ever since. All was
well.
### Service Request 1: Broken D Key
All was well, that is, until the middle of winter term. I was typing up an
assignment for a university class. I was working as usual, when I suddenly
noticed that the "d" key stopped working - it had to be pressed rather weird
to register on the computer. I looked down, and discovered that the key had
snapped in half. The top part of the key fell off shortly thereafter.
{{< figure src="brokenkey.jpg" caption="The broken D key shortly after the above events." >}}
At that point, I was more surprised than anything. I hadn't heard of something
like this ever happening, especially under circumstances as normal as typing.
Regardless, I contacted support, and set up a repair appointment. Things only
went downhill from there.
Again, the appointment was scheduled, and only a few days later, another
technician arrived at my house. The only way to repair the key, he said,
was to replace the whole keyboard. They keyboard happens to be located
underneath all the other hardware, and so, the entire laptop had to be
disassembled and reassembled from scratch. He worked for about an hour, and
eventually, he put the machine together. The words of the previous
technician, who wanted to avoid doing exactly what had just been done, echoed
in my head:
> Things are bound to break when you do this.
I asked him to test it, just to make sure everything works. Sure enough,
not everything did work: the machine no longer had sound!
### Service Request 2: No sound
During diagnostics, the laptop did not emit the "beep" it usually does. This
was the first sign. Booting into Windows, the sound icon was crossed out in
red, and no sound was present. Booting into Linux led to similar results.
The microphone on the machine did not seem to work either. The service
technician said that he didn't have the parts to repair it, told me he'd call
it in, and left. Soon after, I got an email asking for times I'm available to
call: I said "any time except for 1-4 pacific time". DELL support proceeded
to call me at 3pm pacific time, when I had no service. Unable to reach me,
they promptly notified me that they are archiving my service request.
This all occurred near finals week at my university, so I had to put the issue
on hold. I had to maintain my grades, and I had to grade heaps of assignments
from other students. Though the lack of sound was annoying, it wasn't as
pressing as preparing for exams, so it was during spring break that I finally
called again, and scheduled the service appointment. By then,
{{< sidenote "right" "pandemic-note" "the pandemic was in full swing," >}}
Just for posterity, in 2020, there had been an outbreak of COVID-19,
a Coronavirus. Many states in the U.S., including my own, issued
the orders for lockdown and social distancing, which meant the closing
of schools, restaurants, and, apparently, the cessation of in-person
repairs.
{{< /sidenote >}}and DELL told me they'd mail me a box to put my laptop in, and
I'd have to mail it off to their service center. Sure, I thought, that's
fine. If it's at the service center, they won't ever "not have the required
parts". I told the tech support person my address, he read it back to me, and
so it was settled.
Until, that is, the box arrived at the wrong address.
I had received the machine as a gift from my family, who purchased the
computer to arrive at their address. The box arrived at that address too,
despite my explicit instructions to have it deliver to my current residence.
Since my family and I live 2 hours apart, it took 4 total hours to get the box
to me (a drive that couldn't be made right away!), and by the time I had it,
DELL was already threatening me again with closing the service request.
Eventually, I was able to mail the machine off, and about 5 business days
later (business days during which I did not have a working machine, which is
very necessary for my school and job) I received it back. I was excited to
have the machine back, but that didn't last very long. As I was using the
computer with Wolfram Mathematica (a rather heavy piece of software running
under Linux), I noticed that it was discharging even while plugged in. I
booted into Windows, and was greeted with a warning, something along the
lines of: "you are using a slow charger. Please use the official adapter".
But I was using the official adapter! I also tried to plug my mouse into the
relevant USB-C port, only to discover that it did not work. I had to make
another service requests.
### Service Request 3: Broken Charging Port
This time, I made sure to tell the person on the other end of the support
call to please send it to my address. I asked if there was anything I can do,
or anyone I can contact, and was told "no, just mail the computer in again."
I obliged. The box arrived at the right address this time, so I was able to
ship it off.
In the "describe your issue" field on the provided form, I begged the
technicians to send me a working machine. "Please", I wrote "Last time I got
a machine back from support, it was still broken. I really need it for school
and work!". 5 business days later, I received the machine back. I plugged it
in to make sure it worked, only to find out . . . that the very same charging
port that I requested be repaired, is still broken! It would've been funny,
if it wasn't infuriating. How is it possible for me to receive a machine from
repairs, without the thing I asked to repair being as much as improved?!
Worse, a day after I received the machine back (I was able to keep using it
thanks to it having two USB-C ports capable of charging), the LCD suddenly
flashed, and started flickering. Thinking it was a software glitch, I
restarted the machine, only to discover the same flickering during the boot
animation and menu. Not only was the charging port not repaired, but now my
LCD was broken! (in the below picture, the screen is meant to be blue, but
the bottom part of the display is purple and flickering).
{{< figure src="brokenlcd.jpg" caption="The broken LCD." >}}
### Service Request 4: Broken LCD
I called in to support again, and they once again told me to ship the machine
off. What's worse, they accused me of breaking the port myself, and told me
this was no longer covered under basic warranty. I had to explain all over
again that the port worked fine before the fateful day the D-key snapped. They
told me they'd "look into it". Eventually, I received a box in the mail. I
wasn't told I would be receiving a box, but that wasn't a big deal. I mailed
off the machine.
The UPS shipping was always the most streamlined part of the process. A day
later, I was told my machine was received intact. Another day, and I was
informed that the technicians are starting to work on it. And then,
a few hours later:
> __Current Status:__
> The part(s) needed to repair your system are not currently in stock.
> __What's Next:__
> In most cases the parts are available is less than five days.
A few days is no big deal, and it made sense that DELL wouldn't just
have screens lying around. So I waited. And waited. And waited. Two weeks
later, I got a little tired of waiting, and called the repair center.
An automated message told me:
> We're currently experiencing heavy call volumes. Please try again later. Goodbye.
And the call was dropped. This happened every time I tried to call, no matter
the hour. The original status update -- the one that notified me about the
part shortage -- came on May 8th, but the machine finally arrived to me
(without prior warning) on June 2nd, almost a month later.
The charging port worked. Sound
worked. The screen wasn't flickering. I was happy for the brief moments that
my computer was loading. As soon as I started vim, though, I noticed something
was off: the fonts looked more pixelated. The DPI settings I'd painstakingly
tweaked were wrong. Now that I thought about it, even the GRUB menu was
larger. My suspicion growing, I booted into Windows, and looked at the display
settings. Noticeably fewer resolutions were listed in the drop-down menu;
worse, the highest resolution was 1080p. After almost a month of waiting,
DELL replaced my 4k laptop display with a 1080p one.
### System Replacement: Worse LCD Screen
I admit, I was angry. At the same time, the absurdity of it all was also
unbearable. Was this constant loop of hardware damage, the endless number of
support calls filled with hoarse jazz music, part of some kind of Kafkaesque
dream? I didn't know. I was at the end of my wits as to what to do. As a last
resort, I made a tweet from my almost-abandoned account. DELL Support's Twitter
account quickly responded, eager as always to destroy any semblance of
transparency by switching to private messages:
{{< tweet 1268064691416334344 >}}
I let them know my thoughts on the matter. I wanted a new machine.
{{< figure src="dm_1.png" caption="The first real exchange between me and DELL support." >}}
Of course we can proceed further. I wanted to know what kind of machine I was getting,
though. As long as it was the same model that I originally bought,
{{< sidenote "right" "replacement-note" "it would be better than what I have." >}}
At least in principle, it would be. Perhaps the wear and tear on the replacement
parts would be greater, but at least I would have, presumably, a machine
in good condition that had the 4k screen that made me buy it in the first place.
{{< /sidenote >}}
Despite this, I knew that the machine I was getting was likely refurbished.
This _had_ to mean that some of the parts would come from other, used, machines.
This irked me, because, well, I payed for a new machine.
{{< figure src="dm_2.png" caption="Ah, the classic use of canned responses." >}}
Their use of the canned response, and their unwillingness to answer this simple
question, was transparent. Indeed, the machine would be made of used
parts. I still wanted to proceed. DELL requested that I sent an image of
my machine which included its service tag, together with a piece of
paper which included my name and email address. I obliged, and quickly got a response:
{{< figure src="dm_3.png" caption="If it was me who was silent for 4 days, my request would've long been cancelled. " >}}
Thanks, Kalpana. Try to remember that name; you will never hear it again, not in this post.
About a week later, I get the following beauty:
{{< figure src="dm_4.png" caption="Excuse me? What's going on?" >}}
My initial request was cancelled? Why wasn't I told? What was the reason?
What the heck was going on at DELL Support? Should I be worried?
My question of "Why" was answered with the apt response of "Yes",
and a message meant to pacify me. While this was going on, I ordered
a
{{< sidenote "right" "pinebook-note" "Pinebook Pro." >}}
The Pinebook a $200 machine has, thus far, worked more reliably than any DELL product
I've had the misfortune of owning.
{{< /sidenote >}} It was not a replacement for the DELL machine, but rather
the first step towards migrating my setup to a stationary computer,
and a small, lightweight SSH device. At this point,
there was no more faith in DELL left in my mind.
Soon, DELL required my attention, only to tell me that they put in
a request to see that status of my request. How bureaucratic. Two
more names -- Kareem and JKC -- flickered through the chats,
also never to be seen again.
{{< figure src="dm_5.png" caption="Not much of a conversation, really." >}}
Finally, on July 9th (a month and six days after my first real message to DELL
support), I was notified by my roommates that FedEx tried to deliver a package
to our house, but gave up when no one came to sign for it. On one hand, this
is great: FedEx didn't just leave my laptop on the porch. On the other hand,
though, this was the first time I heard about receiving the machine. I got
to the house the next day, unpacked the new computer, and tested all the things
that had, at one point, failed. Everything seemed to work. I transfered all my
files, wiped the old computer clean, and mailed it off. I also spent some
time dealing with the fallout of DELL PremierColor starting on its own,
and permanently altering the color profile of my display. I don't have the
special, physical calibration device, and therefore still suspect that my
screen is somewhat green.
Today, I discovered that the microphone of the replacement machine didn't work.
### Am I The Problem?
When the mysterious FedEx package arrived at my door on July 9th, I did some
digging to verify my suspicion that it was from DELL. I discovered their
HQ in Lebanon, TN. This gave me an opportunity to
{{< sidenote "right" "reviews-note" "see" >}}
See, of course, modulo whatever bias arises when only those who feel strongly leave reviews.
{{< /sidenote >}} whether or not I was alone in this situation. I was genuinely
worried that I was suffering from the technical variant of
[Munchausen Syndrome](https://www.webmd.com/mental-health/munchausen-syndrome#1),
and that I was compulsively breaking my electronics. These worries were
dispelled by the reviews on Google:
{{< figure src="reviews_1.png" caption="Most of the reviews are pretty terse, but the ratings convey the general idea." >}}
There were even some that were shockingly similar in terms of the apparent
incompetence of the DELL technicians:
{{< figure src="reviews_2.png" caption="Now, now, Maggie, I wouldn't go as far as recommending Apple." >}}
So, this is not uncommon. This is how DELL deals with customers now. It's
awfully tiring, really; I've been in and out of repairs continuously for
almost half a year, now. That's 2.5% of my life at the time of writing,
all non-stop since the D-key. And these people probably have spent considerable
amounts of time, too.
### It's About the Principle
The microphone on my machine is rather inconsequential to me. I can, and regularly do,
teleconference from my phone (a habit that I developed thanks to DELL, since
my computer was so often unavailable). I don't need to dictate anything. Most
of my communication is via chat.
Really, compared to the other issues (keyboard, sound, charging, USB ports, the broken or low-resolution screen)
the microphone is a benign problem. As I have now learned, things could be worse.
But why should the thought, _"It could be worse"_, even cross my mind
when dealing with such a matter? Virtually every issue that has
occurred with my computer thus far could -- should! -- have been diagnosed
at the repair center. The 'slow charger' warning shows up in BIOS,
so just turning the computer on while plugged in should make it obvious something
is wrong; doubly so when the very reason that the laptop was in repairs
in the first place was because of the faulty charger. I refuse to believe
that screens with different resolutions have the same part identifier,
either. How have the standards of service from DELL fallen so low?
How come this absurd scenario plays out not just for me, but
for others as well? It would be comforting, in a way, to think
that I was just the 'exceptional case'. But apparently, I'm not.
This is standard practice.
### Tl;DR
Here are he problems I've had with DELL:
* The machine shipped, apparently, with a missing piece of insulation.
* The "D" key on the keyboard snapped after only a few months of use.
* While repairing the "D" key, the DELL technician broke the computer's sound and microphone.
* While repairing the sound and microphone, the DELL technicians broke a charging port.
* The DELL technicians failed to repair the charging port, mailing me back a machine
exhibiting the same issues, in addition to a broken LCD screen.
* The repair of the LCD screen took almost a month, and concluded
with me receiving a worse quality screen than I originally had.
* The system replacement that followed the botched LCD repair took
over a month to go through.
* The replaced system was made partially of used parts, which
DELL refused to admit.
* The microphone on the replacement system was broken.
### Closing Thoughts
I will not be sending my system in again. It doesn't make sense to do so -
after mailing my system in for repairs three times, I've measured empirically that
the chance of failure is 100%. Every service request is a lottery, dutifully
giving out a random prize of another broken part. I no longer wish to play;
as any person who gambles should, I will quit while I'm ahead, and cut my losses.
However, I hope for this story, which may be unusual in its level of detail,
but not its content, to be seen by seen by someone. I hope to prevent
someone out there from feeling the frustration, and anger, and peculiar amusement
that I felt during this process. I hope for someone else to purchase a computer
with money, and not with their sanity. A guy can hope.
If you're reading this, please take this as a warning. __DELL is a horrible
company. They have the lowest standards of customer support of any
U.S. company that I've encountered. Their technicians are largely incompetent.
Their quality assurance is non-existent. Stay away from them.__

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 227 KiB

View File

@@ -12,7 +12,7 @@ __py-starbound__, nicely enough, actually has a file named `FORMATS.md`. This fi
> This section will contain information on how to retrieve a value from a BTreeDB5 database.
Not very helpful. Before I go into what I managed to determine from the code, we may first take a look at one thing that we already know about the world format - it is a [B-Tree](https://en.wikipedia.org/wiki/B-tree).
## Binary Search Trees
### Binary Search Trees
The B-Tree is a generalization of a Binary Search Tree, or BST for short. Binary Search trees (and B-Trees in general) operate on data that can be ordered consistently, the simplest example being numbers. For instance, as an example, I'll be using a BST that holds integers. A BST is made up of nodes, objects that actually hold the pieces of data that the tree itself organizes.
In a BST, the nodes are organized in a simple way. Each node can have up to two _children_ (sub-nodes), and each of those can have up to two children, etc. The children are generally classified as _right_ and _left_. Conventionally, left children always have a value that is below (or comes before) the value of the node whose child they are (their _parent_), and right children have a bigger value.
@@ -45,7 +45,7 @@ __Although the average efficiency of a Binary Search Tree is \\(O(\log n)\\), me
This isn't good enough, and many clever algorithms have been invented to speed up the lookup of the tree by making sure that it remains _balanced_ - that is, it _isn't_ arranged like a simple list. Some of these algorithms include [Red-Black Trees](https://en.wikipedia.org/wiki/Red%E2%80%93black_tree), [AVL Trees](https://en.wikipedia.org/wiki/AVL_tree), and, of course, B-Trees.
## B-Trees
### B-Trees
B-Trees are a generalization of Binary Search Trees. That means that every Binary Search Tree is a B-Tree, but not all B-Trees are BSTs. The key difference lies in the fact that B-Trees' nodes aren't limited to having only two child nodes, and can also have more than one value.
Each B-Tree node is a sorted array of values. That is, instead of a single number like the BST that we've looked at, it has multiple, and these numbers _must_ be sorted. Below are some examples of B-Tree nodes:
@@ -64,7 +64,7 @@ This is solved using another property of B-Trees - the number of children of a n
If we were looking for the number 15, we'd look between the 10 and the 20, examining the 2nd node, and if we were looking for 45 we'd look past the 30, at the 4th node.
## Starbound B-Trees and BTreeDB5
### Starbound B-Trees and BTreeDB5
The BTreeDB5 data structure uses something other than integers for its keys - it uses sequences of bytes. These bytes are compared in a very similar fashion to integers. The game first looks at the first number in the sequence of bytes (like the largest digit in an integer), and if that's the same, moves on to the next one. Also, Starbound B-Trees not only have the values, or _keys_, that they use to find data, but the data itself.
The "nodes" in the BTreeDB are called "blocks" and are one of three types - "index", "leaf", and "free" nodes. "Index" nodes are like the `(10, 20, 30)` node in the above example - they point to other nodes, but actually store no data themselves. The "leaf" nodes actually contain the data, and, if that data is longer than the maximum block size, "leaf" nodes contain the index of the next leaf node where the user might continue to read the data. The "free" nodes are simply free data, empty and ready for Starbound to fill them with something useful.

Binary file not shown.

View File

@@ -2,20 +2,21 @@
@import "mixins.scss";
$margin-width: 30rem;
$margin-offset: 1.5rem;
$margin-inner-offset: 0.5rem;
$margin-outer-offset: 1rem;
@mixin below-two-margins {
@media screen and
(max-width: $container-width +
2 * ($margin-width + 2 * $margin-offset)) {
(max-width: $container-width-threshold +
2 * ($margin-width + $margin-inner-offset + $margin-outer-offset)) {
@content;
}
}
@mixin below-one-margin {
@media screen and
(max-width: $container-width +
($margin-width + 3 * $margin-offset)) {
(max-width: $container-width-threshold +
($margin-width + $margin-inner-offset + $margin-outer-offset)) {
@content;
}
}
@@ -29,10 +30,18 @@ $margin-offset: 1.5rem;
@mixin margin-content-left {
left: 0;
margin-left: -($margin-width + $margin-offset);
margin-left: -($margin-width + $container-min-padding + $margin-inner-offset);
@include below-two-margins {
display: none;
}
}
@mixin margin-content-right {
right: 0;
margin-right: -($margin-width + $margin-offset);
margin-right: -($margin-width + $container-min-padding + $margin-inner-offset);
@include below-one-margin {
display: none;
}
}

View File

@@ -6,7 +6,7 @@
}
@mixin below-container-width {
@media screen and (max-width: $container-width){
@media screen and (max-width: $container-width-threshold){
@content;
}
}

View File

@@ -2,8 +2,6 @@
@import "mixins.scss";
@import "margin.scss";
$sidenote-width: 30rem;
$sidenote-offset: 1.5rem;
$sidenote-padding: 1rem;
$sidenote-highlight-border-width: .2rem;
@@ -56,7 +54,6 @@ $sidenote-highlight-border-width: .2rem;
margin-top: 1rem;
margin-bottom: 1rem;
width: 100%;
display: none;
.sidenote-checkbox:checked ~ & {
display: block;
@@ -71,10 +68,6 @@ $sidenote-highlight-border-width: .2rem;
}
@include below-one-margin {
.post-content {
max-width: 100%;
}
.sidenote-content.sidenote-right {
@include hidden-sidenote;
margin-right: 0rem;

View File

@@ -1,6 +1,7 @@
@import "variables.scss";
@import "mixins.scss";
@import "margin.scss";
@import "toc.scss";
body {
font-family: $font-body;
@@ -39,13 +40,22 @@ pre code {
display: block;
padding: 0.5rem;
overflow-x: auto;
background-color: $code-color;
border: $code-border;
}
div.highlight table pre {
div.highlight table {
border: $code-border !important;
border-radius: 0px;
pre {
margin: 0;
}
code {
border: none;
}
}
.container {
position: relative;
margin: auto;
@@ -54,15 +64,17 @@ div.highlight table pre {
box-sizing: border-box;
@include below-container-width {
padding: 0rem 1rem 0rem 1rem;
padding: 0 $container-min-padding 0 $container-min-padding;
margin: 0;
max-width: $container-width + 2 * $container-min-padding;
}
@include below-two-margins {
left: -($margin-width + $margin-offset)/2;
left: -($margin-width + $margin-inner-offset + $margin-outer-offset)/2;
}
@include below-one-margin {
position: initial;
left: 0;
}
}
@@ -71,8 +83,7 @@ div.highlight table pre {
background-color: $primary-color;
border: none;
color: white;
transition: color 0.25s;
transition: background-color 0.25s;
transition: color 0.25s, background-color 0.25s;
text-align: left;
&:focus {
@@ -231,3 +242,7 @@ figure {
text-align: center;
}
}
.twitter-tweet {
margin: auto;
}

View File

@@ -0,0 +1,49 @@
@import "variables.scss";
@import "mixins.scss";
$toc-color: $code-color;
$toc-border-color: $code-border-color;
.table-of-contents {
@include margin-content;
@include margin-content-left;
display: flex;
flex-direction: column;
align-items: end;
margin-bottom: 1rem;
em {
font-style: normal;
font-weight: bold;
font-size: 1.2em;
display: block;
margin-bottom: 0.5rem;
}
#TableOfContents > ul {
padding-left: 0;
}
nav {
margin: 0px;
}
ul {
list-style: none;
padding-left: 2rem;
margin: 0px;
}
a {
padding: 0;
}
div.wrapper {
@include bordered-block;
padding: 1rem;
background-color: $toc-color;
border-color: $toc-border-color;
box-sizing: border-box;
max-width: 100%;
}
}

View File

@@ -1,14 +1,16 @@
$container-width: 45rem;
$container-min-padding: 1rem;
$container-width-threshold: $container-width + 2 * $container-min-padding;
$standard-border-width: .075rem;
$primary-color: #36e281;
$primary-color-dark: darken($primary-color, 10%);
$code-color: #f0f0f0;
$code-color-dark: darken($code-color, 10%);
$border-color: #bfbfbf;
$code-color: #f0f0f0;
$code-border-color: darken($code-color, 10%);
$font-heading: "Lora", serif;
$font-body: "Raleway", serif;
$font-code: "Inconsolata", monospace;
$standard-border: $standard-border-width solid $border-color;
$code-border: $standard-border-width solid $code-border-color;

View File

@@ -10,6 +10,14 @@
</div>
<div class="post-content">
{{ if not (eq .TableOfContents "<nav id=\"TableOfContents\"></nav>") }}
<div class="table-of-contents">
<div class="wrapper">
<em>Table of Contents</em>
{{ .TableOfContents }}
</div>
</div>
{{ end }}
{{ .Content }}
</div>
{{ end }}

View File

@@ -4,8 +4,9 @@
<nav>
<div class="container">
<a href="/">Home</a>
<a href="https://github.com/DanilaFe">GitHub</a>
<a href="/about">About</a>
<a href="https://github.com/DanilaFe">GitHub</a>
<a href="/Resume-Danila-Fedorin.pdf">Resume</a>
<a href="/tags">Tags</a>
<a href="/blog">All Posts</a>
</div>