and has 0 comments

  Rhythm of War feels like a setup for something, like an interlude. Also, while it largely expands the scope of the story, it relies a lot of existing characters and their stories in previous books, without doing that smart thing Brandon Sanderson usually does to remind people what they were. I don't even know if it's possible with this large of a story. And remember, this is just the fourth in a ten book series!

  And so my reading of this book suffered from two things: I didn't quite remember what everything was about and the story just got too large! Meaning that I have to choose whether I care about individual characters and the sides they take or if I see everything as a big saga where people don't really matter. At this point, though, the choice is very difficult to make.

  To summarize, I loved the book, but not as much as I remember liking the previous three. The pace was slower, the implications grander, but also not reaching closure. A lot more characters, types of spren, gods, realms and bindings were revealed, but now I have to wait another two years to see what they were actually about. And Kaladin's pain now went into some weird directions, like battle schock and psychological help and accepting limitations in order to go forward. Was everyone depressed in 2020?! I thought Sanderson was immune to depression.

  Anyway, it seems to me that I will have to plan well the arrival of the fifth book in the series, probably by rereading the first four. Only then will the story click as it should and not make me feel like a stupid Taravangian.

  I was working on a grid display and I had to properly sort date columns. The value provided was not a datetime, but instead a string like "20 Jan 2017" or "01 Feb 2020". Obviously sorting them alphabetically would not be very useful. So what I did was implement a custom sorting function that first parsed the strings as dates, then compared them. Easy enough, particularly since the Date object in Javascript has a Parse function that understands this format.

  The problem came with a string with the value "01 Jan 0001" which appeared randomly among the existing values. I first thought it was an error being thrown somewhere, or that it would not parse this string or even that it would be an overflow. It was none of that. Instead, it was about handling the year part.

  A little context first:

Date.parse('01 Jan 0001') //978300000000
new Date(0) //Thu Jan 01 1970 00:00:00

Date.parse('01 Jan 1950') //-631159200000
new Date(Date.parse('01 Jan 1950')) //Sun Jan 01 1950 00:00:00

Date.parse('31 Dec 49 23:59:59.999') //2524600799999
Date.parse('1 Jan 50 00:00:00.000') //-631159200000

new Date(Date.parse('01 Jan 0001')) //Mon Jan 01 2001 00:00:00

  The first two lines almost had me convinced Javascript does not handle dates lower than 1970. The next two lines disproved that and made me think it was a case of numerical overflow. The next two demonstrated it was not so. Now look closely at the last line. What? 2001?

  The problem was with the handling of years that are numerically smaller than 50. The parser assumes we used a two digit year and translates it into Date.parse('01 Jan 01') which would be 2001. We get a glimpse into how it works, too, because everything between 50 and 99 would be translated into 19xx and everything between 00 and 49 is considered 20xx.

  Note that .NET does not have this problem, correctly making the difference between a 2 digit and 4 digit year.

  Hope it helps people.

Update: due to popular demand, I've added Tyrion as a Github repo.

Update 2: due to more popular demand, I even made the repo public. :) Sorry about that.

Intro

  Discord is something I have only vaguely heard about and when a friend told me he used it for chat with friends, I installed it, too. I was pleasantly surprised to see it is a very usable and free chat application, which combines feature of IRC, other messenger applications and a bit of Slack. You can create servers and add channels to them, for example, where you can determine the rights of people and so on. What sets Discord apart from anything, perhaps other than Slack, is the level of "integration", the ability to programatically interact with it. So I create a "bot", a program which stays active and responds to user chat messages and can take action. This post is about how to do that.

  Before you implement a bot you obviously need:

  All of this has been done to death and you can follow the links above to learn how to do it. Before we continue, a little something that might not be obvious: you can edit a Discord chat invite so that it never expires, as it is the one on this blog now.

Writing code

One can write a bot in a multitude of programming languages, but I am a .NET dev, so Discord.NET it is. Note that this is an "unofficial" library, so it may not (and it is not) completely in sync with all the options that the Discord API provides. One such feature, for example, is multiple attachments to a message. But I digress.

Since my blog is also written in ASP.NET Core, it made sense to add the bot code to that. Also, in order to make it all clean code, I will use dependency injection as much as possible and use the built-in system for commands, even if it is quite rudimentary.

Step 1 - making dependencies available

We are going to need these dependencies:

  • DiscordSocketClient - the client to connect to Discord
  • CommandService - the service managing commands
  • BotSettings - a class used to hold settings and configuration
  • BotService - the bot itself, which we are going to make implement IHostedService so we can add it as a hosted service in ASP.Net

In order to keep things separated, I will not add all of this in Startup, instead encapsulating them into a Bootstrap class:

public static class Bootstrap
{
    public static IWebHostBuilder UseDiscordBot(this IWebHostBuilder builder)
    {
        return builder.ConfigureServices(services =>
        {
            services
                .AddSingleton<BotSettings>()
                .AddSingleton<DiscordSocketClient>()
                .AddSingleton<CommandService>()
                .AddHostedService<BotService>();
        });
    }
}

This allows me to add the bot simply in CreateWebHostBuilder as: 

WebHost.CreateDefaultBuilder(args)
   .UseStartup<Startup>()
   .UseKestrel(a => a.AddServerHeader = false)
   .UseDiscordBot();

Step 2 - the settings

The BotSettings class will be used not only to hold information, but also communicate it between classes. Each Discord chat bot needs an access token to connect and we can add that as a configuration value in appsettings.config:

{
  ...
  "DiscordBot": {
	"Token":"<the token value>"
  },
  ...
}
public class BotSettings
{
    public BotSettings(IConfiguration config, IHostingEnvironment hostingEnvironment)
    {
        Token = config.GetValue<string>("DiscordBot:Token");
        RootPath = hostingEnvironment.WebRootPath;
        BotEnabled = true;
    }

    public string Token { get; }
    public string RootPath { get; }
    public bool BotEnabled { get; set; }
}

As you can see, no fancy class for getting the config, nor do we use IOptions or anything like that. We only need to get the token value once, let's keep it simple. I've added the RootPath because you might want to use it to access files on the local file system. The other property is a setting for enabling or disabling the functionality of the bot.

Step 3 - the bot skeleton

Here is the skeleton for a bot. It doesn't change much outside the MessageReceived and CommandReceived code.

public class BotService : IHostedService, IDisposable
{
    private readonly DiscordSocketClient _client;
    private readonly CommandService _commandService;
    private readonly IServiceProvider _services;
    private readonly BotSettings _settings;

    public BotService(DiscordSocketClient client,
        CommandService commandService,
        IServiceProvider services,
        BotSettings settings)
    {
        _client = client;
        _commandService = commandService;
        _services = services;
        _settings = settings;
    }

    // The hosted service has started
    public async Task StartAsync(CancellationToken cancellationToken)
    {
        _client.Ready += Ready;
        _client.MessageReceived += MessageReceived;
        _commandService.CommandExecuted += CommandExecuted;
        _client.Log += Log;
        _commandService.Log += Log;
        // look for classes implementing ModuleBase to load commands from
        await _commandService.AddModulesAsync(Assembly.GetEntryAssembly(), _services);
        // log in to Discord, using the provided token
        await _client.LoginAsync(TokenType.Bot, _settings.Token);
        // start bot
        await _client.StartAsync();
    }

    // logging
    private async Task Log(LogMessage arg)
    {
        // do some logging
    }

    // bot has connected and it's ready to work
    private async Task Ready()
    {
        // some random stuff you can do once the bot is online: 

        // set status to online
        await _client.SetStatusAsync(UserStatus.Online);
        // Discord started as a game chat service, so it has the option to show what games you are playing
        // Here the bot will display "Playing dead" while listening
        await _client.SetGameAsync("dead", "https://siderite.dev", ActivityType.Listening);
    }
    private async Task MessageReceived(SocketMessage msg)
    {
        // message retrieved
    }
    private async Task CommandExecuted(Optional<CommandInfo> command, ICommandContext context, IResult result)
    {
        // a command execution was attempted
    }

    // the hosted service is stopping
    public async Task StopAsync(CancellationToken cancellationToken)
    {
        await _client.SetGameAsync(null);
        await _client.SetStatusAsync(UserStatus.Offline);
        await _client.StopAsync();
        _client.Log -= Log;
        _client.Ready -= Ready;
        _client.MessageReceived -= MessageReceived;
        _commandService.Log -= Log;
        _commandService.CommandExecuted -= CommandExecuted;
    }


    public void Dispose()
    {
        _client?.Dispose();
    }
}

Step 4 - adding commands

In order to add commands to the bot, you must do the following:

  • create a class to inherit from ModuleBase
  • add public methods that are decorated with the CommandAttribute
  • don't forget to call commandService.AddModuleAsync like above

Here is an example of an enable/disable command class:

public class BotCommands:ModuleBase
{
    private readonly BotSettings _settings;

    public BotCommands(BotSettings settings)
    {
        _settings = settings;
    }

    [Command("bot")]
    public async Task Bot([Remainder]string rest)
    {
        if (string.Equals(rest, "enable",StringComparison.OrdinalIgnoreCase))
        {
            _settings.BotEnabled = true;
        }
        if (string.Equals(rest, "disable", StringComparison.OrdinalIgnoreCase))
        {
            _settings.BotEnabled = false;
        }
        await this.Context.Channel.SendMessageAsync("Bot is "
            + (_settings.BotEnabled ? "enabled" : "disabled"));
    }
}

When the bot command will be issued, then the state of the bot will be sent as a message to the chat. If the parameter of the command is enable or disable, the state will also be changed accordingly.

Yet, in order for this command to work, we need to add code to the bot MessageReceived method: 

private async Task MessageReceived(SocketMessage msg)
{
    // do not process bot messages or system messages
    if (msg.Author.IsBot || msg.Source != MessageSource.User) return;
    // only process this type of message
    var message = msg as SocketUserMessage;
    if (message == null) return;
    // match the message if it starts with R2D2
    var match = Regex.Match(message.Content, @"^\s*R2D2\s+", RegexOptions.IgnoreCase);
    int? pos = null;
    if (match.Success)
    {
        // this is an R2D2 command, everything after the match is the command text
        pos = match.Length;
    }
    else if (message.Channel is IPrivateChannel)
    {
        // this is a command sent directly to the private channel of the bot, 
        // don't expect to start with R2D2 at all, just execute it
        pos = 0;
    }
    if (pos.HasValue)
    {
        // this is a command, execute it
        var context = new SocketCommandContext(_client, message);
        await _commandService.ExecuteAsync(context, message.Content.Substring(pos.Value), _services);
    }
    else
    {
        // processing of messages that are not commands
        if (_settings.BotEnabled)
        {
            // if the bot is enabled and people are talking about it, show an image and say "beep beep"
            if (message.Content.Contains("R2D2",StringComparison.OrdinalIgnoreCase))
            {
                await message.Channel.SendFileAsync(_settings.RootPath + "/img/R2D2.gif", "Beep beep!", true);
            }
        }
    }
}

This code will forward commands to the command service if message starts with R2D2, else, if bot is enabled, will send replies with the R2D2 picture and saying beep beep to messages that contain R2D2.

Step 5 - handling command results

Command execution may end in one of three states:

  • command is not recognized
  • command has failed
  • command has succeeded

Here is a CommandExecuted event handler that takes these into account:

private async Task CommandExecuted(Optional<CommandInfo> command, ICommandContext context, IResult result)
{
    // if a command isn't found
    if (!command.IsSpecified)
    {
        await context.Message.AddReactionAsync(new Emoji("🤨")); // eyebrow raised emoji
        return;
    }

    // log failure to the console 
    if (!result.IsSuccess)
    {
        await Log(new LogMessage(LogSeverity.Error, nameof(CommandExecuted), $"Error: {result.ErrorReason}"));
        return;
    }
    // react to message
    await context.Message.AddReactionAsync(new Emoji("🤖")); // robot emoji
}

Note that the command info object does not expose a result value, other than success and failure.

Conclusion

This post has shown you how to create a Discord chat bot in .NET and add it to an ASP.Net Core web site as a hosted service. You may see the result by joining this blog's chat and giving commands to Tyr, the chat's bot:

  • play
  • fart
  • use metric or imperial units in messages
  • use Yahoo Messenger emoticons in messages
  • whatever else I will add in it when I get in the mood :)

and has 0 comments

  Winter Tide, the first book of the series, was a refreshing blend of Lovecraftian Mythos and a perspective focused on balance and peace, rather than power problem solving. So, a year and a half ago, while I said I enjoyed reading it, I was also saying that it was a bit slow in the beginning.

  Deep Roots has a few things going against it. The novelty wore off, for once. Then the characters are not reintroduced to the reader, there are no flashbacks or summaries, so I had no idea who everyone was anymore. Finally, it had almost the same structure as the first book, but without introducing new lore elements and instead just popping up new characters, as if keeping track of existing ones wasn't enough work. It starts slow and the pace only picks up towards the end. This made it hard for me to finish the book and maintain interest in the story.

 This time, Aphra and her motley crew need to stop the Outer Ones, ancient creatures with immense power, from saving humanity from extinction. Yes, it is a worthy goal, but they want to do it by enslaving and controlling Earth, treating us like the impetuous children that we are. With such a cosmic threat I would have expected cosmic scenes, powerful emotions, explosive outcomes, but it was all very civilized and ultimately boring.

  Bottom line: Ruthanna Emrys' fresh perspective persists in this second installment of the Innsmouth Legacy series, but it isn't fresh anymore. I am sure the experience is much better if you read Winter Tide just before it. As it stands, Deep Roots reads like a slightly boring detective story with some mystical elements sprayed in. I don't regret reading it, but I don't feel the need for more.

and has 0 comments

  Just wanted to give you a heads up that Siderite's blog now has a Discord server and you can easily talk to me and other blog viewers that connected there by clicking on the Discord icon on top of the search box. I just heard of this Discord app and it seems to permit this free chat server and invite link. Let me know if anything goes wrong with it.

  Try it! 

and has 0 comments

  One can take a container in which there is water and keep pouring oil in and after a time there will be more oil than water. That's because oil is hydrophobic, it "fears water" in a direct translation of the word. You can then say that the percentage of oil is higher than the percentage of water, that there is more oil in the container. Skin color in a population doesn't work like that, no matter how phobic some people are. Instead of water and oil, it's more like paint. One can take a container in which there is white paint and keep pouring black, red, yellow and brown paint in, but from a very early stage, that paint is no longer white.

  I keep finding these statistics about which part of the world is going to have Whites in a minority after a while. Any statistic counting people by color of skin is purist in nature and, as we should know by now, the quest for purity begets violence. The numbers are irrelevant if the basis of these statistics is conceptually wrong. In a true openly diverse population, white skin color should disappear really quick. The only chance for it to exist is if people with white skin would not mingle with people of any other color.

  What is a White person? Someone who has white skin? Someone who has European ancestry? Someone who has no ancestry that is not European? Are Jews white? How about coptic Egyptians? Some Asians are really white, too. There is no argument that uses the concept of White which is not directly dependant on the idea of racial purity. And then there is Non-White. A few days ago someone was noting that it feels weird to use the term Latino, considering how many different countries and interests are represented by the people labeled as such. So how can anyone meaningfully use a term like Non-White, which groups together Black people, Mexicans, Chinese, Indians, Eskimos and Native-Americans, among many others? Two "African-American" people of identical skin color may be as different as someone can imagine: one a many generations American with slave ancestors, the other a middle-class African recently arrived in the US.

  What I am saying is that the most politically correct terms, used (and imposed) by proponents (and arbiters) of racial justice and equality, are as purist as they could be. The only argument that one can possibly bring here is that purism is somehow different and distinct from racism. This is absurd. One can be a purist and not be racist, but not the other way around. In fact, when people are trying to limit your freedom of expression because some of your words or concepts may be offensive, they are in fact fighting for the purity of ideas, one that is not marred by a specific idea of purity that they are against. These are similar patterns, so similar in fact that I can barely see a difference. No wonder this kind of thinking has taken root most in a country where a part of its founders were called Puritans!

  So how about we change the rhetoric to something that does not imply segregation or a quest for purity or a war on something or cancelling other people or creating safe spaces or hating something that is other? And the phrase above is not ironic, since I am not proposing we fight against this kind of ideas, only that we acknowledge their roots and that we come up with new ones. Let us just grow in different directions, rather than apart.

and has 0 comments

   This is one of those books that when summarized sounds a lot better than the actual story. In The Sorcerer of the Wildeeps, we discover a brutal medieval and magical world, where magic is related to people from the stars who are considered gods, but may just be very technologically advanced humans. Yet this is just a vague backdrop for a two hundred and something pages novel. The main focus is on a guy who alternates between a very rough vernacular Black English and technobabble which one else seems to understand, who has magical powers and is part of a gang of fighters that work as security for caravans. He is a demigod, the blood of the star people courses through his veins, but he hides that part of him from the world. He is also in love with another guy who has the blood and much of the text concerns this gay relationship, which he also hides from the world. There are also some brutal fight scenes, but they don't bring anything to the story other than to make it a fantasy.

  I saw other people just as confused as I am. Maybe there was some subtlety that I missed and that is why so many people praise this first novel of Kai Ashante Wilson, a guy who started writing in 2013. Why are the reviews so wonderful? To me it felt like an above average pulp story, akin to those about cowboys riding dinosaurs. The writing style is also difficult enough to make the book less entertaining that it could have been. It's like Wilson rubs our noses into some intellectual shit that I can't even smell. Or is it that is it just another mediocre book that gets positive political reviews because it promotes Black culture and features gay people?

  So my conclusion is that I did not enjoy the book. It took me ages to finish it and I couldn't relate to any of the characters. I thought the world was very interesting though, which makes this even more frustrating, since it was barely explored.

and has 0 comments

  It has been a long time since I've finished a book. I just didn't feel like it, instead focusing on stupid things like the news. It's like global neurosis: people glued to their TV screens listening to what is essentially the same thing: "we have no control, we don't know enough and we feel better bitching about it instead of doing anything to change it". I hope that I will be able to change my behavior and instead focus on what really matters: complete fiction! :)

  Anyway, Unfettered is a very nice concept thought up by Shawn Speakman: a contribution based anthology book. Writers provide short stories, complete with a short introduction, as charity. The original Unfettered book was a way by which writers helped Speakman cover some of his medical expenses after a cancer diagnosis and the idea continued, helping others with the same problem. This way of doing things, I believe, promotes a more liberating way of writing. There is no publisher pressure, no common theme, writers are just exploring their own worlds, trying things out.

  Unfettered III contains 28 shorts stories from authors like Brandon Sanderson, Lev Grossman, Mark Lawrence, Terry Brooks, Brian Herbert, Scott Sigler and more. Funny enough, it was Sanderson's own addition to the Wheel of Time literature that I found most tedious to finish, mostly because I couldn't remember what the books were about anymore and who all the characters were. But the stories were good and, even if the book is twice as large as I think it should have been, it was entertaining. Try it out, you might enjoy this format.

and has 2 comments

  There is this feeling in the online community that no matter what governments and corporations do, we will find ways of avoiding restrictions and remain free. Nowhere is this feeling stronger than in the media and software piracy circles. Yet year after year people get more and more complacent, moving from having to find and download the content they enjoy to streaming services that end up asking for more money than TV and cinema combined, moving from desktop games to mobiles, switching from software you own to software you subscribe to and lease. And every year more and more "hydra heads" get cut and none grow back.

  Today we say goodbye to HorribleSubs.info, a web site that provided a free archive of hundreds of anime show torrent/magnet links which were subtitled in English. "You could technically say COVID killed HorribleSubs.", the notice on the web site now says. If you think about it, it was difficult to understand how the site survived for so long, when my blog was closed for showing a manga image taken from Google and a YouTube video after a copyright request from Japan. How could these guys maintain a directory of almost every popular anime and get away with it? But they will be missed, regardless of the real reason for their disappearance.

  It's hard to say how this will affect people. TorrentFreak hasn't even written something about it yet. Will this mean that less translated anime will be available? Or maybe even make it harder to find anime at all? It's a shocking development... Hail Hydra!

and has 0 comments

  I've read today this CNN article: 'Star Trek: Discovery' to introduce history-making non-binary and transgender characters. And it got me thinking on what this means for the Star Trek universe. It means absolutely nothing. Star Trek has had people turned into other species, duplicated, merged, their genetic code altered and fixed, made young and old. It has had species with no gender, multiple genders and various philosophies. It has interspecies relationships, including sexual.

  Star Trek has tackled intolerance many times, usually by showing the Federation crew having contact with an alien species that does the same things we do today, in caricature. It tackled race intolerance, from Kirk's kiss with Uhura to the episode with the species with black on one side and white on the other discriminating the people who had their colors the other way around. It tackled gender discrimination in multiple situations. It tackled sex change and identity change with the Trill. It featured multi sex civilisations. The happy tolerance train seems to stop with anything related to using inorganic technology with the human body, but no one is perfect and Janeway was awful with everybody.

  A person who is biologically a man yet desires to be treated as a woman would be normal for Star Trek. It would be inconsequential. If they go the way of the oppressed member of another culture that they meet, they will not solve anything, they will just have another weird alien around, which defeats the purpose. If they go with a non-binary crewmember they should not acknowledge the fact except in passing. Yes, habituate the public with the concept, let them see it in a series and get used to it, but the people in Star Trek should already have passed that point. Hell, they could go with a person who changes their sex every one in a while, to spice things up.

  What I would do is have a character who is clearly of a different sex than the gender they identify with and someone badgering them to have a proper sex change while they refuse. Now that would be a Star Trek worthy dilemma. You want to make it spicy? Have them go to the doctor and change their race instead, behave like a black person while wearing the high tech equivalent of blackface. What? Are you refusing someone the ownership of their identity?

  I really doubt they will go that way, though. Instead they will find some way of bringing the subject up again and again and again and throw it in our faces like a conflict that has to be resolved. In the bright and hopeful future, there should be no conflict about it! This CBS announcement should not have existed. You want to put some transgender people in, OK, put them in. It's not a boasting point, is it? The announcement basically does the opposite of what they claim to do: "Oh, look, we put non binary people in our series! How quaint! Hurrah! Only we do it, come watch the freak show!".

  Please, writers, please please please, don't just write stories then change the gender or race of characters because it's en vogue. Stop it with the gender swapping, which is the creative equivalent of copy and paste. Write with the story in mind, with the context, with the characters as they would normally behave. Don't add characters after you've thought of the story just to make them diverse either. Just write stories with characters that make sense! You don't know people from that demographic? Find one, spend time with them, then adjust your characters accordingly. I am so tired of tiny female action heroes, flamboyant and loud gays and the wise old lesbian. How come no one finds those offensive? It's like someone said "OK, we will have shitty black and female and non-cis characters for now. When people get used to them, we will actually have them do something and be realistic and perhaps in 2245 we'll even have them be sympathetic".

  They tried the woke way from the very beginning in Discovery, with the Stamets/Culber gay couple. They kept showing them kissing and washing their teeth together and other stuff like that, when it made little difference to the story. Most people on Star Trek are written as single, for some weird reason that makes no sense, unless their relationship furthers the story. Riker and Troi could be the exception, though, yet even they were not kissy kissy on the bridge all the time. I never understood that couple. Dax and Worf made more sense, for crying out loud! And remember Starfleet is a military organization. You may put women and men and trans and aliens and robots together in a crew, but their role is to do their job. Their sex, their gender even less, makes no difference.

  Gene Roddenberry was a dreamer of better futures, where all of our idiotic problems have been left behind and reason prevailed, but even he imagined a third World War leading to humanity changing its ways as a start. Star Trek has always analysed the present from the viewpoint of an idyllic future, a way of looking back that is inherently rational: "Imagine the future you want, then wonder what would people from that time think of you". It's brilliant! Don't break that to bring stupid into the future. To tackle present social issues you have to first be a Trekkie, already there in the exalted future, before you consider the dark ages of the 21st century with a fresh perspective.

  I've just read a medical article that seems to be what we have been looking for since this whole Covid thing started: an detailed explanation of what it does in the body. And no, it didn't come from doctors in lab coats, it came from a supercomputer analysing statistical data. Take that, humans! Anyway... First of all, read the article: A Supercomputer Analyzed Covid-19 — and an Interesting New Theory Has Emerged. And before you go all "Oh, it's on Medium! I don't go to that crap, they use a paywall!", know that this is a free article. (also you can read anything on Medium if it seems to be coming from Twitter)

  Long story short (you should really read the article, though) is that the virus binds to the ACE2 receptors - and degrades them, then tricks the body to make even more ACE2 receptors (even in organs that normally don't express them as much) to get even more virus in. The virus also tweaks the renin–angiotensin system  which leads to a Bradykinin storm which causes multiple symptoms consistent with what is seen in hospitals and leaves many a doctor stumped: dry cough, blood pressure changes, leaky blood vessels, a gel filling one's lungs (making ventilators ineffective), tiredness, dizziness and even loss of smell and taste. Also, because of a genetic quirk of the X chromosome, women are less affected, which also is shown in statistical data on severe cases.

  Quoting from the article: several drugs target aspects of the RAS and are already FDA approved to treat other conditions. They could arguably be applied to treating Covid-19 as well. Several, like danazol, stanozolol, and ecallantide, reduce bradykinin production and could potentially stop a deadly bradykinin storm. Others, like icatibant, reduce bradykinin signaling and could blunt its effects once it’s already in the body.

  Good stuff, people! Good stuff! The person responsible for this is Daniel A Jacobson and his research assistants should take all the credit! Just kidding.

  But how new is this? Bradykinin is not an unknown peptide and we have known from the very beginning what ACE does and that Covid binds to it. My limited googling shows doctors noticing this as soon as the middle of March. In fact, the original article that the Medium article is based on is from July 7! Here is a TheScientist take on it: Is a Bradykinin Storm Brewing in COVID-19?

  For more info, here is a long video talking about the paper: Bradykinin Storm Instead of Cytokine Storm?

[youtube:tDbRfur36sE]

  If you really are into medicine, check this very short but very technical video about Bradykinin, from where I also stole the image for this post: Bradykinin | Let the Drama begin!

[youtube:d39-IcoWHkY]

  I hope this provided you with some hope and a starting point for more research of your own.

and has 0 comments

  For a more in depth exploration of the concept, read Towards generic high performance sorting algorithms

Sorting

  Consider QuickSort, an algorithm that uses a divide and conquer strategy to sort efficiently and the favourite in computer implementations.

  It consists of three steps, applied recursively:

  1. find a pivot value
  2. reorder the input array so that all values smaller than the pivot are followed by values larger or equal to it (this is called Partitioning)
  3. apply the algorithm to each part of the array, before and after the pivot

  QuickSort is considered generic, meaning it can sort any type of item, assuming the user provides a comparison function between any two items. A comparison function has the same specific format: compare(item1,item2) returning -1, 0 or 1 depending on whether item1 is smaller, equal or larger than item2, respectively. This formalization of the function lends more credence to the idea that QuickSort is a generic sorting algorithm.

  Multiple optimizations have been proposed for this algorithm, including using insertion sort for small enough array segments, different ways of choosing the pivot, etc., yet the biggest problem was always the optimal way in which to partition the data. The original algorithm chose the pivot as the last value in the input array and the average complexity was O(n log n), but worse case scenario was O(n^2), when the array was already sorted and the pivot was the largest value. Without extra information you can never find the optimal partitioning schema (which would be to choose the median value of all items in the array segment you are sorting).

  But what if we turn QuickSort on its head? Instead of providing a formalized comparison function and fumbling to get the best partition, why not provide a partitioning function (from which a comparison function is trivial to obtain)? This would allow us to use the so called distribution based sorting algorithms (as opposed to comparison based ones) like Radix, BurstSort, etc, which have a complexity of O(n) in a generic way!

  My proposal for a formal signature of a partitioning function is partitionKey(item,level) returning a byte (0-255) and the sorting algorithm would receive this function and a maximum level value as parameters.

  Let's see a trivial example: an array of values [23,1,31,0,5,26,15] using a partition function that would return digits of the numbers. You would use it like sort(arr,partFunc,2) because the values are two digits numbers. Let's explore a naive Radix sorting:

  • assign 256 buckets for each possible value of the partition function result and start at the maximum (least significant) level
  • put each item in its bucket for the current level
  • concatenate the buckets
  • decrease the level and repeat the process

Concretely:

  • level 1: 23 -> bucket 3, 1 -> 1, 31 -> 1, 0 -> 0, 5 -> 5, 26 -> 6, 15 -> 5 results in [0,1,31,5,15,6]
  • level 0: 0 -> 0, 1 -> 0, 31 -> 3, 5 -> 0, 15 -> 1, 6 -> 0 results in [0,1,5,6,15,31]

Array sorted. Complexity is O(n * k) where k is 2 in this case and depends on the type of values we have, not on the number of items to be sorted!

  More complex distribution sorting algorithms, like BurstSort, optimize their function by using a normal QuickSort in small enough buckets. But QuickSort still requires an item comparison function. Well, it is easy to infer: if partFunc(item1,0) is smaller or larger than partFunc(item2,0) then item1 is smaller or larger than item2. If the partition function values are equal, then increase the level and compare partFunc(item1,1) to partFunc(item2,1).

  In short, any distribution sorting algorithm can be used in a generic way provided the user gives it a partitioning function with a formalized signature and a maximum level for its application.

  Let's see some example partitioning functions for various data types:

  • integers from 0 to N - maximum level is log256(N) and the partition function will return the bytes in the integer from the most significant to the least
    • ex: 65534 (0xFFFE) would return 255 (0xFF) for level 0 and 254 (0xFE) for level 1. 26 would return 0 and 26 for the same levels.
  • integers from -N to N - similarly, one could return 0 or 1 for level 0 if the number is negative or positive or return the bytes of the equivalent positive numbers from 0 to 2N 
  • strings that have a maximum length of N - maximum level would be N and the partition function would return the value of the character at the same position as the level
    • ex: 'ABC' would return 65, 66 and 67 for levels 0,1,2.
  • decimal or floating point or real values - more math intensive functions can be found, but a naive one would be to use a string partitioning function on the values turned to text with a fixed number of digits before and after the decimal separator.
  • date and time - easy to turn these into integers, but also one could just return year, month, day, hour, minute, second, etc based on the level
  • tuples of any of the types above - return the partition values for the first item, then the second and so on and add their maximum levels

  One does not have to invent these functions, they would be provided to the user based on standard types in code factories. Yet even these code factories will be able to encode more information about the data to be sorted than mere comparison functions. Stuff like the minimum and maximum value can be computed by going through all the values in the array to be sorted, but why do it if the user already has this information, for example.

  Assuming one cannot find a fixed length to the values to be sorted on, like real values or strings of any length, consider this type of sorting as a first step to order the array as much as possible, then using something like insertion or bubble sort on the result.

Finding a value or computing distinct values

  As an additional positive side effect, there are other processes on lists of items that are considered generic because they use a formalized form function as a parameter. Often found cases include finding the index of an item in a list equal to a given value (thus determining if the value exists in a list) and getting the distinct values from an array. They use an equality function as a parameter which is formalized as returning true or false. Of course, a comparison function could be used, depending on if its result is 0 or not, but a partitioning function can also be used to determine equality, if all of the bytes returned on all of the levels are equal.

  But there is more. The format of the partition function can be used to create a hash set of the values, thus reducing the complexity of the search for a value from O(n) to O(log n) and that of getting distinct values from O(n^2) to O(n log n)!

  In short, all operations on lists of items can be brought together and optimized by using the same format for the function that makes them "generic": that of a partitioning function.

Conclusion

  As you can see, I am rather proud of the concepts I've explained here. Preliminary tests in Javascript show a 20 fold improvement in performance for ten million items when using RadixSort over the default sort. I would really love feedback from someone who researches algorithms and can even test these assumptions under benchmark settings. Them being complex as they are, I will probably write multiple posts on the subject, trying to split it (partition it?) into easily digestible bits

 The concept of using a generic partitioning function format for operations on collections is a theoretical one at the moment. I would love to collaborate with people to get this to production level code, perhaps taking into account advanced concepts like minimizing cache misses and parallelism, not only the theoretical complexity.

 More info and details at Towards generic high performance sorting algorithms

and has 0 comments

  There are a lot of fascinating ideas and anecdotes in this book, especially in the areas which I wouldn't have considered interesting before reading it. Rabid is the type of book that I love, both because the subject is fascinating but also because of the effort the author made to research and write the content in a digestible format.

  In this book Bill Wasik and Monica Murphy describe the history of the rabies virus, how it affected humankind culturally, historically and, of course, medically. We learn in this book that there is a strong possibility that the myths of vampire and werewolf stem from the behaviour of people affected by rabies, the theme of beast biting person and turning them into one of their own proven irresistible even in times where no one understood how diseases work. Was Hector rabid when fighting Achilles? Were berserkers affected by rabies? Then we go into the actual zoonotic origin of the virus, a staggering 60% of infectious diseases affecting humans being of animal original initially. An idea I found extremely interesting is that farmers took over from hunter gatherers in so little time and so thoroughly because raising animals made them get new diseases to which they developed immunity, any contact with non farming populations thus fatally destroying them. Finally, a very nice perspective on Louis Pasteur, who is more popularly renowned for developing pasteurization and thus providing us with better tasting drinks than his final triumph which was a vaccine for rabies and an institute dedicated to studying infectious diseases.

  Bottom line: it might sound like a weird subject to read about or at least one hard to digest. The authors' writing is very good, the research splendid, and the book short enough to not take too much of the reader's time. I recommend it!

and has 0 comments

  The Book of the Ancestor trilogy consists of Red Sister, Grey Syster, Holy Sister, books that tell the continuous story of Nona, a girl with magical powers who is trained as a warrior nun by the church on a feudal world called Abeth. It feels almost the same as the Harry Potter books: a school for children where they learn only exciting stuff like magic and fighting and where the group of friends that coalesces around the main character has to solve more and more complex and dangerous problems. And it pretty much has the same issues, as any of the actors in the story could have easily handled a little child regardless of her powers because... she's a child! Also, the four "houses" are here replaced with genetic lines that provide the owner with various characteristics.

  Anyway, I liked all three books, although I have to say that I liked them less and less as the ending approached. Tools used to solve some problems were not used for similar issues later on, the girls were learning more and more stuff and become more and more powerful, while all of their opponents seemed to lack the ability to reach their level even with greater numbers and funding and, maybe worse of all, whenever it was inconvenient to detail the evolution of the characters and the story, Mark Lawrence just skips to some point in the future. Thus, each of the last two books is separated from the previous one by two years!

  Another qualm that I have with the series is that the author spent a lot of effort to create a magical world, with a dying sun and with a vague history that may or may not have involved spaceships and an alien race, with various magical tools that can be combined to various and epic effects, with several kingdoms, each different from another. Then the story ends, as if all we could or should ever care about is what happens to Nona.

  Bottom line: if you liked Harry Potter, you might want to read this series. It pushes the same buttons, while getting less and less consistent as more stuff is added, then leaving you wanting more of the world that was described, even if you didn't especially liked the characters or their choices.

and has 0 comments

  The Broken Ladder is a sociology book that is concise and to the point. I highly recommend it. Keith Payne's thesis is that most of the negative issues we associate with poverty or income are statistically proven to be more correlated with inequality and status. And this is not a human thing, as animal studies show that this is a deeply rooted behavior of social animals like monkeys and has a genetic component that can be demonstrated to as simple creatures as fruit flies.

  There are nine chapters in the book, each focusing on a particular characteristic of effect of social inequality. We learn that just having available a sum of money or a set of resources is meaningless to the individual. Instead, more important is how different those resources are from other people in the same group. Inequality leads to stress, which in turn leads to toxic behaviors, health problems, developmental issues. It leads to risk taking, to polarization in politics, it affects lifespan, it promotes conspiracy theories, religious extremism and racism.

  It is a short enough book that there is no reason for me to summarize it here. I believe it's a very important work to examine, as it touches on many problems that are present, even timeless. Written in 2017, it feels like a explanatory pamphlet to what gets all the media attention in 2020.