This is my log ... When I accomplish something, I write a * line that day. Whenever a bug / missing feature / idea is mentioned during the day and I don't fix it, I make a note of it and mark it with ?. Some things get noted many times before they get fixed. Occasionally I go back through the old notes and mark with a + the things I have since fixed, and with a ~ the things I have since lost interest in. --- Matteo Landi # 2020-03-27 Fucking figured out why my `PS1` was breaking input with Alacritty: https://github.com/alacritty/alacritty/issues/3512 It turns out I was using non-printing characters in the prompt... without escaping them! Hence `bash` was not able to properly calculate the length of the prompt, hence `readline` blew up! You can read more about escaping non-printing characters, here: https://superuser.com/questions/301353/escape-non-printing-characters-in-a-function-for-a-bash-prompt # 2020-03-26 My reg-cycle plugin is not quite working as expected... sometimes you yank some text... then yank something else but when you try to paste what you yanked the first time (`p` followed by `gp`), other stuff will be pasted instead (like text previously deleted). Few things to look at: - `:help redo-register` - https://vim.fandom.com/wiki/Remembering_previous_deletes/yanks ? fix reg-cycle # 2020-03-24 Today I learded, you can quickly setup SSH proxies by adding the following to your ~/.ssh/config file: Host server.example.com ProxyCommand ssh -q proxy.example.org nc %h %p By this, when we connect to the server by git push, git pull or other commands, git will first SSH to server.example.com. As the ssh client will check the config file, the above rule makes it set up a proxy by SSH to proxy.example.org and relaying the connection to %h (server.example.com) with port %p (22 by default for SSH) by nc (you need to have nc installed on proxy). This way, the git connection is forwarded to the git server. And why would this be relevant? Think about this scenario: 1. Main laptop: decent CPU / memory, connected to the home WIFI, and company VPN 2. Dev laptop: good CPU / memory, connected to the home WIFI, but not to the company VPN (because you know, you don't want to connect and re-enter auth tokens... **twice**) Well, with this proxy trick setup, you would still be able to access configured internal services (e.g. git) from the dev laptop without it being connected to the company VPN. Quite handy! https://www.systutorials.com/git-through-ssh-tunnel-as-proxy/ --- Something is messing up my bash prompt, when running it inside Alacritty: 1. Open a new instance > open -n /Applications/Alacritty.app --args --config-file /dev/null The new instance is opened, and the configured prompt is printed: matteolandi at hairstyle.local in /Users/matteolandi > █ 2. Press `up-arrow` to access the last executed command: matteolandi at hairstyle.local in /Users/matteolandi > open -n /Applications/Alacritty.app --args --config-file /dev/null█ 3. Press `ctrl+a` to get to the beginning of the line Expected behavior: the cursor moves indeed at the beginning of the line matteolandi at hairstyle.local in /Users/matteolandi > █pen -n /Applications/Alacritty.app --args --config-file /dev/null Actual behavior: the cursor stops somewhere in the middle of the line matteolandi at hairstyle.local in /Users/matteolandi > open -n█/Applications/Alacritty.app --args --config-file /dev/null Note 1: if I now press `ctr-e` (to get to the end of the line), the cursor get past the current end of the line: matteolandi at hairstyle.local in /Users/matteolandi > open -n /Applications/Alacritty.app --args --config-file /dev/null █ Note2: if I hit `ctrl+c` (to reset the prompt), press `up-arrow` again, and start hammering `left-arrow`, it eventually gets to the beginning of the line: matteolandi at hairstyle.local in /Users/matteolandi > open -n /Applications/Alacritty.app --args --config-file /dev/null ^C matteolandi at hairstyle.local in /Users/matteolandi 130 > █pen -n /Applications/Alacritty.app --args --config-file /dev/null And if I know press `ctrl+e` (again, to get to the end of the line), the cursor does actually move to the end of the line: matteolandi at hairstyle.local in /Users/matteolandi 130 > open -n /Applications/Alacritty.app --args --config-file /dev/null█ I opened a new [ticket](https://github.com/alacritty/alacritty/issues/3512) to the project's GitHub page -- hopefully someone will get back to me with some insights. # 2020-03-23 * finished reading [Node.js 8 the Right Way: Practical, Server-Side JavaScript That Scales](https://www.amazon.com/Node-js-Right-Way-Server-Side-JavaScript/dp/168050195X) # 2020-03-22 Re-set up SSH on my W10 laptop: 1. Re-install: `cygrunsrv`, and `openssh` 2. Open a cygwin terminal, **as-administrator**, and run: $ ssh-host-config -y $ vim /etc/sshd_config # make sure 'PubkeyAuthentication yes' is there, and not commented out $ cygrunsrv -S sshd 3. Add a Firewall rule to let in all incoming connections on TCP: 22 Note: if you do this on a company's laptop, `sshd` will try and contact the company's AD when authenticating connections, so make sure your laptop is connected to the Intranet. Links: - - # 2020-03-21 I have got a bit serious about trying to use `w3m` for web browsing (yeah, I know, I bit ironic coming from me, given that my team is working on a SPA unable to load shit without Javascript enabled). First things first... mappings! keymap r RELOAD keymap H PREV keymap L NEXT keymap C-U PREV_PAGE keymap C-D NEXT_PAGE keymap gg BEGIN keymap w NEXT_WORD keymap b PREV_WORD keymap zz CENTER_V keymap o COMMAND "GOTO /cgi-bin/b1.cgi; NEXT_LINK" keymap t COMMAND "TAB_GOTO /cgi-bin/b1.cgi; NEXT_LINK" keymap ( NEXT_TAB keymap ) PREV_TAB keymap gt TAB_LINK keymap d CLOSE_TAB keymap x CLOSE_TAB keymap :h HELP keymap :o OPTIONS keymap :q QUIT keymap gs SOURCE keymap V EXTERN keymap p GOTO /cgi-bin/cb.cgi keymap P TAB_GOTO /cgi-bin/cb.cgi keymap yy EXTERN "url=%s && printf %s "$url" | cb && printf %s "$url" &" I am going to change these quite a lot over the following weeks, but these should be enough to get you started using it. A few gems - You won't be able to read all the pages you visit inside `w3m`... so when you bump into one, just press `V` (or `M`) to open the current page in a configured external browser - There is no support for a default search engine (a bit annoying), but given that you can customize the hell out of `w3m`, I was able to work around that with the following: `COMMAND "GOTO /cgi-bin/b1.cgi; NEXT_LINK"`. This renders the content of cgi script that I created, and moves the cursor to the next link (so I can then press ``, type anything I want to search for, press `` to quit editing the text field, press `` to focus the Submit button, and press `` again to submit... easy uh?). Anyway, this is the content of my cgi script (it's a simple wrapper for [bunny1](https://github.com/ccheever/bunny1)): #!/usr/bin/env bash set -euo pipefail cat < bunny1 wrapper
EOF - Another intersting thing you can do by using a custom cgi script, is opening withing `w3m` the URL saved in the OS clipboard. How? 1) read the content of the clipboard (assuming it's a URL), 2) tell `w3m` to go to that URL (yes, your cgi script can send commands to `w3m` ): #!/usr/bin/env bash set -euo pipefail echo "w3m-control: GOTO $(cb --force-paste)" - This last tip will teach you how to _copy_ the current page URL into the OS clipboard. We are going to use the `EXTERN` command for this (i.e. invoke an _external_ command and pass the current URL as the first argument) and in particular we will: 1) set `url` to the input URL, 2) print `$url` (without the trailing new-line) and 3) pipe that into [cb](https://matteolandi.net/cb.html) to finally copy it into the OS clipboard. keymap yy EXTERN "url=%s && printf %s "$url" | cb && printf %s "$url" &" Few interesting articles I found when setting this all up: - Bunch of `w3m` related tricks: - A vim like firefox like configuration for w3m: --- Finally decided to give [Alacritty](https://github.com/alacritty/alacritty) a go, and damn... now I wished I had tried it earlier: scrolling in `vim` inside a `tmux` session is finally fast again! It supports `truecolors` too, so what else could I ask more? Well, one thing maybe I would ask for: [support multiple windows](https://github.com/alacritty/alacritty/issues/607). We can _multi-instantiate_, and that's fine, but those instances would not be recognized as being part of the same group (i.e. multiple entries in the dock, no support for `cmd + backtick`)... and that would make it really hard for people to set up keystrokes to focus a specifc instance of a group (currently with `Terminal.app`, I have a mapping to focus the group, and then then automatically inject: `cmd + opt + 1` to select the first instance of the group, `cmd + opt + 2` for the second...). There is a `--title`, but Keyboard Maestro would not let me select an application given its title (also, I do want the title of the window to be dynamic, based on the current process...). There is a `--class` option, but it seems to be working on Linux only... so what else? Well, after a bit of fiddling I finally found a solution: $ cp -r /Applications/Alacritty.app /Applications/Alacritty-scratch.app $ vim /Applications/Alacritty-scratch.app/Contents/Info.plist # And change CFBundleName CFBundleDisplayName accordingly It's ugly, I know it, it's a bit like installing the same application twice, with a different name, but who cares? I mean, scrolling is damn fast now <3 PS. remember to re-sync these '/Applications' directories the next time that an new release of `alacritty` is published. # 2020-03-17 _Rant against software development in 2020: start_ We added `prettier` to our repo because you know, it feels good not to have to worry about indentation and style; so we linted all of our files (yes, it was a massing changeset), and then carried on. Except...changesets pushed by different team members looked different from one another, like `prettier` settings were not properly parsed...and yet all of them had their IDEs properly configured. Well, it turns out 'prettier-vscode', `prettier` plugin for VisualStudio Code, is not picking up '.prettierignore' unless it's in the project working directory: . And guess what? Since we have a monorepo, we have different '.prettierignore' files, one per sub-module...so that's why changesets were looking different. Long story short: - `prettier` does not support that behavior: - 'prettier-vscode' does not want to support that, unless `prettier` support that: So yeah, users, go fuck yourselves! Well, of course there are [workarounds](https://code.visualstudio.com/docs/editor/multi-root-workspaces), but still, `git` works that way, `npm` works that way...why not implement a similar behavior? I am pretty sure there are reasons, but still... _Rant against software development in 2020: start_ --- Few notes on how to implement multitenancy with Elasticsearch: [shared-index](https://www.elastic.co/guide/en/elasticsearch/guide/current/shared-index.html), i.e. a single index, shared by all the tenants, in which all the documents have a tenant identifer PUT /forums { "settings": { "number_of_shards": 10 }, "mappings": { "post": { "properties": { "forum_id": { "type": "string", "index": "not_analyzed" } } } } } PUT /forums/post/1 { "forum_id": "baking", "title": "Easy recipe for ginger nuts", ... } This approach works, but all the documents for a specific tenant will be scattered across all the shards of the index. Enters [document routing](https://www.elastic.co/guide/en/elasticsearch/guide/current/routing-value.html): PUT /forums/post/1?routing=baking { "forum_id": "baking", "title": "Easy recipe for ginger nuts", ... } By specifing 'baking' as routing value, we are basically telling Elasticsearch to always use a single shard... which one? `shard = hash(routing) % number_of_primary_shards` (not that we care, as long as it's always the same). Oh, and when searching data (with routing), don't forget to filter by the tenant identifer, or you ill accidentally retrun other tenant's data: GET /forums/post/_search?routing=baking { "query": { "bool": { "must": { "match": { "title": "ginger nuts" } }, "filter": { "term": { "forum_id": { "baking" } } } } } } But...can we do better, and have Elasticsearch do the filtering for us? Yes, [aliases](https://www.elastic.co/guide/en/elasticsearch/guide/current/faking-it.html) to the rescue! When you associate an alias with an index, you can also specify a filter and routing values: PUT /forums/_alias/baking { "routing": "baking", "filter": { "term": { "forum_id": "baking" } } } Now we can use the 'baking' index when indexing, and forget about _routing_: PUT /baking/post/1 { "forum_id": "baking", "title": "Easy recipe for ginger nuts", ... } Similarly, when searching, we can omit the _routing_ parameter, and the filters too! GET /baking/post/_search { "query": { "match": { "title": "ginger nuts" } } } Next step: see if it's possible to implement ACL so that one user can connect to Elasticsearch and only see their aliases: neither the aliases used by other tenants, nor the big, fat, shared index. # 2020-03-16 ? ctags and javascript files: https://www.reddit.com/r/vim/comments/fj9tsz/do_lsps_make_tag_generating_tools_obsolete/fkn1tsk/ # 2020-03-15 Asking for help, on [reddit](https://www.reddit.com/r/node/comments/fj1zru/best_practices_on_deploying_multitenant/?), on best-practices on how to implement multitenancy Node.js applications: Hello Everyone, I am trying to add [multitenancy](https://en.wikipedia.org/wiki/Multitenancy) support to an existing Node.js/Express.js application, and while the Web is full of articles on how to properly segregate data (e.g. additional 'tenant_id' column, different databases), I am currently struggling to find best-practices on how to properly implement it... let me explain. Irrespective of your chosen data segregation model, there comes a point throughout the life-cycle of the user request where you will have to: 1) figure out what the current tenant is, 2) use that information to fetch the _right_ data (e.g. run custom _by-tenant-id_ queries, or by selecting the proper database connection). I am particularly interested in hearing how others have implemented this, especially given that point 1 and 2 might be a few asynchronous function calls away. There are 3 possible options I could think of, to solve this problem: 1. Use libraries like [continuation-local-storage](https://github.com/othiym23/node-continuation-local-storage), or its successor [cls-hooked](https://github.com/Jeff-Lewis/cls-hooked) - these libraries rely on experimental APIs and require monkey-patching of other asynchronous libraries to make sure the context is indeed properly propagated - when users ask about [maturity](https://github.com/Jeff-Lewis/cls-hooked/issues/22), their authors suggest to heavily test internally and make sure the context is never lost - other users on [reddit](https://www.reddit.com/r/node/comments/7g30kv/expressjs_logging_info_with_global_unique_request/) confirm they had a lot of issues with cls, and because of that only used it for logging purposes 2. Pass a context object around, **everywhere** (a la Golang's [context](https://golang.org/pkg/context/)) - it's a **lot** of work, especially for an already existing application (and I am not [alone](https://www.reddit.com/r/node/comments/8qi3dv/express_forward_custom_headers_received_from/) in thinking this) 3. Defer the initialization of the used services until the request is received, and inject this context object as a constructor argument. - this seems to require less changes as compared to the previous option, but again, it forces you to defer the initialization of **all** your services until you know what the request tenant is Now, all this journey has led me to believe that maybe this is not the _right_ way to implement multitenancy inside Node.js applications; maybe, instead of having a single Node.js application being able to serve requests coming from different tenants, we should just _multi-instantiate_ this application and have each of these instances serve one specific tenant only. This is one realization that I had when thinking about all this: - Service multitenancy (i.e. a single service, being able to serve requests coming from different tenants), is not required, but could save you some money later on, as that would let you utilize hardware resources to full extent - Service multi-instantiation (i.e. different services, each one specialized to serve a specific tenant), is not required, but it's **inevitable**: there are only so many requests you will be able to serve with a single multitenant service So probably I shouldn't worry much about how to add support multitenancy support to an existing Node.js application, but rather _multi-instantiate_ it and put a broker in front of it (i.e. Nginx). Let me know your thoughts. --- Interesting article about [Zone.js](https://medium.com/ngconf/deep-dive-into-zone-js-part-1-execution-context-92166bbb957): the dummy implementation is ridiculously simple! var currentZoneFrame = {zone: root, parent: null}; class Zone { static get current() { return currentZoneFrame.zone; } run(callback, applyThis, applyArgs) { _currentZoneFrame = {parent: _currentZoneFrame, zone: this}; try { return callback.apply(applyThis, applyArgs); } finally { _currentZoneFrame = _currentZoneFrame.parent; } } } This is how you are expected to use it: var zoneA = Zone.current.fork({name: 'zoneA'}); function callback() { // _currentZoneFrame: {zone: zoneA, parent: rootZoneFrame} console.log('callback is invoked in context', Zone.current.name); } // _currentZoneFrame = rootZoneFrame = {zone: root, parent: null} zoneA.run(function() { // _currentZoneFrame: {zone: zoneA, parent: rootZoneFrame} console.log('I am in context', Zone.current.name); setTimeout(zoneA.wrap(callback), 100); }); Too bad, the [TC39 proposal](https://github.com/domenic/zones) has been withdrawn! --- * finished reading [Graph Algorithms: Practical Examples in Apache Spark and Neo4j](https://www.amazon.com/Graph-Algorithms-Practical-Examples-Apache/dp/1492047686) # 2020-03-09 MacOS Bundle IDs: function mac-os-bundle-id() { lsappinfo info -only bundleid "$*" | cut -d '"' -f4 } Note: this only works if the the application you are querying the bundle ID for, is running. https://superuser.com/questions/346369/getting-the-bundle-identifier-of-an-os-x-application-in-a-shell-script # 2020-03-08 English writing lessons: - Use verb + adverb, not the opposite Good: I have worked closely with... Bad: I have closely worked with... - Use 'be experienced with' instead of 'have experience with' - Don't use 'seem'... be assertive: either one is, or is not! - Use 'further' instead of 'improve' --- TIL: you can pass '60m' to `docker logs --since ...` to get logs of the last hours... I knew how to se data/times with `--since`, and always been doing the math in my head to figure out the correct cut-off date-time value; but being able to `--since 60m` is just... pure gold! # 2020-03-07 Today I finally got around to toy a bit with MacOS Karabiner-Elements, and customize my configuration even further so that: - `'` behaves exactly as `` (i.e. ``-modifier when held down, `` when tapped) - `[` behaves exactly as `` (i.e. _movement_-modifier when held down, `` when tapped) What's the deal with these _movement_-modifiers? Just to give you an idea: - ` + h` -> `` - ` + j` -> `` - ` + k` -> `` - ` + l` -> `` - ` + p` -> _paste from clipboard_ - ` + b` -> _one word backward_ - ` + w` -> _one word forward_ For the record, these are the relevant blocks of my karabiner.json file that enabled this: { "description": "Tab as modifier", "manipulators": [ { "description": "tab as modifier", "from": { "key_code": "tab" }, "to": [ { "set_variable": { "name": "tab_modifier", "value": 1 } } ], "to_after_key_up": [ { "set_variable": { "name": "tab_modifier", "value": 0 } } ], "to_if_alone": [ { "key_code": "tab" } ], "type": "basic" }, { "conditions": [ { "name": "tab_modifier", "type": "variable_if", "value": 1 } ], "description": "tab + h as left_arrow", "from": { "key_code": "h" }, "to": [ { "key_code": "left_arrow" } ], "type": "basic" }, { "conditions": [ { "name": "tab_modifier", "type": "variable_if", "value": 1 } ], "description": "tab + j as left_arrow", "from": { "key_code": "j" }, "to": [ { "key_code": "down_arrow" } ], "type": "basic" }, { "conditions": [ { "name": "tab_modifier", "type": "variable_if", "value": 1 } ], "description": "tab + k as left_arrow", "from": { "key_code": "k" }, "to": [ { "key_code": "up_arrow" } ], "type": "basic" }, { "conditions": [ { "name": "tab_modifier", "type": "variable_if", "value": 1 } ], "description": "tab + l as left_arrow", "from": { "key_code": "l" }, "to": [ { "key_code": "right_arrow" } ], "type": "basic" }, { "conditions": [ { "name": "tab_modifier", "type": "variable_if", "value": 1 } ], "description": "tab + p as shift inssert", "from": { "key_code": "p" }, "to": [ { "key_code": "v", "modifiers": [ "left_command" ] } ], "type": "basic" } ] } { "description": "open_bracket as modifier", "manipulators": [ { "description": "open_bracket as modifier", "from": { "key_code": "open_bracket" }, "to": [ { "set_variable": { "name": "open_bracket", "value": 1 } } ], "to_after_key_up": [ { "set_variable": { "name": "open_bracket", "value": 0 } } ], "to_if_alone": [ { "key_code": "open_bracket" } ], "type": "basic" }, { "conditions": [ { "name": "open_bracket", "type": "variable_if", "value": 1 } ], "description": "open_bracket + w as left_alt + right_arrow (one word forward)", "from": { "key_code": "w" }, "to": [ { "key_code": "right_arrow", "modifiers": [ "left_alt" ] } ], "type": "basic" }, { "conditions": [ { "name": "open_bracket", "type": "variable_if", "value": 1 } ], "description": "open_bracket + b as left_alt + left_arrow (one word backward)", "from": { "key_code": "b" }, "to": [ { "key_code": "left_arrow", "modifiers": [ "left_alt" ] } ], "type": "basic" } ] } --- ? implement these very same mappings on Windows (AutoHotKey + SharpKeys) # 2020-03-06 TIL: when you have an unresponsive SSH connection, you can kill it with `~.` Also, you can type `~?` and get the list of supported escape codes: remote> cat # then type ~? Supported escape sequences: ~. - terminate connection (and any multiplexed sessions) ~B - send a BREAK to the remote system ~C - open a command line ~R - request rekey ~V/v - decrease/increase verbosity (LogLevel) ~^Z - suspend ssh ~# - list forwarded connections ~& - background ssh (when waiting for connections to terminate) ~? - this message ~~ - send the escape character by typing it twice https://superuser.com/questions/985437/ssh-escape-key-only-works-when-connection-is-stuck --- * finished reading [Composing Software](https://leanpub.com/composingsoftware) ? I realized my kindle highlights for books I haven't purchased through amazon are not getting displayed here: https://read.amazon.com/ Need to figure out a way to extract them # 2020-03-05 Convert markdown files to PDF on MacOS: > brew install pandoc > brew install homebrew/cask/basictex > ln -s /Library/TeX/Root/bin/x86_64-darwin/pdflatex /usr/local/bin/pdflatex > pandoc multi-tenancy.md -o multi-tenancy.pdf # 2020-03-03 When we estimate, it is worth considering worst-case scenarios as well as best-case scenarios in order to provide a more accurate estimate. A formula I often use in estimation templates is the following (derived from a book I read a long time ago): [Best + (Normal * 2) + Worst] / 4 Keep a risk register alongside the estimates Review the estimates and the risk register at the end of the sprint Estimating work is never easy. No two tasks are the same, and no two people are the same. Even if they were, the environment (code base) would most certainly not be the same. Getting to a precise estimation of work is almost impossible without doing the work itself. Source: # 2020-02-16 + R-type notation for documenting Javascript functions ? Auto-curry recursive magic spell (from composing software) # 2020-02-15 All right, I finally got around to get `sic(1)` up and running on my laptop and I am quite...OK with the result. Why sic and not irssi, or weechat, you migth ask; I don't know, I guess I just got bored... Anyway, [sic](https://tools.suckless.org/sic/) stands for simple irc client, and it really lives up to its name: - let you connect to a server (plain connection, no SSL) - support `PASS` authentication - outputs a single stream of messages (yes, all the messages received on the different channels you joined, are just...comingled) That's it, really. Suckless tools might seem...lacking at first -- well, I guess they really are -- but there is a reason for that: they usually are highly composable, which means that most of the times you can combine them with other tools (suckless' tools, or unix tools in general) and say implement a feature that was originally missing. For example, `sic(1)` does only support plaintext connections; not a problem, create a secure TCP connection using `socat(1)` and have `sic(1)` connect to the local relay instead: socat tcp-listen:6697 openssl-connect:irc.freenode.net:6697 sic -h 127.0.0.1 -p 6697 -n your-nickname Or, `sic(1)` does not support colors, but everyone would agree that a certain level of color does not hurt; easy, pipe `sic(1)`'s output into `sed(1)`, et voila: sic -h 127.0.0.1 -p 6697 -n your-nickname \ sed/your-nickname/${RED}your-nickname${DEFAULT}/ Or, you want to store every message ever received or sent? easy, that's what `tee(1)` is for: sic -h 127.0.0.1 -p 6697 -n your-nickname \ tee -a ~/irc/server.log \ ... Or you can add a typing hisory using `rlwrap(1)`, or on-connect auto-commands (e.g. join #vim) by using a combination of shell pipes, `echo(1)` and `cat(1)`. Anyway, I guess you got the idea...and if anything, I hope I made a you a bit curious about this whole _compose-all-the-things_ philosophy. These are the scripts I ended up creating -- feel free to poke around and tell me if anything intersting come into your mind: - [sicw](https://github.com/iamFIREcracker/dotfiles/blob/master/bin/sicw) - [sicw-rlwrap](https://github.com/iamFIREcracker/dotfiles/blob/master/bin/sicw-rlwrap) --- Say you have an interactive process like a REPL or something; how can you send some data/text to it, before entering in interactive mode, waiting for the user to type anything? For example: how can you pass some _auto-commands_ like `:j #suckless` and `:j #lisp` to `sic(1)`, so that you don't have to manually type them every time you connect to the server? Note, you still want to be able to manually send messages to the server, after the auto-commands have been sent. Well, I found the solution somewhere on StackOverflow (unfortunately I cannot find the link to the page anymore): #!/usr/bin/env bash echo -en "$@" cat - What this `input` script does, is: - `echo(1)` its input argument > echo -en 'foo\nbar\n' foo bar - Then start reading from stdin (i.e. enter interactive mode) Clever, isn't it? > input ':j #suckless\n' ':j #vim\n' | sic --- * cb: added support for xsel(1) -- not that I needed it, but since I stumbled upon it when looking at other people dotfiles repos, I figured I should just steal it ? add SelectAndSendToTerminal("^Vap") to my .vimrc -- currently there is `SendSelectionToTerminal`, but it forces me to use nmap/xmap, instead of nnoremap/xnoremap, and I have to manually save/restore the state of the window (annoying) ? add kr to the family of 2-letters-named tools, to run `keyring` remotely -- will have to be careful to make sure people on the same network can not snoop my passwords ;-) # 2020-02-14 The Old Way Using Decorate-Sort-Undecorate This idiom is called Decorate-Sort-Undecorate after its three steps: - First, the initial list is decorated with new values that control the sort order. - Second, the decorated list is sorted. - Finally, the decorations are removed, creating a list that contains only the initial values in the new order. From: https://docs.python.org/3/howto/sorting.html Spent a good 30 mins trying to find this reference... somehow I remembered it was called Wrap-Sort-Unwrap and Google wasn't returning anything meaningful. After the fact, I was able to find this in the Python Cookbook Recipes by searching for pack/unpack. # 2020-02-13 + remap '/" to F13, and then use it as a hyper when hold-down (e.g. +S as CTRL) + remap '[" to F14, and then use it as a hyper when hold-down (e.g. +w to move forward one word, +b to move backward one word) # 2020-02-08 * finished reading [Remote](https://www.amazon.com/Remote-Office-Not-Required/dp/0804137501) ? bunny1 in common lisp # 2020-02-06 I got tired of the colors of my prompt (probably because the default colors palette used by most of the terminal applications are kind of terrible and I don't want to bother finding new colorscheme), so here is what I am experienting with: - username: bold (previously pink) - hostname: cyan / pink (previously yellow / blue) - path: underline (previously green) It's definitely less colored as it used to be, and I am pretty sure I will tweak it again over the following days, but let's see if it works --- ? https://github.com/ds26gte/scmindent # 2020-02-03 Vim was segfaulting when opening lisp files, and after bisecting my ft_commonlisp `autogroup` I found the culprit: au syntax lisp RainbowParenthesesLoadRound I dug deeper and found that the line in the plugin that Vim was tripping over, was: for each in reverse(range(1, s:max_depth)) It did not take me long to get to this vim issue: https://github.com/vim/vim/issues/5541, and changing `reverse(range(...))` with a simple `range()` with negative 'stride' did solve the problem for me: ``` diff --git a/autoload/rainbow_parentheses.vim b/autoload/rainbow_parentheses.vim index b4963af..455ad22 100644 --- a/autoload/rainbow_parentheses.vim +++ b/autoload/rainbow_parentheses.vim @@ -106,7 +106,7 @@ func! rainbow_parentheses#load(...) let b:loaded = [0,0,0,0] endif let b:loaded[a:1] = s:loadtgl && b:loaded[a:1] ? 0 : 1 - for each in reverse(range(1, s:max_depth)) + for each in range(s:max_depth, 1, -1) let region = 'level'. each .(b:loaded[a:1] ? '' : 'none') let grp = b:loaded[a:1] ? 'level'.each.'c' : 'Normal' let cmd = 'sy region %s matchgroup=%s start=/%s/ end=/%s/ contains=TOP,%s,NoInParens fold' ``` Opened a MR for this: https://github.com/eapache/rainbow_parentheses.vim/pull/12 # 2020-02-02 Finally got around to enhance pmdb.lisp (Poor Man's debugger) to add a few macros to facilitate profiling of slow functions -- not really debugging per-se, I will give you that, but since I did not have a Poor Man's profiler package yet and I wanted to get this done quickly, I figured I could shove it inside pmdb.lisp and start using it (I can always refactor it later). Anyway, these are the macros that I added: - DEFUN/PROFILED: a DEFUN wrapper to create functions that, when executed, would keep track of their execution times - PROFILE: a macro (similar to TIME) to evaluate a given expression and output information about profiled functions (e.g. % of CPU time, execution time, number of calls) Say you TIMEd an expression and decided you wanted to try and understand where the bottlenecks are; the idea is to replace all the DEFUNs of all the potential bottleneck-y functions, with DEFUN/PROFILED, and then replace TIME with PROFILE. For example, say you started from something like: (defun fun-1 (&aux (ret 0)) (dotimes (n 10000) (incf ret)) ret) (defun fun-2 (&aux (ret 0)) (dotimes (n 2000000) (incf ret)) ret) (defun fun-3 (&aux (ret 0)) (dotimes (n 200000000) (incf ret)) ret) (defun main () (fun-1) (fun-2) (fun-3)) To time the execution time of MAIN, you would probably do the following: (time (main)) ; Evaluation took: ; 0.396 seconds of real time ; 0.394496 seconds of total run time (0.392971 user, 0.001525 system) ; 99.49% CPU ; 910,697,157 processor cycles ; 0 bytes consed ; ; 200000000 Perfect! But what's the function that kept the CPU busy, the most? Replace all the DEFUNs with DEFUN/PROFILED: (defun/profiled fun-1 (&aux (ret 0)) (dotimes (n 10000) (incf ret)) ret) (defun/profiled fun-2 (&aux (ret 0)) (dotimes (n 2000000) (incf ret)) ret) (defun/profiled fun-3 (&aux (ret 0)) (dotimes (n 200000000) (incf ret)) ret) Replace TIME with PROFILE, et voila', you now have a better idea of where your bottleneck might be: (profile (main)) ; Profiled functions: ; 98.00% FUN-3: 449 ticks (0.449s) over 1 calls for 449 (0.449s) per. ; 2.00% FUN-2: 8 ticks (0.008s) over 1 calls for 8 (0.008s) per. ; 0.00% FUN-1: 0 ticks (0.0s) over 1 calls for 0 (0.0s) per. ; ; 200000000 It also works with nested function calls too: (defun/profiled fun-4 (&aux (ret 0)) (dotimes (n 200000000) (incf ret)) ret) (defun/profiled fun-5 (&aux (ret 0)) (dotimes (n 200000000) (incf ret)) (fun-4) ret) (defun main () (fun-1) (fun-2) (fun-3) (fun-4) (fun-5)) (profile (main)) ; Profiled functions: ; 39.00% FUN-5: 880 ticks (0.88s) over 1 calls for 880 (0.88s) per. ; 21.00% FUN-4: 464 ticks (0.464s) over 1 calls for 464 (0.464s) per. ; 20.00% FUN-5/FUN-4: 451 ticks (0.451s) over 1 calls for 451 (0.451s) per. ; 19.00% FUN-3: 430 ticks (0.43s) over 1 calls for 430 (0.43s) per. ; 0.00% FUN-2: 9 ticks (0.009s) over 1 calls for 9 (0.009s) per. ; 0.00% FUN-1: 0 ticks (0.0s) over 1 calls for 0 (0.0s) per. ; ; 200000000 Percentages would start to lose meaning here (FUN-5/FUN-4 execution time would be included in FUN-5's one), but still I am quite happy with the overall result. PS. If you read PCL, you should notice quite a few similarities with the WITH-TIMING macro explained at the end of the book. --- I am going to leave it here (another e-learning platform): https://www.edx.org/ # 2020-01-31 * finished reading [The Mythical Man-Month](https://www.amazon.com/Mythical-Man-Month-Software-Engineering-Anniversary/dp/0201835959) # 2020-01-26 Few Lisp book recommendations (in this order): * Common Lisp: A Gentle Introduction to Symbolic Computation * Practical Common Lips ? Paradigms of Artificial Intelligence Programming (PAIP) ? Common Lisp Recipes (CLR): http://weitz.de/cl-recipes/ ? Patterns of software Book by Richard P. Gabriel ? On Lisp (Paul Graham): http://www.paulgraham.com/onlisp.html ? Let Over Lambda: https://letoverlambda.com/ --- * finished reading [Practical Common Lisp](https://www.amazon.com/gp/product/1590592395?ie=UTF8&tag=gigamonkeys-20&linkCode=as2&camp=1789&creative=9325&creativeASIN=1590592395) # 2020-01-24 Got presented with the following info messages this morning, while booting up my dev box: [default] GuestAdditions seems to be installed (6.0.14) correctly, but not running. Got different reports about installed GuestAdditions version: Virtualbox on your host claims: 5.2.8 VBoxService inside the vm claims: 6.0.14 Going on, assuming VBoxService is correct... Got different reports about installed GuestAdditions version: Virtualbox on your host claims: 5.2.8 VBoxService inside the vm claims: 6.0.14 Going on, assuming VBoxService is correct... VirtualBox Guest Additions: Starting. VirtualBox Guest Additions: Building the VirtualBox Guest Additions kernel modules. This may take a while. VirtualBox Guest Additions: To build modules for other installed kernels, run VirtualBox Guest Additions: /sbin/rcvboxadd quicksetup VirtualBox Guest Additions: or VirtualBox Guest Additions: /sbin/rcvboxadd quicksetup all VirtualBox Guest Additions: Kernel headers not found for target kernel 4.15.0-72-generic. Please install them and execute /sbin/rcvboxadd setup VirtualBox Guest Additions: Running kernel modules will not be replaced until the system is restarted All right then, let's install them and see what happens: > sudo apt-get install linux-headers-$(uname -r) ... > sudo /sbin/rcvboxadd setup VirtualBox Guest Additions: Starting. VirtualBox Guest Additions: Building the VirtualBox Guest Additions kernel modules. This may take a while. VirtualBox Guest Additions: To build modules for other installed kernels, run VirtualBox Guest Additions: /sbin/rcvboxadd quicksetup VirtualBox Guest Additions: or VirtualBox Guest Additions: /sbin/rcvboxadd quicksetup all VirtualBox Guest Additions: Building the modules for kernel 4.15.0-72-generic. update-initramfs: Generating /boot/initrd.img-4.15.0-72-generic VirtualBox Guest Additions: Running kernel modules will not be replaced until the system is restarted Reload the virtual box and.... > vagrant reload ... [default] GuestAdditions 6.0.14 running --- OK. ==> default: Checking for guest additions in VM... ... # 2020-01-19 All right, I feel dumb... For almost one year I have been dreaming of a `WITH-SLOTS` macro to help with structures and not only classes, and today I stumbled upon a piece of Common Lisp code where the author was using `WITH-SLOTS` with a structure. "That has to be homemade/library macro, right?" I thought. Well, it turns out `WITH-SLOTS` works with slots, and strucutres use slots too, so I have been waiting all this time.. for nothing. Let's look at the bright side of this, though: now I can finally use it! ``` (defstruct foo bar baz) => FOO (let ((foo (make-foo :bar 123 :baz 'asd))) (with-slots ((bar1 bar) (baz1 baz)) foo (format T "~a ~a~&" bar1 baz1))) => 123 ASD NIL ``` Reference: http://clhs.lisp.se/Body/m_w_slts.htm --- ? implement topaz's paste, in common lisp: https://github.com/topaz/paste # 2020-01-18 EVAL-WHEN is there to tell the file compiler whether it should execute code at compile-time (which it usually does not do for function definitions) and whether it should arrange the compiled code in the compiled file to be executed at load time. This only works for top-level forms. Common Lisp runs the file compiler (remember we are talking about compiling files, not executing in a REPL) in a full Lisp environment and can run arbitrary code at compile time (for example as part of the development environment's tools, to generate code, to optimize code, etc.). If the file compiler wants to run code, then the definitions need to be known to the file compiler. Also remember, that during macro expansion the code of the macro gets executed to generate the expanded code. All the functions and macros that the macro itself calls to compute the code, need to be available at compile time. What does not need to be available at compile time, is the code the macro form expands to. This is sometimes a source of confusion, but it can be learned and then using it isn't too hard. But the confusing part here is that the file compiler itself is programmable and can run Lisp code at compile time. Thus we need to understand the concept that code might be running at different situations: in a REPL, at load time, at compile time, during macro expansion, at runtime, etc. Also remember that when you compile a file, you need to load the file then, if the compiler needs to call parts of it later. If a function is just compiled, the file compiler will not store the code in the compile-time environment and also not after finishing the compilation of the file. If you need the code to be executed, then you need to load the compiled code -> or use EVAL-WHEN -> see below. https://stackoverflow.com/a/45683744 --- All right, replaced a bunch of `(if (< a b) -1 (if (> a b) 1 0))` expressions with `(signum (- a b))`, but I wonder: doesn't a mathematical/cs function exist, do refer to this? It seems too common as a pattern, not to have an agreed name -- https://www.reddit.com/r/Common_Lisp/comments/eqf061/is_the_following_a_commonknown_mathematicalcs/? Its name is: three-way comparison, a.k.a. spaceship operator --- Figured out how to customize Clozure-CL REPL prompt... it couldn't be easier, could it?! ``` ;; What sorcery is this? In summary: ;; ;; - Use the first argument as conditional (I believe it represents the number ;; of pending stacktraces or something ;; - If none, call PACKAGE-PROMPT -- we need to re-add one argument to the stack ;; or invoking user defined functions would fail ;; - If there are pending exceptions to process, print the lot followed by `]` ;; ;; FORMAT directives 101 ;; ;; ~[...~] Conditional expression. This is a set of control strings, called ;; clauses, one of which is chosen and used. The clauses are separated by ~; ;; and the construct is terminated by ~]. Also, ~:; can be used to mark a ;; default clause ;; ~/name/ calls the user defined function, NAME ;; ~:* ignores backwards; that is, it backs up in the list of arguments so ;; that the argument last processed will be processed again. ~n:* backs up ;; n arguments. ;; ;; Source: https://lists.clozure.com/pipermail/openmcl-devel/2015-January/010862.html (setf ccl::*listener-prompt-format* "~[~:*~/package-prompt/~:;~:*~d]~]") ``` --- ? aoc: abcl: hangs on 2018/22 ? aoc: allegro: attempts to call RECURISVELY, which is undefined.... ? aoc: ccl: hangs on 2018/22 ? aoc: clisp: =: NIL is not a number (last 1AM test appears to be 2019/18) ? aoc: cmulc: Structure for accessor AOC/2019/24::TILES is not a AOC/2019/24::ERIS: NIL ? aoc: ecl: hangs on 2019/20 # 2020-01-17 Finally got around to refactor 2019/15, and fix the +1 issue The issue was pretty dumb... when reading 2 from the robot I ended up setting oxygen with the current position, and not with the position after the movement! Thanks to some Reddit folk, I ended up simplifying the solution by implementing a simple dfs to explore the map (no more 5000 ticks random walking) It feels good! # 2020-01-16 Couple of TILs (not all today's really, but whatever): - `:serial T` inside ASDL systesms, make sure that components are compiled in order; otherwise, in case of inter-file dependencies, one is forced to to use `:depends-on`, which is annoying - you need to run `(ql:update-dist "...")` to download an updated version of a dist, or `(ql:update-all-dists)` to download them all --- Fixed xml-mitter#6, added tests support to the project Fixing #6 was pretty easy; in fact, double quoting WITH-RSS optional arguments was all we had to do. Adding unit tests support to the module on the other end, wound up being not as easy as I initially thought it would, but still, I am quite happy with the final result. I split the tests support in 3 different sub-activities: 1) Add a package (and a system) for the tests: `:xml-emitter/tests` 2) Add a Makefile to run the tests This turned out to be more annoying than expected: - The interactive debugger needs to be disabled when tests fail, or the CI job would hang forever - The spawn REPL needs to be shut down when tests pass, or again, it would cause the CI job to hang forever - Certain implementations did not exit with non-0 code when tests failed And all this needed to be done outside of the testing system (hence in the Makefile) becasue I still wanted to leave things interactive when people tried to run the tests with `(asdl:test-system ...)` 3) Add a .travis.yml file to test the library with multiple implementations The result: - Makefile ``` .PHONY: test test-sbcl test-ros lisps := $(shell find . -type f \( -iname \*.asd -o -iname \*.lisp \)) cl-print-version-args := --eval '\ (progn \ (print (lisp-implementation-version)) \ (terpri))' cl-test-args := --eval '\ (progn \ (ql:quickload :xml-emitter/tests :verbose T) \ (let ((exit-code 0)) \ (handler-case (asdf:test-system :xml-emitter) \ (error (c) \ (format T "~&~A~%" c) \ (setf exit-code 1))) \ (uiop:quit exit-code)))' all: test # Tests ----------------------------------------------------------------------- test: test-sbcl test-sbcl: $(lisps) sbcl --noinform $(cl-test-args) test-ros: $(lisps) ros run $(cl-print-version-args) $(cl-test-args) ``` - .travis.yml ``` language: generic env: global: - PATH=~/.roswell/bin:$PATH - ROSWELL_INSTALL_DIR=$HOME/.roswell - ROSWELL_VERSION=19.12.13.103 - ROSWELL_URL="https://raw.githubusercontent.com/roswell/roswell/v$ROSWELL_VERSION/scripts/install-for-ci.sh" matrix: - LISP=abcl - LISP=allegro - LISP=ccl - LISP=clisp - LISP=cmucl - LISP=ecl - LISP=sbcl install: - curl -L $ROSWELL_URL | sh script: - make test-ros ``` This is it! # 2020-01-15 Today I installed `roswell` again, because I wanted to test `xml-emitter` with abcl, but somehow it seemed the runtime corrupted: > brew install roswell ... boring output > ros install sbcl sbcl-bin/1.4.0 does not exist.stop. > ros help > sbcl-bin/1.4.0 does not exist.stop. > Making core for Roswell... > sbcl-bin/1.4.0 does not exist.stop. > sbcl-bin/1.4.0 does not exist.stop. Googling for the error I stumbled upon this GH issue: https://github.com/roswell/roswell/issues/216, and after nuking ~/.roswell, `ros setup` successfully installed the latest version of sbcl. --- I was erroneously trying to use `cl-travis` instead of plain `roswell`. Why erroneusly? `cl-travis` uses `CIM`: https://github.com/sionescu/CIM, which turns out to have been deprectated in [2017](https://keens.github.io/blog/2017/01/29/deprecating_cim/). What's the replacement? `ros` and similars -- so better directly use `ros` instead. The command `ros` however won't be available unless you tweak a couple of ENV vars: ``` global: - PATH=~/.roswell/bin:$PATH - ROSWELL_INSTALL_DIR=$HOME/.roswell ``` # 2020-01-11 * Fix DIGITS compilation error: `integer` is not a variable #64 # 2020-01-05 Imported yet another trick from SJL, this time to automatically load all the .lisp files conained a directory -- this way I don't have to update aoc.asd everytime I add a new day. First you define a new class, and then implement the generic method COMPONENT-CHILDREN: ``` (defclass auto-module (module) ()) (defmethod component-children ((self auto-module)) (mapcar (lambda (p) (make-instance 'cl-source-file :type "lisp" :pathname p :name (pathname-name p) :parent (component-parent self))) (directory-files (component-pathname self) (make-pathname :directory nil :name *wild* :type "lisp")))) ``` Next, you use `:auto-module` in your components list as follows (2nd line): ``` (:file "intcode") (:auto-module "2017") (:module "2018" :serial t :components ((:file "day01") ``` --- TIL, VALUES in Common Lisp, is SETF-able! ``` (loop :with remainder :do (setf (values n remainder) (truncate n base)) :collect remainder :until (zerop integer))) ``` --- * vim: add mapping to (in-package) the current file in the repl + quickutil: pull request to fix a problem with DIGITS: use `n` instead of `integer` when calling :until ? quickutil: pull request to change DIGITS to optionally return digits in the reverse order (this way I can ditch DIGITS-REVERSE) # 2020-01-04 * add ADJACENTS to aoc.utils * change BFS to use QUEUE instead of poor's HEAPQ # 2020-01-03 * add quickutils to aoc ? https://itnext.io/a-wild-way-to-check-if-a-number-is-prime-using-a-regular-expression-4edfb725f895 # 2020-01-02 ? http://play.elevatorsaga.com/ ? PARTIAL-1: evaluate only once, and not every-time the lambda function is called # 2019-12-31 Migrated all the mercurial repository from Bitbucket to Github: cd ~/tmp git clone https://github.com/frej/fast-export.git hg clone ssh://hg@bitbucket.org/iamFIREcracker/logfilter git init logfilter.git ../fast-export/hg-fast-export.sh -r ../logfilter git remote add origin git@github.com:iamFIREcracker/logfilter.git git push origin --all git push origin --tags # repeat https://git-scm.com/book/en/v2/Git-and-Other-Systems-Migrating-to-Git --- * migrated logfilter to github -- thanks bitbucket * migrated strappo-api to github -- thanks bitbucket * migrated weblib to github -- thanks bitbucket * migrated strappo-analytics to github -- thanks bitbucket * migrated strappon to github -- thanks bitbucket * migrated getstrappo to github -- thanks bitbucket * migrated deploy-strappo to github -- thanks bitbucket * migrated osaic to github -- thanks bitbucket * migrated expensio to github -- thanks bitbucket * migrated medicinadellosport to github -- thanks bitbucket * migrated webpy-facebook-login to github -- thanks bitbucket * aoc: 2019/18/2 -- this is fucking it! ? aoc: 2019/18/2: 400s to complete part 2... I blame it on HEAPQ ? aoc: 2019/18/2: don't manually update the input to generate the 4 sub-vaults + aoc: add DIGITS function to utils, and use it inside 2019/04 and 2019/16 + aoc: 2019/17: use PRINT-HASH-MAP-GRID + aoc: 2019/20: figure out a way to programmatically figure out if a portal is on the outside or on in the inside ? aoc: add goap-p to search algo, and review dijkstra and bfs.. + aoc: add FLATTEN to utils + common lisp use quickutils! ? update logfilter repo references to the new github location ? update osaic repo references to the new github location # 2019-12-30 * aoc: 2019/18/1 ? aoc: 2019/18/1: 20s to complete part 1... # 2019-12-27 * aoc: 2019/22/2 * aoc: 2019/25 ? aoc: 2019/22/2: explain all that math stuff, and move some of it inside utils.lisp ? aoc: 2019/25 automate it?! # 2019-12-26 * aoc: 2019/22/1 ? aoc: 2019/22 -- explain the math behind it, the modeling # 2019-12-25 * aoc: 2019/24/2 ? aoc: 2019/24: better logic for recurive neighbors ? aoc: 2019/24: how to efficiently add levels to the state? currently we end up with way more states, and that takes processing time # 2019-12-24 * aoc: 2019/24/1 # 2019-12-23 * aoc: 2019/23 # 2019-12-22 * aoc: 2019/21 # 2019-12-21 * aoc: 2019/20 # 2019-12-20 * aoc: 2019/14/2 + aoc: 2019/14/2 better euristic (binary search from cargo-limit and 0) + create a function to return sortable-difference between two numbers: a < b ? -1 : a > b ? 1 : 0, and use it for 2019/14/2, phases, robots # 2019-12-19 * aoc: 2019/19 ? aoc: proper solution for 2019/19/2 (figure out how to calculate the direction of the uppear border of the beam -- now I manually calculated it by looking at the output) # 2019-12-18 * aoc: 2019/17 * aoc: 2019/16/2 ? find a proper solution for 2019/17/2 -- right now I got the solution by manually looking at the map, and figuring out program inputs. Also, there is no proper solution at the moment :) ? explain the realization about 2019/16/2 # 2019-12-16 * aoc: 2019/16/1 + define CIRCULAR, and use it for 2019/16, and 2018/01: https://stackoverflow.com/questions/16678371/circular-list-in-common-lisp # 2019-12-15 You cannot really schedule `sudo` commands with crontab, you better use root's crontab instead... So, to fix datetime drifing on Virtualbox: # crontab -l 28 * * * * service ntp restart --- AOC 2019/12: That was freaking painful -- for me at least. Part 1 was easy: parse input, apply gravity, apply velocity, repeat, calculate energy. Part 2? Not quite! At first I tried to look at the output of some of the variables to figure out if there were any patters, and noticed: - the *baricenter* of the system (i.e. sum of all xs, sum of all the ys, sum of all the zs) was constant - the overall velocity of the system (i.e. sum of all vxs, sum of all vys, sum of all the vzs) was 0 But unfortunately none of the above turned out to be useful/usable. Something else that I played a lot with, was looking at each moon separately: I would still simulate the whole universe of course, but inspect a moon at time, again, hoping to find patterns to exploit. I was hoping that each moon had a different cycle, and that the overlall system cycles could be calculated as the the LCM of each moon cycles, but each moon was influencing the others, so my logic was flawed. Anyway, I put this on hold, then tried again, I eventually gave up and looked for some help on Reddit, and this is what brought me to the solution [0]: Note that the different dimensions are independent of each other. X's don't depend on y's and z's. The idea that the overall cycle would be calculated as the LCM of other *independent* systems cycles was right; unfortunately I failed to understand what these *independent* systems were. [0] https://www.reddit.com/r/adventofcode/comments/e9jxh2/help_2019_day_12_part_2_what_am_i_not_seeing/ --- * aoc: 2019/15 * aoc: 2019/12/02 + aoc: 2019/15: figure out a proper way to explore the map (now it goes on for 50000 steps) + aoc: 2019/15: why my BFS costs are off by 1? + aoc: print-hash-table-as-grid # 2019-12-14 * aoc: 2019/14/1 ? refactor partial-1/partial-2 to accept a form, instead of a FN and ARGS ? also, why is the following form not compiled? (partial-2 #'* -1 (- _1 _2)) It spits the following error: The value -1 is not of type (OR (VECTOR CHARACTER) (VECTOR NIL) BASE-STRING SYMBOL CHARACTER) # 2019-12-13 My virtualbox date and time is getting out of sync when the laptop is suspended. A temporary fix was to 1) install ntp, 2) restart the service every hour: > crontab -l 28 * * * 0 sudo service ntp restart But there has to be a better way to fix this / prevent this from happening --- * aoc: 2019/13 -- intcode problem, with some AI (well ... a simple `(- ball-x bar-x)` seems to do just fine) # 2019-12-12 * aoc: 2019/10/2 -- phases are hard * aoc: 2019/11 -- phases are hard * aoc: 2019/12/1 ? aoc: 2019/10 needs some serious refactoring around phases, and vector math + aoc: 2019/11/2 -- figure out a way to test for the output string # 2019-12-11 * aoc: 2019/09 -- "parameters in relative mode can be read from or written to." GODDAMMIT! * aoc: 2019/10/a -- my direction-reduce function was able to reduce (5, 0) into (1, 0), (5, 5) into (1, 1), (2, 4) into (1, 2), but **not** (4 6) into (2 3) -,- # 2019-12-10 Wasted the night trying to get a solution for AOC 2019/09, but somehow it ain't working. The logic is simple: ``` for each asteroid, as 'curr' initialize 'remaining' as **all** asteroids for each remaining asteroid, as 'other' calculate the line of sigth between 'current' and 'other' remove from 'remaining' any asteroid hidden **behind** 'other' maximize 'remaining' ``` So the biggest challenge here would be to calculate the sight direction vector between 'curr' and 'other'. Few things to take into account though: - when dir is (0 0): return (0 0) - when dir-x is 0: return vertical unit vector (preserve direction) - when dir-y is 0: return hirizontal unit vector (preserve direction) - when dir-y divides dir-x: reduce it (so we can return (2 1) instead of (4 2)) - when dir-x divides dir-y: reduce it (so we can return (1 2) instead of (2 4)) - otherwise return dir, as-is All good, except it doesn't work -- and I have no idea why... # 2019-12-09 * aoc: 2019/07 + aoc: 2019/05 -- use intcode as per 2019/07 + aoc: 2019/08 -- add test for output part2 # 2019-12-08 * aoc: 2019/08 + aoc: 2019/08/2 -- figure out a way to test for the output string # 2019-12-08 * aoc: 2019/07 -- part2 missing still, fucking opcodes # 2019-12-07 * aoc: 2019/06 # 2019-12-05 * aoc: 2019/05 -- fucking opcodes :-O # 2019-12-04 I am trying to look into compilation errors thrown when loading my :AOC project, but even though I shuffled functions/macros around to make sure functions are defined before they are getting used, it still errors out ... The function AOC::ARGS-REPLACE-PLACEHOLDER-OR-APPEND is undefined. It is defined earlier in the file but is not available at compile-time. I am afraid I need to re-study again how Lisp code is being parsed/compiled, and if there is anything we can do about it. I am on the go right now, so this will have to wait until tomorrow. --- Why on earth would someone use rlwrap with `--pass-sigint-as-sigterm`? I mean, all my *-rlwrap scripts had it, but it was kind of dumb if you think about it... Most of the times rlwrap is used with REPLs, and REPLs usually catch SIGINT to interrupt running operations (e.g. break out from an infinite loop), so why would someone kill the REPL, instead of leave the REPL deal with it? I am sure there are legit use cases for this, it's just that I cannot think of any. --- Damn, my Lisp is really rusty! Today I re-learned about: - :THEREIS and :ALWAYS keywords on LOOP --- Few days ago I threw PR/PRL inside .sbclrc hoping I could handily use them from any REPL I had open. Well I was right and wrong at the same time: I could indeed use PR/PRL in the REPL, but only if inside the :CL-USER package. So I moved those macros inside a new file, made a package out of them (pmdb, poor's man debugger, lol), and loaded it from my sbclrc file; after that I was finally able to use those PR/PRL from any package -- too bad I had to use the package classifier to use them (i.e. PMDB:PR). To fix that, I went on and added :pmdb to the :use form of the package I was working on. Alternatively, I could have created two vim abbreviations like: inoreabbr pr pmdb:pr inoreabbr prl pmdb:prl but for some reasons, abbreviations ain't working while editing Lisp files -- I bet on parinfer getting in the way.. --- * aoc: 2019/04 ? SCASE: a switch for strings -- and maybe use it on 2019 day 03 ? figure out why abbreviations are not working on Lisp files # 2019-12-03 * aoc: 2019/03 -- needs some clean up tho ? ONCE-ONLY: https://stackoverflow.com/questions/9808928/understanding-how-to-implement-once-only-lisp-macro # 2019-12-02 Unfortunately I spent too much time because of a wrong use of RETURN: you can indeed use RETURN (which is the same as RETURN-FROM NIL) only from inside a DO or PROGN block, otherwise you have to use RETURN-FROM followed by the name of the surrounding function. Or...you can create a named block with BLOCK, and RETURN-FROM it: https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node85.html --- * aoc: 2019/02 # 2019-12-01 * aoc: 2019/01 + PR, PRL defined inside .sbclrc, is not available when running the REPL via VLIME -- https://stackoverflow.com/questions/22524213/cant-access-cl-user-symbols-in-new-package-defined-with-make-package + loading :aoc does not work cleanly anymore -- have to load RECURSIVELY first, then all the ARGS-* related functions needed for PARTIAL, then it will eventually work... # 2019-11-26 ? plan-rss: use #'string> when merging, and get rid of the call to REVERSE ? reg-cycle: use localtime() instead of timers # 2019-11-25 plann-rss Previously, testing PARSE-OPTS was a bit of a pain as your REPL could end up getting accidentally killed if passed in options would cause the parsing logic to invoke OPTS:KILL; it goes without saying it that this was very annoying. The fix for this was simple though: 1) Create a new condition, EXIT 2) ERROR this condition (with the expected exit code) instead of directly invoking OPTS:EXIT 3) Invoke PARSE-OPTS inside HANDLER-CASE, and invoke OPTS:EXIT when the EXIT condition is signalled This way: - When run from the command line OPTS:EXIT will be called, and the app will terminate - When run from the repl, an unmanaged condition will be signalled, and the debugger will be invoked --- * plan-rss: add --max-items option # 2019-11-18 Angular, AOT, and custom decorators Plan on using custom decorators with Angular, and AOT? Well, don't...or at least, beware! We did implement a custom decorator lately, to add behavior to existing Angular components in a mixins kind of fashion, and while everything seemed to be working just fine on our local boxes -- where the app runs in JIT mode -- once we switched to prod mode -- where AOT is enabled -- all components decorated with our custom decorator stopped working, completely, like Angular did not recognize them as components...at all. After some fussing around, I noticed that the moment I added a dummy `ngOnInit` to such components, all of a sudden the `ngOnInit` defined by our custom decorator would be run; but not any of the others `ng*` methods enriched by our decorator though, just `ngOnInit`, the one that we also re-defined inside the Angular component. This led me to this Angular ticket: https://github.com/angular/angular/issues/3149, where someone seemed to have a pretty good understanding of what was going on: Appears the reason here is that Angular places `ngComponentDef` generated code before all `__decorate` calls for class. `ɵɵdefineComponent` function takes `onInit` as a reference to `Class.prototype.ngOnInit` and overridding it later has no any effect. ... Here's a pseudo-code of what happens: ``` class Test { ngOnInit() { console.log('original') } } const hook = Test.prototype.ngOnInit; Test.prototype.ngOnInit = () => { console.log(1); } hook(); // prints 'original' ``` In case of JIT compilation `componentDef` is created lazily as soon as `getComponentDef` gets called. It happens later. Someone at the end even tried to suggest a possible solution, https://github.com/angular/angular/issues/31495#issuecomment-549873125, but when I noticed that the author suggested that we added `ngOnInit` to the final component, I hoped there would be a better way to work-around this (especially because we would be forced to redefine `ngOnInit`, `ngOnChanges`, `ngAfterViewInit` and `ngOnDestroy`). Further digging took me to this other Angular ticket: https://github.com/angular/angular/issues/16023, where I found a couple of not-so-promising comments, left almost at the start of the thread: This is a general "problem" with AOT: It relies on being able to statically analyze things like lifecycle hooks, properties with @Input on them, constructor arguments, ... As soon as there is dynamic logic that adds new methods / properties, our AOT compiler does not know about this. Immediately followed by: As we are moving to AOT by default in the future, this won't go away. Sorry, but closing as infeasible. All right, to recap: - Typescript compiler, `tsc`, supports Decorators just fine - Angular's compiler is a custom compiler, different from `tsc` - Angular's compiler is not 100% feature-compatible with `tsc` - Since Angular's compiler does not properly implement support for custom decorators, it's not possible to use custom decorators that enrich objects prototypes (Angular would not notice any enriched method) So it turns out the components annotated with our custom decorator do have to implement `ng*` methods -- we cannot reply on the decorator adding them -- otherwise the AOT compiler would not know of them, and not call them at the right time...Sweet! The solution? Create a base class implementing these `ng*` methods, and have the components annotated with our custom decorator *extend* this base class; the only problem is that we would also have to add `super()` to the component's constructor -- which could be annoying -- but it's either this or no custom decorators, so I guess this will do just fine. ``` export class ObserveInputsBase implements OnChanges, OnDestroy { ngOnChanges(changes: SimpleChanges): void {} ngOnDestroy(): void {} } @Component({ ... }) @CustomDecorator export class TodoComponent extends ObserveInputsBase { constructor(){ super(); } } ``` --- The thing is, :read! appears to be stripping ^M already, irrespective of the use of ++opts:   :read !powershell.exe Get-Clipboard :read ++ff=unix !powershell.exe Get-Clipboard :read ++ff=dos !powershell.exe Get-Clipboard To be honest, I'd expect the second one, where we specify ++ff=unix, to leave trailing ^M, but somehow that's not happening, and for your reference (after I vim -u NONE):   :verbose set ff Returns 'fileformat=unix'   :verbose set ffs Returns 'fileformats=unix,dos' So am I correct if I say that there is something "weird" going on with system()?  I also found the following at the end of system()'s help page, but somehow the experienced behavior is not the documented one:   To make the result more system-independent, the shell output   is filtered to replace with for Macintosh, and   with for DOS-like systems. I even tried to give systemlist() a go, but each entry of the array still has that trailing ^M, so it really seems like Vim cannot properly guess the fileformat from the command output. I am really in the dark here. # 2019-11-16 Trying to understand why copy-pasting things on Vim/Windows generates trailing ^M... Let's create a file with `\r\n`s (that's how the clipboard appears to be populated when I copy things from Chrome): > echo -en "OS cliboards are hart...\r\naren't they?\r\n" > clipboard.txt > cat clipboard.txt OS cliboards are hart... aren't they? > cat clipboard.txt -A OS cliboards are hart...^M$ aren't they?^M$ Let's open up vim now (`vim -u NONE`) and do some experiments: 1) read 'clipboard.txt', under the cursor :read clipboard.txt The content is loaded, not trailing ^M, at the bottom I see: "clpboard.txt" [dos format] 2 lines, 40 characters 2) load 'clipboard.txt' using `read!` :read! cat clipboard.txt The content is loaded, no trailing ^M 3) load the content into a variable, then paste it under the cursor :let @@ = system('cat clipboard.txt') | exe 'normal p' The content is loaded, trailing ^M are added Let's now push the file into the OS clipboard: > cat clipboard.txt | clip.exe Open up vim again, and: 4) paste from the `*` register (works with `Shift+Ins` too): :exe 'normal "*p' OS clipboard is read, no trailing ^M 5) load the content of the clipboard using `read!` :read! powershell.exe Get-Clipboard The content is loaded, no trailing ^M 6) Read the content of the clipboard into a variable, then paste it under the cursor :let @@ = system('powershell.exe Get-Clipboard') | exe 'normal p' OS clipboard is read, trailing ^M are added So what's the difference between *reading* a file, and calling another program that reads it and outputs its content? --- ? add Makefile to my-env/dotfiles, so I can simply run make to reubild what changed # 2019-11-12 More typescript niceties Earlier I thought it wasn't possible to enforce (at compile-time) that a decorator were applied to a class implementing specific interface, and because of it I wound up implementing run-time checks (check the notes of some previous .plan file). Well I was wrong! First you define an interface for the constructor: ``` export interface UrlDescriptorConstructor { new (...args: any[]): UrlDescriptor; } ``` (for reference, here is how `UrlDescriptor` is looking like) ``` export interface UrlDescriptor { generatePathname(): string; generateParams(): any; } ``` Next you change the signature of your decorator to expect the target class to be a constructor for that type: ``` export function SearchHrefDirective(target: UrlDescriptorConstructor) { ... ``` # 2019-11-11 ? cb.vim is adding ^M when pasting on Windows -- :.!cb --force-paste doesn't though... # 2019-11-10 Typescript interfaces are not available at runtime: they are getting used at compilation time for type-checking, but then thrown away. So, if we want to implement run-time checking and throw an error if the decorator is being applied to a non-compatible object, we are forced to: - Define a dummy class, implementing the interface - Create an instance of such class - Store its `Object.keys()` https://stackoverflow.com/a/43910186/348524 --- Downgrading a Angular attribute directive is simply **not** supported: https://github.com/angular/angular/issues/16695 People suggest that you can wrap `downgradeComponent()` calls and change `component.restrict` from 'E' to 'A' or 'EA', but I could not make it work -- and to be honest I am kind of happy that it did not work, as otherwise I would have probably left the hack there, and it would broken again on the next upgrade of the framework. So what to do about it? - Migrate the old component in need of the new directive (duh) - Maintain two directives: one for Angular, and one for Angular.js --- Fucking figured out how, with AutoHotKeys, to activate window and cycle through all the similar windows, with the same hotkey. At first I thought I would implement it with something like: - if the focused window does not match "activation criteria", then activate one that maches it - otherwise, activate the next matching window Well, it turns out AutoHotKeys already supports all this, via [window groups](https://www.autohotkey.com/docs/commands/GroupAdd.htm); the idea is simple, you define first a group of windows (all with a given title, class, or exe), and then you call `GroupActivate` instead of `WinActivate`. For example: - Define te ChromeGroup ``` GroupAdd, ChromeGroup, ahk_exe chrome.exe ``` - Activate the group / cycle its windows using `Hyper+k`: ``` F15 & k::GroupActivate, ChromeGroup, R ``` (`R`, is for activating the most recent window of the group: https://www.autohotkey.com/docs/commands/GroupActivate.htm) --- ? cg: br regexp is not working under Windows # 2019-11-08 * plan-rss: preserve empty lines -- we are using ~& instead of ~% (use ~^ to avoid the last newline) * plan-rss: v0.0.6 # 2019-11-07 Not really sure what happened, but now CL travis-ci builds have started working again... Anyway, I am going to add the .sh script into every CL repo I am building via travis-ci, as using a single scrip hosted on gist/github turned out not to be a good idea; I mean, when the script works, fine, but when it doesn't and have to update it, then I am forced to update all the repos too, as each of them would still be pointing to the old version of the script. Also, having the script checked into the repo, sounds like the right thing to do, especially if one wants reproducible builds. ``` #!/usr/bin/env bash ROSWELL_VERSION=19.09.12.102 ROSWELL_URL="https://github.com/roswell/roswell/releases/download/v$ROSWELL_VERSION/roswell_${ROSWELL_VERSION}_amd64.zip" OS_WIN=$(uname -s | grep -e MSYS_NT) if [ -n "$OS_WIN" ]; then ROSWELL_IN_PATH=$(echo $PATH | grep -F /tmp/roswell) if [ -z "$ROSWELL_IN_PATH" ] ; then echo "/tmp/roswell not found \$PATH" exit 1 fi echo "Downloading Roswell (v$ROSWELL_VERSION) from: $ROSWELL_URL" curl -L "$ROSWELL_URL" \ --output /tmp/roswell.zip unzip -n /tmp/roswell.zip -d /tmp/ fi # Run roswell's CI script, and since it will find `ros` already available # in $PATH, it would not try to build it but instead will install the specified # CL implementation + quicklisp curl -L "https://raw.githubusercontent.com/roswell/roswell/v$ROSWELL_VERSION/scripts/install-for-ci.sh" ``` All good! I also remembered I had asked roswell mainteiners about adding Windows support to install-for-ci.sh -- https://github.com/roswell/roswell/issues/374 -- and it appears they did it! Switching from custom .sh (wrapping the official one), to simply using the official one was pretty easy: 1) Define the following ENV variables ``` env: - PATH=~/.roswell/bin:$PATH - ROSWELL_INSTALL_DIR=$HOME/.roswell ``` 2) Call roswell's ci script: ``` install: - curl -L https://raw.githubusercontent.com/roswell/roswell/release/scripts/install-for-ci.sh | sh ... ``` And that's it! Bonus: you might want to define `LISP` too, and make it point to a specific version -- again, reproducible builds are the right thing to do! --- * added roswell-for-ci.sh to plan-rss and cg * plan-rss: use MD5 of plan entries as content of `` tags * plan-rss: v0.0.5 ? cg: parse git push output to quickly get a reference of the last commit ID -> ' f5455f40f1..6443447668 story/US2709-Workstreams-Make-workstreams-visible-only-to-their-members -> story/US' # 2019-11-06 * plan-rss: v0.0.4 + travis-ci builds are failing on Windows -- ros is erroring out # 2019-11-05 * plan-rss: `--disable-pre-tag-wrapping` * plan-rss: remove `-m`, `-s` options (use longer versions instead) # 2019-11-04 * Use plan-rss (CL version) to generate https://matteolandi.net/plan.xml # 2019-11-03 Mac OS builds on Travis-CI were not working lately, and after some quick Googling around I stumbled upon this: https://github.com/travis-ci/travis-ci/issues/10482 The following indeed seems to fix the problem: ``` before_install: # 2019-10-30: Homebrew/brew.rb:23:in `require_relative': ... # ... unexpected keyword_rescue, expecting keyword_end # https://discourse.brew.sh/t/missing-file-homebrew-dead/5657 - if [ "$TRAVIS_OS_NAME" = osx ]; then set -x && brew update-reset && if ! brew install gsl; then cd "$(brew --repo)" && git add . && git fetch && git reset --hard origin/master; fi; set +x; fi ``` But what the hell should I install `gsl`? Let's try something else (`brew update-reset` only): ``` before_install: # 2019-10-30: Homebrew/brew.rb:23:in `require_relative': ... # ... unexpected keyword_rescue, expecting keyword_end # https://discourse.brew.sh/t/missing-file-homebrew-dead/5657 - if [ "$TRAVIS_OS_NAME" = osx ]; then set -x && brew update-reset && set +x; fi ``` This one works too, so I guess I am going to stick with this for now! --- Setting up Travis-CI deployment keys can be as easy as running `travis setup releases`, or way more messier (that's the price you have to pay if you don't to share your username/password with `travis`). 1) Add a new Oauth token: https://github.com/settings/tokens, and copy it -- only the `public_repo` scope seems to be required to upload assets 2) Run `cb | travis encrypt` (if you run it from the specific repo itself, it will automatically guess the Travis-CI space) 3) Copy the secured token inside your .travis.yml file deploy: provider: releases api_key: secure: XXX_SECURE_XXX skip_cleanup: true file: bin/$DEST on: repo: iamFIREcracker/plan-rss tags: true Also, when *all* conditions specified in the `on:` section are met, your build will deploy. Possible options: - `repo`: in the form `owner_name/repo_name`. - `branch`: name of the branch (or `all_branches: true`) - `tags` -- to have tag-based deploys - other.. Useful links: - https://docs.travis-ci.com/user/deployment/releases/ - https://stackoverflow.com/questions/25302518/travis-ci-setup-releases-with-github-token - https://github.com/travis-ci/travis-ci/issues/2982 - https://github.com/travis-ci/docs-travis-ci-com/pull/1383/files --- * plan-rss: v0.0.1 * plan-rss: fix Windows build issue * plan-rss: remove trailing new-line from * plan-rss: v0.0.2 * plan-rss: --version not working for dev version (i.e. not taking into account commits since last tag) * plan-rss: travis ci binaries crashing (fucking had forgotten to check in some changes...) * plan-rss: v0.0.3 + plan-rss: ERROR instead of OPTS:EXIT so one can test opts parsing without accidentally closing the REPL # 2019-11-02 Update on my journery to a Common Lisp version of plan-rss Figured out a way to wrap "description" values inside CDATA/pre elements, but I don't think I am 100% satisfied by the solution (reason why I haven't submitted a MR yet). So basically I added a new key argument to RSS-ITEM, `descriptionWrapIn` a 2 elements LIST, which the function would use to wrap the content of "description" (the first element, before, and the second after). Here is what I came up with: ``` (xml-emitter:rss-item date :link *link* :description (plan-day-content day) :descriptionWrapIn '("" "]]>")) ``` Don't take me wrong, it works, but I just don't think it's good way of implementing it. Another approach would be to convert RSS-ITEM into a macro and let it accept a `BODY` that users can specify to further customize the content of the item. Something along the lines of: ``` (xml-emitter:with-rss-item (date :link *link*) (xml-emitter::with-simple-tag ("description") (xml-emitter::xml-as-is "") (xml-emitter::xml-out (plan-day-content day)) (xml-emitter::xml-as-is "]]>")))))) ``` More verbose, but more flexible too; in fact, yesterday's MRs to add `isPermaLink` to ``, and to support Atom's link with rel=self would not be required anymore if RSS-ITEM and RSS-CHANNEL-HEADER were macros. Channel with Atom link: ``` (xml-emitter::with-rss-channel-header (*title* *link* :description (read-channel-description) :generator *generator* :image *image*) (xml-emitter::empty-tag "atom:link" `(("href" ,*atom-link-self*) ("rel" "self") ("type" "application/rss+xml")))) ``` Item with guid (isPermaLink=false) and description wrapped inside CDATA/pre blocks: ``` :do (xml-emitter:with-rss-item (date :link *link* :pubDate "XXX") (xml-emitter::simple-tag "guid" (format NIL "~a#~a" *link* date) '(("isPermaLink" "false"))) (xml-emitter::with-simple-tag ("description") (xml-emitter::xml-as-is "") (xml-emitter::xml-out (plan-day-content day)) (xml-emitter::xml-as-is "]]>")))))) ``` --- TIL: you can pretty print a Common Lisp condition with: `(format t "~a" cond)` --- CL: unix-opts Required options seem to conflict with typical use of `--help` or `--version`: https://github.com/libre-man/unix-opts/issues/10 Here is a possible workaround (adapted by another one found in the thread there): ``` (defun parse-opts (&optional (argv (opts:argv))) (multiple-value-bind (options) (handler-case (handler-bind ((opts:missing-required-option (lambda (condition) (if (or (member "-h" argv :test #'equal) (member "--help" argv :test #'equal) (member "-v" argv :test #'equal) (member "--version" argv :test #'equal)) (invoke-restart 'opts:skip-option) (progn (format t "~a~%" condition) (opts:exit 1)))))) (opts:get-opts argv)) (opts:unknown-option (condition) (format t "~a~%" condition) (opts:exit 1)) (opts:missing-arg (condition) (format t "~a~%" condition) (opts:exit 1))) (if (getf options :help) ... ``` Important bits: - We do want to invoke the `OPTS:SKIP-OPTION` restart when any of the special options is set -- this way the library will use `NIL` and move on - HANDLER-BIND -- and not HANDLER-CASE -- needs to be used to handle `OPTS:MISSING-REQUIRED-OPTION`, especially if we want to call INVOKE-RESTART (with HANDLER-CASE the stack is already unwound, when the handler runs. Thus the restart established in the function is gone.) --- * xml-emitter: RSS macros (https://github.com/VitoVan/xml-emitter/pull/5) (also cancelled the other MRs, as this one would now give more control to the user) * plan-rss: add pubDates (rfc2822) # 2019-11-01 * xml-emitter: Add support for guid isPermaLink=false (https://github.com/VitoVan/xml-emitter/pull/3) * xml-emitter: Add support for atom:link with rel="self" (https://github.com/VitoVan/xml-emitter/pull/4) # 2019-10-30 Finally A/I came back online, and I was finally able to create a request for a mailing list (to use it with the other college friends). Anyway, the request has been created, so hopefully over the following days we will hear back from them...stay tuned! # 2019-10-27 Few bits I found around, about generating html emails, starting from markdown, with Mutt. Mutt's editor first: ``` set editor = "nvim -c 'set ft=mdmail' -c 'normal! }' -c 'redraw'" ``` Then a macro, activated with `H` or whatever, to pipe the body of the message to `pandoc` to generate the html version of the message, and attach it to the email: ``` macro compose H "| pandoc -r markdown -w html -o ~/.mutt/temp/neomutt-alternative.html~/.mutt/temp/neomutt-alternative.html" ``` And the '.vim/syntax/mdmail.vim' syntax file: ``` if exists("b:current_syntax") finish endif let s:cpo_save = &cpo set cpo&vim " Start with the mail syntax as a base. runtime! syntax/mail.vim unlet b:current_syntax " Headers are up to the first blank line. Everything after that up to my " signature is the body, which we'll highlight as Markdown. syn include @markdownBody syntax/markdown.vim syn region markdownMailBody start="^$" end="\(^-- $\)\@=" contains=@markdownBody " The signature starts at the magic line and ends at the end of the file. syn region mailSignature start="^-- $" end="\%$" hi def link mailSignature Comment let b:current_syntax = "mdmail" let &cpo = s:cpo_save unlet s:cpo_save ``` --- Niceties to cat / edit the content of an executable (`which`-able program); the first one, will open the specified command/executable, in your configured editor ``` function ew() { $EDITOR $(which "$1"); } complete -c ew -w which ``` While this other one, will simply `cat` its content: ``` function cw() { cat $(which "$1"); } complete -c cw -w which ``` --- Found this intersting thread started by Aaron Swartz, from 2008, on HTTP sessions: https://groups.google.com/forum/#!topic/webpy/KHCDcwLpDfA I was initially confused by it -- and somewhat I still am -- as I could not really understand where Aaron was getting at with this, but then I guess his biggest complain was that sessions require servers to store information locally, on a per-user basis, breaking the *stateless* idea of the web. Cookies are good (and Session cookies are good to, as far as I can tell), but Aaron did not seem lo like the use of cookies for session management; for that, one should use Digest access authentication (https://en.wikipedia.org/wiki/Digest_access_authentication). Cookies, for session managements, I think they are good, especially because they prevent username/password to fly around -- even though hashed -- betweeen the clients and the server; better to generate a temporary session ID and have clients pass that to the server. Does this break Web's stateless-ness? Yes of course, but is that really a problem? I mean, if the session is stored on disk, then yes, it's a problem, because the client will have to hit the same server to restore its session (or at least it would be uselessly complicated to manage it);but if one instead used a different session storate system, like Redis, then client requests could be handled by any available server in the pool, and it would be able to restore the specific client session just fine. Anyway, the sad thing is, we are never going to get to know what Aaron had in mind when started that thread. --- Changed my household booklet spreadsheet to: - Handle cash withdrawal by entering two expenses: one negative on my bank account, and one positive, as cash bucket - Changed the payment type sheet to know read the starting balance by summing up all the "positive" expenses Sooner or later this will have to become an app... # 2019-10-20 Disabled dark-theme on matteolandi.net # 2019-10-17 Burpees! Rounds: 6 Time on: 0:30 Time off: 1:00 Still no jumping. # 2019-10-10 I was reading this hackernews post (https://news.ycombinator.com/item?id=21195913) about adding support for a dark-theme, and even though the overall activity took me less than one minute to complete (well, my website is very *simple*, so I am sure adding dark-theme support for a more complex website / web app is going to take longer, usually), one commend made me wonder: "What if someone was interested in *muting* the browser colors, the container's, but not the content? Should we add some Js to let people toggle between light and dark mode?" Few examples: - Google, does not seem to support a dark theme - Wikipedia, does not seem to support a dark theme - Facebook, does not seem to support this either Again, https://matteolandi.net is nothing compared to Google and Wikipedia, so honestly...I am not that surprised they haven't added support for dark-mode yet; but still, is it possible they haven't done it yet because, as things stand, it's not yet a "problem" for them? Somewhere on the Web I read that one should tweak CSS only to solve a specific user problem, so maybe adding a dark-them is not going to solve any of their problems, who knows... Anyway, if you have dark-mode enabled at OS/browser level, going to https://matteolandi.net you should be presented with a dark version of the website -- and here is what I added to my style.css to make this happen: ``` @media (prefers-color-scheme: dark) { body { background-color: #444; color: #e4e4e4; } a { color: #e39777; } img { filter: grayscale(30%); } } ``` --- * added dark-them to matteolandi.net # 2019-10-09 Started reading The Mythical Man-Month (TMMM) --- Finally got around to create a SPID ID, so I can - hopefully - easily access all my health record and anything else PA related, without having to use obsolete smart card readers or remember crazy PINs. You have to request a SPID ID to one of the entity providers out there, and they might ask you for proof of identity; lucky me, Hello Bank is partnering with Poste Italiane (an entity provider), so I could easily request an ID without much of a hassle: the bank trasferred all the required documents over, and in 15 mins I had my SPID created. I then installed PosteID on my mobile, signed in with my SPID ID, created a new code (and there was silly me thinking the SPID ID would be the last username/password to remmeber), and that's it: every time I try to access PA websites and choose SPID as login workflow, I would receive a notification on my mobile asking me to confirm or deny the access -- living in the future! Few handy PA links: - Fascicolo Sanitario Elettronico (FSE): http://fascicolosanitario.regione.toscana.it/ - CUP online: https://prenota.sanita.toscana.it/ --- I have decided to list all the applications I have on my mobile, so I can 1) monitor which I have been constantly using vs the ones I throw away after a bit, and 2) easily re-setup my mobile in case Google refused to re-install my previously installed apps. Here we go: - Any.do (notes) - Authenticator (2FA codes) - Booking.com - British Airways - Calendar (Google) - Chrome - Dropbox - E-mail (non G-mail accounts) - Feedly - File Manager+ - Fineco - Firefox Nightly - Fogli (Google spreadsheets) - Gmail - Hello bank! - Hoplite (roguelike game) - Instagram - Maps - Materialistic (hacker news) - My3 - Netflix - Odyssey (endless runner snowboarding game) + Orario Treni (train timetables) - PosteID (SPID identity) - Reddit - Ryanair - SAP concur (tracking expenses when traveling) - Satispay - Settle Up - Shazam (the future!) - Signal - Snapseed - Spotify - Stremio - Tasker - Teams (Microsoft) - TV Time - Twitter - Waze - WhatsApp - Youtube Damn, I was not expecting this to be that long... --- Few "interesting" links: - koa.js, from the creators of express.js: https://koajs.com/ - Apex ping, easy and sexy monitoring for websites, applications, and APIs: https://apex.sh/ping/#pricing --- nlp.js - Train the NLP manager, then save it on disk so you don't have to train it again: https://github.com/axa-group/nlp.js/blob/master/docs/nlp-manager.md - NER Manager: https://github.com/axa-group/nlp.js/blob/master/docs/ner-manager.md --- My Node.js application kept on failing to start up from my new Virtualbox box, blowing up with a ENOSPC error. echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p --- ? setting up a vpn on my DO instance (for mobile, and laptop) # 2019-10-08 * finished reading [Natural Language Processing with Python: Analyzing Text with the Natural Language Toolkit](https://www.amazon.com/Natural-Language-Processing-Python-Analyzing/dp/0596516495) + add dark mode support to matteolandi.net, just for fun -- Dark mode in CSS with `prefers-color-scheme` - https://news.ycombinator.com/item?id=21195913 # 2019-10-07 Reading about Natural Language Processing (NLP), and Named Entity Recognition (NER) in particular, to be able to populate search engine filters, based on user's free text input mkdir ~/tmp/ner cd ~/tmp/ner virtualenv -p python3 venv pip install nltk pip install numpy https://towardsdatascience.com/simple-nlp-search-in-your-application-step-by-step-guide-in-scala-22ca1ce3e475 https://towardsdatascience.com/natural-language-processing-with-spacy-in-node-js-87214d5547 https://medium.com/code-brew-com/natural-language-processing-using-python-or-nodejs-a76c91dcd68c --- I was reading this book about NLP, and there was this exercise about text tokeninzation, that the author thought about solving it using simulated annealing. Now, I always found simulated annealing fascinating, and because of that I decided to dump the exercise (and solution) here for future reference. Let's first define a function, `segment()`, that given some text (i.e. a string, like 'Hello world!'), and another string representing where such text should be broken into chunks, would split the text into chunks: ``` def segment(text, segs): # text = 'helloworld' # segs = '000100000' # => ['hello', 'world'] words = [] last = 0 for i in range(len(segs)): if segs[i] == '1': words.append(text[last:i + 1]) last = i + 1 words.append(text[last:]) return words ``` `evaluate()` instead is a function that evaluates the _quality_ of a *segmentation*. ``` def evaluate(text, segs): words = segment(text, segs) text_size = len(words) lexicon_size = len(''.join(list(set(words)))) return text_size + lexicon_size ``` Now the simulated annealing: basically we start with a *random* segmentation kernel, and start randomly flipping *bits* on/off, evaluating how the new segmentation compares with the best one found so far; repeat this multiple times, always reducing the number of *bits* to flip (e.g. temperature, decreasing over time) ``` def flip(segs, pos): return segs[:pos] + str(1 - int(segs[pos])) + segs[pos + 1:] def flip_n(segs, n): for i in range(n): segs = flip(segs, randint(0, len(segs) - 1)) return segs def anneal(text, segs, iterations, cooling_rate): temperature = float(len(segs)) while temperature > 0.5: best_segs, best = segs, evaluate(text, segs) for i in range(iterations): guess = flip_n(segs, int(round(temperature))) score = evaluate(text, guess) if score < best: best, best_segs = score, guess score, segs = best, best_segs temperature = temperature / cooling_rate print(evaluate(text, segs), segment(text, segs)) return segs ``` # 2019-10-04 * finished reading john carmak's collection of .plan files I am taking some time off from work next week, and I plan to do some research on Natural Language Processing (NPL); in particular I would like to experiment with Named Entity Recognition (NER) in the context of search engines (populate search filters based on users free text input). Also, I will be in London for the next couple of days to meet some friends, so don't expect much done (other than reading, I guess). # 2019-10-03 Time for another burpees training session. Last time it took me almost 3 days to fully *recover* so I figured I should try with something lighter this time: Rounds: 6 Time on: 0:30 Time off: 1:00 6 rounds instead of 8, and no jumping since the beginning (last time I had to stop in the middle of the second round). Let's what my body thinks about this tomorrow or the day after. # 2019-10-02 Was playing with my dev box earlier, trying to test persistent drives -- this way, once a new LTS is released I can destroy the box, recreate it, and pick up from where I left -- and accidentally wiped out my ConnectION workspace; I did not realize `vagrant destroy` would delete also attached drives... SO has a solution for this: https://stackoverflow.com/a/45146027/348524 - Mark the storage drive as hot-pluggable - Register a pre-destroy hook, and inside of it detach the drive -- so it wont be automatically deleted Unfortunately that does not seem to work as expected: syntax errors, triggers being invoked multiple times. I guess I will be creating a custom `./vagrantw` wrapper to take care of all this --- Getting a reference to `Window` in Angular, with AOT enabled, is super painful. `Window` is defined as an interface, and because of that you cannot simply rely on Angular DI, but instead you have to...sacrifice few animals the God of forsaken programmers! First you define a named provider: ```typescript // file: ./window-ref.service.ts import { InjectionToken } from '@angular/core'; export const WindowRef = new InjectionToken('WindowRef'); export function windowRefFactory() { return window; } export const WindowRefProvider = { provide: WindowRef, useFactory: windowRefFactory } ``` Next you register the provider into your main module: ```typescript // file: ./app.module.ts import { WindowRefProvider } from './utils/window-ref.service'; @NgModule({ providers: [ WindowRefProvider, ... ``` Finally you update your dependent services / components as follows: ```typescript import { Injectable, Inject } from '@angular/core'; import { WindowRef } from '@connection-ui/utils/window-ref.service'; @Injectable() export class HelloWorldService { $window: Window; constructor(@Inject(WindowRef) $window) { this.$window = $window as Window; } ``` Yeah, you need to use the named injector but without any type (i.e. `any`), and then inside the constructor you cast it to `Window`; it's retarded, but that's the only solution I could come up with. --- Played with mocking classes in Typescript, and stumbled upon something neat: ```typescript type PublicInterfaceOf = { [Member in keyof Class]: Class[Member]; } class MockHelloWorld implements PublicInterfaceOf { hello(name) { return "Mocked!"; } } ``` https://www.michaelbromley.co.uk/blog/mocking-classes-with-typescript/ --- ? create vagrantw to properly deal with persistent drives, and prevent they are removed when `vagrant destroy`-ing # 2019-09-30 * enhanced vitality.vim to support Terminal.app escapes -- for changing the cursor shape I have been thinking about this lately, and I don't think relying on env variables to figure out which terminal Vim or other programs are being run into, is The Right Thing. I mean, it works pretty decently for local sessions, but the moment you ssh into a remote box and create a Tmux session for some long-running activity, your screwed. For example, say that I am at work (Windows, Mintty), and I log into my remote Workstation. When you do so -- ssh into a remote box -- based on your settings, some/all your env variables on your local host will be maved available in your remote session; this could seem sexy at first, because you might have Vim-specific settings based on the running terminal emulator, but what if you create a Tmux session (which saves env variables), start some work, and decide to carry it on from home, where you have a different terminal? You won't be destroying your tmux session, but since it was created earlier, with a different terminal emulator, some of the already pre-configured variables might not apply for the *new* client. I believe the Right Thing to do here, is to use escape sequencies, when available, to query the Terminal emulator capabilities -- which is what the Mintty manteiner is suggeseting people do: https://github.com/mintty/mintty/issues/867 Anyway, I guess for the time being I will kill myself and set/unset env variables based on the client I am connecting from, and based on the tmux environment. <3 I am going to dump here the steps to reprovision my Linux box: cd /some/dir curl -O https://raw.githubusercontent.com/iamFIREcracker/my-env/master/Vagrantfile vagrant up vagrant ssh cd /data mkdir Workspace ln -s $(pwd)/Workspace ~/Workspace ln -s $(pwd)/Workspace ~/workspace git clone --recurse-submodules git@github.com:iamFIREcracker/my-env.git ln -s $(pwd)/my-env ~/my-env cd my-env git clone --recurse-submodules git@github.com:iamFIREcracker/dotfiles.git bash install.sh --force --bootstrap --os-linux # 2019-09-28 I have been trying to create a private mailing on https://inventati.org for the last couple of days, but they seem to have put 'Request a service' page on mainentance mode, and by the look of their tech blog it seems like they are undergoing a big infrastructure update. Anyway, shot them an email to understand if there is something we can do about it (e.g. sign up, and register a service). Follows another dump of notes re multitenancy... Benefits of multitenancy: - Segregation of data across the clients - Manage and customize the app according to the client's needs (e.g. separate branding, separate feature flags) - Maximize efficiency while reducing the cost needed for centralized updates and maintenance Ways of implementing this: - Logical separation of data - physical separation of data https://blog.lftechnology.com/designing-a-secure-and-scalable-multi-tenant-application-on-node-js-15ae13dda778 # 2019-09-27 * fixed commentstring for autohotkey files TIL: Vim will try and syn-color a maximum of `synmaxcol` characters. -- `:help 'synmaxcol'` To separate various tenants' data in Redis, we have – like SQL – two options. The first is to maintain independent Redis server instances, and switch out the connection per request. Of course, this has the disadvantage of reducing or eliminating reuse of the connection pool, as the Rails server must open a new set of connections to the Redis server on each web request. Instead, we recommend using a simple "namespace" concept to identify each key with its tenant. What's a Redis namespace? When working with Redis, it's customary to namespace keys by simply prepending their name with the namespace, i.e. namespace:key_name. We often do this manually, and write classes to encapsulate this process, so that other parts of our applications needn't be aware of it. https://quickleft.com/blog/multi-tenant-applications-redis/ # 2019-09-26 Started doing burpees -- used this timer web-app to set the pace: https://fitlb.com/tabata-timer Rounds: 8 Time on: 0:30 Time off: 1:00 For those unfamiliar with what a burpee is: 1) Begin in a standing position. 2) Move into a squat position with your hands on the ground. (count 1) 3) Kick your feet back into a plank position, while keeping your arms extended. (count 2) 4) Immediately return your feet into squat position. (count 3) 5) Stand up from the squat position (count 4) Well, I initially tried to jump instead of stand up at step 5), but then I quickly realize I would not have made it alive that way, so I opted for something more lighter. Anyway, it was tough, and sometimes I even stopped before the time, but I guess I shouldn't be beating myself too much about it: in the end it's the first session. Let's if tomorrow I can get out of my bed. # 2019-09-23 + Change autohotkeys comment string from `/* .. */` is being used In a multi-tenant app, each request that comes in can be for a separate tenant. We need two things from the outset: - a way to determine what tenant a request is for - and a way to process the request in the context of that tenant. Single login leading to tenants ``` def current_tenant current_user.tenant end ``` Don't change your public API as follows: /tenant/:tenant_id/model_name/:model_id. Users will never go off wandering to a nother tenant, and since a user is linked to a tenant only, all the magic can happen behind the scenes without the user knowing anything about it. Another approach is to implement a custom piece of middleware that would take care of figuring out the tenant, and change configs (e.g. DB user) accordingly: ``` module Rack class MultiTenant def initialize(app) @app = app end def call(env) request = Rack::Request.new(env) # CHOOSE ONE: domain = request.host # switch on domain subdomain = request.subdomain # switch on subdomain @tenant = TENANT_STORE.fetch(domain) # Do some configuration switching stuff here ... @app.call(env) end end end ``` And the list of tenants could be configured as: - in memory hash (not dynamic, will require a bounce of the server) - database (dynamic, but you would still want to cache some data, or it would slow down every request) https://quickleft.com/blog/multi-tenant-applications-detecting-the-tenant/ How to provide the *right* data for each user, based on their tenant? - have a single database, but to use associations to control the siloing. Pros: single database (less maintenance); Cons: you forget about that node-tenant relation, and all of a sudden users are able to see data from other tenants - multiple independent copies of the application's database. Pros: once the connection is switched, siloing is complete and guaranteed; Cons: configuration changes at run-time, possibly invalidating some of the db connection pool techniques implemented by drivers Also, the database is not the only "feature" you might want to swap out based on the user tenant; for example, if you were using Redis, you might want to try and implement (and then switch) [Redis namespace](https://stackoverflow.com/questions/8614858/namespaces-in-redis) ```node var fooRedis = new Redis({ keyPrefix: 'foo:' }); fooRedis.set('bar', 'baz'); // Actually sends SET foo:bar baz ``` https://quickleft.com/blog/multi-tenant-applications-separating-sql-databases/ # 2019-09-22 * finished reading [The History of the Future: Oculus, Facebook, and the Revolution That Swept Virtual Reality](https://www.amazon.com/History-Future-Facebook-Revolution-Virtual-ebook/dp/B01NATTA7F) * cleaend/validated matteolandi.net pages -- broken elements inside in particular * cg: better rm-rmr support for mac and linux outputs: it's funny how little the output of the same command on different OSes could change...I mean, what's the point, really?! * aadbook: added py3 support! yay ? fzf's install script seems to delete bin/fzf-tmux on Windows/Cygwin only -- on Mac, I run it, and fzf-tmux was stll there Spent some time trying to get offlineimap to work with Python3, but no luck: - https://github.com/OfflineIMAP/offlineimap/issues/354 - https://github.com/OfflineIMAP/offlineimap/issues/373 This is going to be annoying, especially when python2 sunsets in January next year... Started reading John Carmack .plan archive: https://github.com/ESWAT/john-carmack-plan-archive I have also started learning more about multi-tenant applications, so I am going to start dropping notes here. A multi-tenant application needs to be carefully designed in order to avoid major problems. It: - needs to the detect the client (or tenant) that any individual web request is for; - must separate persistent data for each tenant and its users; - can not allow cached data (e.g. views) to leak beetween tenants; - requires separate background tasks for each tenant; - must identify to which tenant each line of log output belongs. https://quickleft.com/blog/introduction-to-designing-multi-tenant-web-applications/ What’s the definition? (of a multi-tenant application) We don’t think there is a clear, concise definition. In broad strokes, y'know it when y'see it. You might say that the application is multi-tenant if the architects ever had to decide between deploying a number of separate (not pooled) instances of the app, or deploying a single instance. Or you might say it has to do with data segregation: the idea that there are some datasets that simply shouldn't touch, shouldn't interact, and shouldn't be viewable together in any combination. This seems to be the “definition” that most closely fits the examples we’ve come up with, and the spirit of the term (as we understand it). Set up different domains per client: - client1.domain.com - client2.domain.com - ... https://quickleft.com/blog/what-is-a-multi-tenant-application/ # 2019-09-18 + cg rm -> rmdir rm '.tmux-plugins/tmux-fpp/' rm: cannot remove '.tmux-plugins/tmux-fpp/': Is a directory ? git co bin/fzf-tmux # XXX why on earth would the install script delete fzf-tmux # 2019-09-09 Started looking into rewriting plan-rss into CL. I found xml-emitter, but I am afraid I will have to submit a couple PRs for it to be able to generate valid RSS2.0 + xml-emitter: missing support for isPermaLink + xml-emitter: missing CDATA support + xml-emitter: Missing atom:link with rel="self" # 2019-09-08 * finished reading [Hackers & Painters: Big Ideas from the Computer Age](https://www.amazon.com/Hackers-Painters-Big-Ideas-Computer-ebook/dp/B0026OR2NQ) + aadbook does not work with python3 ? offlineimap (the version I am manually building) does not work with python3 ? cg: add an option to use a single guesser only -- that would help me replace urlview completely TIL, you can stop Vim to increment numbers in octal notation (i.e. C-A) with the following :set nrformats-=octal. # 2019-09-07 * Figured out why my .plan was not being getting updated anymore -- it turns out the two cron jobs I had created (one for .plan, the other for plan.xml) were sharing the same name, so the second one (for plan.xml) would override the first one * Changed some of my ansible scripts to run specific commands (i.e. init of avatar or init download of .plan file) only if the target file does not exist already -- if it does, the cron job would take care of updating it in due time: https://raymii.org/s/tutorials/Ansible_-_Only_if_a_file_exists_or_does_not_exist.html * Copying more entries from the old journal into this file ~ I am still having second thoughts on whether updating old .plan entries should force an update on the relevant plan.xml ones (i.e. republish them with new content). # 2019-09-06 * Copying more entries from the old journal into this file ? .plan does not automatically sync on my phone, unless I open Dropbox first # 2019-09-05 * enhance install.sh to isntall the various rubygems I am using * enhance install.sh to abort when running bogus version of rubygems * plan-rss: better item pubdate management: 1) new item: use date of the plan entry, 2) content differs: use $NOW, 3) re-use last pubdate + .plan (and plan.xml) did not update overnight.. again! + plan-rss: publish last 10 recently updated entries # 2019-09-04 * Started caching gravatar locally, on my box; I hoped this could fix the problem with my RSS channel image not being rendered, but it did not work -- still not image on Feedly, and with the other RSS client I am using on Mac OS * Fixed my rubygems cygwin env When trying install any gem, I would be presented with the following error: > gem install tmuxinator ERROR: While executing gem ... (NameError) uninitialized constant Gem::RDoc After some Googling around, I landed on this GitHub [page](https://github.com/rubygems/rubygems/issues/2483) where they suggest to manually edit 'rubygems/rdoc.rb' file, and move RDoc logic inside the begin/rescue block -- where it should be vim /usr/share/rubygems/rubygems/rdoc.rb # move the last line inside begin/rescue block Everything started working again since then + Add step to install.sh to guard against this rubygems/rdoc weirdness + plan-rss: plan.xml did not update overnight -- manually running update-plan-xml did the trick though I cannot get any RSS client I found to parse the image that I added to my plan.xml file, but I checked the specification, and it should be correct: - Someone on SO pointing about the `` tag in the specification: https://stackoverflow.com/questions/12524435/supply-an-image-thumbnail-with-my-rss-feed - Codinghorror's feed: https://feeds.feedburner.com/codinghorror -- it seems to be using ``, but somehow his image is popping up, mine not - Another feed (taken from NetNewsWire) where image is being used: https://cate.blog/feed/ - Some guy on SO suggesting that we use favicon.ico: https://stackoverflow.com/questions/23006691/adding-favicon-to-rss-feed - GitHub's repo feed: https://github.com/iamFIREcracker/cg/commits/master.atom -- it's ATOM, and when adding this to NetNewsWire, it seems to be using the favicon.ico * Opened a ticket to NetNewsWire to see if RSS2.0 channel's image is supported or not -- https://github.com/brentsimmons/NetNewsWire/issues/968 # 2019-09-03 ~ plan-rss: remove old pub-date logic Regarding the image not showing on Feedly: could it be that we are not actually pointing to a proper image resource, but rather to an endpoint that will generate it?! ? tmuxinator on windows: https://github.com/tmuxinator/tmuxinator/issues/490 # 2019-09-02 * Backed up all the .env.properties into Dropbox * plan-rss: populate * plan-rss: fucking fixed (I guess) new-line madness * plan-rss: pubDate matching plan entry's date - XML-escape all your content - Wrap it into `
` ... `
` - Wrap it into `` - Voila'! No need to append `
`s to each line -- `
` will do the trick!

~ plan-rss: populated  but feedly still does not show it
? Cygwin + Tmux weirdnesses: if I don't force tmux to set TERM=xterm-256color, everything will go black and white -- it turns out TERM is being set to screen, and not screen-256color
? Tmux weirdnesses: if I don't force tmux to set TERM=xterm-256color, tmux will set it to screen-256color which doesn't support italics well

# 2019-09-01
* Started moving my old journal entries over to my .plan.work file

Regarding CHERE_INVOKING, on Cygwin: it turns out that the cygwin default /etc/profile checks for an environment variable named CHERE_INVOKING and inhibits the change of directory if set.  So by default, when sourcing /etc/profile, cygwin would take you to your home directory, unless CHERE_INVOKING=1 is set -- https://superuser.com/a/345967

* Implemented plan-rss script -- in bash for now

And here there was me thinking it would be easy to manually generate RSS:

- XML escape VS CDATA...it turns out you need both: XML escape because the output has to be valid HTML, CDATA if you want the parser to ignore non-existng HTML tags
- Channel's description has to be plain text -- item's description has to be HTML compatible

+ plan-rss: new-fucking-lines
+ plan-rss: publish latest 5 entries only
+ plan-rss: rewrite in CL

# 2019-08-31
* added Makefile and install.sh support for cb
* removed tmux,winpty from PATH -- now installing it into ~/local/bin
* fucking managed to run fzf on Cygwin!

Created `fzf` wrapper that would start `fzf` via cmd.exe on Windows:

    #!/usr/bin/env bash

    EXE=$(realpath ~/.vim/pack/bundle/start/fzf/bin/fzf)
    if ! hash cygpath 2>/dev/null; then
      "$EXE" "$@"
    else
      stdin=$(tempfile .fzf.in)
      stdout=$(tempfile .fzf.in)
      wrapper=$(tempfile .fzf.sh)
      trap "rm -f $wrapper $stdout $stdin" EXIT

      # save stdin into a temporary file
      cat - > "$stdin"

      # create wrapper
      cat > "$wrapper" < $stdout
    EOF

      # run fzf through cmd.exe -- stolen from: https://github.com/junegunn/fzf.vim
      cmd.exe /C 'set "TERM=" & start /WAIT sh -c '"$wrapper"
      cat "$stdout"
    fi

And updated `fzf-tmux` as follows -- no need to run tmux if we would be spawning a `cmd.exe` anyway

    EXE=~/.vim/pack/bundle/start/fzf/bin/fzf-tmux
    if [ -f ~/.vim/pack/bundle/start/fzf/bin/fzf.exe ]; then
      # While on Cygiwn, fzf-tmux would normally open a tmux pane, which would open
      # a cmd.exe (yes, because on cygwin fzf has to be run through a cmd.exe).
      #
      # That's ridicolous, let's skip the tmux part completely
      EXE=fzf
    fi

    $EXE "$@"

* fucking figured out (well, partially) how to make tmux preserve current path when splitting panes: it appears I have to export CHERE_INVOKING=1.  That also fixes a problem I have always had when running tmuxinator on Windows where it would not always move in the expected directory
+ what the hell is CHERE_INVOKING, and why do I need to set it on Windows, but not on MacOS/Linux
+ backup .env.properties files

Mutt was behaving weirdly when run inside tmux, and googling around I discovered that it's not recommended to set TERM to anything different from screen/scren-256color while inside tmux: https://superuser.com/questions/844058/tmux-mutt-not-redrawing.  So I tried to disable the lines where I override TERM in my .tmux.conf, let's see what happens.

# 2019-08-30
* added Makefile and install.sh support for br
* added Makefile and install.sh support for cb
* removed a bunch of wrappers from ~/bin (br, cb, urlview, mutt-notmuch-py)
+ fzf ain't working on cygwin: https://github.com/junegunn/fzf/wiki/Windows#fzf-outputs-character-set-not-supported-when-term-environment-variable-is-set and https://github.com/junegunn/fzf/wiki/Cygwin
+ try to ditch the following wrappers: fzf-tmux -- find a way to install them locally (make install PREFIX=..., or the same for python crap)
+ avoid to update PATH for tmux, and winpty

# 2019-08-28
* Created [cb.vim](https://github.com/iamFIREcracker/cb.vim) -- still a WIP, but it's working, with my set up at least

# 2019-08-27
* Added new reading.html page to matteolandi.net, listing all the books I remember having read
* Changed ansible scripts to re-use letsencrypt certificates for the docker registry -- so I don't have to re-generate self signed certificates every year

# 2019-08-25
* Suppressed CR/LF warnings raised when working on Unix Git repos from Windows -- https://stackoverflow.com/questions/5834014/lf-will-be-replaced-by-crlf-in-git-what-is-that-and-is-it-important

    git config core.autocrlf input

* Figured out a way to prevent `git up` from updating the dotfiles submodule while on the parent repo, my-env: add `update = none` to the sub-module entry inside .gitmodules

    [submodule "dotfiles"]
            path = dotfiles
            url = git@github.com:iamFIREcracker/dotfiles.git
            ignore = dirty
            update = none

Why would I want that you might ask?  Well, I usually keep the dotfiles submodule up to date already (e.g. `cd dotfiles; git up`), so when I decide to sync my-env, I don't want `git up` to accidentally checkout an older version of dotfiles.

* Created tmux binding to spawn a command/application in a split pane -- similar to how `!' on mutt lets you spawn an external command
* finished reading [Black Box Thinking: Why Most People Never Learn from Their Mistakes--But Some Do](https://www.amazon.com/Black-Box-Thinking-People-Mistakes-But/dp/1591848229)

# 2019-08-24
* Cleaned up configs for mutt and friends (offlineimap, msmtp)
* Restored Ctrl+Enter behavior on Terminal.app

    Change some terminal mappings

    - Ctrl-Enter key from ✠ to ◊
    - Shift+Enter key from ✝ to Ø

    Why?  Because I recently switched from iTerm2 to Terminal.app, and
    I could not find a way to creat custom shortcuts (e.g. to generate the
    maltese cross on Ctrl+Enter).

    I tried then using Karabiner-Element, but it doesn't seem it supports
    unicodes, so the only solution I found was:

    - Figure out a new char to use (the lozenge), whose Option+key shortcut
    was known (Option+Shift+v)
    - Tell Karabiner elements to generate that sequence when Ctrl+Enter is
    pressed
    - Change all the other settings to use the lozenge instead of the
    maltese cross

    Too bad I could not use a more meaningful character, like the carriage
    return one.

    PS. https://pqrs.org/osx/karabiner/json.html#root-data-structure

Now that these key mappings have been restored too, Terminal.app is pretty much usable; I do not see any performance improvements (when scrolling through Tmux + Vim panes), and true color support is not there, but that's OK, I will survive I guess

# 2019-08-22
Still fighting with non-blocking vim channels, and how to close stdin making sure that all the pending write operations are flushed first

# 2019-08-21
* Updated async.vim MR to wait for transmit buffer to be empty before closing stdin
* Asked on vim_dev about a weird ch_sendraw behavior -- not sure if it's expected behavior, or a bug

    Hello everyone,

    ch_sendraw seems to fail when sending large chunks of data to a job started in non-blocking mode.  Is this expected?

    Steps to reproduce:

    - Copy the following into a file, and source it from vim

    let args = ['cat']
    let opts = {
          \ 'noblock': 1
          \}

    let job = job_start(args, opts)
    let data = repeat('X', 65537)

    call ch_sendraw(job, data)
    call ch_close_in(job)

    Expected behavior:

    - Nothing much

    Actual bevarior:

    - E630: channel_write_input(): write while not connected

    If on the other hand, I initialized data with 65536 bytes instead of 65537, then no error would be thrown.

    In case you needed it, here is the output of `vim --version`

    > vim --version
    VIM - Vi IMproved 8.1 (2018 May 18, compiled Aug 16 2019 02:18:16)
    macOS version
    Included patches: 1-1850
    Compiled by Homebrew
    Huge version without GUI.  Features included (+) or not (-):
    +acl               -farsi             -mouse_sysmouse    -tag_any_white
    +arabic            +file_in_path      +mouse_urxvt       -tcl
    +autocmd           +find_in_path      +mouse_xterm       +termguicolors
    +autochdir         +float             +multi_byte        +terminal
    -autoservername    +folding           +multi_lang        +terminfo
    -balloon_eval      -footer            -mzscheme          +termresponse
    +balloon_eval_term +fork()            +netbeans_intg     +textobjects
    -browse            +gettext           +num64             +textprop
    ++builtin_terms    -hangul_input      +packages          +timers
    +byte_offset       +iconv             +path_extra        +title
    +channel           +insert_expand     +perl              -toolbar
    +cindent           +job               +persistent_undo   +user_commands
    -clientserver      +jumplist          +postscript        +vartabs
    +clipboard         +keymap            +printer           +vertsplit
    +cmdline_compl     +lambda            +profile           +virtualedit
    +cmdline_hist      +langmap           -python            +visual
    +cmdline_info      +libcall           +python3           +visualextra
    +comments          +linebreak         +quickfix          +viminfo
    +conceal           +lispindent        +reltime           +vreplace
    +cryptv            +listcmds          +rightleft         +wildignore
    +cscope            +localmap          +ruby              +wildmenu
    +cursorbind        +lua               +scrollbind        +windows
    +cursorshape       +menu              +signs             +writebackup
    +dialog_con        +mksession         +smartindent       -X11
    +diff              +modify_fname      -sound             -xfontset
    +digraphs          +mouse             +spell             -xim
    -dnd               -mouseshape        +startuptime       -xpm
    -ebcdic            +mouse_dec         +statusline        -xsmp
    +emacs_tags        -mouse_gpm         -sun_workshop      -xterm_clipboard
    +eval              -mouse_jsbterm     +syntax            -xterm_save
    +ex_extra          +mouse_netterm     +tag_binary
    +extra_search      +mouse_sgr         -tag_old_static
      system vimrc file: "$VIM/vimrc"
        user vimrc file: "$HOME/.vimrc"
    2nd user vimrc file: "~/.vim/vimrc"
          user exrc file: "$HOME/.exrc"
          defaults file: "$VIMRUNTIME/defaults.vim"
      fall-back for $VIM: "/usr/local/share/vim"
    Compilation: clang -c -I. -Iproto -DHAVE_CONFIG_H   -DMACOS_X -DMACOS_X_DARWIN  -g -O2 -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=1
    Linking: clang   -L. -fstack-protector-strong -L/usr/local/lib -L/usr/local/opt/libyaml/lib -L/usr/local/opt/openssl/lib -L/usr/local/opt/readline/lib  -L/usr/local/lib -o vim        -lncurses -liconv -lintl -framework AppKit  -L/usr/local/opt/lua/lib -llua5.3 -mmacosx-version-min=10.14 -fstack-protector-strong -L/usr/local/lib  -L/usr/local/Cellar/perl/5.30.0/lib/perl5/5.30.0/darwin-thread-multi-2level/CORE -lperl -lm -lutil -lc  -L/usr/local/opt/python/Frameworks/Python.framework/Versions/3.7/lib/python3.7/config-3.7m-darwin -lpython3.7m -framework CoreFoundation  -lruby.2.6

    Thanks,
    Matteo

* Created new async.vim MR to workaround ch_sendraw erroring out when data is bigger than 65536 bytes -- in this case, we would fall back to manual data chunking

# 2019-08-14
* Flipped the order of my plan notes!

    " Select all the end of lines *not* preceding a new day note
    /\v\n(# [0-9]{4})@!
    " Change all the end of lines with a § -- unlikely to use character
    :%s//§
    " Sort all the collapsed notes (! is for reverse :help sort)
    :'<'>sort!
    " Change § back to \r
    :%s/§/\r

# 2019-08-12
~ Use gx to open the URL under the cursor

# 2019-08-10
* Changed my clipboard vim wrappers to copy asynchronously, using aync.vim; it was not working initially, because the job in the background never exited (kept on listening on stdin), but by hacking the library (basically adding support for sending data and close stdin) I eventually managed to make it work.

? cb.vim -- https://vim.fandom.com/wiki/Using_the_Windows_clipboard_in_Cygwin_Vim

# 2019-08-03
I decided to give Terminal.app a try (instead of iTerm2) and one thing I immediately noticed what the lack of True Color support -- Vim would start in almost B&W, and it took me a good 15 minutes to figure out what was wrong

# 2019-07-20
* finished reading [The Peter Principle: Why Things Always Go Wrong](https://www.amazon.it/Peter-Principle-Things-Always-Wrong/dp/0062092065)

# 2019-07-07
* opened a ticket to `goobook` to see why `goobook add` is not working anymore: https://gitlab.com/goobook/goobook/issues/82
* fixed a bug with `cg` preventing all the guessers from being run -- it got recently introduced when we kept on processing guessers even though a command got already guessed
* enhance the personal budget google sheets to include expense source (cash, debit, credit...)

# 2019-06-19
* cleaning up my vimrc -- from https://www.reddit.com/r/vim/wiki/vimrctips

`nomodeline` -- it's dangerous
No more `nocompatible` -- it's useless
Don't use shortnames

    /\v<\w\w\=   " should match ts=
    /\v<\w\w\w\= " should match sts=

Allow your functions to `abort` upon encountering an error -- this uses a zero-width negative look-behind assertion... https://stackoverflow.com/a/10328015

    /\v\s+function.*(abort.*)@ git-add/reset/rm, rm -> rmr)
* cg: redirect stderr as well 2>&1
* cg: keep on processing guessers, even after a match -- this makes it possible for users to define multiple suggestions for the same input line
* cg: add support for guessers which return multiple suggestions

? tmux-externalpipe: spawn a new pane for the selection

# 2019-06-05
+ ?? .file -> rm....
+ rm "is directory" -> rm -rf...

# 2019-06-01
* published new repo / matteolandi.net page for: [tmux-externalpipe](https://github.com/iamFIREcracker/tmux-externalpipe) -- with it I could deprecate tmux-urlview / tmux-fpp, and hopefully simplify `cg` too

# 2019-05-21
* cg: stop `fzf` from sorting `cg`'s output
* released cg 0.2.0

# 2019-05-20
* cg: fix bug with downloader -- it would blow up in case ./bin was missing
* don't output duplicate suggestions
* add page for `cg` on matteolandi.net

# 2019-05-19
* implemented some cg guessers
* fixed a bug with cg build script where when bumbing a version, `git rev-list` would blow because unable to find the base version
* released cg 0.1.0

# 2019-05-18
* got `cg` to build automatically (for Linux, Mac OS) using travis-ci
* calculate version at build time, and use `git rev-list %s..HEAD --count` to append a build number to the base version

Windows build are kind of working too; list of caveats:

- set `langugage: shell` or it will hang
- created custom roswell install script to fetch binaries somehwere, add them to $PATH, and then invoke the ufficial roswell ci script -- by then, ros is found available, so it will set it up and not compile it: https://gist.github.com/iamFIREcracker/3564db5a24e296e087da49513d6cd00c

# 2019-05-16
* added command line opts to cg:  -h/--help, and -v/--version
* added a `download` script to `cg`, to download the latest binary available (Linux, Mac OS, Windows)

+ cg: git branch --set-upstream-to=origin/ opts

# 2019-05-15

I managed to get QUICKLISP automatically installed as part of the build process, however, I am not 100% sure that's the way to go; some users on Reddit pointed out that it might not be ideal for a build script to automatically install dependencies under someone's HOME.

Next experiments:

+ Try to get `cg` to build using travis-ci
+ Add a `./download` script to fetch the latest release

# 2019-05-13

All right, I think I figured out a way to avoid asking users to symlink/clone my repo inside ~/quicklisp/local-projects; putting the following at the top of my build.lisp file, right above (ql:quickload :cg), should do the trick:

    ;; By adding the current directory to ql:*local-project-directories*, we can
    ;; QL:QUICKLOAD this without asking users to symlink this repo inside
    ;; ~/quicklisp/local-projects, or clone it right there in the first place.
    (push #P"." ql:*local-project-directories*)

Also, about getting "deploy" automatically installed: I guess I can throw in there a (ql:quickload :deploy) and get the job done, right?

Still need to figure out how easy it would be to get QUICKLOAD automatically installed;  well, maybe I found one: https://stackoverflow.com/questions/40903944/how-to-do-dynamic-load-load-in-common-lisp

# 2019-05-12

* cg: documentation

# 2019-05-10

* cg: documentation and refactoring

I feel like `cg` is ready for people to give it a try, so maybe I will post it on Reddit... maybe..

# 2019-05-09

* cg: fzf script to enable multi-select

# 2019-05-08

* change `cg`'s Makefile not to specify `sbcl` full path -- let the SHELL figure it out, as the path might be different based on the OS

# 2019-05-06

* more `cg` guessers: 'git branch -D', pruning '[gone]' branches, `sudo apt install`
? cg: nvm use --delete-prefix v8.16.0

# 2019-05-05

* finally got around to implement [cg](https://github.com/iamFIREcracker/cg), a command line utility (written in Common Lisp) that tries to extract commands to run from its input -- e.g. you pipe `git log -n ..` into it, and it will recommend you to `git show` a specific commit.  It parses definitions from a RC file (err, a lisp file), and ships with FZF and TMUX plugins

# 2019-05-04

Participated to Google Code Jam Round1-C but did not make it to the second one:

- 1st problem: miss-read the description and took a pass on it -- even though, in the end, it turns out I should have started with that
- 2nd problem: the interactive ones, as since I have never done an interactive problem, I took a pass on it too
- 3rd problem: it did not seem to complicated at first (especially if you were amining for completing the first part), but somehow I bumped into recursion errors and could not submit a working solution on time

Lesson learned:

- Google Code Jam is though, and you better prepare for it... in advance
- You gotta read carefully man!

# 2019-05-01
* bumbped aadbook dependencies -- github sent me an alert that one of them contained a vulnerability
* set up keyring to play well with `twice` -- https://pypi.org/project/twine/
* finished my urlview vim wrapper -- https://github.com/iamFIREcracker/dotfiles/commit/722022590003dc2e78a4a15b5f7c09e60400d451

# 2019-04-25

Fucking figured out why [bracketed paste mode](https://cirw.in/blog/bracketed-paste) had stopped working sometime during the last 3/4 months...finally!  Long story short, readline 8.0 was released this January, and with it shipped a fix for https://lists.gnu.org/archive/html/bug-bash/2018-01/msg00097.html, a fix which I think broke something somewhere else.  Anyway, if your bash setup includes a custom prompt command - set via PROMPT_COMMAND - that outputs some text with newline characters in it, when bracketed paste mode is on and you try to run a pasted command, changes are you would end up running not just the command you pasted but also one or more empty commands as well (depending on the number of newline chars that are generated inside the custom prompt command).

   matteolandi at hairstyle.local in /Users/matteolandi
   $ bind 'set enable-bracketed-paste on'

   matteolandi at hairstyle.local in /Users/matteolandi
   $ pwd | pbcopy

   matteolandi at hairstyle.local in /Users/matteolandi
   $ echo "CMD+V + Enter"
   CMD+V + Enter

   matteolandi at hairstyle.local in /Users/matteolandi
   $ /Users/matteolandi
   -bash: /Users/matteolandi: Is a directory

   matteolandi at hairstyle.local in /Users/matteolandi

   matteolandi at hairstyle.local in /Users/matteolandi
   126 >

Luckily for me I was able to fix my bashrc setup and move my custom prompt command `echo` calls into `$PS1` (see: https://github.com/iamFIREcracker/dotfiles/commit/4f683bf10bac71ecd06b28f8d0db76591f53f273#diff-85cf9b51417c7cfb8766aa6b56f7edb9R745https://github.com/iamFIREcracker/dotfiles/commit/4f683bf10bac71ecd06b28f8d0db76591f53f273#diff-85cf9b51417c7cfb8766aa6b56f7edb9R745), but I wonder if this wasn't a regression actually -- to be honest I don't know if it's expected to genrate output from custom prompt commands or not.

* added initial support for mintty terminal to vitality.vim

# 2019-04-21

Use `hexdump(1)` to view text as binary data (use `-C` to display original strings alongside their binary representation).

And why would I care so much about viewing text as binary data?  Because I am trying to understand bracketed pasted mode, and figure out if the extra new-line when pasting text in the terminal is intentional or a bug (readline? iTerm? iTerminal?)

# 2019-04-16
* fucking fixed iterm2/tmux/term-mode weirdness -- vitality.vim lacked support for [terminal-mode](https://github.com/sjl/vitality.vim/pull/42)

? vitality for mintty -- mintality
+ read lispwords from project file

# 2019-04-14

Had some *fun* with [sic](https://tools.suckless.org/sic/), and implemented a wrapper to:

- create a SSL proxy using `socat` -- https://unix.stackexchange.com/questions/491846/using-socat-to-make-a-secure-tcp-connection-to-an-irc-server/491940#491940
- keep track of input history using `rlwrap`
- store IRC logs using `tee`
- highlight my name with `gsed`

# 2019-04-13
* finally got gcj-qualifications-3-criptopangrams accepted by the grader -- it's possible it was blowing up because of memory issues before, and I did fix that by reading the ciphertext one piece at a time (i.e. multiple `(read)`) instead of read-line + split + parse

If a codejam solution is RE-ing out, but cannot find any apparent flaw in the algorithm, try to invoke the GC at the end of each case -- `(sb-ext:gc)`

# 2019-04-06
* implemented Clojure's threading macro, in Common Lisp -- https://github.com/iamFIREcracker/adventofcode/blob/099925161207767e785647fb3747f0b5ad6c46f2/utils.lisp#L262-L297
* refactored 2018 day 11 to use Summed-area table!

# 2019-04-04
* implemented Summed-area Table data-structure, in Common Lisp -- https://github.com/iamFIREcracker/adventofcode/commit/92519ded8e7e544acda1b12e0aaed942488b6834

# 2019-03-24
* implemented DEFUN/MEMO macro to define a memoized function

Tried to see if I could somehow expose the cache used (i.e. replacing `LET` with `DEFPARAMETER`) but could not find a way to programmatically generate a name as the concatenation of the function name, and a `/memo` literal.  Anyway, I am happy with it already..

    (defmacro defun/memo (name args &body body)
      (with-gensyms (memo result result-exists-p)
        `(let ((,memo (make-hash-table :test 'equalp)))
           (defun ,name ,args
             (multiple-value-bind (,result ,result-exists-p)
                 (gethash ,(first args) ,memo)
               (if ,result-exists-p
                 ,result
                 (setf (gethash ,(first args) ,memo) (progn ,@body))))))))

# 2019-03-23
* changed PARTIAL-1 to support nested substitution via SUBST
* added PARTIAL-2 (similar to PARTIAL-1, but with the returned function accepting 2 arguments instead of 1)

I am pretty excited about what I came up with. At first I got stuck when trying to use SUBST because I couldn't get nested backticks/commans to work as expected; then I realized I coudl do all the transformation inside the macro, and only put inside backticks the transformed argument list

    (defmacro partial-1 (fn &rest args)
      (with-gensyms (more-arg)
        (let ((actual-args (subst args '_ more-arg)))
          `(lambda (,more-arg)
              (funcall (function ,fn) ,@actual-args)))))

# 2019-03-21
* factored out A-STAR from 2018 22

? DESTRUCTURING-BIND for COMPLEX
? how to destructuring-bind an espression, ignoring specific elements? https://stackoverflow.com/questions/25463763/any-good-way-to-declare-unused-variables-in-destructuring-bind/25464689#25464689)

# 2019-03-20

? gotta go vim-sexp a try -- and tpope's [mappings](https://github.com/tpope/vim-sexp-mappings-for-regular-people) in particular

# 2019-03-19
*refactored 2018 22

- Use COMPLEX instead of (LIST X Y)
- Use CASE / ECASE instead ASSOC
- Add poor's heap-queue implementation to utils
- Review a-star implementation -- soon I will factor out a new function for it

The execution time dropped from ~40 seconds to ~5

In the process I have also added HASH-TABLE-INSERT to utils, and used that wherever I saw fit

# 2019-03-17

? change RING related methods to use DEFINE-MODIFY-MACRO -- https://blog.cneufeld.ca/2014/01/the-less-familiar-parts-of-lisp-for-beginners-define-modify-macro/, and https://stackoverflow.com/questions/31587429/define-modify-macro-with-operator-argument

# 2019-03-16
~ review vim-yankring config -- I have a custom branch to override some mappings which hopefully could go; also, it stomps on vim-surround mappings too

Extracts from https://practicaltypography.com/why-racket-why-lisp.html:

Everything is an expression. [Lisps] Most programming languages are a combination of two syntactically distinct ingredients: expressions (things that are evaluated to produce a value) and statements (things that express an action). For instance, in Python, `x = 1` is a statement, and `(x + 1)` is an expression.

Every expression is either a single value or a list. [Lisps] Single values are things like numbers and strings and hash tables. (In Lisps, they’re sometimes called atoms.) That part is no big deal.
Macros. [Racket] Also known in Racket as syntax transformations. Serious Racketeers sometimes prefer that term because a Racket macro can be far more sophisticated than the usual Common Lisp macro.

# 2019-03-14
? read about Y-combinator: https://rosettacode.org/wiki/Y_combinator#Common_Lisp -- I am pretty sure there are better resources out there

# 2019-03-13
* completed advent of code 2017 day23-3 -- and with it AoC 2017!!!

Oh boy, I am done reverse-engineering assembly code -- probably because I am bad at it.  Few lesson learned though:

- Use TAGBODY to create CL code matching your input
- When iterating over big ranges of numbers (like here, where we working in the range 108100 - 125000), one could try to make such range smaller, hopefully making it easier to figure out what the code is actually doing

Enjoy my abomination -- it's ugly, still it helped me realize we were counting non-prime numbers

    (defun solve-part-2 ()
      (let ((b 0) (c 0) (d 0) (e 0) (f 0) (g 0) (h 0))
        (tagbody
          (setf b 81
                c b)
          part-a
            (prl 'part-a)
            (setf b (* b 100)
                  b (- b -100000)
                  c b
                  c (- c -17000))
          part-b
            (prl 'part-b b c (break))
            (setf f 1
                  d 2)
          part-e
            (prl 'part-e)
            (setf e 2)
          part-d
            (prl 'part-d d e b)
            (setf g (- (* d e) b))
            (if (not (zerop g))
              (go part-c))
            ; (prl 'resetting-f (break))
            (prl 'resetting-f)
            (setf f 0)
          part-c
            (prl 'part-c d e b)
            (setf e (- e -1)
                  g e
                  g (- g b))
            (if (not (zerop g))
              (go part-d))
            (setf d (- d -1)
                  g d
                  g (- g b))
            (if (not (zerop g))
              (go part-e))
            (if (not (zerop f))
              (go part-f))
            ; (prl 'setting-h (break))
            (prl 'setting-h)
            (setf h (- h -1))
          part-f
            (prl 'part-f b c)
            (setf g b
                  g (- g c))
            (if (not (zerop g))
              (go part-g))
            (return-from solve-part-2 (prl b c d e f g h))
          part-g
            (prl 'part-g)
            (setf b (- b -17))
            (go part-b))))

Also, this! from [reddit](https://www.reddit.com/r/adventofcode/comments/7lms6p/2017_day_23_solutions/drnjwq7/)


    The key to understanding what this code does is starting from the end and working backwards:

    - If the program has exited, g had a value of 0 at line 29.
    - g==0 at line 29 when b==c.
    - If g!=0 at line 29, b increments by 17.
    - b increments no other times on the program.
    - Thus, lines 25 through 31 will run 1000 times, on values of b increasing by 17, before the program finishes.

    So, given that there is no jnz statement between lines 25 and 28 that could affect things:

    - If f==0 at line 25, h will increment by 1.
    - This can happen once and only once for any given value of b.
    - f==0 if g==0 at line 15.
    - g==0 at line 15 if d*e==b.
    - Since both d and e increment by 1 each in a loop, this will check every possible value of d and e less than b.
    - Therefore, if b has any prime factors other than itself, f will be set to 1 at line 25.

    Looking at this, then h is the number of composite numbers between the lower limit and the upper limit, counting by 17.

# 2019-03-12
* completed advent of code 2017 day25-1 -- it was painful to parse the input, and as many did in the megathread, I should have manually parsed it, hopefully got some scores, and then implemneted the input parser, but you know, I am late for the leaderboard anyway, so...

# 2019-03-11
* completed advent of code 2017 day24-1
* completed advent of code 2017 day24-2

+ `goobook-add` is not working anymore -- it ends up creating an account having name and email both set to the email address: `goobook-add Matteo Landi matteo@matteolandi.net` creates a new account with name: matteo@matteolandi.net, and address: matteo@matteolandi.net
? in CL, what's the idiomatic way of filtering a list keeping the elements matching a specific value? `(remove value list :test-not 'eql)`? `(remove-if-not #'= value list)`

# 2019-03-10
* finally solved all the vim-fugitive issues I was facing since the last upgrade; it turns out some plugin functions were deprecated, and other plugins (e.g fugitive-gitlab were still them) --  after I upgraded those plugins as well, everything started to work just fine
* completed advent of code 2017 day22-1
* completed advent of code 2017 day22-2
* completed advent of code 2017 day22-1 -- reused code from day18 and managed to quickly get a star for part y; for part2 it seems like I need to reverse engineer that code, which I don't want to do right now

TIL:

- I can use `:when .. :return ..` instead of `:do (when .. (return ..))`
- I can use `:with .. :initially ..` to create and initialize `LOOP`'s state instead of
`LET` + initialization logic *outside* of `LOOP`

? implement MULF to multiply a place -- http://stevelosh.com/blog/2016/08/playing-with-syntax/

# 2019-03-09
* completed advent of code 2017 day21-1
* completed advent of code 2017 day21-2

I decided to represent the pixels grid as a string, so I had to implement logic for:

- accessing specific sub-squares of such grid -- `(+ (* i * size) j)`
- combine sub-squares together -- https://github.com/iamFIREcracker/adventofcode/commit/0e47962ac47d568573d16a08d8d8be27b9f21a12

It takes less than 4 seconds to complete the 18th iteration, so I did not bother optimizing further; I have however read something interesting on Reddit:

> After 3 iterations a 3x3 block will have transformed into 9 more 3x3 blocks, the futures of which can all be calculated independently. Using this fact I just keep track of how many of each "type" of 3x3 block I have at each stage, and can thus easily calculate the number of each type of 3x3 block I'll have 3 iterations later.

So yeah, this can be optimized further but you know, I am engineer..

# 2019-03-08
* optimize solution for 2017 day20 -- add GROUP-BY, refactored UNIQUE-ONLY to use GROUP-BY, and that's it, from O(N²) to O(N)!

? Is it guaranteed that iterating over hash-table keys would return them in insertion order? It seems to be the case for SBCL at least, but is it implementation dependent?

# 2019-03-07
* optimized 2017 day 17

We are interested in the value next to 0 after 50000000 iterations, so we don't have to keep the ring buffer up to date, we can simply simulate insertions, and keep track of all the values inserted after value-0

# 2019-03-06
* completed advent of code 2017 day20-1
* completed advent of code 2017 day20-2

Really sloppy solution, mine.  The problem statement says:

    Which particle will stay closest to position <0,0,0> in the long term?

So I said, why don't I let the simulation run for some time until the particle closest to <0,0,0> doesn't change (part1), or no more particles collide with one another (part2)? Well, it turned out 500 iterations were enough to converge to the right answers!

~ find a clever (and more generic) way to solve 2017 day20 -- implement a function to check when all the particles are moving away from <0,0,0>

# 2019-03-05
* completed advent of code 2017 day18-1
* completed advent of code 2017 day18-2
* completed advent of code 2017 day19-1
* completed advent of code 2017 day19-2

Day 18:

Another "here is your instructions set, create a compiler for it" kind of exercise.  Part1 was OK, it did not take me long to got it right; part2 instead, it got messier than expected, but overall it doesn't look that _bad_ and it runs pretty quickly too, so...

# 2019-03-04
* completed advent of code 2017 day16-1
* completed advent of code 2017 day16-2
* completed advent of code 2017 day17-1
* completed advent of code 2017 day17-2

+ Make day17-2 run faster -- it takes minutes for that 50_000_000 iterations on that ring buffer to complete

# 2019-03-03
* completed advent of code 2017 day15-1
* completed advent of code 2017 day15-2

Upgraded vim-fugitive, bus since then, I cannot `:Gcommit` anymore.  Even when I do it from the command line, not only I see the following error message, but when I try to save, Vim complains with: `E13: File exists (add ! to override)` ...

```
Error detected while processing function 41_config[4]..119_buffer_repo:
line    1:
E605: Exception not caught: fugitive: A third-party plugin or vimrc is calling fugitive#buffer().repo() which has been removed. Replace it with fugitive#repo()
Error detected while processing function FugitiveDetect:
line   11:
E171: Missing :endif
```

+ Need to figure this out, as it's super annoying

+ implement something similar to `SUMMATION`, but for `MAX` and `MIN` -- `MAXIMIZATION` and `MINIMIZATION`

Also, TIL (or better, remembered) that you can `(loop :repeat N ...)` to execute a loop `N` times

# 2019-03-02
* completed advent of code 2017 day13-1
* completed advent of code 2017 day13-2
* completed advent of code 2017 day14-1
* completed advent of code 2017 day14-2

Day13 part 2 kept me busy more than expected, but eventually figured out what I was doing wrong:

    Severity == 0 isn't the same as not getting caught.

Also, at first I implemented this, but simulating the dilay, and the packets passing through the different firewall layers; it worked, but was slow as balls (around 13 secs), so I decided to do some math to figure out collisions, without having to simulate anything.  It paid off, as now it takes around 0.4secs to find the solution.

Day14 part 1 was all about re-using Knot Hashes from day 10, and figure out an hexadecimal to binary conversion.  Part 2 instead, disjointsets, again!

+ implement a proper hex to binary macro or something -- `HEXADECIMAL-BINARY`

# 2019-03-01
* completed advent of code 2017 day11-1
* completed advent of code 2017 day11-2
* completed advent of code 2017 day12-1
* completed advent of code 2017 day12-2

Day11 was all about hex grids; found this interesting [article](https://www.redblobgames.com/grids/hexagons/) about how to represent hex grids, and how to calculate the distance between tiles -- I opted for the _cube_ representation.

Day12 instead, disjointsets!

# 2019-02-27
* completed advent of code 2017 day10-1
* completed advent of code 2017 day10-2

Couple interesting `FORMAT` recipes:

- `~x` to print a hexadecimal (`~2,'0x~` to pad it with `0`s)
- `~(string~)` to lowercase a string

# 2019-02-26
* completed advent of code 2017 day8-1
* completed advent of code 2017 day8-2
* completed advent of code 2017 day9-1
* completed advent of code 2017 day9-2

It would be nice if there was a macro similar to `WITH-SLOTS`, but for structures, so I wouldn't have to do the following:

```
    (loop
      :with registers = (make-hash-table :test 'equal)
      :for instruction :in data ; XXX isn't there a better way to handle this?
      :for reg-inc = (reg-inc instruction)
      :for inc-delta = (inc-delta instruction)
      :for op = (comp-op instruction)
      :for reg-comp-1 = (reg-comp-1 instruction)
      :for value-comp = (value-comp instruction)
```

# 2019-02-25
* completed advent of code 2017 day7-1
* completed advent of code 2017 day7-2

A little messy my solution for 2017 day 07 -- gotta find someone else's solution and compare it with mine to see what can be improved

# 2019-02-24
* completed advent of code 2017 day5-1
* completed advent of code 2017 day5-2
* completed advent of code 2017 day6-1
* completed advent of code 2017 day6-2

# 2019-02-23
* completed advent of code 2017 day4-1
* completed advent of code 2017 day4-2

In CL, you can use `:thereis` to break out of a `LOOP` when a given condition is `T`:

```
(loop
  :for (a . rest) :on pass-phrase
  :thereis (member a rest :test 'equal)))
```

There are `:never` and `:always` too!

# 2019-02-21
* completed advent of code 2017 day3-1
* completed advent of code 2017 day3-2

+ is there anything similar to `WITH-SLOTS`, but for structures?

# 2019-02-20
* completed advent of code 2017 day2-1
* completed advent of code 2017 day2-2

TIL, `MULTIPLE-value-LIST`:

```
(loop
  :for values :in data
  :for (divisor number) = (multiple-value-list (evenly-divisible-values values))
  :summing (/ number divisor))))
```

# 2019-02-19
* completed advent of code 2017 day1-1
* completed advent of code 2017 day1-2

# 2019-02-16

fixed DNS problems after upgrading Ubuntu from 16.04 to 18.04

> After not understanding why this wouldn't work I figured out that what was also needed was to switch /etc/resolv.conf to the one provided by systemd. This isn't the case in an out-of-a-box install (for reasons unknown to me).

```
sudo ln -sf /run/systemd/resolve/resolv.conf /etc/resolv.conf
```

Source: https://askubuntu.com/questions/907246/how-to-disable-systemd-resolved-in-ubuntu/938703#938703

Also, recently W10 started pushing me to use a new Snip tool, so had to figure out how to change my AutoHotKey files to use that:

> I figured it out! Create a shortcut as described here: https://stackoverflow.com/questions/35125286/how-to-start-windows-store-apps-in-autohotkey and then incorporate that shortcut into the following code:

```
+#s::
IfWinExist Snip & Sketch
{
    WinActivate
    WinWait,  Snip & Sketch,,2
    Send ^n
}
else
{
    Run "F:\Snip & Sketch.lnk"
    sleep, 500
    send, ^n
}
return
```

Source: https://linustechtips.com/main/topic/995171-solved-windows-10-snip-sketch-shortcut-not-working/

# 2019-02-11

[04 – Focal Length](http://www.r-photoclass.com/04-Focal-length/)

Ultra-wide angle – 14-24mm
Wide angle – 24-35mm
Normal – 40-75mm
Mild tele – 85-105mm
Medium tele – 120-300mm
Long and exotic tele – 300-800mm

> This was a very interested exercise for me. I used a kit Canon 18-55mm lens with the shots at 18/35/55. I then approached the hydrant and shot it at 18mm. The most noticeable impact that I saw was the loss of depth you have when you are zooming in versus physically approaching the subject. If you look at the dirt pile just over the top left of the hydrant in the third image versus the fourth, the depth of field and the scope are significantly different versus trying to shoot the same shot just physically closer without any significant zoom. Interesting exercise.
>
> http://imgur.com/a/BosdW

# 2019-02-10

Lightroom shortcuts:

- R: grid overlay (press O to change the grid type; shift-O to rotate the grid)
- L: lights out (dim background, isolating the photo you are working on); press it again, all the controls will now be black
- F: fullscreen
- Y: before & after
- \: compare with original
- J: show where the image is clipping (red: overexposed, blue: underexposed)

Lightroom quick tips:

- Exposure > Tone Auto
- Reset button (bottom right corner) / or double click on the effect
- Grid > Angle: you can now draw a line, and the photo will be rotated based on that
- Radio brush adjustment

# 2019-02-06
* refactored day9 using doubly linked list and ring structures

`ARRAY-IN-BOUNDS-P` can be used to figure out if an index (list of indexes) is a valid array (multi-dimensional array) position

You loop over _lazily_ generated numbers by using `LOOP`:

    (loop :for var = initial-value-form [ :then step-form ] ...)

For example, to indefinitely loop over a _range_ of values:

    (loop
      :for player = 0 :then (mod (1+ player) players)

# 2019-02-05
* implement `RECURSIVELY` macro

# 2019-02-03
+ `recursively` macro -- and use it for day08
? is it possible to do `:collecting` with a specific type
? parsing input is still annoying...

# 2019-01-28

Need to concatenate multiple images into a single PDF? [Easy](https://askubuntu.com/questions/493584/convert-images-to-pdf/557975#557975): `convert "*.{png,jpeg}" -quality 100 outfile.pdf`

# 2019-01-27
* converted my advent-of-code solutions repository to [quickproject](https://xach.livejournal.com/278047.html)

It's important to symlink your project, or its .asd file won't properly load up:

    mkdir -p ~/quicklisp/local-projects/
    ln -s $(pwd) ~/quicklips/local-project/$(dirname)

Once you do this, and you open a REPL inside a _quickproject_, a simple `(ql:quickload ...)` should work just fine

# 2019-01-26
* replaced TSlime with a custom-made mini-plugin that that ships with a couple of `` mappings: one to list all the buffers and pick a terminal you want you send data to, and another to send the current selection to the selected terminal

# 2019-01-24

Trying to make sense of lisp indentation with Vim and Vlime, and it turns out you have to `set nolisp` (or simply not `set lisp` if you were explicitly setting it): https://github.com/l04m33/vlime/issues/26

? I still don't get why I should use `lispwords` though (I thought Vlime used a different set of rules), but maybe I just don't know what I am doing...

# 2019-01-20
* got more familiar with CL `loop`'s syntax

`loop` macro supports iterating over a list of elements and exposing not just the current element, but also the remaining ones; it's implemented using the `:on` phrases (this will make `var` step var over the cons cells that make up a list.).

    (let ((data '(1 2 3 4 5 6 6)))
      (loop
        :for (a . remaining) :on data
        :for b = (find a remaining)
        :do (when b
              (return a))))

Note that you cannot simply `:for b :in remaining` because `remaining` will be `NIL` when accessed, forcing and early exit from `loop` (this does not happen when we use [Equals-Then Iteration](http://www.gigamonkeys.com/book/loop-for-black-belts.html)).

`loop` macro supports `count` (and `counting`), so expressions like:

    :summing (if (hash-table-find 3 freqs) 1 0) :into threes

can be simplified into:

    :counting (hash-table-find 3 freqs) :into threes

# 2019-01-19
* configured my inputrc to popup an editor (rlwrap-editor) with C-xC-e, when playing with Node, Python, or SBCL -- which are rlwrap-wrapped
* uninstalled `roswell` and installed plain `sbcl` instead; had to install quicklisp though, to make it work with vlime, but that was [easy](https://gist.github.com/jteneycke/7947353)

1 Downlaod quicklisp: `curl -O https://beta.quicklisp.org/quicklisp.lisp`
2 Execute the following forms from a sbcl REPL:

```
(load quicklisp.lisp)
(quicklisp-quickstart:install)
```

3 Finally instruct SBCL to always load quicklisp -- add the following to your '.sbclrc: `(load "~/quicklisp/setup.lisp")`

# 2019-01-17
* finished reading: [Common LISP: A Gentle Intorduction to Symbolic Compoutation](https://www.amazon.com/Common-LISP-Introduction-Computation-Engineering/dp/0486498204)

# 2019-01-13
* completed advent of code day15-1
* completed advent of code day15-2 -- it's official, I don't know how to code a-star. Also, I am unlucky: my solution was working with pretty much all the inputs I found only, but still it wasn't accepting mine.  Anyway, there was a bug in my a-star algorithm, and once fixed it, it finally accepted my answer (funny how it was working OK for all the other inputs).
* completed andvent of code 2018 -- now time to look at the solutions again, and see what can be improved, factored out
* fixed my laptop -- `brewski` fucked it Vim (it would not start up), and it turned out there were some issues with [python2 vs python3](https://superuser.com/questions/1115159/how-do-i-install-vim-on-osx-with-python-3-support/1381420#1381420)

# 2019-01-10
* completed advent of code day23-2

I spent a few hours trying to come up with a proper solution for this, but I gave up eventually; I got so clouded by the size of the input (not just the number of nanobots, but also their coordinates and their radius sizes), than rather than immediately searching for the correct solution, I should have just tried things in a trial and error fashion.

The solution I happened to copy from Reddit goes as follows (I am still not 100% sure it is generic, but it worked for my input so...): we sample the three dimensional space looking for a local maximum; once we have one, we zoom in around that maximum, and keep on sampling, until the area is a point.  Of course we have to take into account not only the number of nanobots covering the sampled point, but also its distance to the origin.

# 2019-01-08
* completed advent of code day21-2

After spending almost an hour to try and undestand what the problem was doing, I gave up and peeked into Reddit to see how other people had solved it and... it turned out I got the problem all wrong!  The program loops forever, and the only way to break out of it is if (in my case) R0 is loaded with some random values, say 10 234 55 1 12 and 10 again.

Now, if you load R0 with 10, it will break out immediately; if you load it with 234 it will check against 10 and then 234 (at which point it will exit); the problem asks for the number for which the program will run the maximum number of instructions, and the answer is the latest value of the loop: 12. You load R0 with 12, and the program will run and check against 10, 234, 55, 1, and finally 12!

The program run in 511 seconds but I don't want to try to understand what the program does and simplify it (or transpile it into C or something).  I can try and make opcodes run faster, but that's it

? Make Elf opcodes run faster -- use arrays instead of consing

# 2019-01-07
* completed advent of code day11-2 -- before, I were generating all the squares, calculating the energy of a square, and then finding the max; now, I am doing all this using for loops, without allocating much stuff. It's slow as balls, but only after 19 minutes it spit the right solution :P

+ make solution for dat11-2 more efficient

# 2019-01-06
* completed advent of code day25-1 -- disjointset
* completed advent of code day7-2

? write a popn macro popping n elements from place

# 2019-01-05
* completed advent of code day24-1 -- it took me quite some time to get all the rules implemented, but it worked eventually
* completed advent of code day24-2 -- starting from a boost of 2 I started to double it until I found **a** winning boost; then run a binary search between the winning one, and its half

# 2019-01-04
* completed advent of code day20-1 -- Dijkstra
* completed advent of code day20-2

# 2019-01-03
* completed advent of code day22-2 -- the solution (a-star) runs in 48 seconds.. common lisp doesn't ship with an implementation of heapq, so adding+sort on every step is probably killing the performance

+ make day22-2 run fuster -- find a good implementation of heapq

Simple Dijkstra/path-finding Python implementations: https://www.redblobgames.com/pathfinding/a-star/implementation.html

# 2018-12-31

? parse string using regexpes in common lisp

# 2018-12-30
* completed advent of code day23-1 -- it took me forever to realize I was looping over the sequence I had passed to `sort` -- which is destructive -- and that some elements were gone (I should have either `copy-seq`d or looped over the sorted list)
* completed advent of code day22-1

+ memoize decorator in common lisp

# 2018-12-26
* completed advent of code day21-1

I looked for all the comparison instructions where R0 (the only one we could mutate) appeared as one of the argument; put a breakpoint on the instruction, and inspected what the expected value was, used that as R0 and that's it.

# 2018-12-25
* completed advent of code day19-2

I started analyzing all the comparison instructions and the status of the registers before and after these were executed.  Then tweaking the initial state of the registers to fast forward the program execution I managed to understand that the program was trying to calculate the sum of the divisors of a given number (where _given_ means some funky formula I have no idea of... I simply looked at the registers to see what this number was).

# 2018-12-24
* completed advent of code day17-1 -- it took me forever to get this right.. I am dumber that I thought I were
* compelted advent of code day17-2 -- once you get part 1 right..
* compelted advent of code day18-1
* compelted advent of code day18-2
* compelted advent of code day19-1

# 2018-12-21
* completed advent of code day16-2

? learn about lispwords -- vim

# 2018-12-20
* completed advent of code day16-1

? figure out how to parse day16-2 input as a single file, instead of 2

# 2018-12-16
* finished reading [Weapons of Math Destruction: How Big Data Increases Inequality and Threatens Democracy](https://www.amazon.com/Weapons-Math-Destruction-Increases-Inequality/dp/0553418815)
* completed advent of code day13-1
* completed advent of code day13-2
* completed advent of code day14-1
* completed advent of code day14-2 -- slow-ish

? make solution for dat14-2 more efficient

# 2018-12-15
* completed advent of code day12-1
* completed advent of code day12-2

? understand why day12-2 first set of loops need to be skipped (> cur-gen 10000)

# 2018-12-14
* completed advent of code day10-1
* completed advent of code day10-2
* completed advent of code day11-1

# 2018-12-13
* completed advent of code day9-1
* completed advent of code day9-2 -- slow as balls, had to leave it going during the night

+ more efficient solution for day9-2

# 2018-12-12
* completed advent of code day8-1
* completed advent of code day8-2

# 2018-12-11
* completed advent of code day7-1 -- it did not seem that complex... yet it took me forever to implement the dependency-tree traversal

# 2018-12-10
* completed advent of code day6-1
* completed advent of code day6-2

# 2018-12-09
* completed advent of code day4-1
* completed advent of code day4-2
* completed advent of code day5-1 -- it takes around 2 minutes to run... we can do better
* completed advent of code day5-2 -- left running at night

+ more efficient solution for day5-1 and day5-2

# 2018-12-08
* completed advent of code day3-1
* completed advent of code day3-2

# 2018-12-06
* completed advent of code day2-1
* completed advent of code day2-2

# 2018-12-05
* completed advent of code day1-2
* cleared Spotify cache

Couple interesting articles on how to clear/configure Spotify's cache:

- https://support.spotify.com/it/using_spotify/troubleshooting/reinstallation-of-spotify/
- https://community.spotify.com/t5/Desktop-Mac/Why-the-cache-Spotify-app-on-OSX-takes-up-10G-of-space/td-p/4544523

# 2018-12-03
* completed advent of code day1-1

# 2018-11-25
* fixed editorconfig configuration for .vimrc -- https://github.com/editorconfig/editorconfig-vim/issues/114

# 2018-11-13
* finished reading [Ripensare la smart city](https://www.amazon.com/Ripensare-smart-city-Italian-Francesca-ebook/dp/B07GWZJJT1)

# 2018-11-11
* managed to get tern.js to reference `async function` definitions -- I did not realize I had `"ecmaVersion": 6` in my .tern-project file
* created a PR to tern.js to factor out server-boostrapping logic into a different file people can use to easily start a Tern server without the HTTP wrapper

Read a few articles about [Initiative Q](https://initiativeq.com/): https://breakermag.com/initiative-q-is-all-the-bad-parts-of-crypto-and-none-of-the-good-ones/ and https://www.vox.com/the-goods/2018/11/8/18075816/initiative-q-email-cryptocurrency-bitcoin-paypal-saar-wilf, and I feel like it's just a big scam: they are giving away free money in exchange of...your data, and information about your inner circles (you only have 5 invites, so you need to be carefull whom you are sending it to; and when you receive one, since you know the sender very well and you know they only had 5 invites, you might as well simply accept it and join the community because again...you really trust the sender of the invite and he wouldn't have sent it to you if the thiing was a scam).  Anyway, let's see how it pans out.

# 2018-11-10
* finished reading [Machines of Loving Grace: The Quest for Common Ground Between Humans and Robots](https://www.amazon.com/Machines-Loving-Grace-Common-Between/dp/0062266691)
Played around with Tern.js internals to understand: 1) why `findDef` of an `async function` is not returning anything, 2) how to enhance js-langserver to support pretty much all the supported properties one can configure inside .tern-config/.tern-project

Regarding point 1 above, I tried to factor out a boostrap.js file inside tern.js/lib, containing all the code necessary to start up a Tern server (code gently borrowed from tern.js/bin/tern) -- let's see if tern.js maintainers are interested in merging my PR

# 2018-11-06
* submitted PR to rainbow_paretheses.vim for configuring the maximum number of levels to color

# 2018-11-04
+ tern.js does not recognize `async function foo() ..` as definition

# 2018-11-03

Finally found some time to play with Microsoft's [Language Server Protocol](https://microsoft.github.io/language-server-protocol/specification)

* js-langserver: submitted PR for loading ESLint from the project directory -- instead of the language server one
* js-langserver: updated the LS manifest based on implemented features
* js-langserver: fixed 'rename' -- the protocol must have changed since the first implementation, and 'rename' wasn't working anymore

# 2018-11-01
* aadbook: 0.1.2 -- fix potential security vulnerability related to `requests==2.19.1`

# 2018-10-31
* vim-lsp: use loclist instead of quickfix for document diagnostics https://github.com/prabirshrestha/vim-lsp/pull/195

# 2018-10-29
* expensio: initial support (server/client) for expenses: list, create
* expensio: edit of group categories
* expensio: fixed exception loggin when `json: true`

# 2018-10-28
* expensio: add logging of HTTP requests using morgan

? expensio: fix logging of warnings/errors with winston

# 2018-10-27
* expensio: initial support (server/client) for groups: list, create
* expensio: initial support (server/client) for group-members: list, create
* expensio: initial support (server/client) for groupcategories: list, create

? expensio: confirm group membership
? expensio: hierarchical categories

# 2018-10-26
* expensio: almost complete auth workflow -- missing: emails
* expensio: authenticate paymentmethods-related workflows

? expensio: dynamically make 'code' required
? expensio: intercept UNIQUE CONSTRAINT and return 409
? expensio: stop reloading pm/expenses -- especially when going back after pm/expense edit

# 2018-10-25
* expensio: added back/front end support for editing paymentmethods
* expensio: created passport/bearer based auth layer

# 2018-10-23
* expensio: created skeleton Node.js app -- logger, module loader, express, openrecord
* expensio: created skeleton Angular app -- boilerplate, Login/Register pages

? expensio: send auth tokens via email

# 2018-10-22
* created MR for goobook, fixing pip installation -- setup.py is trying to read a file not included in the manifest
* fixed goobook crash happening when user contacts list contained some contacts without neither a name, nor a email address
* added support for local `b:goobookprg` to vim-goobook
* added editorconfig integration to dotfiles/vim

# 2018-10-20
* improved vim-syntax for .plan files
* fixed `aadbook authenticate` bug where the comamnd could fail when initializing the Auth object, leaving the user with anything to do but wipe everything out

? don't advance mutt cursor when deleting/archiving threads

# 2018-10-14
* finished setting up GPG w/ Mutt!!

It's been a long journey, but I finally did it.  At first I tried setting up all the pgp_* mutt commands to verify/sign/encrypt/decrypt, but while encrpyting seemed to work, when I tried to decrypt my own messages (i.e. message I had sent to my own address) Mutt errored out complaining that GPG was not able to decrypt the message -- note that manually running the `gpg` command on a copy of the message worked without any issues.  Anyway, I finally gave up and went on and added `set crypt_use_gpgme` to my .muttrc (had to re-install mutt, with `--with-gpgme` flag).

Few related (and interesting) articles:

  Gnu PGP Intro: https://www.gnupg.org/gph/en/manual.html#INTRO
  Getting Started with PGP and GPG: https://medium.com/@johnnyRose/getting-started-with-pgp-and-gpg-58c8d6c35df5
  Simpler GnuPG & Mutt configuration with GPGME: https://henrytodd.org/notes/2014/simpler-gnupg-mutt-config-with-gpgme/
  GPG / Mutt / Gmail: https://gist.github.com/bnagy/8914f712f689cc01c267

# 2018-10-11
* disabled caching of html|css files on matteolandi.net

# 2018-10-10
* finished reading [Masters of Doom: How Two Guys Created an Empire and Transformed Pop Culture
](https://www.amazon.com/Masters-Doom-Created-Transformed-Culture/dp/0812972155)

# 2018-10-09
* added personal GPG key to matteolandi.net
* added fuzzy-finding support to aadbook by replacing each whitespace ` ` with `.*`
* added fuzzy-finding support to goobook

# 2018-10-08
* fix s format on matteolandi.net sitemap

# 2018-10-07
* yet another matteolandi.net restyle -- ripped of css rules from: http://bettermotherfuckingwebsite.com/
* automatic generation of sitemap.xml

# 2018-10-06
* cleaned up mail related scripts (personal/work)
* added .plan page to matteolandi.net
* images on matteolandi.net are no more cropped on mobile

# 2018-10-05
* set up a .plain file, some vim scripts, and a bash script to manage it

+ add a `logfilter` page on matteolandi.net
? fix mutt 'indicator' colors taking over 'index' ones!
| I want index rules to be more important than indicator one: https://unix.stackexchange.com/questions/138325/mutt-how-to-display-selected-new-messages-differently-in-index

? pin specific gmail/microsoft certificates
| http://www.offlineimap.org/doc/FAQ.html#does-offlineimap-verify-ssl-certificates

+ PGP mutt
| https://www.linuxjournal.com/content/encrypt-your-dog-mutt-and-gpg
| https://medium.com/@johnnyRose/getting-started-with-pgp-and-gpg-58c8d6c35df5

? live notification fetcher plugin on matteolandi.net

# 2018-10-03
* make matteolandi.net mobile friendly

# 2018-10-02
* add aadbook page to matteolandi.net

# 2018-10-01
* aadbook: fix soft-authentication issue

# 2018-09-29
* vim-goobook completion works even on lines starting with spaces/tabs

# 2018-09-28
* make vim-goobook platform independent

# 2018-09-26
* fix certbot on matteolandi.net

# 2018-09-24
* finished reading [Ghost in the Wires: My Adventures as the World's Most Wanted Hacker](https://www.amazon.com/Ghost-Wires-Adventures-Worlds-Wanted/dp/0316037729)

# 2018-09-21
* reimplement vim-goobook filtering logic in pure vimscript

# 2018-09-16
* aadbook: always regenerate tokens when invoking 'authenticate'
* aadbook: don't refresh auth token, unless expired

# 2018-09-14
* finished reading [When Google Met WikiLeaks](https://www.goodreads.com/book/show/21839908-when-google-met-wikileaks)

# 2018-09-09
* finished reading [The Art of Invisibility: The World's Most Famous Hacker Teaches You How to Be Safe in the Age of Big Brother and Big Data](https://www.amazon.com/Art-Invisibility-Worlds-Teaches-Brother/dp/0316380504)

# 2018-09-08
* first aadbook release!
* fork vim-goobook and add support for specifying a custom program (e.g. aadbook)

# 2018-09-07
* reinstalled docker-ce on Ubuntu

https://docs.docker.com/install/linux/docker-ce/ubuntu/

# 2018-08-29

A tale of Sinon, Chai, and stubs

Imports:

    const chai = require('chai');
    const expect = chai.expect;
    const sinon = require('sinon');
    const sinonChai = require('sinon-chai');
    chai.should();
    chai.use(sinonChai);

Then inside the tests:

    expect(mock.search).to.have.been.calledOnce;
    expect(mock.search.firstCall).to.have.been.calledWith(args);

# 2018-08-26
* finished reading [No Place to Hide: Edward Snowden, the NSA, and the U.S. Surveillance State](https://www.amazon.com/No-Place-Hide-Snowden-Surveillance-ebook/dp/B00E0CZX0G)
* setting up Mutt on Windows

Cannot find mutt-patched, so let's go with the default

    apt-cyg mutt

It turns out, default mutt now ships with the sidebar patch -- yay!!!

Manually installed installed `offlineimap` using Cygwin

    apt-cyg install offlineimap

+ build offlineimap from sources

SSL certificates on Cygwin are put inside a different directory, so I had to customize files a little to point to the right path

+ create a script to inspect the disk and symlink certificates in a known location (e.g. ~/.certs)

Used python's [keyring](https://pypi.org/project/keyring/) library to handle secrets (it work on MacOS too!)

urlview is not available on Cygwin; I tried with [urlscan](https://pypi.org/project/urlscan/) for a couple of days but ended up with manually building urlview from sources:

    git clone ...
    apt-cyg install \
        autoconf \
        automake \
        gettext \
        libcurses-devel
    ./configure
    autoreconf -vfi
    make

Created `~/bin/urlview` script delegating to `~/opt/urlview/urlview` (location of my local urlview repo); created `~/.urlview` containing:

    COMMAND br

Luckily for me, msmpt is available on Cygwin, so a simple `apt-cyg install ...` did the trick

# 2018-08-25
* force merge to keep feature branches in the history

This [post](http://www.boekhoff.info/how-to-keep-feature-branches-in-the-git-history/)
does a good job at explaining how to keep feature branches in the git history
(i.e. forgin Git to always create a merge commit).

In a nutshell, to disable _fast-forward_ merges completely:

     git config --global --add merge.ff false

While to disable them just for `master`:

    git config --global branch.master.mergeoptions  "--no-ff"

# 2018-08-20
* re-learned how to release on pypi

Make sure your `~/.pypirc` file is current:


    [distutils]
    index-servers:
        testpypi
        pypi

    [testpypi]
    repository: https://test.pypi.org/legacy/
    username: landimatte

    [pypi]
    repository: https://upload.pypi.org/legacy/
    username: landimatte

Clean up the `dist/` directory:

    rm -rf dist/*

Build the source distribution package:

    python setup.py sdist

Release to the *test* instance of Pypi, and check everything is in place:

    twine upload dist/* -r testpypi

Release to the *public* instance of Pypi:

    twine upload dist/* -r pypi

# 2018-08-19
* finished reading [Cypherpunks: Freedom and the Future of the Internet](https://www.amazon.com/Cypherpunks-Freedom-Internet-Julian-Assange/dp/1944869085)

# 2018-08-01
* init'd new work windows box -- and addes stuff to my-env's README

# 2018-07-12
* fucking fixed weird Cygwin/SSH username issues

Symptoms: cygwin `USER` and `USERNAME` env variables are different[0]:

    $ set | egrep "USER=|USERNAME="
    USER=IONTRADING+User(1234)
    USERNAME=mlandi

Where does the value of USER come from?  Take a look in '/etc/profile'. You
will find the following:

    # Set the user id
    USER="$(/usr/bin/id -un)"

`id` gets it's data from /etc/passwd (which, if not there, you might have to
create with the following command[1]):

    mkpasswd -c > /etc/passwd

You will have to kill all the cygwin processes to see this working.

https://superuser.com/questions/783183/cygwin-user-and-username-are-different
https://stackoverflow.com/questions/19613654/rename-change-cygwin-username

# 2018-07-05
* finished reading [Modern Vim: Craft Your Development Environment with Vim 8 and Neovim](https://www.amazon.com/Modern-Vim-Development-Environment-Neovim/dp/168050262X)

# 2018-06-05
* finished reading [The Senior Software Engineer](https://www.amazon.com/Senior-Software-Engineer-Practices-Effective/dp/0990702804)
* make `appcelerator` work again

Unset `_JAVA_OPTS` as scripts are too fragile and the `javac` command is not properly parsed

    ~/Library/Application Support/Titanium/mobilesdk/osx/7.2.0.GA/node_modules/node-appc/lib/jdk.js

Added log messages because of uncaught exceptions

# 2018-06-03
* learned how to take a peek at a repo's remote

Fetch incoming changes:

    git fetch

Diff `HEAD` with `FETCH_HEAD`:

    git diff HEAD..FETCH_HEAD

If the project had a changelog, you could just diff that:

    git diff HEAD..FETCH_HEAD -- some/path/CHANGELOG

Bring changes in:

    git pull --ff

# 2018-01-08
* finished reading [Never Split the Difference: Negotiating As If Your Life Depended On It](https://www.amazon.com/Never-Split-Difference-Negotiating-Depended-ebook/dp/B014DUR7L2)

# 2017-12-27
* fucking fixed MacOS not going to sleep!

As of OS X 10.12, the nfsd process now holds a persistent assertion preventing
sleep. This is mostly a good thing, as it lets NFS work on networks where
wake-on-lan packets don't work. However, this means that when plugged in, OS
X will no longer automatically sleep. If you unload the daemon, sleep works
fine, and I noticed that vagrant starts nfsd if required on vagrant up.

This is now worse with 10.12.4 as a call to nfsd stop is no longer supported
when system integrity protection is turned on. Thus the only way to let the
machine sleep is to prune /etc/exports and restart nfsd. It would be nice to at
least add a cli command to have vagrant prune /etc/exports (so we can script in
when we want nfs pruned) or even better have it automatically prune exports on
suspend and halt and re-add on wake and up.

For this I created the following, and added it to my .bashrc:

```
function fucking-kill-nfsd() {
    # https://github.com/hashicorp/vagrant/issues/8103
    sudo sh -c "> /etc/exports"
    sudo nfsd restart
}
```

[Source](https://github.com/hashicorp/vagrant/issues/8103)

# 2017-12-21
* fix symbolic links on Windows/cygwin

Define the following environment variable:

    CYGWIN=winsymlinks:nativestrict

https://stackoverflow.com/questions/18654162/enable-native-ntfs-symbolic-links-for-cygwin/18659632#18659632

# 2017-12-20
* learned how to clone repos subdirectories

This can be done by using *sparse checkouts* (available in Git since 1.7.0):

    mkdir 
    cd 
    git init
    git remote add -f origin 

This creates an empty repository with your remote, and fetches all objects but
doesn't check them out.  Then do:

    git config core.sparseCheckout true

Now you need to define which files/folders you want to actually check out. This
is done by listing them in `.git/info/sparse-checkout`, eg:

    echo "some/dir/" >> .git/info/sparse-checkout
    echo "another/sub/tree" >> .git/info/sparse-checkout

Last but not least, update your empty repo with the state from the remote:

    git pull origin master

To undo this instead:

    echo "/*" > .git/info/sparse-checkout
    git read-tree -mu HEAD
    git config core.sparseCheckout false

https://stackoverflow.com/a/13738951/348524
https://stackoverflow.com/a/36195275/348524

# 2017-12-15
* fixed key-repeat timings on Mac OS

We would do this by using Karabiner/Karabiner-Elements, but that option is long gone, so we need to do that by using `defaults`:

    defaults write NSGlobalDomain InitialKeyRepeat -int 11
    defaults write NSGlobalDomain KeyRepeat -float 1.3

In particular, the above sets:

- Delay until repeat: 183 ms
- Key repeat: 21 ms

https://github.com/tekezo/Karabiner-Elements/issues/551#issuecomment-277987194

# 2017-12-12
* fixed slow bash login shells on Mac OS

Unlink old (and slow) bash-completion scripts:

    brew unlink bash-completion

Install bash-completion for bash4:

    brew install bash-completion@2

Change '.bash\_profile' to source the new bash\_completion files

    if [ -f $(brew --prefix)/share/bash-completion/bash_completion ]; then
        . $(brew --prefix)/share/bash-completion/bash_completion
    fi

Finally, make sure your default `bash` shell is the +4 one:

    sudo bash -c 'echo /usr/local/bin/bash >> /etc/shells'
    chsh -s /usr/local/bin/bash

https://troymccall.com/better-bash-4--completions-on-osx/
https://stackoverflow.com/a/44293632/348524

# 2017-11-26
* finished reading [Don't Make Me Think, Revisited: A Common Sense Approach to Web Usability (Voices That Matter)](https://www.amazon.com/Dont-Make-Think-Revisited-Usability/dp/0321965515)

# 2017-10-21
* Installed Common Lisp on Mac OS

    brew install roswell # https://github.com/roswell/roswell
    ros install sbcl/1.3.13

# 2017-10-13
* finished reading [Programming JavaScript Applications: Robust Web Architecture with Node, HTML5, and Modern JS Libraries](https://www.amazon.com/Programming-JavaScript-Applications-Architecture-Libraries/dp/1491950293)

# 2017-09-26
* finished reading [Practical Vim: Edit Text at the Speed of Thought](https://www.amazon.com/Practical-Vim-Edit-Speed-Thought-ebook/dp/B018T6ZVPK)

# 2017-08-27
* edit markdown files with live-reload

Install `markserv`:

    npm install markserv -g

Move into the folder containing any markdown file you would like to preview, and run:

    markserv

# 2017-08-08
* web resource caching

Manually adding headers via express or anything:

```
'Cache-Control': 'public, max-age=31536000' // allow files to be cached for a year
```

Nginx configuration:

```
    location /static {
      alias /site;

      location ~* \.(html)$ {
        add_header Last-Modified $date_gmt;
        add_header Cache-Control 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0';
        if_modified_since off;
        expires off;
        etag off;
      }
    }
```

# 2017-07-25

Me of the future, do enjoy this!

    " Dear /bin/bash: fuck you and your bullshit, arcane command-line behaviour.
    "
    " Basically, I want to set this to a non-login, non-interactive bash shell.
    " Using a login/interactive bash as Vim's 'shell' breaks subtle things, like
    " ack.vim's command-line argument parsing. However, I *do* want bash to load
    " ~/.bash_profile so my aliases get loaded and such.
    "
    " You might think you could do this with the --init-file command line option,
    " which is used to specify an init file. Or with --rcfile. But no, those only
    " get loaded for interactive/login shells.
    "
    " So how do we tell bash to source a goddamned file when it loads? With an
    " *environment variable*. Jesus, you have multiple command line options for
    " specifying files to load and none of them work?
    "
    " Computers are bullshit.
    let $BASH_ENV = "$HOME/.bashrc"

# 2017-04-26
* finished reading [To Sell is Human: The Surprising Truth About Persuading, Convincing, and Influencing Others](https://www.amazon.com/Sell-Human-Surprising-Perusading-Influencing-ebook/dp/B009OINFH4)

# 2017-02-19
* finished reading [Designing with the Mind in Mind: Simple Guide to Understanding User Interface Design Guidelines](https://www.amazon.com/Designing-Mind-Understanding-Interface-Guidelines/dp/0124079148)

# 2017-01-18
* finished reading [Drive: The Surprising Truth About What Motivates Us](https://www.amazon.com/Drive-Surprising-Truth-About-Motivates/dp/1594484805)

# 2017-01-08
* finished reading [The Everything Store: Jeff Bezos and the Age of Amazon](https://www.amazon.com/Everything-Store-Jeff-Bezos-Amazon-ebook/dp/B00BWQW73E)

# 2017-01-06
* downloaded all my intagram photos

- Load your profile page on the browser, and scroll to the bottom until you reached the end of your stream.
- Open the developer console, right click on the  element, copy it, and dump it somewhere
- Run the following command: `lynx -dump -listonly -image_links ../foo.html | grep cdninstagram | - 640x640 | awk '{print $2}' | xargs -n 1 -P8 wget`

# 2017-01-03

Found this nice hack to quickly get the average color of an image

    img2 = img.resize((1, 1))
    color = img2.getpixel((0, 0))

https://www.hackzine.org/getting-average-image-color-from-python.html

# 2016-12-31
* figured out a way to always update vbguest add-ons: `vagrant plugin install vagrant-vbguest` (http://kvz.io/blog/2013/01/16/vagrant-tip-keep-virtualbox-guest-additions-in-sync/)

Also, you could instruct Vagrant to automatically install required dependencies with the following:
```
required_plugins = %w( vagrant-hostmanager vagrant-someotherplugin )
required_plugins.each do |plugin|
   system "vagrant plugin install #{plugin}" unless Vagrant.has_plugin? plugin
end
```
Source: http://stackoverflow.com/a/25918153/348524

# 2016-12-28
* found a way to debug why a node app refuses to grecefully shut-down

```
// as early as possible
const wtf = require('wtfnode');
setInterval(function testTimer() {
  wtf.dump();
}, 5000);
```

# 2016-12-26
* successfully built vim, from sources

    sudo apt-get install -y \
        liblua5.1-0-dev \
        libluajit-5.1-dev \
        lua5.1 \
        luajit \
        python-dev \
        ruby-dev

    sudo ln -s /usr/include/lua5.1 /usr/include/lua5.1/include

    cd ~/opt
    git clone https://github.com/vim/vim
    cd vim

    ./configure \
            --with-features=huge \
            --enable-multibyte \
            --enable-largefile \
            --disable-netbeans \
            --enable-rubyinterp=yes \
            --enable-pythoninterp=yes \
            --enable-luainterp=yes \
            --with-lua-prefix=/usr/include/lua5.1 \
            --with-luajit \
            --enable-cscope \
            --enable-fail-if-missing \
            --prefix=/usr
    make

# 2016-09-16

Run the last history command (like `!!`):

    fc -s

Run last command without appending it to the history:

    HISTFILE=/dev/null fc -s

# 2016-09-14

Move pushed commit to a different branch

    git checkout $destination-branch
    git cherry-pick $change-done-on-the-wrong-branch
    git push
    git checkout $origin-branch
    git revert $change-done-on-the-wrong-branch

Related reads
- http://christoph.ruegg.name/blog/git-howto-revert-a-commit-already-pushed-to-a-remote-reposit.html
- http://stackoverflow.com/a/9180445

# 2016-09-13

Analyze angular performance:

```
angular.element(document.querySelector('[ng-app]')).injector().invoke(['$rootScope', function($rootScope) { var a = performance.now(); $rootScope.$apply(); console.log(performance.now()-a); }])
```

or:

```
angular.element(document.querySelector('[ng-app]')).injector().invoke(['$rootScope', function($rootScope) {
  var i;
  var results = []
  for (i = 0; i < 100; i++) {
    var a = performance.now();
    $rootScope.$apply();
    results.push(performance.now()-a);
  }
  console.log(JSON.stringify(results));
}])
```

# 2016-09-12
* finished reading [The Startup Playbook: Secrets of the Fastest-Growing Startups from their Founding Entrepreneurs](https://www.amazon.com/Startup-Playbook-Fastest-Growing-Startups-Entrepreneurs/dp/1452105049)

# 2016-08-21

lvh.me is a domain that resolves to localhost, and it works with subdomains too (xxx.lvh.me would still resolve to localhsot).  Why is this helpful? Because you could use it when developing applications that need subdomain configurations.

https://reinteractive.net/posts/199-developing-and-testing-rails-applications-with-subdomains

# 2015-11-05
* finished reading [Clean Code: A Handbook of Agile Software Craftsmanship](https://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882)

# 2015-09-03
* finished reading [Zero to One: Notes on Start Ups, or How to Build the Future Kindle Edition](https://www.amazon.com/Zero-One-Notes-Startups-Future/dp/0804139296)

# 2015-09-07
* finished reading [The Pragmatic Programmer: From Journeyman to Master](https://www.amazon.com/Pragmatic-Programmer-Journeyman-Master/dp/020161622X)

# 2015-08-01
* finished reading [The Lean Startup: How Today's Entrepreneurs Use Continuous Innovation to Create Radically Successful Businesses](https://www.amazon.com/Lean-Startup-Entrepreneurs-Continuous-Innovation/dp/0307887898)

# 2013-10-01
* finished reading [Java Concurrency in Practice](https://www.amazon.com/Java-Concurrency-Practice-CONCURRENCY-PRACT-ebook/dp/B004V9OA84)

# 2013-02-01
* finished reading [Python Cookbook](https://www.amazon.com/Python-Cookbook-Third-David-Beazley/dp/1449340377)

# 2012-11-03
* finished reading [Effective Programming: More Than Writing Code](https://www.amazon.com/Effective-Programming-More-Than-Writing-ebook/dp/B008HUMTO0)

# 2012-10-24
* finished reading [Simplify](https://www.amazon.com/Simplify-Joshua-Becker-ebook/dp/B006431ADS)

# 2012-09-12
* finished reading [Design Patterns: Elements of Reusable Object-Oriented Software](https://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)

# 2012-08-23
* finished reading [Vim and Vi Tips: Essential Vim and Vi Editor Skills](https://www.amazon.com/Vi-Tips-Essential-Editor-Skills/dp/8360869006)

# 2012-08-17
* finished reading [Functional Programming for Java Developers: Tools for Better Concurrency, Abstraction, and Agility](https://www.amazon.com/Functional-Programming-Java-Developers-Concurrency/dp/1449311032)

# 2012-08-10
* finished reading [Effective Java (2nd Edition)](https://www.amazon.com/Effective-Java-2nd-Joshua-Bloch/dp/0321356683)

# 2011-06-15
* finished reading [Growing Object-Oriented Software, Guided by Tests](https://www.amazon.com/Growing-Object-Oriented-Software-Guided-Tests/dp/0321503627)

# 2011-04-02
* finished reading [Getting Real: The Smarter, Faster, Easier Way to Build a Successful Web Application](https://www.amazon.com/Getting-Real-Smarter-Successful-Application/dp/0578012812)

# 2011-03-14
* finished reading [Learning the vi and Vim Editors: Text Processing at Maximum Speed and Power](https://www.amazon.com/Learning-Vim-Editors-Processing-Maximum-ebook/dp/B005EI85BE)

# 2011-01-29
* finished reading [Rework](https://www.amazon.com/Rework-Jason-Fried/dp/0307463745)