L'Olimpohttps://thecrowned.org/2023-09-30T00:00:00+02:00I write code and technical documentation. And rants.<br><br>Technical Writer<br>Software EngineerThe differences between junior and senior software engineers2023-09-30T00:00:00+02:002023-09-30T00:00:00+02:00Stefano Ottolenghitag:thecrowned.org,2023-09-30:/junior-vs-senior-engineers<p>How can you tell whether you are dealing with a junior or a senior engineer, when it comes to software? And if you are looking to advance your career, how can you <em>become</em> senior? What …</p><p>How can you tell whether you are dealing with a junior or a senior engineer, when it comes to software? And if you are looking to advance your career, how can you <em>become</em> senior? What are the distinctive traits you should be looking for?</p>
<div class="toc"><span class="toctitle">Table of Contents</span><ul>
<li><a href="#juniors-vs-seniors">Juniors vs Seniors</a><ul>
<li><a href="#not-just-surfacing-issues-but-also-finding-the-best-solution">Not just surfacing issues, but also finding the best solution</a></li>
<li><a href="#removing-instead-of-adding">Removing instead of adding</a></li>
<li><a href="#avoid-using-tech-to-solve-non-tech-problems">Avoid using tech to solve non tech problems</a></li>
<li><a href="#think-long-term">Think long term</a></li>
<li><a href="#can-comment-code">Can comment code</a></li>
</ul>
</li>
<li><a href="#how-to-become-senior">How to become senior</a><ul>
<li><a href="#read-other-peoples-code-study-it-and-question-it">Read other people's code, study it and question it</a></li>
<li><a href="#print-code-out">Print code out</a></li>
<li><a href="#come-up-with-several-solutions-and-compare-them">Come up with several solutions, and compare them</a></li>
<li><a href="#iterate-over-your-code">Iterate over your code</a></li>
</ul>
</li>
</ul>
</div>
<h2 id="juniors-vs-seniors">Juniors vs Seniors</h2>
<h3 id="not-just-surfacing-issues-but-also-finding-the-best-solution">Not just surfacing issues, but also finding the best solution</h3>
<p>This is the <em>single</em> skill that makes you senior. Decent software engineers should be able to spot issues before they blow up and make themselves apparent to the whole company, but the way you address those issues tells a lot about your level. <strong>The <em>quality</em> of your solutions makes you senior</strong>.</p>
<h3 id="removing-instead-of-adding">Removing instead of adding</h3>
<p>Juniors are eager to write <em>a lot</em> of code. Their metric is quantitative: if I push a lot of commits, with a lot of code, I have achieved a lot. To them, there is no value in a more elegant solution: all that matters is <em>a</em> solution.</p>
<p>Seniors are wary of adding code, and instead seek to <em>remove</em> it. If they can fix the issue by taking away a bunch of code, they will happily do so. They understand that any codebase is complex already, littered by chunks left by people who thought "I'm just gonna get this fixed real quick and then come back to polish it appropriately" and never did so, that everything they write needs to be maintaned through the years and has the potential of turning into the very bloat they have to wade through every day.</p>
<h3 id="avoid-using-tech-to-solve-non-tech-problems">Avoid using tech to solve non tech problems</h3>
<p>Juniors have a narrow view: issues in your code are solved with <em>more code</em>. The answer to <em>any</em> problem comes from the skill they are most versed in.
For an analogy, imagine having foot pain when walking:</p>
<ul>
<li>a shoe dealer thinks you need better shoes</li>
<li>a surgeon thinks you need surgery</li>
<li>a homeopathy practitioner thinks you need natural remedies</li>
<li>a PT thinks you need to train your feet.</li>
</ul>
<p>Everybody sees the problem from the narrow perspective of their expertise, but none of them stops to investigate and discover that, maybe, you are sitting too much and need to walk <em>more</em>.</p>
<p>While seniors understand that it's hard to be <em>experts</em> in many fields, they are eager to expand their knowledge to surrounding fields, with the assumption that that's the way to achieve a more holistic view of their own field.</p>
<p>Seniors understand what the solution to a code issue is not always <em>more code</em>. Maybe you can twist your deploy process so that the issue vanishes? Maybe what looked like a Very Smart Solution ® can now be simplified to reduce its complexity, and resize the issue you are facing? Maybe the documentation is not clear, and the customer is using the product in a wrong way?</p>
<h3 id="think-long-term">Think long term</h3>
<p>Juniors write their code, deploy it, and go on with their lives. The moment the code leaves their hands, it's not their problem anymore. They are happy to build a cathedral of code that people will look up to, and think <em>how smart!</em>.</p>
<p>Seniors think like they are going to be there in 10 years, having to troubleshoot issues and implement new features in the codebase. They reason like they have only themselves to blame, and are thus diligent in keeping things <em>simple</em> (rather than <em>smart</em>), and ensuring anybody with an appropriate background is able to pick up their work (because guess what, in 10 years they <em>themselves</em> are going to be those people).</p>
<p>I think it's quite hard to learn this if not <em>the hard way</em>. I find myself in the enviable position of having worked on the same project for more than a decade: the first version of my free WordPress plugin <a href="https://postpaycounter.com/">Post Pay Counter</a> dates back to 2011. More than 10 years have gone past and, more than a dozen addons later, I am certain that I would not be where I am were it not for that. At the same time, you don't achieve seniority by <em>only</em> working long term on a <em>single</em> project. You need to branch out at some point, or you become like a thief who is very good with padlocks from one brand, but helpless with all others.</p>
<h3 id="can-comment-code">Can comment code</h3>
<p>Juniors <em>decorate</em> their code with comments, similarly to the way they write unit tests (does the function return an <em>int</em>?). They often use comments either in places that are self-explanatory, or that <em>should</em> be self-explanatory (but aren't due to poor naming/needlessly convoluted behavior). They act like those novice film producers who think it's funny to interleave their movies with "ironic" remarks that they themselves make while pretending to watch the movie in an empty lounge. Something like "well, that hurt, didn't it [grin]" after the main character falls down a cliff.</p>
<div class="highlight"><pre><span></span><code><span class="c1"># parse result</span>
<span class="n">parse_result_frmt</span><span class="p">(</span><span class="n">records</span><span class="p">,</span> <span class="n">result</span><span class="p">)</span>
</code></pre></div>
<p>Seniors document the overall flow, the <em>choices</em>, and the <em>reasons</em> why something looks odd.</p>
<div class="highlight"><pre><span></span><code><span class="c1"># open but not closed parenthesis are for matching calls with parameters</span>
<span class="n">exclude_functions</span> <span class="o">=</span> <span class="p">[</span>
<span class="s1">'date()'</span><span class="p">,</span> <span class="s1">'date.transaction('</span><span class="p">,</span> <span class="s1">'date.statement('</span><span class="p">,</span> <span class="s1">'date.realtime('</span><span class="p">,</span>
</code></pre></div>
<h2 id="how-to-become-senior">How to become senior</h2>
<p>To a large extent, <em>acting</em> as a senior will <em>make</em> you a senior, at some point. There's practical steps you can take to accelerate the process though.</p>
<h3 id="read-other-peoples-code-study-it-and-question-it">Read other people's code, study it and question it</h3>
<p>To learn to write high-quality code, you need to expose yourself to high-quality code. You can't know if a codebase is high-quality upfront, but even exposing yourself to any code is helpful. But don't just <em>look at it</em> – question it, ask yourself how you would have written code that serves the same purpose. Would have it been better? Worse? Under what metrics? Learn to find flaws in other people's code in the same way mathematicians look for flaws in your arguments.</p>
<p>Most importantly, you have to learn to read and work with code that was not written by <em>you</em>, in the same way as you must be able to read a book in English that explains idea that don't come from <em>you</em>. Look at others' codes, praise them, shout at them, accept them, edit them, tear them to pieces ... but learn to deal with them.</p>
<h3 id="print-code-out">Print code out</h3>
<p>When I started working with <a href="https://github.com/Automattic/Co-Authors-Plus/">Co-Authors Plus</a> during my time at Automattic VIP, the codebase was obscure and messy in my head. You start debugging an issue and then jump from function to function, only keeping in your head the flow needed to debug that one issue. But I wasn't retaining much, I was gaining ~5% proficiency with each troubleshooting. It would have taken me ~15 troubleshootings to become decently proficient with the codebase. Then I wondered: what if I could hop there <em>directly</em>, at 80% proficiency?</p>
<p>And I printed out the codebase.</p>
<p>On physical <em>paper</em>.</p>
<p>Color, with syntax highlighting. Font size ~10.</p>
<p>I turned off the computer, took a pencil and colored markers, and it went like a breeze.</p>
<p>Suddenly I was there, knowing how all the code fit together and how each change would have rippled throughout. And then I started rewriting large chunks of the codebase with an understanding that would have taken me painful days to build just by flipping tabs in my editor.</p>
<p>To this day, I still print out my codebase when I have issues I can't easily troubleshoot, or when I have to do a major refactoring, or when I start collaborating on a project that is already built.</p>
<h3 id="come-up-with-several-solutions-and-compare-them">Come up with several solutions, and compare them</h3>
<p>Next time you get a problem, come up with a solution. Then, before you implement it (because you do plan on paper before churning out code, <em>don't you</em>?), think of <em>another</em> solution. A different approach, either smarter or dumber. Compare them. List pros and cons of each, and argue for both. Come with a third if you can.</p>
<p>It's hard to develop a feeling for the <em>best</em> solution if you jump all-in as soon as you have an idea – you need to experiment with many ideas.</p>
<h3 id="iterate-over-your-code">Iterate over your code</h3>
<p>Next time you write a piece of code, leave it until the day after, then come back and improve it. Accept that you won't be able to churn out the <em>best</em> version of a function at your first try. Come back, change the variables names, think about the empty lines, the order of conditionals, the number of statements. Dissect your piece of code and improve it in any way you possibly can.</p>
<p>This is an example bit from the book <code>Crafting interpreters</code>, one that likely didn't come out as is at the first attempt.</p>
<div class="highlight"><pre><span></span><code><span class="k">static</span><span class="w"> </span><span class="n">Entry</span><span class="o">*</span><span class="w"> </span><span class="nf">findEntry</span><span class="p">(</span><span class="n">Entry</span><span class="o">*</span><span class="w"> </span><span class="n">entries</span><span class="p">,</span><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">capacity</span><span class="p">,</span><span class="w"> </span><span class="n">ObjString</span><span class="o">*</span><span class="w"> </span><span class="n">key</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="kt">uint32_t</span><span class="w"> </span><span class="n">index</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">key</span><span class="o">-></span><span class="n">hash</span><span class="w"> </span><span class="o">%</span><span class="w"> </span><span class="n">capacity</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="p">(;;)</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="n">Entry</span><span class="o">*</span><span class="w"> </span><span class="n">entry</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="o">&</span><span class="n">entries</span><span class="p">[</span><span class="n">index</span><span class="p">];</span><span class="w"></span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">entry</span><span class="o">-></span><span class="n">key</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="n">key</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="n">entry</span><span class="o">-></span><span class="n">key</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="nb">NULL</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">entry</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="w"> </span><span class="n">index</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="n">index</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span><span class="w"> </span><span class="o">%</span><span class="w"> </span><span class="n">capacity</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>Neural networks, Professor Trelawney, and ping pong smashers: all the issues with artificial intelligence2023-08-17T00:00:00+02:002023-08-17T00:00:00+02:00Stefano Ottolenghitag:thecrowned.org,2023-08-17:/trends-of-aiThe true AI lesson is: the moment you start optimizing for a metric, the players will start using it to game the system.<p>This is me.</p>
<p><img alt="stefano cartoon" src="/images/stefano-cartoon.jpg"></p>
<p>Or rather: this is what an online service promising to turn a picture of me into a cartoon illustration <em>has made me into</em>. The result is what the most conflict-shy of employees would define a <em>promising result</em> — to me, it's not only insufficient, but also a display of all that is wrong with AI. It's not just about the fact that this AI has added <em>snow</em> to a summer picture, and translated me into the Shangai timezone. <strong>The problem is with us, as a community, accepting this as a passable, potentially promising result.</strong> I could dismiss it as a very low-quality implementation, but it's not: instead, it is <em>flawed</em> at its core, and that makes me deeply worried about the state of things, and about how AIs are being deployed all around us.</p>
<p>I have three main points to share here, leading to a grand conclusion. By the end of this post, you will know why I am so concerned about the status of things, and about our future direction. Let's embark on this journey.</p>
<h2 id="first-act-ai-encourages-intellectual-laziness">First act — AI encourages intellectual laziness</h2>
<p>There's a certain practice in the industry today: if you get a problem that you can't solve, throw an AI at it. Jeez, even if you think you <em>can</em> solve the problem algorithmically, just throw an AI at it anyway -- investors will like it better. Explicit algorithms are out of fashion, AI is cool and promising and scores so much better than any human solution. We have plenty of data, plenty of business people, plenty of math tools. It doesn't matter that the data is messy, the business people are rushed, and that nobody cares to look into the math: just get your local geek to vomit the data into some neural net and marvel at the 62% success score. Allow the geek to pick the programming language of his choice, and everybody is happy. You may be <em>extra lucky</em> and even get to participate in discussions about activation functions (and who wouldn't want <em>that</em>?).</p>
<p>To stretch it to the limit, why even bother inventing sorting algorithms when you can take the millions of already sorted lists, feed it into some model with the electricity consumption of the Netherlands, and have it spit out 87% accurately-sorted wild lists? It's a no-brainer. Except that if we stopped <em>to think</em> for a second (or for half a decade, which is the time it takes to bake good ideas), we'd discover that there <em>is</em> an algorithm that can sort lists with infallible 100% accuracy, and if we stopped to think even <em>more</em> we'd discover it is the theoretical best we can achieve. It's called QuickSort.</p>
<p>In general, I see more and more projects being lured by the futuristic promise of High Accuracy™ coming from AI models, and fewer and fewer legacy, out-of-fashion explicit algorithms that pushed me into computer science in the first place. Let's just look at a few examples from recent projects.</p>
<h3 id="judging-ballet-lightness">Judging ballet <em>lightness</em></h3>
<p>The problem is: I give you a bunch of ballet recordings, where a poor creature contorts for 10 seconds each, together with scores from oblivious judges saying whether the ballet depicts <code>lightness</code> or <code>fragilility</code>. Then I give you a bunch of unclassified 10-seconds contortions, and you need to assess their <code>weight</code>: are they <code>light</code>, or are they <code>fragile</code>?</p>
<p><img alt="ballet lightness" src="/images/ballet-lightness.gif"></p>
<p>Any reasonable problem-solver would push back with questions. Can we get some definition of <code>lightness</code> and <code>fragility</code>? What do the judges based their scoring on? Can <em>they</em> give us some definitions? And any <em>unreasonable</em> business man, because of what he has read about AI on the papers, will sneer back with "it's the task of the AI to figure it out". Which is a way of saying: "I don't <em>know</em> what we are doing, but I want it <em>done</em>".</p>
<p>Some colleagues of mine rushed to create beasts of neural networks with tens of hidden layers and thousands of input neurons, creating a maze that a guide dog couldn't have navigated, and then randomly experimented with activation functions, generated extra metadata from the input files, applied filters. They were happy when some attempt increased the accuracy score, sad otherwise. What a thrilling life.</p>
<p>On the opposite side, I started by <em>looking</em> at ballet videos (what a hookup sentence in a gay bar, by the way). I wondered: "why is this one <code>light</code> and this other one <code>fragile</code>?" I felt like it had something to do with sharp movements and sudden variations, so I figured I could take the baricenter and track how much it was moving across frames. It worked decently, not worse than my peers' neural net, and I had ideas about further <em>conceptual</em> improvements. My peers, meanwhile, could only talk about more layers, more data, <em>more</em> nonsense. Which management obviously valued, given its perceived complexity.</p>
<h3 id="assigning-tags-to-content">Assigning tags to content</h3>
<p>I give you a text and a list of possible tags: can you give me the three tags from the list that are <em>most relevant</em> for the content?</p>
<p>These days we'd just throw an AI at it. Classical. BAck in my teen years I only knew rudimentary methods but hey, they turned out to work pretty well. Essentially, <a href="/method-for-pattern-relevance-in-text">I came up</a> with a very flexible regex and a custom expression that decreased the match score based on how many words came up in between each bit of the tag's constituents. Given its explicity, I could also take in punctuation and, for example, exclude matches where the constituents were broken up by a full stop.</p>
<h3 id="predicting-time-series">Predicting time series</h3>
<p>This comes from a friend. We studied together, and she knew I was into AI, so she pinged me to get a Very Smart Solution™. The problem is: there are some commodities which have a price tag attached to it. The price of each commodity changes over time, but we don't know <em>when</em> the change is happening in advance, we only know it's gonna happen at some point. I give you the history of prices, can you tell me in advance <em>when</em> each product is gonna have its price changed?</p>
<p>I boiled it down to forecasting the next element of a numeric sequence, and she agreed. Note how much thought we had already put into this: remapping the prediction not on price, but on the time distance between two price changes for a product. Something for our teachers to be proud of. And forecasting the next element of a sequence is a <em>hard problem</em>.</p>
<p>And then just after I explained what an <em>art</em> inferring the behavior of a numeric sequence really is, she told me about her attempts with AI to tackle it, because... well, she couldn't crack it without. Unsurprisingly, the results were poor. And the reason is that it's not a <em>tacklable</em> problem: too broad, too vague, too hard. The right word here is <em>divine</em>, not <em>predict</em> (never mind they are synonyms). Throwing Professor Trelawney at it may be more effective than throwing an AI at it, but would management approve of it?</p>
<p><img alt="professor trelawney" src="/images/professor-trelawney.jpg"></p>
<p>When we get problems that we don't know how to attack, we should double, triple think about what we should say. What I like to say is "this is a very interesting problem! I'll give it a thought, but as of now I can't think of a smart solution". Then <a href="https://thecrowned.org/deform-an-unstructured-mesh-or-how-a-good-angle-dramatically-simplifies-a-problem">sometimes you'll find a Very Smart Solution™</a>, and sometimes you'll have to say "go to Professor Trelawney, she is your best bet". Throw your bets wits at it, throw somebody else's best wits at it, and then accept that weather forecast for 20 days ahead is just that: divination. <strong>Throwing an AI onto a problem you can't crack exhibits a combination of hopelesness and laziness</strong>, a bit like calling an exorcist to address a bathroom leak that the plumber couldn't fix.</p>
<p>I've even been in academic environments where people suggested using AI to make climate change-related predictions (sea level rise, glacier melting, etc). At best, these people don't understand anything about AI (but all their research is pointless anyway, as they are tuning vital coefficients on historical data that has virtually no current validity).</p>
<p>If there is no explicit algorithm at all, why should AI do better? It is alluring to think that <em>we</em> haven't figured it out, but the AI <em>will</em>. Except it almost never <em>does</em>. And when it fails to live up to the expectations, there is always an excuse, that management very much likes because it is <em>actionable</em>: let's enlarge the dataset; let's diversify the dataset; let's change activation function; let's increase the number of hidden layers. Even just out of random <em>luck</em>, some action <em>will</em> improve the score by a few percent, and you can keep yourself eternally busy by tweaking your model and showing that "look! it gets better and better!". The end result, however, is always a <em>dancing bear</em> — remarkable, because the bear <em>dances</em>, but the dance itself is nothing anybody would pay a ticket for.</p>
<h2 id="second-act-theres-no-learning-in-machine-learning-and-no-intelligence-in-artificial-intelligence">Second act — There's no <em>learning</em> in machine learning, and no <em>intelligence</em> in artificial intelligence</h2>
<p>But AI has its own ways, people will say. <em>Artificial</em> intelligence doesn't work in the same way as <em>human</em> intelligence does. It learns its <em>own</em> patterns. The question is rarely raised: <em>the fuck can we do with patterns that are not human?</em> If I ask you something, I expect you to reply with an answer that conforms to <em>my</em> mental patterns. <a href="https://www.aiweirdness.com/what-does-this-say/">If you reply with something that is correct for <em>your</em> parameters, but wrong for <em>mine</em></a>, you are <em>useless</em> to me.</p>
<p><img alt="ChaGPT ascii art" src="/images/chatgpt-ascii-art.png"></p>
<p>Our state-of-the-art AI models act in a very similar way to middle school kids, tring to figure out what to say to best <em>please</em> the teacher and extort a high mark. They are the same, except that they never grow up. They optimize for one and one metric only: they want to be <em>right</em>. And they have no problem in cheating to get there. We show them a million pictures of cats and non-cats, and we tell them "you're gonna get a good grade if you say <em>cat</em> when there is a cat". And they find some way to make us happy and maximize their reward. It doesn't matter if, in the end, they <em>actually</em> recognize living rooms; it doesn't matter that they would not be able to link the <code>meow</code> sound to cats; it doesn't matter that they have no idea of what the feeling of stroking their fur is. Nothing matters: only the narrow, concrete, single metric the AI optimizes for.</p>
<blockquote>
<h3 id="the-true-ai-lesson-is-the-moment-you-start-optimizing-for-a-metric-the-players-will-start-using-it-to-game-the-system"><em>The true AI lesson is: the moment you start optimizing for a metric, the players will start using it to game the system.</em></h3>
</blockquote>
<p>To be fair, we do this all the time in human work ever since the sciency/positivistic attitude that "everything can be measured" has started to bite us. We want to reward something broad, and we nail it down to concrete, simple metrics to make it pleasingly simply quantitative, and to make it tacklable by machines. For example, say we want to ensure that <em>employees are contributing value to an organization</em>, and that we want to reward those that do, and fire those that don't. To make it measurable, we could say we're gonna keep track of:</p>
<ul>
<li>how many Trello cards each person gets done -> we encourage people to create cards for the smallest of tasks, rewarding those that do meta-work rather than actual work</li>
<li>how many git commits each person pushes -> we get people splitting their work in pointlessly small chunks</li>
<li>how many changes each person's git commits actually carry -> we get people fixing whitespace, changing variable names, etc.</li>
</ul>
<p>Any of the metrics above would be inappropriate, alone, to evaluate people's performance. The crucial point (and the irony) is that those are exactly the ones most likely to surface if we give an AI the job of assessing people's performance, because of course there is a correlation between good employees and git contributions. However, it's only a very narrow piece of the puzzle. The problem is that evaluating the weight of a person's contribution is a complicated, polyedric task. We are evaluating a <em>human's contribution</em>, which can come in many ways, and it requires such a <em>broad</em> evaluation that it's hard to pick up as a <em>pattern</em> in <em>historical</em> data.</p>
<p>Loss functions in neural networks basically optimize for "I want to be right" with respect to the training set, and they will take all possible shortcuts to achieve the best score. Let's not forget about the <a href="https://towardsdatascience.com/is-the-medias-reluctance-to-admit-ai-s-weaknesses-putting-us-at-risk-c355728e9028">skin cancer detection AI</a> that learnt to detect rulers rather than tumors just because cancer pictures come with a ruler for scale.</p>
<p>Finally, the fact that the same model trained on two different datasets results in completely different <em>incarnations</em> of an AI, is yet another proof that whatever these models pick up is not knowledge, nor understanding, nor anything <em>intelligent</em>. <a href="https://basicincometoday.com/opinion-changing-my-mind-about-ai-universal-basic-income-and-the-value-of-data/">As Vi Hart put it in an article I cannot recommend enough</a>,</p>
<blockquote>
<p>Data isn't like a battery you can switch out to make an algorithm run; it is an essential, central component. Data cannot be an afterthought, data cannot be ignored. It doesn't matter if you can write a GAN from scratch in Haskell or compute a gradient descent iteration by hand with pencil and paper. A neural net run on Mozart has Mozart at its core, and to understand the results you need to know Mozart. If you're using ImageNet, you'd best be prepared for a lot of dogs. The AI isn't just "looking at" tens of thousands of dog photos, the AI <em>is</em> dog photos. The data is the fabric, and the code is just the stitching.</p>
</blockquote>
<p>Train an AI on dataset A; train another AI on dataset B. Ask A to produce a picture of a dog, show it to B: it will say it is an octopus. Reverse the roles, and A will label the B-dog as a ventriloquist. Then train two more AIs on the two datasets, match them appropriately with the other twos, and have them interact: they will talk profusely about dogs, without ever slipping. It's like if a Caltech CS graduate had implemented QuickSort and an MIT CS graduate would come and say "Neat implementation of depth-first search!". Would you say that the two graduates have "learnt in their own way", or wouldn't you point one of them to <em>waiters</em> job ads?</p>
<p><img alt="Bard ascii art" src="/images/bard-ascii-art.png"></p>
<p>And even <em>more</em> finally, given that the AI <em>is</em> the data, what further progress will there ever be, if we lock ourselves in the past?</p>
<h2 id="third-act-we-should-not-delegate-to-entities-we-dont-understand">Third act — We should not delegate to entities we don't understand</h2>
<p>I will concede that modern AIs are capable of remarkable feats — jeez, they can <em>generate</em> portaits of <em>whatever</em> person you ask, as they would have been painted by <em>defunct</em> artists. I can certainly <em>not</em> say that there is an explicit algorithm to do that. The algorithm doesn't know there's <em>nipples</em> under the dress it painted, but it certainly produced something outstanding.</p>
<p><img alt="tocatlian as a painting by rembrandt" src="/images/tocatlian_as_a_painting_by_rembrandt.webp"></p>
<p>Actually, we have even <em>no idea</em>, since <strong>modern AI is super mega proprietary</strong>. That, regardless of what you think of open source, is so, so <em>wrong</em> in light of the previous point. When the best AI to recognize cats will become the de-facto digital definition of <em>cat</em>, and the dataset will be kept secret, nobody will have the tools to create other AIs that can speak with it. We'll all be locked into that single implementation, for which its creator will be able to bill whatever they want.</p>
<p>Anyway, the main point here is that we have no clue of how these methods work. This applies not only to painting AIs, but also to less modern AIs, like neural nets: <strong>nobody understands AI, and we shouldn't delegate tasks to opaque entities</strong>. When two humans disagree ... they can have a chat. When two AIs disagree ... what do they do? And when a human and an AI disagree, what do <em>we</em> do? What happens if an AI recommends surgery and a doctor does not? Will we question the AI, or the doctor? The doctor can explain <em>why</em> he has come to his conclusion, but the AI? There's no psychologist for AIs (the first guy to become such is in for a fortune), so how do we get a peek into his thought flow?</p>
<p>I asked myself why it is that I value understanding so much. After all, I myself don't <em>understand</em> skin cancer. I don't <em>understand</em> how a car engine works, or how it's built. But the point is that <em>there exists</em> somebody who <em>does</em>. My mechanic understands car engines, and that gives him the power to fix mine. My doctor understands cancer, which gives him power to cure mine. If neither my mechanic nor my doctor understand their shit, there is <em>somebody</em>, <em>somewhere</em>, who does. AI, instead, is a different story. There is no AI, <em>anywhere</em>, who understands its domain. And because their "learning" is narrow and focused on extremely specific bits, there will never be, which makes me <em>a tad</em> uncomfortable about using AI <em>at all</em>, for <em>any</em> task.</p>
<p>Now. Since I said that modern AIs are definitely capable of remarkable feats, should we not use it in those realms? In theory, I have nothing against using AI to generate unique pictures for a presentation, or to compose a pathetic jingle, or whatever other innocuous purpose Bob may decide to waste the few carbon fossils leftovers to train his model. I'm sure it's gonna make our stay on this planet so much more worth it. But there's always going to be a John that writes a newspaper article about AI having reached oustanding levels ("it can <em>paint</em>!") and a David who owns a medical institute and who will push for AI in healthcare so he can get rid of half his workforce. It's always a slippery slope, and what feels innocuous can spiral out of control. Our current life is filled with things we don't like, that we are stuck with, and that had a very <em>innocuous</em> beginning.</p>
<!-- For example, having health insurance paid by your employer in the US (i.e. private healthcare) is a consequence of the government being worried about inflation after WW1, and thus strongly depressing wages after the war ended. As an afterthought, they passed a tax-exemption on healthcare insurance, so that companies rushed to offer it as a perk to employees. (bad example?) -->
<p>In general, a lack of understanding makes us <em>vulnerable</em> (think of your 80-year-old grandma who doesn't understand tech and has a smartphone cluttered with cleaning apps and antivirus and betting apps), so AI <em>are</em> vulnerable. Deploying to sensitive/important fields something vulnerable makes us <em>all</em> vulnerable. It creates an infrastructure that we don't understand and have no control over. It's certainly not going to <em>take over</em>, but we are going to wrestle with it in every little thing — it's going to become a hinder to our lives.</p>
<p>But also: why do we <em>need</em> to use AI, if we can do without with much simpler methods? Do we really need AIs for filtering spam emails? Given that Gmail's spam filter still lets through some trash and filters out legitimate emails, I reckon we could do a decent enough job with a bunch of hardcoded criterias, if we could get the time to hone them through the years, as the AI has had. Do we need AI to filter spam traffic, or can we do with a 3-failed-logins=lockout criteria?</p>
<p>And yet, the largest share of developers <a href="https://www.jetbrains.com/lp/devecosystem-2022/">identified AI/ML as the most prosiming technology in 2022</a>. What is it that you <em>need</em> it for? Is it uncracklable problems? Is it problems that have a decent explicit approach? In both cases, don't touch it even with a stick!</p>
<h3 id="the-danger-of-ai-is-ping-pong-smashers">The danger of AI is ping pong <em>smashers</em></h3>
<p><img alt="timo boll ping pong" src="/images/timo-boll.jpg"></p>
<p>So if the AI tech is <em>so so</em> prosiming, shall we really <em>not</em> use it? And that's the whole point: I don't find this tech so promising. Because of how narrow and stupid AI is, I don't think it will ever cross a threshold were it becomes <em>useful</em> for mortals.</p>
<p>I regard current AI models as ping pong smashers: impressive if you don't play ping pong, but disappointing if you know the game. Smashers tend to win a fair lot of games at the beginning, and impress their friends with the power of their disjointed shoulder, but their rise to success halts as soon as they meet opponents that play <em>spin</em> at them, or short-low balls. Smashers are then at an impasse: the weapon that got them there doesn't seem to do anything against these sort of players. They will try to perfection their smashing technique, and eventually they will succeed in smashing a few balls with spin, or a few short balls, but never enough to win a game against the <em>proper players</em>. At that point, it's a rubber wall. <strong>The only way they can keep advancing is by going back</strong> and learning to play <em>proper</em> ping pong, or be doomed to sub-mediocrity.</p>
<p><strong>Today's AI models are smashers.</strong> They optimize for narrow metrics and try to game the system in order to maximize their reward. They smash all the time, but they always disappoint when confronted with a human that knows his shit. And instead of sending the AI back to ping pong 101 and learning spin, we put all our efforts in perfecting its smashing technique. <em>If only it could stretch its arm 1% more, it could smash balls 1% shorter.</em> Except it's no effort for a proper player to leave balls yet 1% <em>more</em> shorter, so that the AI struggles again. Instead, it needs to learn new shots, instead of trying to force its success with failing ones. <strong>AI models need to be re-engineered in a different way, in the same way as ping pong smashers need to re-learn to play, properly this time.</strong> As Lito Tejada-Flores said in <a href="https://www.youtube.com/watch?v=CaoA8uUZHQU">Breakthrough on skis</a>, "The movements experts use are not the same the intermediates do. They are just <em>different</em>."</p>
<p>You take <em>any</em> AI model, and it's been blatantly broken. <a href="https://scholar.google.it/citations?view_op=view_citation&hl=en&user=83ZcXXwAAAAJ&citation_for_view=83ZcXXwAAAAJ:RYcK_YlVTxYC">Adversarial attacks on malware recognitition neural networks</a>. <a href="https://arstechnica.com/information-technology/2023/07/why-ai-detectors-think-the-us-constitution-was-written-by-ai/">AI detectors thinking the US constitution was written by AI</a>. There's just about for everything.</p>
<p>One more time from <a href="https://basicincometoday.com/opinion-changing-my-mind-about-ai-universal-basic-income-and-the-value-of-data/">Vi Hart</a>:</p>
<blockquote>
<p>When I listen to music “compositions” generated by AI, I can hear the collaged and combined bits from their data sources. The average listener might not hear beyond “yes, this definitely sounds like a music,” and be quite impressed because they are told an AI created it.</p>
<p>In other cases, the listening experience is like reading a Markov chain of text. I am not surprised the AI could copy words and mash them into locally plausible sets, but it makes no overall sense. Meanwhile to those less fluent in music, I imagine it’s like looking at a Markov chain in another language. If you don’t speak French and are told here’s some impressive creative French written by an AI, it probably looks convincing enough.</p>
<p>The point, though, is that for music and other art-based AI it is hard to ignore the fact that attentive, valuable, human work went into creating the corpus the AI draws from. Without the existing fruit of human creativity, we couldn’t write programs to copy it. Unfortunately, like pre-teens exclaiming “I could do that!” when being introduced to the work of Jackson Pollock or John Cage, the industry applauds shallow copies with no recognition of the physical or conceptual labor that went into their source.</p>
</blockquote>
<p>And, you know, I hust don't want my doctor to be a smasher: I want it to be a proper player. But I fear smashers will take over, if we don't oppose this movement now. It may already be too late.</p>
<p>In his book <a href="https://en.wikipedia.org/wiki/The_Computer_and_the_Brain"><em>The Computer and the Brain</em></a>, John von Neumann philosophized that the human brain works with processes and languages that are much less mathematical in nature than what we have architected computers to work with:</p>
<blockquote>
<p>Math may be a seconday language, built on the primary language truly used by the central nervous system.</p>
</blockquote>
<p>And now, we're trying to use that secondary language to build entities that should mimick the primary one. It just doesn't feel right.</p>
<h2 id="curtain-and-a-bis-on-the-need-for-regulation"><em>Curtain</em>, and a bis on the need for regulation</h2>
<p>I like to regard myself as a Very Smart Man™, but in reality I'm certainly not the first to raise concerns over the trends of AI. I haven't read anywhere quite the same points I have made here, but there's a profusion of opinions everywhere. Including claims that it's The Regulators © who should do something about this. Ban ChatGPT (Italy did), ask for a pause (Elon Musk): whatever it is, we, as always, expect action from the top. And I am oh-so-much-looking-forward to see what the gargantuan army of computer illiterate burecrauts are going to bless us with this time. I'm so much looking forward to see another cookies-situation that has made the Web further inusable, and changed <em>nothing</em> in the fact that, well, companies track every thing you do online because nobody is going to give up, you know, <em>business</em>.</p>
<p><img alt="cookies banners" src="/images/cookies-banners.jpg"></p>
<p>Asking for regulators to step up is refusing to address the problem on our side, and <em>we</em> are the side that generated it. Elon Musk is an OpenAI founder. We value over-engineered opaque neural networks over explicit algorithms in computer science classes. And regulators are just <em>encouraging</em> this mindset, <a href="https://research-and-innovation.ec.europa.eu/research-area/industrial-research-and-innovation/key-enabling-technologies/artificial-intelligence-ai_en">giving away money</a> in exchange for the promise of... yeah, what promise even?</p>
<blockquote>
<p>Well-coordinated use of AI can bring about significant improvements to society. It can help us reach climate and sustainability goals and will bring high-impact innovations in healthcare, education, transport, industry and many other sectors</p>
</blockquote>
<p>You know what would help us reach climate and sustainability goals? Not <a href="https://www.bloomberg.com/news/articles/2023-03-09/how-much-energy-do-ai-and-chatgpt-use-no-one-knows-for-sure">using terawatt of electricity</a> in pointless "artificial intelligence". <br />
Innovations in healthcare = fewer doctors. <br />
Innovations in education = fewer teachers. <br />
Innovations in transport = fewer drivers. <br />
Innovations in industry = fewer workers. <br />
Innovation = cutting costs, that's all it is. Let's be glaringly honest about it, and call it for what it is.</p>
<blockquote>
<p>Another relative measure comes from Google, where researchers found that artificial intelligence made up 10 to 15% of the company's total electricity consumption, which was 18.3 terawatt hours in 2021. That would mean that Google's AI burns around 2.3 terawatt hours annually, about as much electricity each year as all the homes in a city the size of Atlanta.</p>
</blockquote>
<p>The moment I, in my work, throw an AI at a problem I can't crack, I'm guilty of this. <br />
The moment I, in my PhD, talk about how I can harness the powers of AI to address ice melting, I'm guilty of this. <br />
The moment I, as a regulator, offer money for people to be <em>lazy</em> instead of working on explicit algorithms, OR when I encourage them to promise me they can solve a problem they can't crack, I'm guilty of this. <br />
The moment I, as an academic, take that money knowing AI won't fix any of those issues, I'm guilty of it. <br />
The moment I, as a software developer, suggest that we should use AI to summarize the content of our documentation, suggest the next page to read, and by good measures write new pages, I'm guilty of this.</p>
<p><em>We are all guilty of this.</em></p>
<p>I feel some shame in thinking that, one day, I'm gonna tell my children about that age when we used terawatts of precious power to summarize pages of digital vomit. While they drink their <em>one</em> glass of water per day.</p>
<p>What can regulators do? They have no clue of what their money is used for, and we all embellish what we do so it doesn't look like we have wasted their money.</p>
<p>The power is ours, as information workers: to create, to destroy, and to limit.</p>
<h2 id="a-note-on-explainable-ai">A note on Explainable AI</h2>
<p>Every time I have a conversation with "experts" on the topic (let alone that 50% of the "experts" doesn't even know how gradient descent works – they probably think that <em>gradient</em> is a synonym for <em>gradual</em>), they most often agree with my concerns, and then bring up <em>Explainable AI</em>. "It's going to solve all your concerns", they say.</p>
<p>What I gathered from my research is that <strong>explainable AI is not much more than explicit algorithms</strong>. How delightful to discover.</p>
<p>For example, IBM's <a href="http://aix360.mybluemix.net/explanation_cust">Explainable 360</a> is basically tuning a set of ridicolously simple statistical models, one for each different feature they want to include in the model. We are talking of <em>regression models</em> here, which is what gets taught in Statistics I. <a href="https://nbviewer.org/github/IBM/AIX360/blob/master/examples/tutorials/HELOC.ipynb#rule-based-models">They just have a bunch of thresholds</a>, which they have extrapolated from some dataset and that they can evolve in time as more data comes in. It's blazingly simple, and there's also no "AI" involved. It's the most ancient way of smushing together a dataset, and for a good reason: it's coarse. But it's also much closer to what I would do if I were tasked with the problem, because it is much closer to an explicit algorithm than to AI. They still won thousands of dollars for devising "Explainable AI".</p>We won – What does the world look like in 2030?2023-07-12T00:00:00+02:002023-07-12T00:00:00+02:00Stefano Ottolenghitag:thecrowned.org,2023-07-12:/year-2030<p>I close my eyes for a second, and under the buzz of voices around me, I hear birds singing, squirrels chasing each other scratching the bark of trees, branches rustling in the wind. A couple …</p><p>I close my eyes for a second, and under the buzz of voices around me, I hear birds singing, squirrels chasing each other scratching the bark of trees, branches rustling in the wind. A couple times every hour I hear a rumble: an ambulance, a moving van, a car rented by someone who needs to move around things or people that it is hard to move otherwise. But since when private motorised transport has vanished, 2030 is a <em>quiet</em> time. We have stopped owning means of transportation not really because public transportation has become phenomenal (although yes: public transport + 10 minutes on foot do get you anywhere), but because we do not often need to go farther than what we can reach on foot, by bike, or by skates anymore. We have moved to a location where we feel in peace: close a to a thai take away, a cinema, and a pizzeria if we wanted the buzz of the city; close to (or deep into) the woods if that's what gives us breath. We all have the privilege of coming back home without feeling like we need to flee somewhere else.</p>
<p>It happened slowly, and if anything it's been one of the very few positive consequences of Covid-19. We started working from home during the pandemic, and we noticed that in many ways that was so much better than the office. Companies tried at some point to <em>ask</em> that we would regularly show up at the office, but those that got to the point of <em>requiring</em> it ended up facing resignation of the most talented and soon <em>allowed</em> working mostly from home. At that point, proximity to the office was not that important anymore, and we have been able to move where we felt at rest. Even if remoteness is still mostly a white collars' privilege, the working week has been reduced to 25 hours for everybody, and it is expected that every candidate is able to speak of themselves and their hobbies during a job interview.</p>
<hr>
<p>In 2030 we all have utmost respect for nature, in all forms. We look at big trees from below as guardians protecting us, with their majestic trunks and branches interleaved like dream catchers; small trees from above as children to protect; flowers as marvellous expressions of colour.</p>
<p>And hand in hand with a more harmonious relationship with nature, we have also discovered a much more positive relationship with our bodies. Something shocking by 2020 standards is that everybody swims <em>naked</em>, everybody sunbathes <em>naked</em>. Not only because it is <em>allowed</em>, but especially because nobody is ashamed of it, and because we have realised that there is <em>no another way</em>: that as much as we shower without clothes, it makes <em>no</em> sense to have something on to swim. The direct consequence is that the expensive-plastic-genital-coverings-bacteria-breeders industry went awash. Nowadays Wikipedia page states that <em>"it was a multi-billion industry based on hiding the secret that roughly half of the swimmer has the same sort of genitals, the other half the other sort"</em>. Naturist beaches do not exist anymore – or rather, all beaches are such, because going to the beach with a swimsuit attracts the same astonished looks which going there naked in 2020 did. There are no more missed opportunities of getting to a beautiful beach and not being able to take a dip because we forgot our swimsuit, and the ridiculous ballet of holding a towel to swap fabric underwear with other plastic underwear is also over. The world is just a simpler place.</p>
<hr>
<p>An amazing fact is that in 2030 we do not carry <a href="/i-quit-smartphones">computers in our pockets</a> anymore. The turning point came in 2027, when smartphones were equated to cigarettes, and their sale accordingly regulated. From that year, by law all smartphones have to be sold with a compulsory screen saver saying <em>"using the smartphone makes you dumb"</em>, and the back of any case <em>"science says that using smartphones and socials creates anxiety, lack of confidence, and loss of focus</em>". It has taken a while, but we have also realised that we just did not need them: that we can look up the route before leaving home, and worst case ask for directions to passersby or bus drivers; that there's no need to read the news or a book from a 6" screen, because bad news make their way to us anyway, and good ones are worth savouring; that there's no need to send holiday pictures straightaway, nor to constantly text each other; that there's no need to be constantly <em>entertained</em>. Most places are now <em>smartphone-free</em>, and there are apt spaces where its usage is allowed, in the same way as <em>smokers areas</em>. We have understood that, as smoke pollutes our lungs, technology pollutes our brains.</p>
<p>It has taken us a huge effort to disentangle us from the technology we had wrapped ourselves into – the road is still long, but if anything today it is forbidden to sell smartphones and tablets to anybody below the age of 16, it must be possible to use any public service through analogue means, and there is a promising policy under discussion that would forbid advertisements as a business model to private companies. This would really be revolutionary, because it dominos on discouraging companies from breaching our privacy and harvesting as much data on each of us, to then sell our profiles to advertisers. It would also strike a big blow to consumerism, which would not hurt. The consequence is also that there is much less work in IT, and a lot of people have had to reinvent themselves, was it just growing potatoes and knitting.</p>
<p>We have been able (at least in part) to avoid predictions of Wall-E, those that seemed so inevitable in 2025. Now that smartphone usage is decreasing and entertainment is much more <em>intentional</em>, there are much fewer people going around with headphones without uttering a word, creating that dreadful atmosphere that we used to witness on the underground of big cities in the first years of 2020. It happens more and more often that a child points and makes an inappropriate remark, that two strangers exchange comments on their day ... even that a love blossoms from a glance and a smile. The screens cast a bit to the side, we have started to re-nurture our social relations, and to give them the importance they deserve.</p>
<hr>
<p>In 2030 there are a lot of people going around in outfits that under 2020 standards we would associate with the <em>homeless</em>: today, having socks with holes is <em>normal</em>, while having them mismatched (but whole) is <em>rich</em>. And then unstitched backpacks, scratched glasses or glasses with a single arm, frayed sweaters, pullovers with elbows worn out and already mended twice, tshirts so threadbare that you can almost see through them. The reason is surely that people have gained confidence and we have managed to make mainstream the idea that substance matters more than form. Mostly though, there has come about a general awareness that things cost – not money, but <em>resources</em>, and that thus all artefacts must be used until they <em>melt</em>. It is now very clear to us that resources are <em>finite</em>, and that we must treat them as the water from our well: when they are over, we are dead.</p>
<p>We have become much more careful especially after the 2029 green scandal, when we realised that the long-awaited carbon capture plants are very expensive tons of scrap metal. We are working to decommission the ones built in the early 2020s and plant the equivalent in trees in our daily environments: they cost little, have little to no maintenance, and broaden our breath. There has been a strong opposition from the green lobby, because environmentalism was the largest legal criminal market until a few years ago. In the end, for once, politics raised its voice though, and we got out whole – not really because parliament has become a idyllic wise man hangout (we are not <em>that</em> advanced), but because it had to cave to the outrageous public remonstrances of the last few years. Countries were basically stopped for months: large amounts of people worked as usual for their fellows, but stopped paying taxes. That turned out to be a very effective recipe.</p>
<p>We have also learnt to save energy as it got more expensive, already in 2021. Drinking from a glass and directly putting it in the dishwasher is today more barbarian than littering. We have reduced to a third the number of monthly laundries and also the number of hours spent at the TV: there is quite some social stigma towards people who entertain themselves too much in passive activities, and people simply spend more hours in nature, crocheting, reading, writing, listening. Physical proximity to the things we like and the much reduced transportation has had a huge impact on reducing consumption.</p>
<hr>
<p>On teachers pressure, the education system has been recently flipped: now we allow students to design their own curriculum, and we contribute by helping them to find mentors in the activities they are interested into. We force them also to get exposed to other topics though, and they acknowledge that we do it for <em>their</em> sake and not for <em>ours</em>.</p>
<p>Broadly, we again hold in high esteem education: of those that put effort into learning (for real, not just to pass some tests) and of those that put effort into teaching (for real, not just handing out high marks to avoid trouble with parents). We value people wholly as they are: as unique individuals.</p>
<hr>
<p>An extraordinary fact of 2030 is that every morning, all couples, flatmates, families, hug each other communicating each other that physical proximity is their energy, and come back home to hug again and prepare dinner, bake bread, pizza, and remind themselves that is what matters: <a href="/life-adagio">living <em>adagio</em></a>. Everything else is just a side.</p>Abbiamo vinto – Che faccia avrebbe il mondo nel 2030?2023-07-12T00:00:00+02:002023-07-12T00:00:00+02:00Stefano Ottolenghitag:thecrowned.org,2023-07-12:/it/anno-2030<p>Chiudo gli occhi per un attimo, e sotto l'intermittente brusio di voci intorno a me, sento degli uccelli cinguettare, degli scoiattoli rincorrersi raspando la corteccia degli alberi, le fronde che stormiscono quando si alza il …</p><p>Chiudo gli occhi per un attimo, e sotto l'intermittente brusio di voci intorno a me, sento degli uccelli cinguettare, degli scoiattoli rincorrersi raspando la corteccia degli alberi, le fronde che stormiscono quando si alza il vento. Un paio di volte all'ora passa un rombo: un'ambulanza, un furgone dei traslochi, una macchina in affitto per chi deve spostare cose o persone che è difficile spostare in altro modo. Ma da quando il trasporto privato a motore è scomparso, il 2030 è un tempo <em>silenzioso</em>. Abbiamo smesso di avere mezzi propri non tanto perchè il trasporto pubblico sia divenuto eccezionale (anche se sì: i mezzi pubblici + 10 minuti di camminata consentono di raggiungere qualunque luogo), ma perchè non abbiamo più così tanto bisogno di andare più lontano di quanto possiamo raggiungere a piedi, in bici, o in pattini. Ci siamo trasferiti in un luogo in cui ci sentiamo in pace: vicino a un take away thai, un cinema, e una pizzeria se volevamo il brusio della città; nelle vicinanze (o nel cuore) del bosco, se quello è ciò che ci dà respiro. Abbiamo tutti il privilegio di tornare a casa senza sentire il bisogno di fuggire altrove.</p>
<p>E' successo lentamente, e se non altro è stato uno dei pochissimi effetti positivi del Covid-19. Abbiamo iniziato a lavorare da casa durante la pandemia, e ci siamo resi conto che per molti versi era infinitamente meglio dell'ufficio. Le aziende a un certo punto hanno provato a <em>chiedere</em> che ci facessimo vedere regolarmente in ufficio, ma chi è arrivato al punto di <em>esigerlo</em> si è ritrovato a fronteggiare le dimissioni delle persone più talentuose e presto a <em>concedere</em> di lavorare per lo più da casa. A quel punto, la prossimità all'ufficio non è più così determinante, e abbiamo potuto trasferirci dove ci sentivamo in pace. Anche se quello della distanza è ancora per lo più un privilegio dei colletti bianchi, la settimana lavorativa è per tutti stata ridotta a 25 ore, e ci si aspetta che durante un colloquio di lavoro ogni candidato sia in grado di parlare di se stesso e dei suoi hobby.</p>
<hr>
<p>Nel 2030 abbiamo tutti un rispetto reverenziale per la natura, in ogni forma. Osserviamo gli alberi grandi da sotto in su come dei guardiani che ci proteggono, con i fusti maestosi e le fronde intrecciate come un acchiappasogni; gli alberi giovani dall'alto in basso come dei figli da proteggere; i fiori come meravigliose espressioni di colore.</p>
<p>E di pari passo con un rapporto armonioso con la natura, abbiamo anche maturato un rapporto molto più positivo con i nostri corpi. Una cosa scioccante dalla prospettiva del 2020 è che tutti fanno il bagno <em>nudi</em>, prendono il sole <em>nudi</em>. Non solo perchè è <em>consentito</em>, ma soprattutto perchè nessuno se ne vergogna, e anche perchè abbiamo capito che non c'è un <em>altro</em> modo: che come facciamo la doccia senza nulla addosso, non si capisce perchè dovremmo coprirci per fare il bagno. Di riflesso, è andata a bagno l'industria dei costosi coprigenitali di plastica che ospitano ricche colture batteriche. La pagina Wikipedia di oggi riporta che <em>"era un'industria multimiliardaria fondata sul nascondere il fatto che circa la metà dei bagnanti ha lo stesso tipo di genitali, l'altra metà l'altro tipo"</em>. Di conseguenza, le spiagge nudiste non esistono più – o meglio, tutte le spiagge lo sono, perchè andare in spiaggia con il costume attira gli stessi sguardi sbalorditi che nel 2020 attirava l'andarci nudi. Non ci sono più le occasioni perdute di arrivare a una spiaggia meravigliosa e non poter fare il bagno perchè non abbiamo portato il costume, ed è finito anche il ridicolo balletto del tenere l'asciugamano per cambiare le mutande di stoffa con altre mutande, di plastica. Il mondo è un posto più semplice.</p>
<hr>
<p>Un fatto straordinario è che nel 2030 non portiamo più computer in tasca. Il punto di svolta è venuto nel 2027, quando gli smartphone sono stati equiparati alle sigarette, e la loro vendita è stata regolata di conseguenza. In quell'anno, una legge stabilì che gli smartphone dovessero essere venduti con un salvaschermo obbligatorio che dice <em>"usare lo smartphone rende scemi"</em>, e il retro di ogni cover <em>"la scienza dice che l'uso dello smartphone e dei social rende ansiosi, insicuri, e incapaci di concentrarsi"</em>. C'è voluto un po', ma ci siamo anche resi conto che in fondo non ne avevamo bisogno e basta: che possiamo guardare il tragitto prima di uscire di casa, e che se ci perdiamo possiamo sempre chiedere ai passanti o all'autista del bus; che non c'è bisogno di leggere il giornale o un libro da uno schermo da 6 pollici, perchè tanto le brutte notizie ci arrivano comunque entro sera, e le buone vale la pena guastarsele; che non c'è bisogno di inviare foto delle vacanze sul momento, nè di messaggiarsi di continuo; che non c'è bisogno di essere <em>intrattenuti</em> tutto il tempo. La maggior parte dei luoghi sono ormai <em>smartphone-free</em>, e ci sono delle zone apposta in cui è consentito usarli, in modo simile alle <em>zone fumatori</em>. Abbiamo capito che, come le sigarette inquinano i polmoni, la tecnologia inquina il cervello.</p>
<p>Ci è servito un sacco di sforzo per sbrogliarci nella tecnologia in cui ci eravamo avviluppati – abbiamo ancora strada da fare, ma se non altro ad oggi è vietato vendere smartphone e tablet ai minori di 16 anni, tutti i servizi pubblici devono essere fruibili per via analogica, e c'è un promettente disegno di legge che vieterebbe la pubblicità come modello di business alle aziende private. Questa sarebbe una misura rivoluzionaria, perchè avrebbe un effetto domino che scoraggerebbe le azienda dall'invadere la privacy e raccogliere quanti più dati possibili su ciascuno di noi, per poi rivendere i nostri profili agli inserzionisti. Assesterebbe anche un bel colpo al consumismo, che male non fa. Di riflesso, c'è anche molto meno lavoro nell'IT, e un sacco di persone hanno dovuto trovare qualcos'altro da fare, fosse anche coltivare patate e lavorare a maglia.</p>
<p>Siamo anche (almeno in parte) riusciti a evitare le predizioni di Wall-E, quelle che sembravano così ineluttabili nel 2025. Ora che l'uso dello smartphone è in calo e l'intrattenimento molto più <em>intenzionale</em>, ci sono molte meno persone che girano con le cuffie e non proferiscono parola, creando quell'aria angosciante che si respirava sulla metropolitana delle grandi città nei primi anni 2020. Capita sempre più spesso che qualche bimbo indichi e faccia un commento inappropriato, che due sconosciuti si scambino commenti sulla giornata ... pure che qualche amore sbocci da uno sguardo e un sorriso. Messi un po' da parte gli schermi, abbiamo ricominciato a coltivare le nostre relazioni sociali, a dar loro l'importanza che meritano.</p>
<hr>
<p>Nel 2030 si vede tanta gente andare in giro in una <em>mise</em> che per gli standard del 2020 avremmo definito <em>da pezzente</em>: oggi, avere i calzini bucati è <em>normale</em>, mentre averli spagliati (ma interi) fa <em>ricco</em>. E poi zaini scuciti, occhiali rigati o con una stanghetta sola, felpe sfilacciate, maglioni coi gomiti consumati e già rammendati due volte, magliette così consunte che quasi ci si vede attraverso. Il motivo è di sicuro che le persone hanno guadagnato fiducia in se stesse e siamo riusciti a rendere di massa l'idea che conti di più la sostanza rispetto alla forma. Soprattutto però, si è sviluppata una consapevolezza che le cose constano – non soldi, ma <em>risorse</em>, e che quindi tutti i manufatti vanno usati finchè non si <em>sciolgono</em>. Abbiamo ora ben chiaro che le risorse non sono <em>infinite</em>, e che dobbiamo trattarle come l'acqua del nostro pozzo: quando finiscono, siamo morti.</p>
<p>Siamo diventati molto più attenti soprattutto dopo lo scandalo green del 2029, quando ci siamo resi conto che i tanto attesi impianti di carbon capture sono delle costosissime tonnellate di ferraglia. Stiamo lavorando per decommissionare i pochi impianti già costruiti nei primi anni 2020 e piantare l'equivalente in alberi nei nostri ambienti quotidiani; costano poco, hanno manutenzione quasi nulla, e allargano il respiro di tutti. C'è stata una forte opposizione della green lobby, perchè l'ambientalismo era il più grosso mercato criminale legale fino a qualche anno fa. Alla fine però, per una volta, la politica ha fatto la voce grossa e ne siamo usciti bene – non tanto perchè anche il parlamento sia diventata un idilliaco ritrovo di saggi (non siamo <em>così</em> evoluti), ma perchè ha dovuto cedere alle enormi proteste di piazza degli ultimi anni. Gli stati sono stati praticamente fermi per mesi: tantissime persone lavoravano come al solito per il loro prossimo, ma avevano smesso di pagare le tasse.</p>
<p>Abbiamo anche imparato a risparmiare elettricità man mano che diventava più costosa, già dal 2021. Bere in un bicchiere e metterlo subito a lavare è oggi più barbaro che buttare una cartaccia per terra. Abbiamo trimezzato il numero di lavatrici mensili e anche il numero di ore passate alla tv: c'è un certo stigma sociale nei confronti di chi si intrattiene troppo in attività passive, e passiamo semplicemente più tempo all'aperto, a ricamare, a leggere, a scrivere, ad ascoltare. La prossimità fisica alle attività che ci piacciono e il trasporto molto ridotto hanno molto contribuito a ridurre i consumi.</p>
<hr>
<p>Su pressione degli insegnanti, di recente il sistema scolastico è stato rovesciato: ora diamo agli alunni la possibilità di disegnarsi il proprio curriculum, e noi contribuiamo aiutandoli a trovare dei mentori nelle attività che li interessano. Li obblighiamo però anche ad esporsi ad altre attività, e loro riconoscono che lo facciamo per loro bene e non per un nostro tornaconto.</p>
<p>In generale, siamo ritornati a stimare il valore dell'istruzione: di chi si spende per imparare (per davvero, non solo per passare dei test) e di chi si spende per insegnare (per davvero, non dando voti alti per non avere grane con i genitori).</p>
<hr>
<p>Una cosa meravigliosa del 2030 è che ogni mattina, tutte le coppie, i coinquilini, le famiglie, si abbracciano e si comunicano che la prossimità fisica è la loro energia, e tornano a sera per abbracciarsi di nuovo e preparare la cena, fare il pane, la pizza, e si ricordano che questo è l'importante: <a href="/it/vita-adagio">vivere in adagio</a>. Tutto il resto è contorno.</p>The proof that Go channels aid concurrency2023-07-01T00:00:00+02:002023-07-01T00:00:00+02:00Stefano Ottolenghitag:thecrowned.org,2023-07-01:/go-channels-concurrency<p>All the buzz about Go being the best modern language to handle concurrency... When I rewrote the <a href="https://neo4j.com/docs/go-manual/">Neo4j Go Driver manual</a> I needed to get to the bottom of it. And indeed, there is some …</p><p>All the buzz about Go being the best modern language to handle concurrency... When I rewrote the <a href="https://neo4j.com/docs/go-manual/">Neo4j Go Driver manual</a> I needed to get to the bottom of it. And indeed, there is some truth. After going through <a href="https://go.dev/tour/concurrency/1">A Tour of Go – Concurrency</a>, I managed to stitch together the following example, where a bunch of routines concurrently process entries from a <em>single</em> channel. (The sender spawns a Goroutine, but it's not a requirement – it is here, because the channel is unbuffered and the sender would thus block until a receiver would start receiving; but if you'd make it buffered, then you wouldn't need a routine.)</p>
<div class="highlight"><pre><span></span><code><span class="kn">package</span><span class="w"> </span><span class="nx">main</span><span class="w"></span>
<span class="kn">import</span><span class="w"> </span><span class="p">(</span><span class="w"></span>
<span class="w"> </span><span class="s">"fmt"</span><span class="w"></span>
<span class="w"> </span><span class="s">"time"</span><span class="w"></span>
<span class="p">)</span><span class="w"></span>
<span class="c1">// Produce a channel streaming integers</span><span class="w"></span>
<span class="kd">func</span><span class="w"> </span><span class="nx">sender</span><span class="p">()</span><span class="w"> </span><span class="o"><-</span><span class="kd">chan</span><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="nx">out</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nb">make</span><span class="p">(</span><span class="kd">chan</span><span class="w"> </span><span class="kt">int</span><span class="p">)</span><span class="w"></span>
<span class="w"> </span><span class="nx">l</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="p">[]</span><span class="kt">int</span><span class="p">{</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="mi">5</span><span class="p">,</span><span class="mi">6</span><span class="p">,</span><span class="mi">7</span><span class="p">,</span><span class="mi">8</span><span class="p">,</span><span class="mi">9</span><span class="p">,</span><span class="mi">10</span><span class="p">}</span><span class="w"></span>
<span class="w"> </span><span class="k">go</span><span class="w"> </span><span class="kd">func</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="nx">_</span><span class="p">,</span><span class="w"> </span><span class="nx">entry</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="k">range</span><span class="w"> </span><span class="nx">l</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="nx">out</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="nx">entry</span><span class="w"></span>
<span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="w"> </span><span class="nb">close</span><span class="p">(</span><span class="nx">out</span><span class="p">)</span><span class="w"></span>
<span class="w"> </span><span class="p">}()</span><span class="w"></span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">out</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
<span class="c1">// Receivers listen on a channel and log on a separate one</span><span class="w"></span>
<span class="kd">func</span><span class="w"> </span><span class="nx">receiver</span><span class="p">(</span><span class="nx">c</span><span class="w"> </span><span class="o"><-</span><span class="kd">chan</span><span class="w"> </span><span class="kt">int</span><span class="p">,</span><span class="w"> </span><span class="nx">log</span><span class="w"> </span><span class="kd">chan</span><span class="w"> </span><span class="kt">string</span><span class="p">,</span><span class="w"> </span><span class="nx">n</span><span class="w"> </span><span class="kt">int</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="k">go</span><span class="w"> </span><span class="kd">func</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="nx">entry</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="k">range</span><span class="w"> </span><span class="nx">c</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="nx">log</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Sprintf</span><span class="p">(</span><span class="s">"Receiver %v processed %v"</span><span class="p">,</span><span class="w"> </span><span class="nx">n</span><span class="p">,</span><span class="w"> </span><span class="nx">entry</span><span class="p">)</span><span class="w"></span>
<span class="w"> </span><span class="nx">time</span><span class="p">.</span><span class="nx">Sleep</span><span class="p">(</span><span class="nx">time</span><span class="p">.</span><span class="nx">Duration</span><span class="p">(</span><span class="nx">n</span><span class="p">)</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="nx">time</span><span class="p">.</span><span class="nx">Second</span><span class="p">)</span><span class="w"></span>
<span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="w"> </span><span class="nb">close</span><span class="p">(</span><span class="nx">log</span><span class="p">)</span><span class="w"></span>
<span class="w"> </span><span class="p">}()</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
<span class="kd">func</span><span class="w"> </span><span class="nx">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="s">"Start"</span><span class="p">)</span><span class="w"></span>
<span class="w"> </span><span class="k">defer</span><span class="w"> </span><span class="nx">timer</span><span class="p">(</span><span class="s">"Main"</span><span class="p">)()</span><span class="w"></span>
<span class="w"> </span><span class="nx">source</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">sender</span><span class="p">()</span><span class="w"></span>
<span class="w"> </span><span class="nx">log</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nb">make</span><span class="p">(</span><span class="kd">chan</span><span class="w"> </span><span class="kt">string</span><span class="p">)</span><span class="w"></span>
<span class="w"> </span><span class="nx">receiver</span><span class="p">(</span><span class="nx">source</span><span class="p">,</span><span class="w"> </span><span class="nx">log</span><span class="p">,</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span><span class="w"> </span><span class="c1">// processes with a 1 second delay</span><span class="w"></span>
<span class="w"> </span><span class="nx">receiver</span><span class="p">(</span><span class="nx">source</span><span class="p">,</span><span class="w"> </span><span class="nx">log</span><span class="p">,</span><span class="w"> </span><span class="mi">2</span><span class="p">)</span><span class="w"> </span><span class="c1">// processes with a 2 seconds delay</span><span class="w"></span>
<span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="nx">entry</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="k">range</span><span class="w"> </span><span class="nx">log</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="nx">entry</span><span class="p">)</span><span class="w"></span>
<span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
<span class="c1">// Prints caller execution time</span><span class="w"></span>
<span class="kd">func</span><span class="w"> </span><span class="nx">timer</span><span class="p">(</span><span class="nx">name</span><span class="w"> </span><span class="kt">string</span><span class="p">)</span><span class="w"> </span><span class="kd">func</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="nx">start</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">time</span><span class="p">.</span><span class="nx">Now</span><span class="p">()</span><span class="w"></span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="kd">func</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Printf</span><span class="p">(</span><span class="s">"%s took %v\n"</span><span class="p">,</span><span class="w"> </span><span class="nx">name</span><span class="p">,</span><span class="w"> </span><span class="nx">time</span><span class="p">.</span><span class="nx">Since</span><span class="p">(</span><span class="nx">start</span><span class="p">))</span><span class="w"></span>
<span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>For a life in Adagio2023-07-01T00:00:00+02:002023-07-01T00:00:00+02:00Stefano Ottolenghitag:thecrowned.org,2023-07-01:/life-adagio<p>I long for the time to savor hugs, get lost in the scent of the other, of the wool they are wearing, of their body, without pats on the back signalling that it's time to …</p><p>I long for the time to savor hugs, get lost in the scent of the other, of the wool they are wearing, of their body, without pats on the back signalling that it's time to split. A warmth that tastes like home, soft, with closed eyes, is never enough. There should be a law stating that if you have not had at least one savored hug by 3pm, you are excused from work to go <em>look for it</em>.</p>
<p>The time to cook dinner, without easy take-aways. Baking bread, pizza, pasta. Fixing socks, sweaters; learning to knit to craft yourself a hat that warms your head with your effort and patience.</p>
<p>I would love us all to review our priorities: family, friends, and nature at the top; work down a the very bottom; and all material trinkets even more at the bottom, underground.
That we did not evangelize for a slower lifestyle and yet expect our trainee to be the first in the office and the last out. That each squabble, each meeting, each investment, each laundry, each grocery shopping came after human relations. That relations were our most valuable thing, and that we realized how much we can hurt our neighbours by just neglecting them. Above all, that those of us who say "I don't think nature is anything special" laid 15 minutes on the sidewalk of the freeway, without headphones, and decide if it is more of <em>that</em> which they want, or more of the pointlessly relaxing, boringly green, uncomfortably soft <em>useless</em> nature.</p>
<p>I would love us to stop trying fitting together a dinner after a snack after a lunch, with three different people, breaking the conversation with each just to go to the next, and coming back home exhausted even on "holidays". Instead, spending the afternoon with a friend, talking about life, making cookies.
That us who say we'd gladly ditch the car, but then how can we get the kid to judo, buy sushi at the nice chinese place out of the way, pay grandma a visit, get the grassmowner from the store, understood that that's <em>exactly</em> the point: <em>not</em> doing a thousand things, and living every day slower, more <em>adagio</em>.</p>
<p>I would like us to overcome the need of going far away to do something <em>special</em> during the holidays, and being fullfilled by our daily life.</p>
<p>That when somebody comes to us saying they are thinking of starting practicing yoga, and we reply that there is an app for that, as much as there is one for the grocery shopping you then don't have to do anymore, one to read summaries of books you then don't have to do read anymore, one to identify the plants you then don't have to learn to recognize, and also one for the sex you then don't have to do anymore, I wish us to feel the <em>warmth</em> of human proximity. That we don't prefer virtual experiences over real ones, and that we remember that feeling the grass on our face is a <em>privilege</em>, not a hassle.</p>
<p>I'd love us to overcome the enthusiasm of going to Vienna by train rather than by plane, and simply <em>stopped</em> going altogether. That countries financed life in adagio, rather than bulimic fast paced tourism. That instead of planting 50 wind turbines in the middle of the sea, they encouraged us to wash cutlery and clothes once less, to do without the Roomba, the crystal stemware, the second bathroom, the AC perpetually at 20 degrees.</p>
<p>Those of us who have once had a hard time understanding what somebody is describing, and asked them if they can't just google it and show us a picture. They told us they don't have a smartphone, we looked at them with the gravest face in the world and replied "god bless you", and then proceeded to post the picture of our dish on Twitter. To those of us, I wish at least one day <em>in adagio</em>.</p>
<p>That to us all be granted a life in <em>adagio</em>, and above all that its value be appreciated by us all.</p>Per una vita in Adagio2023-07-01T00:00:00+02:002023-07-01T00:00:00+02:00Stefano Ottolenghitag:thecrowned.org,2023-07-01:/it/vita-adagio<p>Desidero il tempo di assaporare gli abbracci, perdersi nell'odore dell'altra persona, della lana che indossa, del suo corpo, senza pacche sulla schiena che comunicano che ora è abbastanza. Un calore dal sapore di casa, soffice …</p><p>Desidero il tempo di assaporare gli abbracci, perdersi nell'odore dell'altra persona, della lana che indossa, del suo corpo, senza pacche sulla schiena che comunicano che ora è abbastanza. Un calore dal sapore di casa, soffice, a occhi chiusi, non è mai abbastanza. Ci vorrebbe una legge che sancisse che se uno non ha assaporato un abbraccio entro le 15, ha il permesso di lasciare il lavoro e <em>andarlo a cercare</em>.</p>
<p>E poi il tempo di prepare la cena, senza facili take away. Impastare il pane, la pizza, fare la pasta. Aggiustare le calze, i maglioni; imparare a fare a maglia per fabbricarsi un cappello che ci riscaldi la testa con il nostro impegno e la nostra pazienza.</p>
<p>Vorrei che noi tutti rivedessimo le nostre priorità: famiglia, amici, e natura in cima; il lavoro in fondo in fondo; tutte le chincaglierie materiali ancora più in fondo, sottoterra.
Che non evangelizzassimo uno stile di vita più lento tuttavia aspettandoci che il nostro tirocinante sia il primo ad arrivare in ufficio e l'ultimo ad andarsene. Che ogni bega, ogni riunione, ogni investimento, ogni lavatrice, ogni spesa venisse dopo le relazioni umane. Che le relazioni fossero la nostra cosa più importante, e che ci rendessimo conto di quanto possiamo fare male al nostro prossimo semplicemente nel trascurarlo.
Soprattutto, che quelli di noi che dicono "Non ci trovo niente nelal natura" si sdraiassero 15 minuti sul marciapiede di un'autostrada, senza cuffie, e decidessero se è più di <em>quello</em> che vogliamo, o più dell'inutilmente rilassante, noiosamente verde, scomodamente soffice <em>insignificante</em> natura.</p>
<p>Vorrei che smettessimo di cercare di incastrare una cena dopo una merenda dopo un pranzo, con tre persone diverse, ogni volta interrompendo la conversazione per passare alla prossima, e ritornando a casa esausti anche nei giorni di "vacanza". Invece, spendere il pomeriggio con un amico, raccontarsi la vita, fare i biscotti.
Che quelli di noi che dicono che farebbero volentieri a meno della macchina, ma poi come fanno a portare il bambino a judo, a comprare il sushi dal cinese buono fuori mano, fare visita alla nonna, portare il taglierba a riparare, capissero che è proprio <em>quello</em> il punto: <em>non</em> fare mille cose, e vivere ogni giorno più <em>adagio</em>.
Che superassimo il bisogno di andare lontano a fare qualcosa di <em>speciale</em> per le vacanze, ed essere appagati dalla quotidianità.</p>
<p>Mi piacerebbe che quando qualcuno viene da noi dicendo che sta pensando di iniziare a fare yoga, e noi rispondiamo che c'è un'app per quello, e anche una per la spesa che così non dobbiamo più fare, una per leggere i riassunti dei libri che così non dobbiamo più leggere, una per identificare le piante che così non dobbiamo più imparare a riconoscere, e anche una per il sesso che così non dobbiamo più fare, ci auguro di sentire il <em>calore</em> della prossimità umana. Che non preferissimo le esperienze virtuali a quelle reali, e che ci ricordassimo che il sentire l'erba sulla faccia è un <em>privilegio</em>, non una grana.</p>
<p>Vorrei che superassimo l'entusiasmo di andare a Vienan in treno invece che in aereo, e semplicemente smettessimo di andarci <em>e basta</em>. Che gli stati finanziassero la vita in adagio, invece che il turismo bulimico e consumistico. Che invece che piantare 50 alberi nel mare, ci incoraggiassero a lavare le posare e i vestiti una volta in meno, a fare a meno del Roomba, del servizio di cristallo, del secondo bagno, dell'aria condizionata perennemente a 20 gradi.</p>
<p>Quelli di noi che una volta hanno avuto difficoltà a capire cosa ci stavano descrivendo, e abbiamo chiesto se non potevano semplicemente googlare una foto e farcela vedere. Ci hanno risposto che non hanno uno smartphone, li abbiamo guardati con l'aria più seria della terra e abbiamo risposto "che dio ti benedica", e poi abbiamo proceduto a postare la foto della merenda su twitter. A quelli di noi, auguro almeno una giornata <em>in adagio</em>.</p>
<p>Che a noi tutti sia concessa una vita in <em>adagio</em>, ma soprattutto che noi tutti ne percepiamo il valore.</p>The best ways to organize pointless meetings2023-03-25T00:00:00+01:002023-03-25T00:00:00+01:00Stefano Ottolenghitag:thecrowned.org,2023-03-25:/bad-meetings<p>All good meetings are similar, but each bad meeting is shitty in its own way. I could also say that it's hard to formalize elements that make meetings ineffective, but that's not true: I have …</p><p>All good meetings are similar, but each bad meeting is shitty in its own way. I could also say that it's hard to formalize elements that make meetings ineffective, but that's not true: I have a neat list. All summarized to one point is: being <em>lazy</em>. Organizing a good meeting requires work upfront, done <em>before</em> the meeting happens -- hell, even before you send out the <em>invites</em>! A shitty meeting is a waste of time, and everybody would much rather take a walk in the woods rather than sitting on a pointless chat. So don't be lazy, do the work. Because <strong>if you, as <em>organizer</em>, are not putting in preparatory work, you are effectively asking other people to put in their own time so that you could save yours</strong>.</p>
<p>But by all means, if you <em>enjoy</em> pointless meetings, then read this as a do-list. You will end up with some of the worst meetings ever. You can thank me, but please don't invite me.</p>
<h2 id="no-agenda">No agenda</h2>
<p>We're not getting a coffee to catch up with our lives and spontanouesly hear the story of the time you hitched hiked through Kazakhstan. And you're not here to hear me speaking from the top of my head. <strong>Be clear about what the point of the meeting is</strong>: do we need to come to a decision? Do you need to show something? In that case, can't you email it? Do you want feedback about something? What the hell are you claiming <em>x</em> minutes from me for? Make it clear to all participants what the points up for discussion are, share any material upfront, and ensure you state out the questions/points you are looking for feedback on.</p>
<p>If I don't know what the point of the meeting is, <br />
a) we're likely gonna spend the first 5 minutes figuring out what to talk about; <br />
b) I'm gonna have to hear your mumbling and build the context myself; <br />
c) you're gonna get feedback <em>from the top of my head</em> (and, guess what, you get a better feedback if I'm given time to <em>think</em>), and you're gonna get it <em>verbally</em> (which gets lost).</p>
<h2 id="no-clear-next-steps">No clear next steps</h2>
<p>When somebody says <em>"this was really a nice meeting"</em>, and it's not clear who is going to do what as soon as we part, I wonder if their diplomatic piece of bullshit was rather an appreciation of the tea the drank during the meeting. If we don't spend the last 2 minutes wrapping up and making sure everybody has a clear idea of what the next steps are, all the time was a waste.</p>
<h2 id="no-meeting-notes">No meeting notes</h2>
<p>Words get lost in the air, and there's a million reasons why writing down what happened during your meeting is a good idea. It happened that, when I missed a meeting and asked what the gist of it had been, it was suggested that we schedule another meeting to update me. How ironic. Other people wondering what decisions were reached and what discussions happened have to rely on other people telling them. Not to talk about the misunderstandings and forgotten bits two months down the line.</p>
<p>But hey, there's a special place for you in <em>hell</em> if the meeting notes you take are on the lines of <em>"discussed branching strategy"</em>. Be clear whether the notes you take are for <em>you</em> to remember, or for others to read. In the second case guess what: it needs to be <em>informative</em>.</p>
<h2 id="more-than-3-participants">More than 3 participants</h2>
<p>This is not a birthday party. When you organize a meeting with > 3 participants, you give away that you don't know what you are doing. Taking a decision when 2 people are involved is already hard, so what do you expect to happen from a meeting with > 3 people? It's just gonna be a nice flea market where everybody gives conflicting opinions and, at best, a diplomatic fight emerges, and, at worst, we skip over the friction points not to piss anybody off.</p>
<p>Usual justifications for large gatherings:</p>
<ul>
<li><em>we want you to be aware of what's happening</em> <br />
Sounds great, send me the meeting notes.</li>
<li><em>we want everybody to feel involved in this decision</em> <br />
Sounds great, make a pool and a written discussion thread.</li>
</ul>
<p>Either it's a <em>lesson</em> that you are holding, or just <em>don't</em>.</p>
<h2 id="no-clear-leader-or-wrong-leader">No clear leader, or wrong leader</h2>
<p>Who is leading the meeting? Are they aware? Who is going to take decisions? What happens if there are two conflicting opinions? Are we all gonna look the other way and pretend there is agreement, or is there somebody invested with authority who is going to say <em>"we do this way and I don't care you if you disagree"</em>? Are they entitled to take decisions, or is it a paralympic-table-tennis-player-turned-tech-manager deciding what frontend framework to use?</p>
<h2 id="showing-a-shitty-presentation">Showing a shitty presentation</h2>
<p>Mamma mia, the dread when someone has something to show, and then they show up with these slides full of text that everybody just reads. <em>Why don't you write it down nicely and send it over?</em> It's gonna take you 1 hour to write, and 5 minutes for me to go through it. If we are a team of 10 people, the total time amounts to 60+5x9 = 105 minutes. If everybody needs to sit in a meeting for 30 minutes, that's 30x10 = 300 minutes. But yeah, you guessed it: the work is <em>on you</em>.</p>
<h2 id="longer-than-30-minutes">Longer than 30 minutes</h2>
<p>After talking for 30 minutes, I'm already covering <em>childhood traumas</em>. What is it that's gonna require more than 30 minutes? If you feel like you need a lot of time, and you're not holding a lesson, it's a sign that you're probably falling prey to one of the pitfalls above. Too many participants makes it lengthy. No agenda makes it hard to nail down the discussion points and tackle them effectively. Showing slides crammed with text takes a lot of time.</p>
<p>I'm sure you can keep it below 30 minutes, if you do the preparatory work. If you are too <s>busy</s> lazy, why do you expect others to put in their time?</p>
<h2 id="recurring-meetings">Recurring meetings</h2>
<p>Give any team a time slot and a title, and some conversation will emerge. It might even be thoughtful. But was it of any use? I'm very skeptical of recurring meetings -- if managed appropriately, they require a lot of time: agenda, notes, roles ... half hour of meeting requires another half hour of work. And in most cases, it's just a time filler. People say "we are going to join and, if nobody has anything to bring up, we'll split straightaway". Expect I'm not really gonna get anything done in the 25 minutes I "got back", since I had planeed to be in a meeting. So I'd much rather call for a meeting <em>when needed</em>, rather than cancel one on the spot.</p>Dissecting explanations of Javascript Promises2023-02-09T00:00:00+01:002023-02-09T00:00:00+01:00Stefano Ottolenghitag:thecrowned.org,2023-02-09:/technical-writing-javascript-promises<p>What is a Javascript <code>Promise</code>? When I was documenting the Neo4j Javascript driver, I did not know. So let's find out, and in so doing tear to pieces explanations I found online. My favorite past …</p><p>What is a Javascript <code>Promise</code>? When I was documenting the Neo4j Javascript driver, I did not know. So let's find out, and in so doing tear to pieces explanations I found online. My favorite past time.</p>
<h3 id="w3docs"><a href="https://www.w3docs.com/learn-javascript/promise.html">W3Docs</a></h3>
<blockquote>
<p>In JavaScript, a promise is a unique object linking the "producing code" and the "consuming code". In other words, it is a "subscription list".</p>
</blockquote>
<p>Three pairs of quotes, but okay. A <code>unique</code> object? Adjectives are not ornamental christmas balls, so how does this <em>unique object</em> would compare to a ... <em>non-unique</em> object? What is a <em>producing code</em> and a <em>consuming code</em>? If you think I know already what they are, then you should also know that I would not be reading this page in that case. 80% of the readers will not know what those are, so you cannot give those concepts for granted. And no, the solution is <strong>not</strong> to open the page with <em>definitions</em>. There is nothing worse than going through a glossary in a technical page.</p>
<blockquote>
<p>The "producing code" takes the time it needs to produce the promised result, and the promise makes the result available to the overall subscribed code whenever it's ready.</p>
</blockquote>
<p>I still don't know what a <em>"producing code"</em> is. And then the biggest flaw of all: using the word you are defineing to explain that same word. I still don't know what a <em>promise</em> is, and here are we talking about <em>promised result</em>. And what is <em>"the overall subscribed code"</em>? I have no idea. When does it get <em>ready</em>? How do I make it so?</p>
<blockquote>
<p>But, JavaScript promises are more complicated that the described simple subscription list: there are additional features and limitations inside them.
Let's start at the very beginning.</p>
</blockquote>
<p>Comma after a leading conjunction, two bold moves. And the whole sentence is just a bullshit filler, saying <em>"promises are complicated! You need an explanation"</em>. Gotcha. Thank you. That's why I'm here, but three paragraphs in and I'm still clueless. And also, your time is up: I'm leaving. But let's look at one more sentence that reassures me that I am not losing anything by leaving:</p>
<blockquote>
<p>The arguments of it [the function], such as the <code>resolve</code> and <code>reject</code>, are considered callbacks provided by JavaScript.</p>
</blockquote>
<p>And here we get the final confirmation that the writer doesn't know what the words they are writing are about. "Are <em>considered</em>"? A callback <em>is</em> a callback. It's like saying "the time on your train ticket is <em>considered</em> to be the departure time". No, it <em>is</em>. Is this written by somebody that really understands promises? Hard to tell.</p>
<p>A quick sum-up of the flaws:</p>
<ul>
<li>lots of concepts given for granted</li>
<li>using the word <code>promise</code> to describe what a <code>promise</code> is</li>
<li>using language at random: sentences that don't really say anything valuable, adjectives used to make sentences <em>less dry</em>, punctuation not accurately set.</li>
</ul>
<h3 id="javascriptinfo"><a href="https://javascript.info/promise-basics">javascript.info</a></h3>
<p>This one starts much better. It starts with an <strong>analogy</strong>:</p>
<blockquote>
<p>Imagine that you're a top singer, and fans ask day and night for your upcoming song. To get some relief, you promise to send it to them when it's published. You give your fans a list.</p>
</blockquote>
<p>This is great, I know how singers work, <em>roughly</em>. It doesn't need a lengthy explanation. And, cherry on top, it uses <code>promise</code> with the meaning it has in natural language, before the Javascript overloading.</p>
<blockquote>
<ol>
<li>A "producing code” that does something and takes time. For instance, some code that loads the data over a network. That’s a "singer”.</li>
<li>A "consuming code” that wants the result of the "producing code” once it’s ready. Many functions may need that result. These are the "fans”.</li>
<li>A promise is a special JavaScript object that links the "producing code” and the "consuming code” together. In terms of our analogy: this is the "subscription list”.</li>
</ol>
</blockquote>
<p>I'm still not totally onboard, but it makes a lot of sense so far, so I feel like I'm ready to see a code example and be able to parse it out somehow. I need to read through a filler first though:</p>
<blockquote>
<p>The analogy isn't terribly accurate, because JavaScript promises are more complex than a simple subscription list: they have additional features and limitations. But it’s fine to begin with.</p>
</blockquote>
<p>Of course it's not accurate: it's an <em>analogy</em>. And that's <em>fine</em>. No physics professor think that water flows are an accurate analogy for electrical currents, but it would be idiotic to not use it. <strong>If it aids understanding, it should be used</strong>. Of course you cannot use it forever: at some point you have to ditch the analogy and face the harsh truth of electromagnetic fields. They are a new concept, but you can think of them as a 3D <em>something</em> that you know, and it's easier and less intimidating. Ditching analogies for fear of coming out as "not-expert" is wrong. (It does need to aid understanding though. If you have to <em>explain</em> the analogy in detail, it's probably not a good one.)</p>
<p>Anyway. The page then continues with text of mediocre quality, but it is filled with enough technical details spelled out well enough to actually be clear. When the writers forgets that he has to <em>fill a page</em>, he can write coincise sentences that get good content across:</p>
<blockquote>
<p>So to summarize: the executor runs automatically and attempts to perform a job. When it is finished with the attempt, it calls <code>resolve</code> if it was successful or <code>reject</code> if there was an error.</p>
</blockquote>
<h3 id="developermozilla"><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises">developer.mozilla</a></h3>
<p>This is quite different. One thing is clear: you may disagree with how content is laid out, but you can see that there's been thought from somebody that deeply understood what he was talking about. Notice how <strong>every word</strong> is carefully laid down in the very first sentence:</p>
<blockquote>
<p>A <code>Promise</code> is an object representing the eventual completion or failure of an asynchronous operation.</p>
</blockquote>
<p>Notice how it's all words you most likely know already (or you should). Then comes a disclaimer that is quite opaque to a newcomer, but makes a lot of sense.</p>
<blockquote>
<p>Since most people are consumers of already-created promises, this guide will explain consumption of returned promises before explaining how to create them.</p>
</blockquote>
<p><strong>They flip the exposition</strong>. Both previous pages started showing how to <em>create</em> a promise, but what do I care. 80% of people are looking up promises because they were using whatever library, got a weird object returned, checked the type, and found out it was a <em>promise</em>. <strong>It makes a lot of sense to flip the exposition so that you are first exposed to <em>how to deal with a promise</em></strong>! It makes it more pedagogical, and relevant to the reader.</p>
<blockquote>
<p>Essentially, a promise is a returned object to which you attach callbacks, instead of passing callbacks into a function.</p>
</blockquote>
<p>Three sentences in, and I still have no questions. Everything is clear. And now comes the first example. It shows how you would do something without promises, and how you would translate the same thing into using promises. It is saying: <em>"here is what you know already. Now here is how you expand it with this other concept."</em> And then it tells why you should care:</p>
<blockquote>
<p>In the old days, doing several asynchronous operations in a row would lead to the classic callback pyramid of doom. With promises, we accomplish this by creating a promise chain. [example]</p>
</blockquote>
<p>And then it digs deeper. What's the syntax like in the general case. How do you chain them. What are the common mistakes. Error handling. And more. But it's <em>tidy</em>. They don't assume you know something until they have covered it, and they don't show off their knowledge of terms. They pick an entry point (and picking one <em>is</em> a skill) and work from there in a pedagogical way.</p>
<h2 id="grafana-exemplars"><a href="https://grafana.com/docs/grafana/latest/fundamentals/exemplars">Grafana exemplars</a></h2>
<p>I landed here by pure chance, cause a collegue started working there, but let's read through. This is the very first page of their documentation. It starts with:</p>
<blockquote>
<p>An exemplar is a specific trace representative of measurement taken in a given time interval.</p>
</blockquote>
<p>I have absolutely no idea of what this sentence means. I <em>can</em> see that the words are carefully laid down, but it's not words I am familiar with. In the Mozilla page, you could assume that 80% of the audience knew every single word of the opening sentence, but here? What is a <code>trace</code>? What makes it <code>specific</code>? What is a <code>measurement</code>? And how do I take a measurement into a trace? I am completely lost.</p>
<blockquote>
<p>While metrics excel at giving you an aggregated view of your system, traces give you a fine grained view of a single request;</p>
</blockquote>
<p>What are <code>metrics</code>? What are <code>traces</code>? Mind you, this is the first <em>real</em> docs page, the first under <code>Fundamentals</code> and after the <code>Introduction to Grafana</code> (which is just an intro to the different versions).</p>
<blockquote>
<p>exemplars are a way to link the two.</p>
</blockquote>
<p>Ok, so <code>exemplars</code>, which I know nothing about, are a way to link <code>metrics</code>, which I know nothing about, with <code>traces</code>, which I know nothing about. That's 46 words that have not led me one inch closer to knowing what the product is, and <em>no links</em> or tooltips either.</p>
<p>Now. The next paragraph switches gears completely:</p>
<blockquote>
<p>Suppose your company website is experiencing a surge in traffic volumes. While more than eighty percent of the users are able to access the website in under two seconds, some users are experiencing a higher than normal response time resulting in bad user experience.</p>
</blockquote>
<p>Oh, here comes a use-case. I understand web traffic, so this should help me in clarifying <code>exemplars</code>. Except that I read, and I am <em>puzzled</em>. When system load is high, <em>all</em> users experience "a higher than normal response time". It's not like 80% get a timeout and 20% get a blizzard experience. So the only use case presented in the first <code>Fundamentals</code> page is... inaccurate?</p>
<blockquote>
<p>Use exemplars to help isolate problems within your data distribution by pinpointing query traces exhibiting high latency within a time interval.</p>
</blockquote>
<p>Here is a packed sentence saying that these <code>exemplars</code> will help me in this use-case. It still doesn't tell me how. It doesn't <em>show</em> me how. However, it doesn't miss the chance of saying that "Support for exemplars is available for the Prometheus data source only." What is a <code>Prometheus data source</code>? What do I care about this piece of information?</p>
<p>I can see many people were involved in writing this page, and a frankenstein came out. Somebody said: <em>we need to have a precise definition</em> [of what?] <em>as opening</em>. Somebody else said: <em>we need to have a practical example</em>. Yet somebody else said: <em>we need to puntualize that this feature doesn't always apply</em>. But nobody was bold enough to take <em>one</em> decision, and all ideas were diplomatically accepted. The result is that the page has no <em>direction</em>, and <strong>the reader experience is neglected in favor of not pissing anybody off</strong>.</p>
<h2 id="google-analytics-4-migration"><a href="https://developers.google.com/analytics/devguides/migration/api/reporting-ua-to-ga4">Google Analytics 4 migration</a></h2>
<p><a href="/the-shit-show-of-google-analytics-4-ga4">I have a chip on my shoulder with Google Analytics</a>, so I have a hard time holding my temper. But let's try. I'm much more annoyed at the API itself rather than the docs, to be honest.</p>
<p>Indeed, the page <em>itself</em> is not too bad. My main complaint is about the number of links. They send you <em>everywhere</em>, confusing you as to what you should be reading to achieve your goal, and <em>nowhere</em> at the same time, because a third of them are 404s. And information is scattered across all these pages, all with a vaguely similar title, and after you jump a couple links and end up with 10 open tabs that kinda look the same, you start wondering: <em>what the hell should I be reading?</em></p>
<blockquote>
<p>The Data API v1 client libraries were designed to get you started fast. By default, client libraries try to automatically find your service account credentials.</p>
</blockquote>
<p>Who would design libraries to get you started <em>slow</em>?</p>The shit show of Google Analytics 4 (GA4) and the retirement of Universal Analytics2022-11-26T00:00:00+01:002022-11-26T00:00:00+01:00Stefano Ottolenghitag:thecrowned.org,2022-11-26:/the-shit-show-of-google-analytics-4-ga4<p>Early in 2022, I started getting emails from Google, warning that starting from 2023 they would retire Universal Analytics (UA), which is what everybody uses to track views from Google Analytics in this decade. Closer …</p><p>Early in 2022, I started getting emails from Google, warning that starting from 2023 they would retire Universal Analytics (UA), which is what everybody uses to track views from Google Analytics in this decade. Closer inspection revealed that this was not a simple change: they were in fact retiring <em>all</em> UA properties, together with the Google Analytics Reporting API we so far used to query data from them. <strong>The future, they said, was Google Analytics 4 (GA4).</strong> I have never seen a change of this <em>magnitude</em> being deployed in such a <em>sloppy</em> way. I do not even know where to begin from, so I will just have a bullet list before my head bursts with frustration.</p>
<ul>
<li><strong>There is no way to migrate a UA property to a new GA4 property.</strong> You can not bring your historical, decade old data into the modern properties. You simply, marvelously, wonderfully lose them. <a href="https://support.google.com/analytics/answer/10759417?hl=en">Quoting</a> from them: "If you have a Universal Analytics property, you <em>must</em> migrate. The earlier you migrate, the more historical data and insights you will have in Google Analytics 4." <br>
Honestly, if you are going to lose all your previous data (and you <em>are</em>), you might as well ditch Analytics altogether and move to the more open, possibly self-hosted <a href="https://matomo.org/">Matomo</a>.</li>
<li><strong>The libraries for the new Analytics Data API are still</strong> <a href="https://github.com/GoogleCloudPlatform/php-docs-samples/issues/1723"><strong>in <em>beta</em></strong></a>. More and more people are being pushed into creating a GA4 property, and we do not <a href="https://github.com/googleapis/google-cloud-php/issues/5491">even have a reliable way</a> of pulling data out of it. I have loads of customers coming to me asking when <a href="https://postpaycounter.com">our product</a> will support GA4, or, even worse, saying that they cannot use our product because they only have a GA4 property. I cannot even articulate how ridiculous it is that GA4 has been live for what, a year? And we are still without stable libraries. I am not going to put in implementation work on a beta, let alone ship it to customers in production.</li>
<li><strong>The <a href="https://developers.google.com/analytics/devguides/migration/api/reporting-ua-to-ga4">migration guide</a> is a joke.</strong> No common login flows, no use cases examples, a flood of broken links.</li>
<li><strong>The GA4 PHP library <a href="https://github.com/googleapis/google-cloud-php/issues/3975">does not even have an example with Oauth2.</a></strong> They only have examples with service accounts which, frankly, I do not even know who will use. Then there comes some saint <a href="https://github.com/googleapis/php-analytics-data/pull/2">submitting a PR with the Oauth2 example,</a> and the only comment he receives is that the specific repo he submitted it against does not accept pull requests. We do not even get a Google employee saying "thank you, I will re-submit it against the right one" ... it just gets closed. Good luck figuring out which of the million repos you should be looking at, anyway.</li>
<li><strong>GA4 properties can not be linked to Adsense.</strong> As it looks right now, UA properties linked to Adsense were the only way to obtain a report of what pages generated how much revenue. <a href="https://github.com/googleapis/google-api-php-client/issues/2340">Unless I will be proven wrong</a>, there will be <strong>no way to generate a view of <code>page paths <-> revenue</code></strong>. I am bewildered that nobody at Google thought about this use case, and I <a href="https://stackoverflow.com/questions/62517780/get-the-total-earnings-from-a-specific-url-using-the-adsense-management-api?rq=1">am</a> <a href="https://stackoverflow.com/questions/62093729/query-google-adsense-data-by-url">not</a> <a href="https://stackoverflow.com/questions/67307270/how-to-get-statistics-from-google-adsense-for-a-specific-url-from-website?noredirect=1&lq=1">alone</a>.</li>
<li>The GA4 PHP library relies on <code>protobuf</code>, which requires <code>php-bcmath</code>, which is <a href="https://github.com/protocolbuffers/protobuf/issues/4465">not included</a> by default in PHP installations. A loads of broken clients. Sure, not strictly a GA4 requirement, but maybe worth checking how <em>common</em> newly-required dependencies are? Google <a href="https://github.com/googleapis/google-cloud-php/issues/2400">acknowledged</a> this in 2019, and has taken no action so far.</li>
<li>There seems to be <strong>some weird interference between GA4 properties and UA properties.</strong> I received reports of customers having both of them in the same account, saying that their authentication gets invalidated every few days. It did not use to happen before, and only happens for people who have some GA4 properties.</li>
</ul>
<p>And do not even get me started on the whole <em>choice</em> of having a new API. What is the <em>benefit</em> for customers? Probably negligible. What is the <em>hassle?</em> Gigantic. You wanna put out a new API? Great, but leave the old one alone. They deliver a <strong>breaking change</strong> that is going to affect millions of websites, and 6 months from the retirement of the Reporting API the do not even have the libraries out of beta, they require to largely revisit current implementations, and the documentation is incomplete and littered with 404s? What company is this, I do not know.</p>I built a Lily58 split keyboard so you don't have to2022-11-04T00:00:00+01:002022-11-04T00:00:00+01:00Stefano Ottolenghitag:thecrowned.org,2022-11-04:/built-a-lily58-split-keyboardBuilding a split keyboard with 58 blank-keycap keys, and switching keyboard layout from QWERTY to Colemak. Ambitious.<p>I had a long summer and decided I would do something to improve the ergonomics of my tech life. Many years ago I bought a Kinesis Advantage, but I was young and naive, and gave up after a week. This time, with all the wisdom of my late twenties, I was ready to take my revenge. <em>How naive.</em></p>
<p><img alt="Lily58 colored keycaps" src="images/lily58-colored-keycaps.jpg"></p>
<h2 id="you-dont-wanna-choose-the-wrong-split-keyboard-do-you">You don't wanna choose the wrong split keyboard, do you?</h2>
<p>I started by losing one year in life expectancy in <em>browsing</em> for split keyboards, even before I decided I would build one myself. This is worse than looking for an apartment in a European capital. I cannot even possibly <em>link</em> all the resources that I went through. Obviously, as anything in tech, I could find absolutely <em>no agreement</em> whatsoever on which was the best option. I guess once you spend $350 on a keyboard, you just spend an hour a day boasting on Reddit about how <em>great</em> it is. Really, <a href="https://aposymbiont.github.io/split-keyboards/">there is a million split keyboards</a> - and that does not even count the dudes that genuinely think that we need <em>one more</em> and went on to redesign a split keyboard because their left thumb is longer than the right one and it was <em>vital</em> to have one thumb cluster one millimeter closer. I'm a practical dude: just sand away that extra millimeter from that left thumb. I never thought there could be more combative people than the ones advocating for spaces in indentation, but hey, we never stop learning.</p>
<p><img alt="Split keyboards gallery" src="images/split-keyboards-gallery.png"></p>
<p>I should also mention that I always felt a mix of indifference and outright <em>contempt</em> for mechanical keyboards. I know I <em>am</em> typing, I do not need my keyboard to remind me and <em>the whole room.</em> 99% of split keyboards are mechanical, so I do not know why I did not drop it here to be honest.</p>
<p>ANYWAY, <strong>I decided it was a Lily58</strong> that I wanted to have. For the largest part, I picked a Lily58 for the same reason why ugly apartments overlooking the highway are still bought by affluent families: I was fed up with searching. But I keep telling myself that there were also <strong>a multitude of good, <em>rational</em> reasons</strong> for that choice:</p>
<ul>
<li>it is an open source project. Open source fanatics are possibly even more dangerous than space-indentation folks, so be careful with arguing here.</li>
<li>it is DIY, so that my mum can be proud that I did it with my hands.</li>
<li>it is lightweight, the thought being that I could travel with it. That's always been my dream: go to the Amalfi coast with my keyboard. Partners are overrated.</li>
<li>it is cheap(er). I got a kit for ~120€, and switches and keycaps for ~50€. So all in all under 200€, while commercial split keyboards sell easily at 300+€.</li>
</ul>
<p>There is then the whole thing of these keyboards being <em>programmable.</em> This means you can program a combination of keys to do... whatever you want! And as much as this might have seemed a staggering <em>innovation</em> in 1998, modern operating systems allow that natively, even with regular keyboards. They are called <em>shortcuts,</em> and <em>scripts</em>. Astonishing, I know. But people say: "but your custom behaviors are not linked to the computer, they are embedded on the keyboard itself!". What can I say. I would love to be able to feel genuine <em>enthusiasm</em> for this, but I just find it <em>appalling.</em> As if I needed to run a custom git workflow on any machine I touch.</p>
<h2 id="building-the-lily58">Building the Lily58</h2>
<p><img style="float:right;width:200px;" src="images/lily58-diode.jpg" /></p>
<p>ANYWAY, <strong>on to the building experience.</strong> I already knew how to solder, and I can tell that this is not the right project if you have never soldered before. I thoroughly enjoyed the building process, and was enthusiastic when my girlfriend mentioned that she might want have wanted me to build one for her. That enthusiasm soon faded when I found myself soldering 58 <em>invisible</em> diodes, whose polarity needs to be respected and is only made apparent by a <em>faded line</em> on the component itself. For scale, here is a diode in a bowl. Be prepared to solder 58 of these minuscule devils. Then 58 switch sockets. Then 2x2x2 rows of connector for the microcontrollers. I should also mention that the official building reference has ambiguous pictures, and <em>japanese captions</em>. At one point I thought I had made a mistake in soldering, and thought to myself "oh well, this was a nice project", ready to <s>throw it in the trash</s> dispose of it as we responsible western citizens do with electronic waste. But then it turned out that I was right all along and, after only 3 days of <em>sniffing tin fumes</em>, a) my understanding of Japanese had improved remarkably, and b) I had no excuse not to give the Lily a spin.</p>
<p>And right when I thought I was ready to type... well, you do need keys on your keyboard, don't you? It took me a while to let sink in the fact that the keyboard kit came, simply, with no way of actually pressing the keys. You need at least switches, and realistically also keycaps, and, god, there starts another <em>quest</em> for <em>which</em>. High or low profile? Cherry MX or a cheaper brand? Red (silent?), brown, blue, or damn rainbow? SA, OEM, XDA, or SDA profile? Blank keycaps? Maybe themed? By this point, I was <em>in pain.</em> I take the top tshirt from the pile in the morning and do not look back. I think the last time I properly picked my clothes was for my last first date, if I even did. <em>I hate choosing.</em> I seriously considered buying an anime-themed keycaps set, and I hate anime. In the end, I picked red Cherry MX and a <strong>colored blank keycaps</strong> set for masochistic reasons that will soon be clear. (And just so that I do not look noobier than I actually was, I did buy the keys at the same time that I bought the base kit. I knew I needed keys.)</p>
<p><img alt="Lily58 building" src="images/lily58-building-1.jpg"></p>
<h2 id="switching-keyboard-layout">Switching keyboard layout</h2>
<p>And here is yet another plot twist. <strong>A DIY split keyboard was not enough</strong>. There is plenty of people writing with one, so how <em>special</em> would I be if it was <em>just</em> split? <strong> No, I needed to learn proper <em>touch-typing</em>, and it needed to happen with a <em>different</em> keyboard layout</strong>. <strong>I also needed to ditch QWERTY.</strong> And, to be fair with my logic, I could <em>not</em> touch-type in qwerty on a split keyboard, so I would have have to learn it anyway. But anyway, here came other days of browsing, cause the devil has made sure to provide us with a comical amount of alternative keyboard layouts: Dvorak, Colemak, Workman, ... not to count the <em>custom</em> ones: because yes, there are people who actually look at their keyboard and are like "nah, the <em>a</em> there... it's not optimal", and they move it somewhere else. Then one week later, just when they are about to get used to the change, they move it somewhere else again. God has at least made sure that these folks cannot communicate much, because if you text them an innocuous "Hi Larry!", you will get a <em>Larry is writing</em> for 10 seconds, and then get "Ho" as reply.</p>
<p>But okay, <strong>so I picked Colemak</strong>, basically because the thought of moving the punctuation to the upper-left corner (Dvorak) was honestly as comical as a mathematician using dynamic programming to arrange dinner with a group of friends. I am progressive, but not <em>that</em> progressive. Leave the damn punctuation alone. Colemak preserves qwerty's layout to a large extent, so the idea is that you do not need to learn totally anew. Which is a fair point: I think I did not struggle much with the keys that were left unmoved. For the other ones... well, different story.</p>
<p>I was confident that Colemak was the right choice, except that I had not realized that Colemak has many <em>flavors</em>. I was in even <em>more pain.</em> There is regular Colemak, which everybody agreed being inferior to Colemak-DH, which came some years later. However, the tragedy is that nobody seems to have informed the <em>Colemak Academy,</em> since their default layout is still regular Colemak, and they are the only website in the world not using cookies to remember your choice. So some days I would practice on regular Colemak, some days on Colemak-DH. What can I say, I am <em>dumb.</em> I blame it on decision fatigue. But fair, <strong>I <em>slouch</em> to the end of the Colemak Academy and achieve a <em>terrific</em> score of 18 WPM,</strong> down from ~80 WPM with a regular qwerty keyboard. I stop to ponder that, if I will be ~4 times slower than before, I will have to sit 4x longer, and I just see the ergonomics flying out the window. I prey to a god I do not believe in that it will not take me forever to get back to speed. Note that I am entirely <em>glitching over</em> the fact that being the Lily a programmable keyboard, you need to actually... <em>program</em> it! You have to decide what each key will do, <em>compile</em> the damn keymap and flash it on both microcontrollers. And then likely fiddle with it half day.</p>
<p>I also soon learnt that what a lone honest voice said on Reddit: the websites that allow you to practice by copying characters you see on screen are useful to start with, but <strong>copying something you read is entirely different than writing from the top of your mind.</strong> When copying, you can focus 100% on where to send your fingers. When writing, you hopefully focus 100% on the content. (My god, think what a place the internet would be if people would not even focus on <em>content</em> but on <em>finger motion</em> instead. It would be a paradise of <em>burps</em>.) For me, still running an outdated, largely single-threaded brain, writing from the top of my head was slower than writing on damn <em>paper</em>.</p>
<p><img alt="Lily58 switches" src="images/lily58-switches.jpg"></p>
<h2 id="colemak-coming-back-to-haunt-me">Colemak coming back to haunt me</h2>
<p>And then, right when I was ready to have <em>fun,</em> <strong>I just accept that this has just been a big <em>mistake.</em></strong> I have no idea how far apart the two halves should be. However I set them, they are always uncomfortable. They are far too high, so I have to hover a meter above the keyboard, which brings huge tension in my shoulders. I have been typing for ~15 years, and not knowing where to send my index fingers is unbearably frustrating and makes me even <em>more</em> tense. And while I could fix all that with <em>patience</em> (as if I had not already invested enough), there is one thing that will never change: <strong>the Lily58 has too few keys. </strong>58 keys seem a lot, until you start using it. I tried using multiple layers and key combos, and then I was constantly looking at my <em>paper</em> map of keys of where the <em>hell</em> the things I was looking for were. I dreaded sitting at the computer. My Lily58 is beautiful, but utterly useless, and <strong>close to the least ergonomic addition</strong> I could do to my workstation.</p>
<p>It is okay, <strong>I can accept the failure.</strong> I have quit a PhD, I can quit a keyboard. The Lily was a demanding, expensive, colorful mistake. I don't even think it made me a more prestigious software developer. One of my neighbors once came in, had a tour of the apartment, left me a polish beer, and asked if the Lily was something "to play music with" (don't ask further about the interaction). <strong>But Colemak...</strong> I was still hooked.</p>
<p>The official reason is that I am not so keen on keeping living with a keyboard layout that is the least ergonomic, and still around for historical reasons only. The truth is that I also noticed that I could <em>somewhat</em> touch-type Colemak on another keyboard, but not qwerty, so it looked like <strong>I had effectively <em>locked myself</em> into Colemak.</strong> I could as well go all in then. I picked back the Kinesis Advantage I had and felt determined to write Colemak with it. I put my hands on and I felt such a <em>relief</em> in <em>resting</em> my palms that I genuinely do not know what all the hype about other keyboards is about. The Reddit comments saying "Get a Kinesis, it's a workhorse" felt <em>reactionary</em> a month ago: now they feel just <em>wise</em>.</p>
<p>Except that then something else happened. I wanted to go cry on my mother's lap, but a) she is half a million kilometers away, and b) I had run out of tears. <strong>The Kinesis Advantage is not programmable, so I had to switch keymap in the OS.</strong> (The Kinesis Advantage2 is programmable, but the Advantage is not. A tiny digit for a big difference. And yes, I spent a solid half day trying to debug my Kinesis because I had never even <em>noticed</em> I owned an Advantage and not an Advantage2.) However, I discovered that Ubuntu 18.04 did <em>not</em> support Colemak-DH, which is the Colemak flavour I had become <em>somewhat</em> proficient in. And yes, we are in 2022, but I am a wise sysadmin enough to know better than upgrading an LTS that is still supported and not broken. So for the first few weeks I could only type Colemak on the Academy's website, which can dynamically remap the keys. Until when I felt ready to start typing Colemak everywhere, and I figured I would finally update to Ubuntu 22.04. This broke half of my software and did not even <em>ship</em> Colemak-DH as a supported keyboard layout. Go figure out fucking why.</p>
<p>The failure of my PhD project <em>paled</em> in comparison to the difficulty of typing Colemak in the wild. I ended up installing Mint over Ubuntu because hey, there was not enough <em>chaos</em> already. With that, I discovered that the Colemak-DH keymap that is shipped with Linux is tailored to the laptop keyboards, which have an extra key between the left shift and the z, which matrix keyboards do not have. This means that I effectively <strong>ended up with the whole bottom row being shifted one character to the right</strong>. It felt like a <em>prank</em> so elaborated that nobody could have devised it. I was <em>speechless</em>. It turned out that the only solution was something called <em>Dreymar's Big(!) Bag of Tricks</em>, which honestly feels like even bigger a prank. The "solution" amounts to cloning a github repo and running a script that gives no feedback over what the hell it has done, and then figuring out how that would have helped. It turned out that it had added some keyboard layouts, among which Colemak-DH for matrix keyboards, but god forbid it to work properly. Even if it is a keymap at the OS level, on every session lock the OS defaults back to qwerty, so I have to manually change it <em>back</em> to Colemak. I still live my daily life with this: I am stunned, and I routinely mistype my password before I realize I have to switch layout. And let's not even get started about <strong>writing in foreign languages.</strong> Every day, I write in English, Swedish and Italian, and no Colemak layout has any <em>Alt-Gr dead keys</em> option. I have to switch layout.</p>
<p>So this is my life these days. <strong>I have two keyboards on my desk, none of which is the Lily58,</strong> which is peacefully resting on a shelf. I use the Kinesis Advantage with Colemak at ~35 WPM and 90% accuracy for half a day, until when I feel that tiredness peculiar of having spoken a foreign language the whole day. Then I switch to a shitty qwerty membrane keyboard and get my day's work done. I would so much love to be able to say "I wrote this blog post with my Lily", but I have would have <em>grown a beard</em> if that was the case. Maybe when Colemak will be ingrained I will be able to give the Lily another spin. Until now, it is for sale at €400.</p>
<p><img alt="Lily58 colored keycaps" src="images/lily58-colored-keycaps.jpg"></p>How I quit smartphones2022-05-01T00:00:00+02:002022-05-01T00:00:00+02:00Stefano Ottolenghitag:thecrowned.org,2022-05-01:/i-quit-smartphones<p>I sit on the metro. The lady on my side unlocks her smartphone, scrolls up to reveal the list of installed apps, opens one at random, goes back to the home screen, and finally locks …</p><p>I sit on the metro. The lady on my side unlocks her smartphone, scrolls up to reveal the list of installed apps, opens one at random, goes back to the home screen, and finally locks the device again. All happens within 5 seconds. Still holding the phone in her hands, she shuffles in her seat and looks again out of the window. I look around – nobody has raised an eyebrow. But also, <em>everybody</em> has their smartphone in their hands. I can count the number of people who are doing something not involving their smartphone on one hand, <em>for the whole train</em>, and it's rush hour on the Stockholm metro. <strong>Then I wonder: how would people feel if all smartphones were to suddenly turn into beers?</strong> What would people think? I guess they would think the train was filled with alcoholics. And indeed, my feeling is that the train <em>is</em> filled with addicts who have crossed the line and do not even realize anymore to <em>be</em> addicts.</p>
<p>Filled with self-satisfaction and judgement towards others, I look down: my smartphone is in my hands. I realize I too am an addict, I too am a drinker. I decide I need to do something about it.</p>
<hr>
<p>One morning I dropped into an electronics store and bought a €45 dumbphone (which a friend has lovely renamed <em>wise-phone</em>). Amazingly, getting it second-hand was more expensive, and I wanted it that <em>very morning</em>, so no shipping from a distant location. The shop clerk asked me if I wanted to pay by card or cash. I was tempted to ask if he would be patient enough for me to go out and pick my <em>cheque book</em> from the <em>saddlebag</em> of my <em>horse</em>.</p>
<p>Back home, I sent a flood of WhatsApp/Telegram messages saying I wouldn't have a smartphone for at least 30 days, but that people should feel free to SMS or ring me at any time. Then I took out the SIM card and put it in what felt like a childhood experience: a Nokia with a keypad and a ridicolous amount of games installed. I left my apartment without my smartphone in my pockets for the first time in years. I went out without Internet access for the first time <em>in a decade</em>.</p>
<blockquote>
<h4 id="the-day-before-fantasizing-about-quitting-my-smartphone-i-felt-anxiety-and-worry-now-out-with-a-feather-in-my-pockets-i-felt-light-and-free-this-i-told-myself-is-how-it-feels-to-take-away-saurons-ring">The day before, fantasizing about quitting my smartphone, I felt anxiety and worry. Now, out with a feather in my pockets, I felt light and free. This, I told myself, is how it feels to take away Sauron's ring.</h4>
</blockquote>
<p><img alt="sauron ring" src="images/sauron-ring.webp"></p>
<h2 id="it-did-not-start-as-a-problem">It did not <em>start</em> as a problem</h2>
<p>Although I was not among the earliest adopters, I did buy a smartphone when many people still didn't have one. I was probably the first in my family, and such I remained for at least a couple of years. But still, having had a smartphone for more than a decade raised for me the question of <em>why</em> I came to reject it so much later in time, and why I did not have negative feelings towards it for such a long time.</p>
<p>My first device was a Galaxy Nexus in ~ 2012. At the time, there wasn't much smartphones could do better than computers: WhatsApp was barely 3 years old; all other messaging apps did not exist; almost no website was optimized for small screens. I had a high-end self-assembled desktop computer, so for sure my new phone didn't out-compete my workstation. But hey, what computer can you actually bring <em>in your pockets</em>? Already fiddling with IT as a teenager, it looked cool to feel and look <em>busy</em> replying to emails from my phone, at any time. To look up information I was wondering about. To have my documents synced there. To find the phone number of a shop <em>from my phone</em> and call straightaway. Magic. Little of this was time sensitive, or better suited to be done on my phone rather than on the computer. But at the same time, there wasn't much harm happening either. Sure, silicon and copper had been mined at the expense of the health of people in remote countries, but <em>my</em> health was <em>fine</em>.</p>
<hr>
<p>The years went by and nothing major happened. In no way did I feel drawn to my phone in the way that brings people to unlock and lock it randomly, for reasons that we'll soon explore. At some point I switched phone just because my Nexus was in pieces (I drop phones, <em>a lot</em>), and started buying second-hand. At no point in time did ditching my smartphone ever occurred to me, mostly because, with time, I had started using it for more and more purposes: I had my documents and music automatically synced on it, I could now text people through the internet, I could record myself playing the piano and send it to my teacher, etc. It was just <em>so convenient</em> on <em>so many regards.</em> However, at that time I was still spending hours reading, writing, crafting videos, developing software - the smartphone was a convenience and a luxury, it never felt like a problem.</p>
<blockquote>
<h4 id="i-was-engaged-in-so-many-activities-that-i-just-did-not-have-the-time-to-use-it-enough-to-make-it-become-a-problem">I was engaged in so many activities that I just did not have the time to use it enough to make it become a problem.</h4>
</blockquote>
<p>Then in 2018 I started traveling. I spent a summer in <a href="/a-day-tour-cork-ireland-best-things-to-see-do">Ireland</a>, a semester in Madrid, and then relocated permanently to Stockholm. My smartphone quickly transitioned from a luxury to a necessity: buying and showing tickets; maps and directions, as well as finding restaurants and cafes; connecting with the people I met; finding and participating in events and activities with quasi-strangers; keeping in touch with my partner. I even invested the time to <a href="/foss-android-apps-quest-going-google-free-oneplus-6">make my phone Google-free</a>. To build myself a life in my home city took 25 years, and I feel like <strong>the smartphone catalyzed the creation of my new life abroad</strong>.</p>
<p>At the same time, <strong>I had my phone in my hands far more than before, and far more pockets of spare time to fill</strong>. My phone quickly filled up with podcasts to listen to and articles to read. And while at the beginning I was cherry-picking the content I wanted to <em>consume</em> on my phone, <strong>it soon became littered with so much content that I could be <em>entertained</em> by it anytime, for any length of time</strong>. Why stare at the void while waiting for the metro, if I can read a Very Interesting™ article about the history of wheat? Why listen to the chattering inside a train, if I can listen to a podcast about Javascript promises (there's no promises, JS is a delusion)? Why <em>waste</em> time if I could <em>use</em> it for something? At that point, I could still argue that, even if I <em>were</em> addicted (which I would have probably denied anyway), it was still a <em>healthy addiction</em>. Or at least, it was hard to argue that my smartphone was harming my cognitive skills. In retrospect, I regard all these activities as just a tad better than regular entertainment: they are passive and only seldom contribute to a richer life experience.</p>
<p>The detonating variable still had to be added, though.</p>
<hr>
<p>It all started tumbling down pretty fast when I found myself unhappy in my workplace. I felt like I did not have enough to do, and what I had was vague and hardly actionable. When I wanted to shy away from a complicated task, I could checkout WhatsApp/Telegram/Facebook/email in the hope that somebody had texted. And since I was at it, I could also read an article. And I would re-emerge 15 minutes later with aching wrists and no accomplishments whatsoever on my bigger task, which would in turn obviously motivate me even less to tackle it. <em>Repeat</em>.</p>
<blockquote>
<h4 id="i-went-from-a-rich-schedule-of-engaging-activities-to-a-sparse-routine-of-appalling-tasks-i-tried-to-flee-these-assignments-and-who-was-there-to-welcome-me-with-open-arms-but-of-course">I went from a rich schedule of engaging activities, to a sparse routine of appalling tasks. I tried to flee these assignments, and who was there to welcome me with open arms? But of course.</h4>
</blockquote>
<p>There are countless studies suggesting that it isn't really phone usage that degrades our cognitive performance, but rather <em>the frequent context switch between tasks</em>. By jumping away from difficult tasks and tackling micro tasks on my phone, or even on my computer at some point, <strong>I created myself an environment in which it became hard to get anything done, because I had de-trained myself from focusing</strong>. I started noticing how I couldn't seem able to focus for long stretches of time anymore, not as I used to be able to do when I was in university. I could blame age, but if this is how life at 27 is already supposed to be, boy have we been cheated upon. After so many years of higher education, this felt like a defeat.</p>
<p>The same loss of cognitive skills could have happened by watching TV series every time I wanted to shy away from my work. And truth be told, I remember it happening in other circumstances: I remember breaking up with my ex and spending days in bed with a laptop and never ending seasons of Chuck. And I remember how at some point I wondered "ugh, how am I gonna break out of this?". The salvation in that case came in the form of an already-scheduled ski trip. It is very, very hard to bring your TV series addiction with you while skiing – it is feasible, but that addiction has constraints. A week later I went back home feeling okay; I avoided TV series in bed for a while and it was no big deal.</p>
<p>To be even fairer, I did not need a smartphone to end up there. As I came to discover, frequent context switches are a product of the Internet, not of smartphones, so a regular computer is enough to trigger it. <strong>The reason why smartphones are so dangerous though is that we can never leave them.</strong> The moment they become our all-round go-to tool, it becomes impossible to step away from them. You can not take a ski trip without your phone: my student card is on it; the GPS navigation to get there is on it; calls to my partner go through it. The moment we turn to our smartphone for communication and maps, reading and music, workout plans and banking... for <em>everything</em>, it becomes impossible to quit it. And then it suddenly develops the <em>potential</em> of becoming a gigantic problem, in the form of a <em>constant distraction</em>.</p>
<p><img alt="smartphone addiction children" src="images/smartphone-addiction-children.webp">
<a href="https://www.verdict.co.uk/smartphone-addiction-children/">Smartphone addiction symptoms found in a quarter of children</a></p>
<h2 id="how-did-it-happen-you-sure-have-some-willpower-dont-you">How did it happen? You sure have some willpower, don't you?</h2>
<blockquote>
<h4 id="nobody-ever-plans-to-develop-an-addiction-it-happens-unconsciously-given-the-right-circumstances-when-we-are-going-through-a-rough-time-in-our-lives-and-there-is-something-that-seems-capable-of-making-that-time-just-a-little-bit-easier-well-fall-for-it">Nobody ever plans to develop an addiction. It happens unconsciously, given the right circumstances. When we are going through a rough time in our lives, and there is something that seems capable of making that time just a little bit <em>easier</em>, we'll fall for it.</h4>
</blockquote>
<p>Addicts have often very normal life paths, except something that pushed them towards the addiction in a very innocent way, at the beginning. Boredom at school. A break up. Job loss. An incident. Whatever it is, sources of consolation are very welcome, in the form of booze, smoke, painkillers. Nobody plans to become addicted to them, they just want a relief from a tough time. You need a lot of discipline or extreme awareness to realize when the relief is turning into another problem.</p>
<p><strong>The great danger with smartphones is that nobody is immune</strong>, and that it's dramatically easy to slip into it. Nobody is immune from any addiction, but you have to take an active step to slip into a more traditional one. You have to physically go in a shop and buy cigarettes, and then start smoking, which by the way will feel awful at the beginning (I'm told). You have to go to the store and buy cans of beer. You have to get a chronic pain and have a doctor prescribe painkillers. After all this, you can develop the addiction, but an active step is needed to begin with.</p>
<p><strong>The difference with smartphones is that virtually anybody who is not <em>starving</em> has an addiction lurking at them all the time</strong>. Phone addiction is triggered by much smaller reasons than the major historical addictions. You just need to be looking for a <em>distraction</em> from a boring moment of your day. And who doesn't have a boring moment in their days? That makes it quite easy to start. And once you start turning to your phone as a <em>distraction from life</em>, the road is paved. When our brain starts realizing that the phone is an easy source of dopamine and reward, it will push to have it as often as possible. Why engage in a difficult task when you can get easy dopamine? Give way to such a behavior a few times, and it will become increasingly difficult to tackle hard tasks, because you brain has become used to getting rewards to much simpler tasks, and will crave for it.</p>
<p>I can really see this having happened to me, looking back. After moving, I was alone and a bit aimless, especially after the enthusiasm of the first few months faded away. I turned to my phone more and more often, even for what I felt were meaningful tasks: reaching out to my partner and connections in my home country; taking pictures; listening to music; reading articles. This was still fine, and is probably the innocent situation for many people. However, when my work became not engaging, the road was paved for me to get an easy distraction: pick up my phone. The more I did it, the more bound to my phone I became. But of course I never meant to become addicted - it just happened. Moreover, it is hugely draining to constantly exercising willpower, in order to restrain phone usage.</p>
<blockquote>
<h4 id="resisting-temptation-is-cognitively-very-demanding-its-much-easier-to-do-away-with-the-temptation-altogether"><em>Resisting temptation</em> is cognitively very demanding: it's much easier to do away with the temptation altogether.</h4>
</blockquote>
<h2 id="but-is-it-really-a-problem-if-i-am-addicted-to-my-phone">But is it really a problem if I am addicted to my phone?</h2>
<p>You can draw your own conclusion, but is there any <em>positive</em> definition of addiction? I don't think so, and if you do, it's only because you haven't come to terms with the fact that your addiction is damaging you.</p>
<p>The problem is also that most often it takes a lot of time and energy for addicts to realize and admit they <em>have</em> an addiction problem. I have met many people who were hugely more addicted than me, and they'd still stare at me blankly the times I picked up a call on my wise-phone: they just couldn't understand why (and how) I would do without a smartphone. Brief conversations revealed that they did realize, on some level, that they were addicted, but they dismissed the matter lightly. The problem is also that the awareness that this is a <em>mass</em> addiction and the cause for loss of focus is only slowly arising now: in the same way as it took decades for smoking to be regarded basically as a self-harm activity, it will take decades for the same to happen with smartphones. After all, it's hard to counteract an addiction that doesn't cause cancer and that's so widely widespread already.</p>
<blockquote>
<h4 id="for-me-it-also-came-down-to-the-kind-of-person-i-wanted-to-be-and-the-behaviors-i-wanted-to-have">For me, it also came down to the kind of person I wanted to be, and the behaviors I wanted to have.</h4>
</blockquote>
<p>I have witnessed (and I still do on a daily basis) worrying behaviors, that I did not want to start engaging in. People randomly unlocking and locking their phone. Fathers out for dinner with two children spending the night with their phone, grunting at the questions the playful kids would ask. People picking up their phone as soon as the spontaneous conversation among a group friends halted for a few seconds. People sitting among people, their phone in their hands, asking "what are you talking about?", and not even hearing the reply. People showing others a silly Instagram video, then getting dragged from story to story. I have experienced each of these as a spectator, and it made me feel so much disconnected from the people and even more alone. At some point I <em>decided</em> that my life priorities would be a) social relationships; b) my physical and mental well-being; and having a computer in my pockets just didn't seem to aid neither.</p>
<p><img alt="Wall-e" src="images/wall-e.jpg"></p>
<h2 id="but-how-do-you-do-without-mapsmusicnfcwhatever">"But how do you do without maps/music/NFC/whatever??"</h2>
<blockquote>
<h4 id="you-find-a-solution-instead-of-being-found-a-solution-constraints-foster-creativity-you-also-need-to-accept-that-youll-have-to-live-at-a-slower-tempo-live-adagio-and-as-all-things-in-life-if-you-welcome-it-rather-than-fight-it-youll-have-a-much-more-pleasant-ride">You find a solution, instead of <em>being found</em> a solution. Constraints foster creativity. You also need to accept that you'll have to live at a slower tempo. <a href="/life-adagio">Live <em>adagio</em></a>. And as all things in life, if you <em>welcome</em> it, rather than <em>fight</em> it, you'll have a much more pleasant ride.</h4>
</blockquote>
<hr>
<p><strong>To navigate to places</strong>, there's many options. If it's enough well-reknown, get as close to it as you can and then start <em>asking people</em> for directions. This is how my parents use to do, that's how <em>I</em> used to do when I was young. People are happy to be helpful (would <em>you</em> grunt if somebody asked <em>you</em> help?), and worst case they'll pull out their phone and show you the map. If you can plan all in advance, draw a map on a small notebook. I was interviewing for jobs shortly after I quit my smartphone, and I had to reach offices in all sorts of unknown locations – these strategies worked every time, and more often than not I've had a chat or a laugh with those strangers.</p>
<p>Maps and navigation are extremely convenient. I cannot anymore afford the luxury of scheduling a trip with no margin for being late, nor can I go somewhere unknown with no prior planning. I have to look up the map beforehand, take notes if needed, and always make sure I have all the relevant information with me when I will be out (when is the last time you <em>wrote down</em> an address?). If I venture in an area of the city that I completely unfamiliar with, I plan to get there half an hour earlier. I have to <em>allow myself to get lost</em>.</p>
<hr>
<p><strong>For music</strong>, the €45 Nokia I bought has Bluetooth and a music player (god, it even has 4G!). Sure: no Spotify, but I don't mind the requirement of having to be <em>intentional</em> over what music I want to listen to. It's a feature, not a bug. If the experience of navigating your library on a shitty device is too painful (and I can say: it <em>is</em>), then use your smartphone as an iPod Touch only for offline music. I do that for podcasts as well. Just work around the constraints.</p>
<hr>
<p><strong>For communication</strong>, welcome back to the world of voice calls. Texting is ridiculously slow. I write in 3 languages every day, and I can only get T9 predictions in 2 languages. This means that it's okay to write in 2 languages, even if it's quite clumsy to switch between the two, but for the third language I have to bang the keys multiple times and write each letter of every word. So forget SMS to talk about your day, plan activities, and the like. You'll appreciate why your father was always ringing you and complaining about texting. For me texts have become pure communication means to say "I am 5 late", or "Meet at X at Y". For anything more than that, I just <em>ring</em> people. It's so much easier, and faster. It sounds overly nostalgic, but I also just <em>enjoy</em> hearing the <em>voice</em> of the person on the other end.</p>
<p>Of course, not all of my acquintances/friends have been fine with this. <strong>Without a smartphone, it's hard to have shallow relationships</strong>. I can not be added to group chats, and the people who would randomly text me once every other month to greet and ask how I was doing hardly text anymore. It is a bit of a loss, but at the same time I want to have meaningful deep relationships with people, and I am not sure I am losing that much by not having shallow transient relationships. However, I did address this by checking WhatsApp/Telegram Web every day or so.</p>
<hr>
<p><strong>For everything else</strong> ... you don't need anything else. Sometimes people come up with questions like "But how do you pay in stores??", and it's my turn to be bewildered. You just use your plastic debit card, as 80% of the world still does.</p>
<blockquote>
<h4 id="theres-mountains-of-non-existent-problems-and-in-truth-ive-most-often-found-them-imaginary-reasons-to-convince-yourself-that-you-cannot-do-without-a-smartphone-you-can">There's mountains of non-existent problems, and in truth I've most often found them imaginary reasons to convince yourself that you cannot do without a smartphone. You can.</h4>
</blockquote>
<p>Society is moving towards being smartphone-first, and if we want to fight against that, we needed to do that yesterday. With the words of one of its employees, "SL [the company behind Stockholm's public transport service] wishes that everybody would have <em>their app</em>." It's hard to login into a bank account without a smart device and <em>their app</em>.</p>
<p>But today is not too late either. Cities are still full of large size maps with a helpful "You are here" marker, street names are still hung on buildings, and in smart cities each contains information on the house numbers you will find on each side and in each block. While there's pressure for our society to become needlessly techy, our environments are still largely designed to work <em>without</em> tech (and for that we have to thank pensioners), and we can leverage that to oppose that movement.</p>
<h2 id="the-review-15-years-later">The review, 1.5 years later</h2>
<p>Initially, I told everybody I would run the experiment for 30 days and then re-evaluate the situation. It's now 1.5 years later, and I'm proud of myself in looking back and ackwnoledging that I've been <em>clean</em> for all that time. There's only been two exceptions:</p>
<ul>
<li>bike packing trips where I needed internet access to follow GPS tracks from the phone,</li>
<li>one very hectic work trip abroad where I could not plan all steps in advance, and <em>needed</em> a computer in my pockets.</li>
</ul>
<p>For all the other circumstances I was right: <em>I don't need a computer in my pockets</em> every day, all day. I already work in IT, and spend countless hours in front of a screen. Being screen-free out of home is just <em>healthy</em>.</p>
<p>Also, the absence of a smartphone has catalyzed new relationships and connections that I doubt would have blommed otherwise. The girl at a classical music concert sitting beside me, opening her flip-phone ("You're the second person I know without a smartphone beside me!"). The couple of pensioners guiding me to a magnolia garden I couldn't find, and telling me about their life on the way. The girl at a department lunch leaving me her number hitting physical keys, "unlocking childhood memories". A friend supporting me in my decision, giving me directions on the phone on where to find the bus lest I miss it.</p>
<p>Don't get me wrong: <strong>it is a privilege to be able to quit smartphones</strong>. It is a privilege to <a href="/life-adagio">live adagio</a>. However, a lot has also to do with the environment we're immersed in and what we find acceptable. When it comes out that I don't have a smartphone among my tech connections (each having at least one smartwatch), people look at me as if I said <em>I live barefoot</em>. When the same comes up among my musician/artist connections, they shrug and just say "I'll text/call you". For them, I'm not that special.</p>
<h2 id="are-there-even-any-wisephones-for-sale-these-days">Are there even any wisephones for sale these days?</h2>
<p>To my surprise, I discovered that there is a thriving market of wisephones. This is mainly divided in three segments:</p>
<ol>
<li>Modern cheap phones tailored to pensioners (my Nokia belongs to this segment). These are not necessarily dumbphones, but rather phones designed to be simple to use. The high-end ones reach up to €150, and have WhatsApp and Facebook built-in. They are often 4G dual-sim Bluetooth phones, so that you can have a <em>modern</em> phone experience. Some of these can be found second-hand from pensioners who "had been given it by their children, but felt like they were too complicated to use" (hard to blame them). Some of them have beautifully gigantic keys.</li>
<li>Second-hand old phones. The problem with these old phones is that a) your SIM is most likely too small to fit, so you'd need a clunky adapter; b) some carriers have (or plan to) dismiss support for old 2G communication protocols, and eventually 3G as well. So if you simply pick up a phone from 15 years ago, it's likely it won't work.</li>
<li>Hipster modern dumbphones. These, like <a href="https://www.thelightphone.com/">The Light Phone</a>, go as up as $299 and are smartphones of some sort dumbed down so that they cannot become too much of a distraction (for ex, you have Internet access, but only to do hotspot to another device; no built-in browser, etc).</li>
</ol>
<p><img alt="Nokia 222" src="images/nokia-222.jpg"></p>
<h2 id="recommended-readings">Recommended readings</h2>
<ul>
<li><a href="https://stolenfocusbook.com/">Stolen Focus: Why You Can't Pay Attention - And How to Think Deeply Again</a></li>
<li><a href="https://www.goodreads.com/book/show/9778945-the-shallows">The Shallows: What the Internet is doing to our Brains</a></li>
<li><a href="https://www.goodreads.com/book/show/54326146-a-world-without-email">A World Without Email: Reimagining Work in an Age of Communication Overload</a></li>
<li><a href="https://www.goodreads.com/book/show/41016873-full-catastrophe-living">Full Catastrophe Living: Using the Wisdom of Your Body and Mind to Face Stress, Pain, and Illness</a></li>
<li><a href="https://www.goodreads.com/book/show/35209767-how-to-break-up-with-your-phone">How to Break Up with Your Phone: The 30-Day Plan to Take Back Your Life</a> (although I don't agree with their plan: I believe more in quitting cold turkey)</li>
<li><a href="https://rrchnm.org/essay/the-bookless-future-what-the-internet-is-doing-to-scholarship/">The Bookless Future: What the Internet is Doing to Scholarship</a></li>
</ul>Deform an unstructured mesh (or how a good angle dramatically simplifies a problem)2022-04-23T00:00:00+02:002022-04-23T00:00:00+02:00Stefano Ottolenghitag:thecrowned.org,2022-04-23:/deform-an-unstructured-mesh-or-how-a-good-angle-dramatically-simplifies-a-problem<p>In the attempt of coupling my PhD's <a href="https://github.com/TheCrowned/FEFFII" title="FEFFII">Finite Elements Framework for Fjord Ice-ocean Interactions (FEFFII)</a> with an ice code, I stumbled upon the issue of deforming the ocean mesh. If a simulation stretches over a …</p><p>In the attempt of coupling my PhD's <a href="https://github.com/TheCrowned/FEFFII" title="FEFFII">Finite Elements Framework for Fjord Ice-ocean Interactions (FEFFII)</a> with an ice code, I stumbled upon the issue of deforming the ocean mesh. If a simulation stretches over a long span of time, the geometry of the ice is likely to change due to melting or re-freezing, so that the interface between the ice and the ocean can become different. If the ice melts, it gives way and leaves room, and that new space should be filled with "new" ocean. However, the ocean mesh cannot be dragged towards the ice through some simple transformation, because the new ice geometry can be quite irregular. If the mesh is instructured, the problem becomes even more complicated.</p>
<p>What follows is my end result, where a 2D unstructured mesh is deformed to match a new boundary layout. I will now detail how I first tackled the problem from the wrong angle, and then how I did solve it with a technique that generalizes to 3D meshes as well. The final algorithm is implemented as part of the <a href="https://github.com/TheCrowned/FEFFII" title="FEFFII framework">FEFFII framework</a>, in <a href="https://github.com/TheCrowned/FEFFII/blob/77c4bea52f2a798bf758ecaee77337ce41a971a0/feffi/boundaries.py#L219" title="feffii.boundaries.deform_mesh"><code>feffii.boundaries.deform_mesh</code></a>.</p>
<p><img alt="2D Mesh Deformation" src="images/mesh-deformation-2d.gif"></p>
<h2 id="a-false-start">A false start</h2>
<p>At first, I thought that since the ocean and ice meshes had an exact common boundary at the beginning of the simulation, it should be possible to keep them in sync by monitoring the evolution of individual nodes, sort of. The idea was that, if the ice profiles changes a bit, I could check where the <em>first</em> node had moved towards, and adjust the <em>first</em> node of the ocean boundary accordingly. Repeating the same procedure for the <em>second</em> node, and then the <em>third</em>, and so on, and I could cover the whole boundary and move the ocean mesh to mimick how the ice had changed.</p>
<p>There were (at least) two issues with this approach:</p>
<ul>
<li><strong>the ice mesh and ocean mesh could have different resolutions</strong>. This means that, while the first nodes of the two meshes would always match, the second node of the two meshes could have different coordinates, so I could not simply move the second node of the ocean mesh with respect to the second node of the ice mesh. This did not seem too much of a big deal: I could smoothly interpolate the change from two ice nodes and spread it over the many ocean nodes found in between.</li>
<li><strong>the meaning of <em>first, second, third</em></strong>, etc, are straightforward on a line (the boundary of a 2D mesh), but <strong>lose any clear sense in two dimensions</strong>. What is the <em>first</em> point in the surface of a square? While there is no way of putting an ordering on a continuous 2D surface (theorem), it is possible to do so on a <em>discrete</em> surface, which is enough for any application. But this would require employing space-filling curves to estabilish a mapping between a line and a surface, blowing up the complexity of the project by an enourmous degree! (This point would have not mattered if I only cared about 2D meshes. However, since I was looking for a technique that would generalize to 3D meshes, I had to care.)</li>
</ul>
<h2 id="a-good-solution">A good solution</h2>
<p>A different approach is <em>order-agnostic</em>, one that aims at moving each ocean boundary node without caring of <em>where</em> it is located. Intuitively, we can <strong>think of each point of the ocean boundary being pulled from its 2 or 3 closest neighbors with force proportional to their proximity</strong>. More formally, the idea is to scan the ocean boundary nodes and, for each node <span class="math">\(x\)</span>,</p>
<ul>
<li>select <span class="math">\(k\)</span> closest neighbours among the ice boundary nodes, with <span class="math">\(k\)</span> = 2 or 3 depending on whether mesh is 2D or 3D;</li>
<li>compute a mean of these closest neighbors, weighted (inversely) with respect to the distance from <span class="math">\(x\)</span>;</li>
<li>move <span class="math">\(x\)</span> to the newly-computed mean.</li>
</ul>
<p>This procedure works both for 2D and 3D meshes and makes no assumptions on the structure of the mesh: <strong>all that is needed is a list containing the points of the new <em>goal</em> ice mesh</strong>. Since the mesh does not acquire nor lose any nodes, it is possible to deform it in this way and still solve PDEs on them that require time stepping.</p>
<p>What follows is a deformation example in 3D and a 2D example where the ocean velocity profile keeps evolving <em>while</em> the mesh changes over subsequent time steps.</p>
<p><img alt="3D Mesh Deformation" src="images/mesh-deformation-3d.gif"></p>
<p><img alt="2D Mesh Deformation velocity" src="images/mesh-deformation-2d-vel.gif"></p>
<script type="text/javascript">if (!document.getElementById('mathjaxscript_pelican_#%@#$@#')) {
var align = "center",
indent = "0em",
linebreak = "false";
if (false) {
align = (screen.width < 768) ? "left" : align;
indent = (screen.width < 768) ? "0em" : indent;
linebreak = (screen.width < 768) ? 'true' : linebreak;
}
var mathjaxscript = document.createElement('script');
mathjaxscript.id = 'mathjaxscript_pelican_#%@#$@#';
mathjaxscript.type = 'text/javascript';
mathjaxscript.src = 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.3/latest.js?config=TeX-AMS-MML_HTMLorMML';
var configscript = document.createElement('script');
configscript.type = 'text/x-mathjax-config';
configscript[(window.opera ? "innerHTML" : "text")] =
"MathJax.Hub.Config({" +
" config: ['MMLorHTML.js']," +
" TeX: { extensions: ['AMSmath.js','AMSsymbols.js','noErrors.js','noUndefined.js'], equationNumbers: { autoNumber: 'none' } }," +
" jax: ['input/TeX','input/MathML','output/HTML-CSS']," +
" extensions: ['tex2jax.js','mml2jax.js','MathMenu.js','MathZoom.js']," +
" displayAlign: '"+ align +"'," +
" displayIndent: '"+ indent +"'," +
" showMathMenu: true," +
" messageStyle: 'normal'," +
" tex2jax: { " +
" inlineMath: [ ['\\\\(','\\\\)'] ], " +
" displayMath: [ ['$$','$$'] ]," +
" processEscapes: true," +
" preview: 'TeX'," +
" }, " +
" 'HTML-CSS': { " +
" availableFonts: ['STIX', 'TeX']," +
" preferredFont: 'STIX'," +
" styles: { '.MathJax_Display, .MathJax .mo, .MathJax .mi, .MathJax .mn': {color: 'inherit ! important'} }," +
" linebreaks: { automatic: "+ linebreak +", width: '90% container' }," +
" }, " +
"}); " +
"if ('default' !== 'default') {" +
"MathJax.Hub.Register.StartupHook('HTML-CSS Jax Ready',function () {" +
"var VARIANT = MathJax.OutputJax['HTML-CSS'].FONTDATA.VARIANT;" +
"VARIANT['normal'].fonts.unshift('MathJax_default');" +
"VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
"VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
"VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
"});" +
"MathJax.Hub.Register.StartupHook('SVG Jax Ready',function () {" +
"var VARIANT = MathJax.OutputJax.SVG.FONTDATA.VARIANT;" +
"VARIANT['normal'].fonts.unshift('MathJax_default');" +
"VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
"VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
"VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
"});" +
"}";
(document.body || document.getElementsByTagName('head')[0]).appendChild(configscript);
(document.body || document.getElementsByTagName('head')[0]).appendChild(mathjaxscript);
}
</script>What do mathematicians do? Inside a mathy mind (3b1b Summer of Math Exposition)2021-10-23T00:00:00+02:002021-10-23T00:00:00+02:00Stefano Ottolenghitag:thecrowned.org,2021-10-23:/mathematicians-inside-mathy-mind-3b1b-summer-math-exposition<p>I took part to the first <a href="https://www.3blue1brown.com/blog/some1">Summer of Math Exposition from 3blue1brown</a>! Check out <a href="https://www.youtube.com/watch?v=2zvGpDkxMd4">the video</a> I made!</p>How to implement the 6-hour workday in a company2021-05-02T00:00:00+02:002021-05-02T00:00:00+02:00Stefano Ottolenghitag:thecrowned.org,2021-05-02:/implement-6-hour-workday-company<p>It is difficult to argue that the current, wide popular 8-hour workday in cognitive-demanding jobs is in any way <em>efficient</em>. It is bad both for employees <em>and</em> employers. Quoting the <a href="https://hbr.org/2018/12/the-case-for-the-6-hour-workday">Harvard Business Review</a>,</p>
<blockquote>
<p>Many of …</p></blockquote><p>It is difficult to argue that the current, wide popular 8-hour workday in cognitive-demanding jobs is in any way <em>efficient</em>. It is bad both for employees <em>and</em> employers. Quoting the <a href="https://hbr.org/2018/12/the-case-for-the-6-hour-workday">Harvard Business Review</a>,</p>
<blockquote>
<p>Many of today’s organizations sabotage flow by setting counter-productive expectations on availability, responsiveness, and meeting attendance, with <a href="https://theblog.adobe.com/email/">research by Adobe</a> finding that employees spend an average of six hours per day on email. Another <a href="https://blog.dscout.com/mobile-touches">study</a> found that the average employee checks email 74 times a day, while people touch their smartphones <a href="https://blog.dscout.com/mobile-touches">2,617 times a day</a>. Employees are in a constant state of distraction and hyper-responsiveness.</p>
</blockquote>
<p>This is insane. And if you yourself think that you do indeed work productively 8 hours a day, think again. I know nobody who will honestly say that they get productive work done 8 hours a day. Three options there:</p>
<ul>
<li>we do work 8 hours, but not getting 8 hours of real work done (someone said meetings?);</li>
<li>we are at the desk for 8 hours, but we don’t work for that long (someone said procrastination and non-work related tasks?);</li>
<li>we do work productively for 8 hours, but that is <strong>not sustainable.</strong> When I truly get demanding stuff done for 8 hours, the strain is so high that I take half the day afterwards off. So it is technically true that people <em>can</em> work productively for 8 hours, but only at the cost of burning out in a couple years. (I have had the experience of truly working long hours in my youth, only at the price of feeling I couldn’t get any meaningful work done for <em>months</em> after a couple years of that routine.)</li>
</ul>
<p>Once, when coming back home from work, I started wondering: <strong>how would it be possible to reduce the amount of hours a company expects employees to work, and yet retain the same output?</strong> How, if I had a company, would I achieve this? How could I have happier employees with more free time and yet a successful firm?</p>
<h2 id="the-problem-with-the-8-hours-workday">The problem with the 8 hours workday</h2>
<p>My thinking stem from two assumptions:</p>
<ol>
<li><strong>we can only expect to work productively 2-4 hours a day</strong>, with the exact amount depending on boundary conditions (after all, there are bad, better and good days). I would not expect anybody to focus deeply for more than 4 hours a day. Like, <em>true focus for 4 true hours</em>. Not fragile focus interrupted by dling of emails or random questions by passers-by.</li>
<li><strong>we are often asked to be in</strong> (often long) <strong>meetings where our presence is not of severe importance</strong>, or that are altogether useless for the whole team.</li>
</ol>
<p>Clearly, point 1 is the time when the company goes forward and makes money, since it is the time work gets actually done. <em>We want to protect those hours.</em> Point 2 is the time no employee looks forward to. It is customary to allocate time for point 2 just to keep employees busy, relying on a well-spread disrespect for other people’s time. <em>These hours we want to peel away.</em></p>
<p>Of course though, every company must also deal with the <strong>biggest challenge of humankind collaboration</strong>: <em>communication</em>. Every employee is a separate human entity, whose brain is detached from all other colleagues’ ones. <em>Information needs to be shared among people in an effective manner</em>, i.e. in a way in which others understand and can act as a result of the exchanged information. <em>This is by far the most difficult part of any job: getting what you have in your head out of it and into someone else’s head.</em> The value of an employee is often in direct proportion to his skill in this field.</p>
<p>To make it harder, <em>there is an outrageous amount of oral communication happening these days in (tech) companies</em>. When you think that you can have effective technical communication verbally, think that even USA presidents have scripts when delivering speeches. Any verbal communication needs to be carefully planned, otherwise it just ends up being wasted time.</p>
<p>We so often require people to be in meetings just because we are too lazy to write a well-structured and -thought text that everybody can read when they want, at the pace they want, and to which they can refer to a month later. Oral communication is instead lost, and, especially if the company has a high turnover, the same stuff needs to explained multiple times.</p>
<p>So how do we fix this all?</p>
<h2 id="the-recipe-for-the-6-or-less-hours-workday">The recipe for the 6 (or less) hours workday</h2>
<p>As often, an efficient schedule is our best friend. As part of their onboarding, employees will be asked for several time slots:</p>
<ol>
<li><strong>2 hours/day when they feel the most productive,</strong> and feel like they can put deep focus in their work. They can assume that <strong>this time will never be interrupted</strong> or scheduled for other activities, unless something really big is up.</li>
<li><strong>2 hours/day when they feel still productive, but to a series-B extent</strong>. These hours are still valuable and important, and an effort will be made to protect them, but they can be disrupted in some circumstances (see below).</li>
<li><strong>2 slots of 1 hour/day when they can engage in communication</strong> and are available for meetings. These should be during somewhat common working hours, and one must be at a fixed time (the idea being that anybody can do their individual work whenever they want, but collaboration needs to happen during the times when most are supposed to be active). <em>It is vital to understand that these hours <strong>do not have</strong> to be filled!</em></li>
</ol>
<p><strong>A real world example</strong>: for a software engineer, the 2 most productive hours could be spent designing the solution to a problem and translating it into code; the 2 B-series hours could be spent writing documentation (both internal and external) and engaging in written communication; 1 communication slot could be used to troubleshoot an issue with a colleague.</p>
<p>These availability slots should be flexible but fixed in advance for at least a couple weeks, although everybody should be free to change them with no notice if no meeting or shared task has yet been planned for a given slot. <em>In a nutshell, it is like saying to employees: “you work when you want and get stuff done, but I need to roughly know when that happens so I can reach out to you if we need to hear from you for some reason.”</em></p>
<p>The only highly suggested routine tip (not to call it a requirement) is that <strong>employees should begin their week by planning it</strong>. Not just when their slots are placed, but what they plan to do during them (of course with some flexibility: random stuff pops up).</p>
<h3 id="how-to-arrange-meetings">How to arrange meetings</h3>
<p>But still, some meetings need to happen. In what way should they be scheduled then?</p>
<p>We live in 2021, and the ridiculous email exchange or doodle polling of “when can we schedule this meeting?” needs to end. Most importantly, this approach hides an often neglected assumption: <em>that it is necessary that you participate to this meeting</em>. However, it often is not, and, to participate, we take time away from more important tasks.</p>
<p>Since there is already some sort of internal tool that will track employees availability time slots, it is not hard to add a meeting scheduler on top. You want a meeting with Joe within a couple of days? Fire up that internal tool, which will show you overlapping times between your and Joe’s 1-hour slots. If none are available within the time range you want, tweak the severity of the matter to include first your B-series productivity hours, then his as well. <strong>You should however receive a notice if you schedule more than 3 meetings a month within productive</strong> hours, so that it does not become a custom to say that “your matter is important”. (Technically, one simply needs to solve an optimization problem to find the best meeting time.)</p>
<p>If you want to involve more people in the meeting, the same system applies. It will naturally be harder to intersect everybody’s availability, but <em>it should be so</em>, and it should remind you that you are requesting other people’s <em>time</em>. Are they really all needed? What contributions will they give? Is this a discussion that can happen in written form? Always remember that, if oral communication between two people is hard, the difficulty increases more than linearly when more people are added. If you call for a meeting with 5 people, you should know that there is a solid 80% probability that you are wasting everybody’s time.</p>
<p>It is apparent that <strong>on a given day, an employee might end up having some free hours</strong> resulting from no bi-directional communication happening in the 1 hour slots. <strong>This is fine,</strong> and we should encourage employees to simply take them off. This would be a huge additional perk that would invite top-achievers at the company (besides the obvious perk of being expected to <em>not</em> work more than 6 hours)!</p>
<p><strong>What about meetings longer than 1 hour?</strong> <br>
A full hour is already an incredibly long time. If it does not fit, then either the meeting was poorly organized (<em>you</em> are taking people’s time, so <em>you</em> should organize and lead the discussion!), or the discussion at hand should not be had orally. I cannot emphasize enough how much (long) written documentation is superior with respect to oral conversations.</p>
<p><strong>What about emergencies/company wide meetings?</strong> <br>
Then productive hours can be impacted. <strong>For very sporadic events, it would also be fine to schedule something out of the 6 hours</strong> employees have scheduled for themselves. But do you really need to have that monthly review with the team/company? Is the daily standup really needed (hint: no)?</p>
<p><strong>How does Slack enters the picture?</strong> <br>
It does not. It is basically entertainment at work. What can not be fixed by email, as it requires an interactive communication, should be taken by phone.</p>Setup OpenWRT on Raspberry Pi 3 B+ to avoid data trackers2020-09-18T00:00:00+02:002020-09-18T00:00:00+02:00Stefano Ottolenghitag:thecrowned.org,2020-09-18:/setup-openwrt-on-raspberry-pi-3-b-to-avoid-data-trackers<p>Thanks to my local ISP for a shitty black-box running their proprietary vomit, but no thanks. I had a spare Raspberry Pi and the urge of having my home internet powered by open source software …</p><p>Thanks to my local ISP for a shitty black-box running their proprietary vomit, but no thanks. I had a spare Raspberry Pi and the urge of having my home internet powered by open source software.</p>
<p>There are already lots of valuable resources on setting up OpenWRT on a Raspberry Pi as a home router. To cite some:</p>
<ul>
<li><a href="https://www.zahradnik.io/raspberry-pi-as-a-home-router">Raspberry Pi as a home router</a></li>
<li><a href="https://openwrt.org/toh/hwdata/raspberry_pi_foundation/raspberry_pi_3_bplus">OpenWrt Raspberry Pi 3 B+ page</a></li>
<li><a href="https://downloads.openwrt.org/snapshots/targets/bcm27xx/bcm2710/">OpenWrt Raspberry Pi 3 B+ snapshots page</a> (there is a stable compatible version, but does not support WiFi 2.4 Ghz, which makes the signal quite stronger)</li>
<li><a href="https://forum.openwrt.org/t/18-06-on-raspberry-pi-3-b/18670/58">OpenWrt forum topic discussion on Raspberry Pi 3 B+</a></li>
</ul>
<p>Upon first setup, I had issues connecting the Raspberry to my Ubuntu laptop and make the first setup. Only later did I learn that I could have simply edited the config file on the microSD and avoided the pain, but anyway, I was able to have it work through ethernet on my laptop by <a href="https://askubuntu.com/a/897544">setting the ethernet interface to be unmanaged</a>.</p>
<p>I found the wifi adapter of the Raspberry to be strong enough to cover a 3-room apartment, and also go outside. When all the confi was right, I just disable DHCP on my ISP router, enable DMZ to the Raspberry (which had a static address by then), and let the Raspberry be the only DHCP server in the network.</p>
<p>You can <a href="https://forum.openwrt.org/t/detect-double-dhcp-server-in-network/45059/3">check if</a> the Raspberry really is the only DHCP server by running the command <code>udhcpc -n -q -s /bin/true -t 1</code>. You should get <code>udhcpc: no lease, failing</code> as last line of output; if you don't, then there is still another DHCP server active in the network.</p>
<h2 id="ads-and-trackers-blocking-through-dnsmasq">Ads and trackers blocking through dnsmasq</h2>
<p>I then wanted to block ads and data trackers through a DNS filter. Starting from <a href="https://www.leowkahman.com/2017/07/23/block-ads-with-openwrt-dnsmasq/">this</a>, I eventually ended up using <a href="https://www.github.developerdan.com/hosts">the first of these lists</a> as DNS blacklist, with a handy bash script that would update the list on a regular basis. Note that data in <code>/tmp</code> is lost on reboot, and data <em>not in</em> <code>/etc</code> is lost on firmware re-flashing.</p>
<div class="highlight"><pre><span></span><code><span class="ch">#!/bin/sh</span>
<span class="nb">set</span> -e
mkdir -p /opt/dnsmasq-blocklist
curl -s --max-filesize <span class="m">7242880</span> -o /opt/dnsmasq-blocklist/developerdan.hosts <span class="s2">"https://www.github.developerdan.com/hosts/lists/ads-and-tracking-extended.txt"</span>
/etc/init.d/dnsmasq restart
</code></pre></div>
<p>If you use some custom DNS, like OpenDNS, make sure <a href="https://dnsleaktest.com/">they are actually working</a>.
<a href="https://paul.is-a-geek.org/2015/06/dns-based-adblock-using-openwrt-opendns-and-dnsmasq/">DNS based adblock using OpenWRT, OpenDNS and dnsmasq</a> is another good resource.</p>
<h2 id="vpn">VPN?</h2>
<p>I first set out to set up a VPN tunnel through ProtonVPN at router level, but only later realized <a href="https://schub.wtf/blog/2019/04/08/very-precarious-narrative.html">how much un-privacy safe this actually is</a>, so I backtracked everything. What might instead be valuable is to host your own WireGuard VPN on the Raspberry to connect to your network from outside, and avoid insecure networks (and still escape tracking, if you use your home router DNS filtering). Some resources on that:</p>
<ul>
<li><a href="https://engineerworkshop.com/blog/how-to-set-up-wireguard-on-a-raspberry-pi/">How to Set Up WireGuard on a Raspberry Pi</a></li>
<li><a href="https://www.reddit.com/r/openwrt/comments/bahhua/openwrt_wireguard_vpn_server_tutorial/">OpenWRT WireGuard VPN Server Tutorial</a></li>
<li><a href="http://chrisbuchan.co.uk/computing/wireguard-setup-openwrt/">WireGuard setup OpenWrt</a></li>
</ul>
<p>After configuration, make sure the IP your devices appear to connect from really is your home's.</p>
<h2 id="sysupgrade">Sysupgrade</h2>
<p>When running on the snapshot image, you'll often have to update (even just for the fact that otherwise it is not possible to install new packages). This will clear up installed packages, albeit their configs will be retained. All files within <code>/lib/upgrade/keep.d</code>, paths listed inside <code>/etc/sysupgrade.conf</code> and listed by command <code>opkg list-changed-conffiles</code> will be retained; everything else deleted. To make it less of a pain, there is a <a href="https://github.com/richb-hanover/OpenWrtScripts#opkgscriptsh">nice script</a> that will dump out a list of currently installed packages and easily reinstall it after upgrading. Here is the <a href="http://downloads.openwrt.org/snapshots/targets/bcm27xx/bcm2710/openwrt-bcm27xx-bcm2710-rpi-3-ext4-sysupgrade.img.gz">Snapshot upgrade URL for Raspberry 3B+</a>.</p>
<p>The list of commands I ran is:</p>
<div class="highlight"><pre><span></span><code><span class="n">sh</span><span class="w"> </span><span class="o">/</span><span class="n">opt</span><span class="o">/</span><span class="n">opkg</span><span class="o">-</span><span class="n">update</span><span class="o">-</span><span class="n">script</span><span class="o">.</span><span class="n">sh</span><span class="w"> </span><span class="o">-</span><span class="n">v</span><span class="w"> </span><span class="n">write</span><span class="w"></span>
<span class="n">wget</span><span class="w"> </span><span class="n">http</span><span class="p">:</span><span class="o">//</span><span class="n">downloads</span><span class="o">.</span><span class="n">openwrt</span><span class="o">.</span><span class="n">org</span><span class="o">/</span><span class="n">snapshots</span><span class="o">/</span><span class="n">targets</span><span class="o">/</span><span class="n">bcm27xx</span><span class="o">/</span><span class="n">bcm2</span><span class="w"></span>
<span class="mi">710</span><span class="o">/</span><span class="n">openwrt</span><span class="o">-</span><span class="n">bcm27xx</span><span class="o">-</span><span class="n">bcm2710</span><span class="o">-</span><span class="n">rpi</span><span class="o">-</span><span class="mi">3</span><span class="o">-</span><span class="n">ext4</span><span class="o">-</span><span class="n">sysupgrade</span><span class="o">.</span><span class="n">img</span><span class="o">.</span><span class="n">gz</span><span class="w"></span>
<span class="n">sysupgrade</span><span class="w"> </span><span class="o">-</span><span class="n">v</span><span class="w"> </span><span class="n">openwrt</span><span class="o">-</span><span class="n">bcm27xx</span><span class="o">-</span><span class="n">bcm2710</span><span class="o">-</span><span class="n">rpi</span><span class="o">-</span><span class="mi">3</span><span class="o">-</span><span class="n">ext4</span><span class="o">-</span><span class="n">sysupgrade</span><span class="o">.</span><span class="n">img</span><span class="o">.</span><span class="n">gz</span><span class="w"></span>
<span class="c1">### After upgrade ###</span><span class="w"></span>
<span class="n">opkg</span><span class="w"> </span><span class="n">update</span><span class="w"></span>
<span class="n">sh</span><span class="w"> </span><span class="o">/</span><span class="n">opt</span><span class="o">/</span><span class="n">opkg</span><span class="o">-</span><span class="n">update</span><span class="o">-</span><span class="n">script</span><span class="o">.</span><span class="n">sh</span><span class="w"> </span><span class="o">-</span><span class="n">v</span><span class="w"> </span><span class="n">install</span><span class="w"></span>
</code></pre></div>
<ul>
<li><a href="https://openwrt.org/docs/guide-user/installation/generic.sysupgrade">Upgrading OpenWrt firmware using LuCI and CLI</a></li>
<li><a href="https://fabianlee.org/2017/02/04/openwrt-upgrading-to-the-latest-snapshot-build/">OpenWrt: Upgrading OpenWrt to the latest snapshot build</a></li>
<li><a href="https://github.com/richb-hanover/OpenWrtScripts#opkgscriptsh">OpenWrtScripts GitHub repo (opkgupdate.sh)</a></li>
</ul>Automatically delete spam/pending comments in WordPress + bbPress2020-09-09T00:00:00+02:002020-09-09T00:00:00+02:00Stefano Ottolenghitag:thecrowned.org,2020-09-09:/automate-spam-pending-comments-deletion-wordpress-bbpress<p>If you expose your bbPress forums to the public, it is likely you will be getting loads of spam every day. Most of it is actually caught either by WordPress itself, and set as <code>pending …</code></p><p>If you expose your bbPress forums to the public, it is likely you will be getting loads of spam every day. Most of it is actually caught either by WordPress itself, and set as <code>pending</code>, or by Akismet, and set as <code>spam</code>. Either way,</p>
<ol>
<li>bulk purging <code>pending</code> content is not possible from wp-admin; one needs to go through each page of comments individually.</li>
<li>spam comments bulk removal can timeout wp-admin if you have many.</li>
</ol>
<p><strong>That is why it is best to schedule the deletion of spam and pending topics as a cron job.</strong> The following two commands will delete spam and pending topics for the previous week (not the current one, so there is some time to review them, if something legitimate is actually marked as spam). Just add them to cron (on Linux) through <code>crontab -e</code> (but care to change the <code>--path</code> value).</p>
<div class="highlight"><pre><span></span><code><span class="m">0</span> <span class="m">1</span> <span class="se">\*</span> <span class="se">\*</span> <span class="m">6</span> wp post delete <span class="k">$(</span>wp post list --post<span class="se">\_</span>type<span class="o">=</span><span class="s1">'topic'</span> --post<span class="se">\_</span>status<span class="o">=</span><span class="s1">'spam'</span> --field<span class="o">=</span>ID --path<span class="o">=</span>/var/www/html --w<span class="o">=</span><span class="k">$(</span><span class="nb">echo</span> <span class="k">$(</span>date +%V<span class="k">)</span>-1 <span class="p">|</span> bc<span class="k">))</span> --force --path<span class="o">=</span>/var/www/html
<span class="m">0</span> <span class="m">2</span> <span class="se">\*</span> <span class="se">\*</span> <span class="m">6</span> wp post delete <span class="k">$(</span>wp post list --post<span class="se">\_</span>type<span class="o">=</span><span class="s1">'topic'</span> --post<span class="se">\_</span>status<span class="o">=</span><span class="s1">'pending'</span> --field<span class="o">=</span>ID --path<span class="o">=</span>/var/www/html --w<span class="o">=</span><span class="k">$(</span><span class="nb">echo</span> <span class="k">$(</span>date +%V<span class="k">)</span>-1 <span class="p">|</span> bc<span class="k">))</span> --force --path<span class="o">=</span>/var/www/html
</code></pre></div>
<p>Doing the same for regular comments is unfortunately not possible, since <a href="https://github.com/wp-cli/wp-cli/issues/4616">WP-CLI does not support array input yet</a>, so it is not possible to filter comments by date.</p>A fix for broken (virtual) buttons and dead touch area on Android phones2020-06-08T00:00:00+02:002020-06-08T00:00:00+02:00Stefano Ottolenghitag:thecrowned.org,2020-06-08:/a-fix-for-broken-physical-buttons-and-dead-touch-area-on-android-phones<p>My old Android smartphone fell too many times and had its physical buttons (back, home, recent apps) not working, which was problematic to do anything. You can go back with in-app buttons most of the …</p><p>My old Android smartphone fell too many times and had its physical buttons (back, home, recent apps) not working, which was problematic to do anything. You can go back with in-app buttons most of the times, but there is no way to switch app or to go back to the desktop.</p>
<p>One good fix is to use an app that will put the back and home button functions as touch gestures, for example swiping from the borders of the screen. To this avail I suggest <a href="https://play.google.com/store/apps/details?id=com.fb.fluid&hl=en_IN">Fluid Navigation Gestures</a>. In case you absolutely needed to trigger the home button, for example, you can connect the phone to the computer, enable ADB, and <a href="https://stackoverflow.com/questions/7789826/adb-shell-input-events">trigger any key you would like</a> with it.</p>
<p>Navigation gestures are a good fix until you realized that not even the spacebar (or the whole lower row of keyboard keys) would not work. So in the end, the real solution is to prevent Android from drawing anything in the bottom part of the screen (or, at any rate, the broken part, wherever it is — as long as it is on some side and not central). And it turns out it is actually possible to <a href="https://android.stackexchange.com/questions/57267/resize-screen-for-dead-touch-zone">resize the screen area in Android</a>!</p>
<p>In the end, the commands I used have been:</p>
<div class="highlight"><pre><span></span><code>adb shell wm overscan 0,0,0,120
adb shell wm density 420
</code></pre></div>
<p>and then the navigation gestures were actually even useless, as I could have the navigation bar on screen.</p>FOSS Android Apps and my quest for going Google free on OnePlus 62020-06-07T00:00:00+02:002020-06-07T00:00:00+02:00Stefano Ottolenghitag:thecrowned.org,2020-06-07:/foss-android-apps-quest-going-google-free-oneplus-6<p>A while ago I decided I did not want to be a product anymore, and start paying for services. I started by breaking free from all the free-but-commercial company services such as Dropbox and Gmail …</p><p>A while ago I decided I did not want to be a product anymore, and start paying for services. I started by breaking free from all the free-but-commercial company services such as Dropbox and Gmail, and ultimately ended up ditching all non open source software on my phone, doing away with anything that was Google-run.</p>
<p><em>There is an alternative</em>: follow through as we embark on the journey of moving to Free and Open Source Software (FOSS) for Android.</p>
<h2 id="easy-replacement-for-essential-services">Easy replacement for essential services</h2>
<p>The problem with non-open source software is that they have to keep building new features into their products, or the market will tag them as dead. They have to keep moving, and some occasional bugfixing is not enough. On the other hand, open source software does often not have this drive. These projects’ maintainers just tend to make sure their software runs smoothly and that it has all it should have.</p>
<p>So, after Dropbox kept implementing useless features and, most of all, would force me to move my folder to a partition formatted with ext4 instead of ntfs, I was fed up. My first step was thus to self-host an instance of <a href="https://nextcloud.com/">Nextcloud</a>. I chose self-host because I wanted control and because I already had a server to host it on, but there are options where you can rely on someone else’s instance and register for a share, pretty much as Dropbox does.</p>
<p><img alt="nextcloud" src="https://www.thecrowned.org/wp-content/uploads/2020/06/nextcloud.jpeg"></p>
<p><strong>And Nextcloud is really, by all means, a valid alternative to Dropbox.</strong> There are clients for all operating systems, including mobile ones, with all the needed features, and they are actively maintained. Actually, it’s more than an alternative to Dropbox! I started with just file hosting, but there are countless apps that can boost your Nextcloud instance. I added <a href="https://apps.nextcloud.com/apps/notes">Notes</a> and did away with Google Keep, but most importantly <a href="https://apps.nextcloud.com/apps/contacts">Contacts</a> and <a href="https://apps.nextcloud.com/apps/calendar">Calendar</a> so that I could sync my phone contacts and meetings not with Google, but with my own server (using the <a href="https://www.davx5.com/download">DAVx Android app</a>, freely available on F-Droid). I also tried the <a href="https://apps.nextcloud.com/apps/passwords">Passwords</a> app to ditch Lastpass, but the Firefox addon was not working super well (end thus ended up signing up for Bitwarden instead). I switched to <a href="https://bitwarden.com/">Bitwarden</a> in the end.</p>
<p>I then scouted for (paid) <strong>privacy-focused email services</strong>. First to grab my interest was <a href="https://tutanota.com/">Tutanota</a>, but after using it a few days it was clear to me that they were still far from being something one could rely on as a power user (no conversations, no option to receive mails from other domains, and more missing). I then went with <a href="https://protonmail.com/">ProtonMail</a>, which I am very happy with — true, some features miss there as well, but they are minor. I now have a primary protonmail address, and (as a paying user) I could link my other addresses so I handle all from one dashboard. It came with a subscription to ProtonVPN as well, but <a href="https://schub.wtf/blog/2019/04/08/very-precarious-narrative.html">commercial VPNs for privacy are just nono</a>. And so Gmail was gone as well.</p>
<p><strong>Not easy to replace was Google Maps</strong>. First I went with <a href="https://github.com/junjunguo/PocketMaps">PocketMaps</a>, but again, not something you can rely on a daily basis (app always opens in the center of the map, regardless of last session, and no way to look for a location unless you ask for directions till there). But then, meet <a href="https://osmand.net/">OsmAnd</a> (free on F-Droid), a beautiful map app with literally everything Google Maps has (except for huge amounts of reviews for places). Not every single restaurant and cafe is marked on the maps, but that is minor. Directions are sometimes not optimal, but they work fine, I still get from A to B in a new city, so that is enough I guess.</p>
<p>Determined to only use open source software on my phone, I also switched from Bittorrent Sync to <a href="https://syncthing.net/">Syncthing</a>. The only non FOSS software I still have is WhatsApp, but let’s hope enough people move to Telegram soon.</p>
<p>So with this I was able to ditch every single Google app I had got accustomed to. But there was still several Google services running in the background…</p>
<h2 id="moving-to-microg-for-lineageos-on-oneplus-6">Moving to MicroG for LineageOS (on OnePlus 6)</h2>
<p><img alt="MicroG for LineageOS" src="https://www.thecrowned.org/wp-content/uploads/2020/06/microglineageos.jpeg"></p>
<p>To really have an open source smartphone, we need to flash a custom ROM. At this moment, the best choice seems to be <a href="https://lineage.microg.org/">MicroG for LineageOS</a>, which is basically <a href="https://lineageos.org/">LineageOS</a> (Android without Google apps) with the addition of <a href="https://microg.org/">MicroG</a> which mimics Google background services for apps that might rely on them (position, push notifications, etc). While LineageOS is actively developed, MicroG seems to be lagging a bit behind as of now, with no Android 10 support yet (i.e. no version 17.1). This is a problem if you already have Android 10 installed on your phone: apparently there is some incompatibility between the bootloaders of Android 10 and Android 9. This means that if you come from Android 10, you must install some other flavor of Android 10 (or do some hacking I haven’t been able to figure out what it is, and <a href="https://www.reddit.com/r/MicroG/comments/fa3yp0/who_here_has_been_able_to_successfully_install_on/">I am not alone</a>).</p>
<p>The main problem is that even TWRP will not really work, at least not the regular one. I had to use <a href="https://sourceforge.net/projects/mauronofrio-twrp/files/Enchilada/">a tweaked version by Mauronofrio</a>. Even with that though, no way I could get MicroG for LineageOS 16 installed. In my case, for OnePlus 6, I was lucky that there was another option: <a href="https://omnirom.org/#about">OmniROM</a>. Using the tweaked TWRP and OmniROM 10, I did manage to flash the custom ROM by following the <a href="https://wiki.lineageos.org/devices/enchilada/install">instructions provided by LineageOS</a> (with all the fiddling due to double A/B system partitions, and I guess I will never update for the same reason!). Anyway, <a href="https://www.reddit.com/r/oneplus/comments/8212xz/differences_between_omnirom_and_lineageos/">differences between LOS and OmniROM</a> are minimal, and they are both open source and sufficiently actively developed. And now, with OmniROM, Google is officially out of my phone.</p>
<h2 id="useful-resources-for-oneplus-6-custom-roms">Useful resources for OnePlus 6 custom ROMs</h2>
<ul>
<li><a href="https://forum.xda-developers.com/oneplus-6/how-to/guide-microg-oneplus-6-source-ligthway-t3874469">This thread on XDA</a> is a good reference, with a list of tested ROMs.</li>
<li><a href="https://www.reddit.com/r/LineageOS/comments/fuf5fm/my_oneplus_6_enchilada_lineageos_171_update_story/">My OnePlus 6 (enchilada) LineageOS 17.1 update story</a></li>
<li><a href="https://www.reddit.com/r/LineageOS/comments/fv8x5j/oneplus_6_and_possibly_newer_upgrade_steps/">Oneplus 6 (and possibly newer?) upgrade steps (including base firmware)</a></li>
<li><a href="https://forum.xda-developers.com/oneplus-6/how-to/guide-noobs-guide-to-b-partitions-op6-t3816123">[GUIDE] The Noob’s Guide to A/B Partitions and Other OP6 Idiosyncrasies</a></li>
<li><a href="https://www.xda-developers.com/how-a-b-partitions-and-seamless-updates-affect-custom-development-on-xda/">How A/B Partitions and Seamless Updates Affect Custom Development on XDA</a></li>
<li><a href="https://forum.xda-developers.com/oneplus-6/how-to/guide-microg-oneplus-6-source-ligthway-t3874469">[GUIDE] MicroG on OnePlus 6 (open source and ligthway alternative to Google services)</a></li>
<li><a href="https://www.reddit.com/r/MicroG/comments/fa3yp0/who_here_has_been_able_to_successfully_install_on/">Who here has been able to successfully install on a OnePlus 6? What am I doing wrong?</a></li>
</ul>A setup for remote piano lesson over Zoom2020-05-24T00:00:00+02:002020-05-24T00:00:00+02:00Stefano Ottolenghitag:thecrowned.org,2020-05-24:/setup-remote-piano-lesson-zoom<p>First create a new sink:</p>
<div class="highlight"><pre><span></span><code>pactl load-module module-null-sink <span class="nv">sink_name</span><span class="o">=</span>zoom_input <span class="nv">sink_properties</span><span class="o">=</span>device.description<span class="o">=</span>zoom_input
</code></pre></div>
<p>then remap into a Zoom monitor source that Zoom will allow selection of:</p>
<div class="highlight"><pre><span></span><code>pactl load-module module-remap-source <span class="nv">master</span><span class="o">=</span>zoom_input.monitor <span class="nv">source_name</span><span class="o">=</span>zoom_mic …</code></pre></div><p>First create a new sink:</p>
<div class="highlight"><pre><span></span><code>pactl load-module module-null-sink <span class="nv">sink_name</span><span class="o">=</span>zoom_input <span class="nv">sink_properties</span><span class="o">=</span>device.description<span class="o">=</span>zoom_input
</code></pre></div>
<p>then remap into a Zoom monitor source that Zoom will allow selection of:</p>
<div class="highlight"><pre><span></span><code>pactl load-module module-remap-source <span class="nv">master</span><span class="o">=</span>zoom_input.monitor <span class="nv">source_name</span><span class="o">=</span>zoom_mic <span class="nv">source_properties</span><span class="o">=</span>device.description<span class="o">=</span><span class="s2">"zoom_mic"</span>
</code></pre></div>
<p>Then create as many new virtual sources as microphones you need to merge together with <code>pactl load-module module-loopback</code> and finally go into Pavucontrol, select <em>Virtual streams</em>, and assign different physical sources in the dropdowns. Then select <em>zoom_input</em> in Zoom as source.</p>The spiritual similarities between playing music and table tennis2020-03-24T15:59:00+01:002020-03-24T15:59:00+01:00admintag:thecrowned.org,2020-03-24:/spiritual-similarities-playing-music-table-tennis<p>As a fellow table tennis player and piano player, I have noticed some interesting spiritual similarities between the two arts. I'm pretty sure they are also common to other sports anyway.</p>
<h2 id="always-think-ahead-the-next-stroke-the-next-note">Always think ahead: the …</h2><p>As a fellow table tennis player and piano player, I have noticed some interesting spiritual similarities between the two arts. I'm pretty sure they are also common to other sports anyway.</p>
<h2 id="always-think-ahead-the-next-stroke-the-next-note">Always think ahead: the next stroke, the next note</h2>
<p>In table tennis, one should not think about scoring a point with the current stroke, but rather be prepared for the next. What ball will I get back given my current playing? What do I expect? How am I going to handle it? If we do not think ahead, we will always fall short when the ball comes back, we will not be prepared. In a similar fashion, when playing an instrument, it is vital to know what the next note will be, what the next musical phrase will be. Giving meaning to a speech is impossible if we do not know how it goes on beyond the very current moment.</p>
<h2 id="mistakes-do-not-matter-if-you-make-them-matter-you-lose">Mistakes do not matter. If you make them matter, you lose</h2>
<p>It is a common phenomenon to make a mistake followed by more mistakes, mostly in the attempt of correcting that mistake/with the case of overthinking the mistake that has already happened. It does happen to make the wrong choice when playing table tennis, or to not get our best stroke, as much as getting a note wrong. This is not a problem as long as we are able to go on and forget that single mistake. Instead, if we focus on that, we end up stressing it and it becomes much heavier.</p>
<h2 id="realize-there-is-a-talk-going-on">Realize there is a talk going on</h2>
<p><a href="https://www.thecrowned.org/need-teaching-learning-languages">Music and table tennis are both languages</a>, they allow to build a speech and to have a dialogue/conversation. It is important to realize <em>there is</em> a meaningful content being carried, that the individual strokes as well as notes do group together to make cohesive clusters. This completely changes our way of playing, both sports and music.</p>FEniCS differences between Function, TrialFunction and TestFunction2020-02-06T11:35:00+01:002020-02-06T11:35:00+01:00Stefano Ottolenghitag:thecrowned.org,2020-02-06:/fenics-differences-function-trialfunction-testfunction<p>The <a href="https://fenicsproject.org/">FEniCS project</a> allows for simple solution of partial differential equations. However, getting started from <a href="https://fenicsproject.org/tutorial/">examples</a> is so quick, it is easy to miss how the inner-workings of it behave. This should not happen (especially …</p><p>The <a href="https://fenicsproject.org/">FEniCS project</a> allows for simple solution of partial differential equations. However, getting started from <a href="https://fenicsproject.org/tutorial/">examples</a> is so quick, it is easy to miss how the inner-workings of it behave. This should not happen (especially if it is a PhD project). There are three vital pieces of the puzzle that it might not be clear what they are for, and the documentation does not help here: the functions <a href="https://fenicsproject.org/docs/dolfin/2017.2.0/python/programmers-reference/functions/function/TrialFunction.html?highlight=trialfunction">TrialFunction</a>, <a href="https://fenicsproject.org/docs/dolfin/2017.2.0/python/programmers-reference/functions/function/TestFunction.html?highlight=testfunction">TestFunction</a>, and <a href="https://fenicsproject.org/docs/dolfin/2017.2.0/python/programmers-reference/functions/function/Function.html">Function</a>.</p>
<p>The thing is that, in the usual Finite Element Methods, we only have the distinction between the trial function <span class="math">\(u \in V\_h\)</span> and test function <span class="math">\(v \in V\)</span>, and even then the distinction is often blurry. In fact, technically, the spaces <span class="math">\(V\_h\)</span> and <span class="math">\(V\)</span> can be different. However, most of the time we take them to be the same (for example, the same discrete space of piecewise linear functions). The names we give them is just to have clearer in our heads what role they play in the game, but we could as well say "two functions in <span class="math">\(V\)</span>". Then why would FEniCS have even a three-fold difference? You can quickly check for yourself that, for example, if you swap the TrialFunction for a Function, FEniCS will complain.</p>
<p>There is no documentation on the topic as far as I have found, so these are my (maybe mistaken) deductions.</p>
<p>The meaning of the TrialFunction is to let FEniCS know what function we are solving for. In fact, when we call</p>
<div class="highlight"><pre><span></span><code><span class="n">solve</span><span class="p">(</span><span class="n">A</span><span class="p">,</span> <span class="n">u_</span><span class="o">.</span><span class="n">vector</span><span class="p">(),</span> <span class="n">b</span><span class="p">)</span>
</code></pre></div>
<p>FEniCS expects to find a TrialFunction in A, which is supposed to be the function we are looking for. On the other hand, <code>u_</code> is the symbol in which the result will be stored, and this needs to be a <code>Function</code> (not <code>Trial</code>, not <code>Test</code>, just a regular <code>Function</code>).</p>
<p>What about the <code>TestFunction</code>? FEniCS expects one as well in A, but why would it care? Well, the way we obtain a (finite) linear system to solve when using FEM, is by rewriting the problem in terms of the basis hat functions <span class="math">\(\phi_i\)</span>. In fact, when solving the problem</p>
<p><span class="math">\(\int u\_h' v' dx = \int f v dx\)</span></p>
<p>exploiting the fact that <span class="math">\(v \in V = \{ \text{piece-wise linear functions null at borders} \}\)</span>, we can rewrite it in terms of the basis of <span class="math">\(V\)</span> as</p>
<p><span class="math">\(\int u\_h' \phi\_i' dx = \int f \phi\_i dx, \ \ i = 1, \cdots, n-1\)</span></p>
<p>and here is where the role of the <code>TrialFunction</code> comes in! If FEniCS knows <span class="math">\(v\)</span> is a trial function, then it can do this replacement with basis functions and effectively build the linear system.</p>
<p>Technically, <code>TrialFunction</code>, <code>TestFunction</code> and simple <code>Function</code> can be distinguished either because they support different methods, as shown in the screenshot below, or by checking the type of a given variable. <code>TrialFunction</code> and <code>TestFunction</code> are of type <code>dolfin.function.argument.Argument</code>, which is the same as <code>fenics.function.argument.Argument</code>, while <code>Function</code> is of type <code>dolfin.function.function.Function</code>.</p>
<p><img alt="fenics trialfunction testfunction funciton" src="https://www.thecrowned.org/wp-content/uploads/2020/02/Screenshot-from-2020-07-22-11-48-16.png"></p>
<p>Further technical details: by default, <code>TrialFunction</code> is an <code>Argument</code> with <code>number == 1</code>, while <code>TestFunction</code> has <code>number == 0</code> (check <a href="https://bitbucket.org/fenics-project/dolfin/src/946dbd3e268dc20c64778eb5b734941ca5c343e5/python/dolfin/function/argument.py#lines-86">/dolfin/function/argument.py</a>). Argument numbers can be checked through <code>u.number()</code>:</p>
<div class="highlight"><pre><span></span><code><span class="kn">import</span> <span class="nn">fenics</span> <span class="k">as</span> <span class="nn">fen</span>
<span class="n">mesh</span> <span class="o">=</span> <span class="n">fen</span><span class="o">.</span><span class="n">UnitSquareMesh</span><span class="p">(</span><span class="mi">10</span><span class="p">,</span><span class="mi">10</span><span class="p">)</span>
<span class="n">V</span> <span class="o">=</span> <span class="n">fen</span><span class="o">.</span><span class="n">VectorFunctionSpace</span><span class="p">(</span><span class="n">mesh</span><span class="p">,</span> <span class="s1">'P'</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span>
<span class="n">trial</span> <span class="o">=</span> <span class="n">fen</span><span class="o">.</span><span class="n">TrialFunction</span><span class="p">(</span><span class="n">V</span><span class="p">)</span>
<span class="n">test</span> <span class="o">=</span> <span class="n">fen</span><span class="o">.</span><span class="n">TestFunction</span><span class="p">(</span><span class="n">V</span><span class="p">)</span>
<span class="n">trial</span><span class="o">.</span><span class="n">number</span><span class="p">()</span> <span class="o">==</span> <span class="mi">1</span> <span class="c1"># True</span>
<span class="n">test</span><span class="o">.</span><span class="n">number</span><span class="p">()</span> <span class="o">==</span> <span class="mi">0</span> <span class="c1"># True</span>
</code></pre></div>
<p><img alt="fenics" src="https://www.thecrowned.org/wp-content/uploads/2020/02/Screenshot-from-2020-07-22-12-32-31.png"></p>
<p>So, in the end:</p>
<ul>
<li>FEniCS expects <strong>at least one</strong> TrialFunction and at least one TestFunction in each variational problem to be solved.</li>
<li>FEniCS expects <strong>no more than one</strong> TrialFunction per problem (cannot mixed them!).</li>
</ul>
<script type="text/javascript">if (!document.getElementById('mathjaxscript_pelican_#%@#$@#')) {
var align = "center",
indent = "0em",
linebreak = "false";
if (false) {
align = (screen.width < 768) ? "left" : align;
indent = (screen.width < 768) ? "0em" : indent;
linebreak = (screen.width < 768) ? 'true' : linebreak;
}
var mathjaxscript = document.createElement('script');
mathjaxscript.id = 'mathjaxscript_pelican_#%@#$@#';
mathjaxscript.type = 'text/javascript';
mathjaxscript.src = 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.3/latest.js?config=TeX-AMS-MML_HTMLorMML';
var configscript = document.createElement('script');
configscript.type = 'text/x-mathjax-config';
configscript[(window.opera ? "innerHTML" : "text")] =
"MathJax.Hub.Config({" +
" config: ['MMLorHTML.js']," +
" TeX: { extensions: ['AMSmath.js','AMSsymbols.js','noErrors.js','noUndefined.js'], equationNumbers: { autoNumber: 'none' } }," +
" jax: ['input/TeX','input/MathML','output/HTML-CSS']," +
" extensions: ['tex2jax.js','mml2jax.js','MathMenu.js','MathZoom.js']," +
" displayAlign: '"+ align +"'," +
" displayIndent: '"+ indent +"'," +
" showMathMenu: true," +
" messageStyle: 'normal'," +
" tex2jax: { " +
" inlineMath: [ ['\\\\(','\\\\)'] ], " +
" displayMath: [ ['$$','$$'] ]," +
" processEscapes: true," +
" preview: 'TeX'," +
" }, " +
" 'HTML-CSS': { " +
" availableFonts: ['STIX', 'TeX']," +
" preferredFont: 'STIX'," +
" styles: { '.MathJax_Display, .MathJax .mo, .MathJax .mi, .MathJax .mn': {color: 'inherit ! important'} }," +
" linebreaks: { automatic: "+ linebreak +", width: '90% container' }," +
" }, " +
"}); " +
"if ('default' !== 'default') {" +
"MathJax.Hub.Register.StartupHook('HTML-CSS Jax Ready',function () {" +
"var VARIANT = MathJax.OutputJax['HTML-CSS'].FONTDATA.VARIANT;" +
"VARIANT['normal'].fonts.unshift('MathJax_default');" +
"VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
"VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
"VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
"});" +
"MathJax.Hub.Register.StartupHook('SVG Jax Ready',function () {" +
"var VARIANT = MathJax.OutputJax.SVG.FONTDATA.VARIANT;" +
"VARIANT['normal'].fonts.unshift('MathJax_default');" +
"VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
"VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
"VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
"});" +
"}";
(document.body || document.getElementsByTagName('head')[0]).appendChild(configscript);
(document.body || document.getElementsByTagName('head')[0]).appendChild(mathjaxscript);
}
</script>Create a WP test site with lots of users and posts2020-01-05T00:00:00+01:002020-01-05T00:00:00+01:00Stefano Ottolenghitag:thecrowned.org,2020-01-05:/create-a-wp-test-site-with-lots-of-users-and-posts<p>The following WP-CLI command <code>data create</code> creates a lot of test data into a WordPress site: 1000 new users, each with 50 posts. Each post gets a random date in the last year and a …</p><p>The following WP-CLI command <code>data create</code> creates a lot of test data into a WordPress site: 1000 new users, each with 50 posts. Each post gets a random date in the last year and a random post content of variable length filled using a <code>lorem ipsum</code> generator.</p>
<div class="highlight"><pre><span></span><code><span class="cp"><?php</span>
<span class="nx">WP_CLI</span><span class="o">::</span><span class="na">add_command</span><span class="p">(</span> <span class="s1">'data create'</span><span class="p">,</span> <span class="s1">'create_test_data'</span> <span class="p">);</span>
<span class="k">function</span> <span class="nf">ppc_create_test_data</span><span class="p">()</span> <span class="p">{</span>
<span class="nv">$user_n</span> <span class="o">=</span> <span class="mi">1000</span><span class="p">;</span>
<span class="nv">$posts_per_user</span> <span class="o">=</span> <span class="mi">50</span><span class="p">;</span>
<span class="k">while</span><span class="p">(</span> <span class="nv">$user_n</span> <span class="o">&</span><span class="nx">gt</span><span class="p">;</span> <span class="mi">0</span> <span class="p">)</span> <span class="p">{</span>
<span class="nv">$user_id</span> <span class="o">=</span> <span class="nx">wp_create_user</span><span class="p">(</span> <span class="nv">$user_n</span><span class="p">,</span> <span class="s1">'password'</span> <span class="p">);</span>
<span class="k">if</span><span class="p">(</span> <span class="o">!</span> <span class="nx">is_wp_error</span><span class="p">(</span> <span class="nv">$user_id</span> <span class="p">)</span> <span class="p">)</span>
<span class="nx">WP_CLI</span><span class="o">::</span><span class="na">line</span><span class="p">(</span> <span class="s2">"Created user "</span><span class="o">.</span><span class="nv">$user_id</span> <span class="p">);</span>
<span class="k">for</span><span class="p">(</span> <span class="nv">$n</span> <span class="o">=</span> <span class="nv">$posts_per_user</span><span class="p">;</span> <span class="nv">$n</span> <span class="o">&</span><span class="nx">gt</span><span class="p">;</span> <span class="mi">0</span><span class="p">;</span> <span class="nv">$n</span><span class="o">--</span> <span class="p">)</span> <span class="p">{</span>
<span class="nv">$post_id</span> <span class="o">=</span> <span class="nx">wp_insert_post</span><span class="p">(</span> <span class="k">array</span><span class="p">(</span>
<span class="s1">'post_author'</span> <span class="o">=&</span><span class="nx">gt</span><span class="p">;</span> <span class="nv">$user_id</span><span class="p">,</span>
<span class="s1">'post_date'</span> <span class="o">=&</span><span class="nx">gt</span><span class="p">;</span> <span class="nb">date</span><span class="p">(</span> <span class="s1">'Y-m-d H:i:s'</span><span class="p">,</span> <span class="nb">mt_rand</span><span class="p">(</span><span class="mi">1663027200</span><span class="p">,</span> <span class="mi">1669852799</span><span class="p">)</span> <span class="p">),</span>
<span class="s1">'post_content'</span> <span class="o">=&</span><span class="nx">gt</span><span class="p">;</span> <span class="nx">lorem</span><span class="p">(</span><span class="mi">500</span><span class="p">),</span>
<span class="s1">'post_title'</span> <span class="o">=&</span><span class="nx">gt</span><span class="p">;</span> <span class="nv">$n</span><span class="p">,</span>
<span class="p">)</span> <span class="p">);</span>
<span class="k">if</span><span class="p">(</span> <span class="o">!</span> <span class="nx">is_wp_error</span><span class="p">(</span> <span class="nv">$post_id</span> <span class="p">)</span> <span class="p">)</span>
<span class="nx">WP_CLI</span><span class="o">::</span><span class="na">line</span><span class="p">(</span> <span class="s2">"Created post "</span><span class="o">.</span><span class="nv">$post_id</span> <span class="p">);</span>
<span class="p">}</span>
<span class="o">--</span><span class="nv">$user_n</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">function</span> <span class="nf">lorem</span><span class="p">(</span><span class="nv">$count</span> <span class="o">=</span> <span class="mi">1</span><span class="p">,</span> <span class="nv">$max</span> <span class="o">=</span> <span class="mi">20</span><span class="p">,</span> <span class="nv">$standard</span> <span class="o">=</span> <span class="k">true</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// https://stackoverflow.com/questions/20633310/how-to-get-random-text-from-lorem-ipsum-in-php</span>
<span class="nv">$output</span> <span class="o">=</span> <span class="s1">''</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="nv">$standard</span><span class="p">)</span> <span class="p">{</span>
<span class="nv">$output</span> <span class="o">=</span> <span class="s1">'Lorem ipsum dolor sit amet, consectetur adipisicing elit, '</span> <span class="o">.</span>
<span class="s1">'sed do eiusmod tempor incididunt ut labore et dolore magna '</span> <span class="o">.</span>
<span class="s1">'aliqua.'</span><span class="p">;</span>
<span class="p">}</span>
<span class="nv">$pool</span> <span class="o">=</span> <span class="nb">explode</span><span class="p">(</span>
<span class="s1">' '</span><span class="p">,</span>
<span class="s1">'a ab ad accusamus adipisci alias aliquam amet animi aperiam '</span> <span class="o">.</span>
<span class="s1">'architecto asperiores aspernatur assumenda at atque aut beatae '</span> <span class="o">.</span>
<span class="s1">'blanditiis cillum commodi consequatur corporis corrupti culpa '</span> <span class="o">.</span>
<span class="s1">'cum cupiditate debitis delectus deleniti deserunt dicta '</span> <span class="o">.</span>
<span class="s1">'dignissimos distinctio dolor ducimus duis ea eaque earum eius '</span> <span class="o">.</span>
<span class="s1">'eligendi enim eos error esse est eum eveniet ex excepteur '</span> <span class="o">.</span>
<span class="s1">'exercitationem expedita explicabo facere facilis fugiat harum '</span> <span class="o">.</span>
<span class="s1">'hic id illum impedit in incidunt ipsa iste itaque iure iusto '</span> <span class="o">.</span>
<span class="s1">'laborum laudantium libero magnam maiores maxime minim minus '</span> <span class="o">.</span>
<span class="s1">'modi molestiae mollitia nam natus necessitatibus nemo neque '</span> <span class="o">.</span>
<span class="s1">'nesciunt nihil nisi nobis non nostrum nulla numquam occaecati '</span> <span class="o">.</span>
<span class="s1">'odio officia omnis optio pariatur perferendis perspiciatis '</span> <span class="o">.</span>
<span class="s1">'placeat porro possimus praesentium proident quae quia quibus '</span> <span class="o">.</span>
<span class="s1">'quo ratione recusandae reiciendis rem repellat reprehenderit '</span> <span class="o">.</span>
<span class="s1">'repudiandae rerum saepe sapiente sequi similique sint soluta '</span> <span class="o">.</span>
<span class="s1">'suscipit tempora tenetur totam ut ullam unde vel veniam vero '</span> <span class="o">.</span>
<span class="s1">'vitae voluptas'</span>
<span class="p">);</span>
<span class="nv">$max</span> <span class="o">=</span> <span class="p">(</span><span class="nv">$max</span> <span class="o">&</span><span class="nx">lt</span><span class="p">;</span><span class="o">=</span> <span class="mi">3</span><span class="p">)</span> <span class="o">?</span> <span class="mi">4</span> <span class="o">:</span> <span class="nv">$max</span><span class="p">;</span>
<span class="nv">$count</span> <span class="o">=</span> <span class="p">(</span><span class="nv">$count</span> <span class="o">&</span><span class="nx">lt</span><span class="p">;</span> <span class="mi">1</span><span class="p">)</span> <span class="o">?</span> <span class="mi">1</span> <span class="o">:</span> <span class="p">((</span><span class="nv">$count</span> <span class="o">&</span><span class="nx">gt</span><span class="p">;</span> <span class="mi">2147483646</span><span class="p">)</span> <span class="o">?</span> <span class="mi">2147483646</span> <span class="o">:</span> <span class="nv">$count</span><span class="p">);</span>
<span class="k">for</span> <span class="p">(</span><span class="nv">$i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="nv">$add</span> <span class="o">=</span> <span class="p">(</span><span class="nv">$count</span> <span class="o">-</span> <span class="p">(</span><span class="nx">int</span><span class="p">)</span> <span class="nv">$standard</span><span class="p">);</span> <span class="nv">$i</span> <span class="o">&</span><span class="nx">lt</span><span class="p">;</span> <span class="nv">$add</span><span class="p">;</span> <span class="nv">$i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
<span class="nb">shuffle</span><span class="p">(</span><span class="nv">$pool</span><span class="p">);</span>
<span class="nv">$words</span> <span class="o">=</span> <span class="nb">array_slice</span><span class="p">(</span><span class="nv">$pool</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nb">mt_rand</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="nv">$max</span><span class="p">));</span>
<span class="nv">$output</span> <span class="o">.=</span> <span class="p">((</span><span class="o">!</span> <span class="nv">$standard</span> <span class="o">&</span><span class="nx">amp</span><span class="p">;</span><span class="o">&</span><span class="nx">amp</span><span class="p">;</span> <span class="nv">$i</span> <span class="o">===</span> <span class="mi">0</span><span class="p">)</span> <span class="o">?</span> <span class="s1">''</span> <span class="o">:</span> <span class="s1">' '</span><span class="p">)</span> <span class="o">.</span> <span class="nb">ucfirst</span><span class="p">(</span><span class="nb">implode</span><span class="p">(</span><span class="s1">' '</span><span class="p">,</span> <span class="nv">$words</span><span class="p">))</span> <span class="o">.</span> <span class="s1">'.'</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">return</span> <span class="nv">$output</span><span class="p">;</span>
<span class="p">}</span>
<span class="cp">?></span><span class="x"></span>
</code></pre></div>How to give a scientific talk2019-12-18T14:31:00+01:002019-12-18T14:31:00+01:00Stefano Ottolenghitag:thecrowned.org,2019-12-18:/give-scientific-talk<p>I have listened to so many talks in so many different fields and topics, that I feel confident enough to provide advice on how to give a proper talk. I have listened to talks ranging …</p><p>I have listened to so many talks in so many different fields and topics, that I feel confident enough to provide advice on how to give a proper talk. I have listened to talks ranging from <a href="http://www.thecrowned.org/ipsc-2014-imagination-important-knowledge">public speaking contests</a> to PhD level seminars. I draw mainly from science talks, but this pretty much applies to any field, really.</p>
<h2 id="0-prepare-your-talk">0. Prepare your talk</h2>
<p>If you do not feel like preparing for your talk, then please just refrain from giving one and save the people from the hassle of listening to a <a href="https://brushingupscience.com/2018/04/09/the-realities-of-15-minute-scientific-talks/">terrible talk</a>. <strong>Everybody needs preparation for a talk</strong> - their experience may make the preparation quicker, but it is still needed. No preparation, no talk.</p>
<h2 id="1-define-your-audience">1. Define your audience</h2>
<p><strong><em>Who</em> is your talk for?</strong> This is the single most important question you need to ask yourself. Who are you speaking for? What background can you expect them to have? Always remember that you are giving a talk <em>for</em> people, so they should be your first focus. Not your work, your presentation, your show - <em>the audience come first!</em> Two presentations on the very same topic can turn out pretty different, if they are aimed at different audiences. <strong>If you do not plan for the right audience, you are likely to give a talk that is just wrong</strong> (and useless to the world).</p>
<p>The most <strong>common mistake is to make the talk too hard</strong> and high level with respect to the audience it will be delivered to. This is most usually a lack of confidence, which results in the perceived need of making something difficult to understand so that</p>
<ol>
<li>people can believe the speaker is very smart;</li>
<li>make people believe that the subject is worth.</li>
</ol>
<p>The latter is based on the widespread implicit assumption that <em>anything that is hard to understand is worth</em>, which is just bullshit. Beware of anything you do not understand.
It is also easy to just copy and paste some formulas or graphs and show those off, without giving away any real understanding.</p>
<p>So, when your talk is ready, revise the material and make sure it can be understood (to a good extent!) by a person having the background you expect from the audience. Most importantly, <em>avoid all jargon</em> unless it is really strictly required. And even then, question your choice of using it. It is fine to use <em>some</em> jargon at <em>advanced</em> talks, just bear in mind that if a listener has to pause and ponder about the meaning some word, you have lost them for the time being. Thus, all jargon used should be sufficiently ingrained in people's head to come without thought.</p>
<h2 id="2-define-what-you-want-to-take-across">2. Define what you want to take across</h2>
<p>What content are you trying to deliver (if any)? List around 3 points and make sure they are thoroughly covered, and that your audience clearly understands those basic pieces of information. A good measure of success is whether 90% of the audience went home with the ability of re-telling those 3 points with clarity and sufficient detail. If not, then the talk was a failure.</p>
<p>Always make a point of <strong>cutting out non-essential stuff</strong>. It is true that you might be telling a year worth of work in just 30 minutes, and you may be tempted to detail all the things that make it look like you have done a lot of work, but again ask yourself: <strong>is this useful to the audience's understanding?</strong> 95% of the times the answer is a sharp no.</p>
<h2 id="3-bring-the-material-together-and-tell-a-story">3. Bring the material together and tell a story</h2>
<p>The fact that you are communicating some objective piece of science does not mean that your talk should be dull. Of course, you should not aim for entertainment for its own sake. But still, strive to link all your material together and <strong>build a narrative with it</strong>, make clear to the audience how each piece is linked to the others. Aim to be a <em>storyteller</em>.</p>
<p>Also, a good question to cover, from the point of view of the audience, is "why should I care?". Do not just show your work, <em>make it meaningful to the audience</em>.</p>
<h2 id="4-do-not-give-the-problem-for-granted">4. Do not give the problem for granted</h2>
<p><a href="https://www.thecrowned.org/reason-mathematics-teaching-failing">All scientific results stem from a question</a>, from an originating problem. Always make sure the audience has the setup clear, and has understood what triggered your piece of work. <em>Never just provide answers, always provide context for them</em>.</p>
<h2 id="5-realize-you-are-a-showman">5. Realize you are a showman</h2>
<p>Remember that, as Galileo put it, "<em>Good teaching is one fourth preparation and three fourths theater.</em>" It could not be truer. The moment you start talking, <strong>you are on stage</strong>, and your thoughts should be much more those of an actor than of a scientist. Your body language, your talking, your engagement with the audience... everything should be much more those of a theater performance than those of a scientist.</p>
<h2 id="6-stay-focused-during-the-talk">6. Stay focused during the talk</h2>
<p>One major (and frequent!) mistake is for the speaker not to be focused <em>on</em> the talk <em>during</em> the talk. Any action that results in the audience losing focus is to be avoided.</p>
<p>Some examples:</p>
<ul>
<li>Pausing to look at the watch. You do need to keep track of time, but do so in an unobtrusive manner that does not draw the audience's attention as well!</li>
<li>Fiddling with the pointer (e.g. mistakenly go backwards with the slides instead of forward; realizing the pointer does not work well).</li>
<li>Digressing on side content, or feeling like you have to elaborate on some minor point. Stick to your main points instead and save the details for questions time. (That is, unless the details carry important insights, but then they should most likely among your main points.)</li>
</ul>
<h2 id="7-try-the-setup-in-advance">7. Try the setup in advance</h2>
<p>It often happens that the setup fails <em>during</em> the talk, or that you realize something is missing. This also hinders your resolution of staying focused during the talk, and distracts your audience. Make sure the projector works, the pointer works, everything is correctly linked to your laptop, and so on. Go through every detail: is there chalk, if you need it?, and so on. <strong>All the atmosphere you have built up to some point is lost if you have to stop</strong> for some technical reason.</p>The need of teaching and learning more languages2019-11-07T16:17:00+01:002019-11-07T16:17:00+01:00admintag:thecrowned.org,2019-11-07:/need-teaching-learning-languages<p>For a second, stop to think about <em>how many languages you know</em>. With languages, I do not mean <em>verbal</em> languages: rather, any means of expressing thoughts and feelings, or of expressing a dialogue. I am …</p><p>For a second, stop to think about <em>how many languages you know</em>. With languages, I do not mean <em>verbal</em> languages: rather, any means of expressing thoughts and feelings, or of expressing a dialogue. I am here do advocate for the learning and teaching of more than just one.</p>
<p><strong>Our western culture is based on the verbal language</strong> - the one you can speak with your friends, read in novels, and write in essays. We educate kids in that, and yet I would argue that very few of them end up being proficient in the verbal language. Speaking a language does not make you proficient in it: that skill is a much higher level one, and involves deep knowledge of the structure of the language, exposure to thousands examples of both good and bad usage of the language, and effortful practice throughout years. Often, people who venture in learning a new language (a verbal one) do not even ever get comfortable with their mother-tongue. What I mean is that <em>although everybody can talk in their own language, few of them have a real mastery of it</em>. Few people, for example, are able to tell a story (and not because of lack of ideas, but for inability of structuring it), and even fewer are able to read one out loud in a way that is vaguely engaging (for example, they cannot look away from the book to the audience, and fill in any gaps in their reading my making up appropriate fill-ups).</p>
<p>But even if people were proficient in the verbal language, this is just one means of expression. <em>It is barely minimum</em>. And even though we study several different subjects at school, they are all taken across through the same verbal language. <strong><em>But what about other, different languages?</em></strong></p>
<p><strong>Musicians</strong>, on the other hand, can rarely express the feelings and the moods of a musical piece through words. They have a different alphabet, one that does not have translation to the verbal one. The fact that sometimes, some situation reminds them of a tune, or inspires them a tune, rather than words, is a clear example that the musical language is different altogether. It is incredible that we are still studying Latin and we are not all studying music.</p>
<p><strong>Sports</strong> is another example where a different language is in place. The main difference between experienced and beginners in table tennis, for example, is in how they frame/live the unfolding of a point. Experienced players <em>see a dialog in it</em>, a conversation that ultimately leads to scoring a point. But it is exactly this sense of structure, this ability to realize how each stroke is connected and what each of them can have as consequence, that gives experienced players an unmatchable advantage. They know what is going to happen, and they know it because they are building something with that language.</p>
<p><strong>Mathematics</strong> is another, different form of expression, albeit this may be questioned. For sure, I would not classify <strong>programming languages</strong> as different languages, as they are after all a bridge between the verbal language and computers, but they are still thought to be as close to verbal as possible. Mathematics on the other hand, especially when it gets abstract enough, can venture pretty far from verbal expression, especially in its syntax. However, most of the formalism has been introduced to make the reasoning swifter, by avoiding long and cumbersome verbal sentences.</p>
<p>My final example is <strong><a href="https://en.wikipedia.org/wiki/Go_(game)">Go</a></strong>, the ancient Chinese strategy board game. Perhaps more than in any other game, a game of Go is a dialogue between two people in a <em>language which is completely foreign to any usual form of expression</em>. Each move says something, to the point that experienced players can re-trace the full game after the end, starting from the beginning. And they do so not out of memory, but like the act of recalling a conversation, where each line comes after the previous one in a sensible way. Here again, Go masters can see the dialogue unfold in their head, while beginners are at a loss and seem to play kind of randomly from the perspectives of experienced players, similarly to a kid who does not really reply on topic.</p>
<p>And yet, I am sure there are many other examples of different forms of expressions that would be worth pursuing. But still, we keep focusing more and more on just the verbal side.</p>The reasons why mathematics teaching is failing2019-11-01T22:17:00+01:002019-11-01T22:17:00+01:00admintag:thecrowned.org,2019-11-01:/reason-mathematics-teaching-failing<p>As a mathematics PhD student, I have seen a lot of mathematics teaching. I have been lucky enough to witness some (very) good teaching, but I have also been inflicted with so much bad teaching …</p><p>As a mathematics PhD student, I have seen a lot of mathematics teaching. I have been lucky enough to witness some (very) good teaching, but I have also been inflicted with so much bad teaching. Sometimes, I just wondered whether the people do even realize that their teaching is horrible, and I most often believe they are just unconscious about it, since most of the teaching they were exposed to was bad for them as well. I guess they just believe there is no way of making a good teaching of math, since most of it seems to be so bad.</p>
<p>I genuinely believe all graduate students in any subject should care about teaching. Even if you do not particularly care about teaching others, it makes your knowledge and understanding stronger. Just thinking at what matters (which is what you <em>would</em> teach), and how it would be best presented, <strong>forces you to thoroughly understand the topics to a deeper level</strong>. And at some point, you will just wonder: what are the essential elements of bad teaching? How can I avoid them?</p>
<h2 id="1-too-many-answers-too-few-questions">#1 - Too many answers, too few questions</h2>
<p>I am genuinely convinced that the most essential element of bad teaching is <strong>providing answers instead/without questions</strong>. Too often we go to class and we get lectured about some method, some theorem, some theory - and <strong>too seldom we get lectured about the path that actually led to that method, theorem or theory</strong>.</p>
<p>Examples: Where does the need for equations come from? When we define derivatives, what problem are we actually trying to solve? What had in his head the guy who came up with measure theory? Answering: "to solve problems", "to measure rates of change", to re-found the theory of integrals" is not enough. Not even "because it is important for applications", which is just equivalent to saying "I don't know". Instead, we need to go through the history that motivated the inspection of the problem, how the question came up, and why it is (still) relevant.</p>
<p><strong>Literally everything in mathematics comes from a question, from a problem, and failing to illustrate it is guarantee of teaching failure</strong>. It is just like being told to do something, but without the reason why we should. It leaves us purposeless, with the feeling of doing something we do not quite grasp the reason for. And what is more, often, if the problem is well-explained, bright students may just be able to come up with the solution themselves, or at least have a hint of idea.</p>
<h2 id="2-too-few-storytellers">#2 Too few storytellers</h2>
<p>Teaching mathematics requires first being a storyteller. As all pieces of math stem from a question, <strong>good teaching involves crafting a story from the material</strong>, and making the students experience a journey through it. How did this piece of math came to be? What is the history behind it, if it is interesting? How does it arise from the material covered before, and how is it linked to it? How does it form a coherent plot with what we already know, and how does it project us forward?</p>Troubleshooting the installation of IRAF on Ubuntu2019-09-06T13:50:00+02:002019-09-06T13:50:00+02:00admintag:thecrowned.org,2019-09-06:/troubleshooting-installation-iraf-ubuntu<p>So, found myself installing IRAF on a friend's laptop running Ubuntu. There are some decent tutorials online about the general steps: for example, <a href="https://glauffer.github.io/2018-08-03-the-easy-way-to-install-iraf/">this one</a>, and the <a href="https://astroconda.readthedocs.io/en/latest/installation.html#iraf-install">official one</a>. However, they all skip all possible …</p><p>So, found myself installing IRAF on a friend's laptop running Ubuntu. There are some decent tutorials online about the general steps: for example, <a href="https://glauffer.github.io/2018-08-03-the-easy-way-to-install-iraf/">this one</a>, and the <a href="https://astroconda.readthedocs.io/en/latest/installation.html#iraf-install">official one</a>. However, they all skip all possible issues that could come up (or at least, that popped up in my case). <a href="http://mips.as.arizona.edu/~khainline/intro_to_IRAF.html">This is another good resource</a> about the setup, config and usage (but skip the Ureka parts).</p>
<blockquote>
<p><strong>The <a href="https://iraf-community.github.io/">community-maintained version of IRAF</a> allows easy installation on some systems, such as Ubuntu.</strong></p>
</blockquote>
<p>The thing is that IRAF is a jumble of stunningly old pieces of software working together on primitive terminals and on peculiar conditions.</p>
<p>In random order, possible issues/tips are:</p>
<ul>
<li>If the packages <code>iraf-all pyraf-all stsci</code> show as non-existent, you have not added the astroconda channel. The command should be
<code>conda config --add channels http://ssb.stsci.edu/astroconda</code></li>
<li>You will still need to manually install <code>ds9</code>:
<code>sudo apt install saods9</code></li>
<li>You do not need to change the default shell to <code>tcsh</code>, although you need it to be installed:
<code>sudo apt install tcsh</code></li>
<li>If <code>xgterm</code> does not execute with error <em>File not found</em>, although the file is clearly there, make sure you have installed 32 bit dependencies:
<code>sudo apt-get install libc6:i386 libz1:i386 libncurses5:i386 libbz2-1.0:i386 libuuid1:i386 libxcb1:i386 libxmu6:i386</code></li>
<li>If <code>conda</code> commands do not work, make sure you have activated the Python environment containing the iraf packages:
<code>cd /path/to/iraf source activate iraf27</code></li>
</ul>
<p>In the end, the exact set of commands that got the whole environment working has been:</p>
<div class="highlight"><pre><span></span><code>tcsh
xgterm -sb <span class="p">&</span>
ds9 <span class="p">&</span>
<span class="nb">cd</span> /path/to/iraf
<span class="nb">source</span> activate iraf27
cl
</code></pre></div>
<p>Then you are into the iraf environment. However, you may use ds9 only to manipulate images: in some cases it may be just enough!</p>Install Windows 7 on a modern, Intel 8th generation computer2019-08-11T16:18:00+02:002019-08-11T16:18:00+02:00admintag:thecrowned.org,2019-08-11:/install-windows-7-modern-intel-8th-generation-computer<p>I spent a good week trying to get Windows 7 to work on a modern laptop, having an Intel i3 8th generation CPU and other recent components, such as a NVMe SSD. What I did …</p><p>I spent a good week trying to get Windows 7 to work on a modern laptop, having an Intel i3 8th generation CPU and other recent components, such as a NVMe SSD. What I did not know in the first place, is that <strong>officially, Windows 7 does not support Intel CPUs later than 6th gen</strong>. Moreover, it does not support UEFI boot (not GPT partition tables). For reference, we are talking of a <a href="https://www.pcspecialist.it/notebook/ultraNoteIV-14/">PC Specialist Ultranote IV 14"</a>. Up to now, <strong>everything works almost flawlessly but the wifi/bluetooth adapter.</strong></p>
<p>I am not gonna provide a full tutorial on how to install Windows 7 on a modern computer, but I <em>am</em> going to list here all the relevant resources that helped me succeed in the mission, as the research turned out to be quite exhausting.</p>
<p>The main issue is that the installer will stop quite soon and abruptly, stating that "<em>A required CD/DVD drive device driver is missing</em>". Clearly, this is bullshit, as the laptop does not even have an optical drive and the installer was running from USB.</p>
<p><img src="http://www.thecrowned.org/wp-content/uploads/2019/08/img_5751005d9e1c2.png" style="float:right" alt="Windows failure" /></p>
<p>Since Windows 7 does not even support USB 3 ports, it could have been an issue with that, and some people advised to just plug the USB to a USB 2 port, or to <a href="http://woshub.com/adding-usb-3-0-controller-drivers-to-windows-7-install-media/">integrate the USB 3 drivers into the installer</a>. None of this helped me the slightest. Even launching the installer from inside a Windows 10 installation had Windows 7 hang on boot at reboot.</p>
<ul>
<li>First, you need to re-build your Windows 7 image, integrating into it recent updates and modern drivers. A guy developed the tool <a href="https://forum.videohelp.com/threads/384921-Windows-7-Image-Updater-SkyLake-KabyLake-CoffeLake-Ryzen-Threadripper/page5">Windows 7 Image Updater</a>, which proved vital in doing that. You just need to get through the instructions and run it to obtain a copy of Windows 7 which can be installed on modern hardware. Technically, Windows 7 is fully compatible with modern hardware, it is just the installer that needs the updates that the OS received through the years to to support it.</li>
<li>Burn the <em>Updated Windows 7 iso</em> (for example with Rufus), selecting MBR as partition table scheme. Turn off <em>UEFI boot</em> in your BIOS. This should get Windows 7 successfully installed on your computer.</li>
<li>Then, some things will not be working. In my case, it was the video driver and the wifi/bluetooth adapter. I did not spend too much time caring for the wifi adapter, as I had a spare external one that worked just fine. My attempts were a failure though, as it looked like the Windows 10 driver from which I was supposed to borrow was quite different than the older Windows 7 one, and it was invocating services and registry keys that did not exist.The video was a different story, and it was important to get it working, if only for the brightness control! My reference was <a href="https://www.texpion.com/2018/06/Intel-7th-and-8th-generation-processor-graphics-driver-for-Windows-7-8-8.1.html">this page</a> (and the only one that worked!), which basically tells you how to port the Windows 10 drivers for your integrated graphics to Windows 7.</li>
</ul>
<p>And here we are, in 2019, still running Windows 7. Just because my mother does not want Ubuntu.</p>Meltdown - Overview of a security vulnerability (Slides)2019-04-24T14:22:00+02:002019-04-24T14:22:00+02:00admintag:thecrowned.org,2019-04-24:/meltdown-overview-security-vulnerability-slides<p>Here is the pdf slides of my presentation about <a href="https://en.wikipedia.org/wiki/Meltdown_(security_vulnerability)">Meltdown</a>, the security vulnerability. First are full slides, then real presentation slides (with stops when asking questions/pausing and pondering). Free for re-use, but nice to …</p><p>Here is the pdf slides of my presentation about <a href="https://en.wikipedia.org/wiki/Meltdown_(security_vulnerability)">Meltdown</a>, the security vulnerability. First are full slides, then real presentation slides (with stops when asking questions/pausing and pondering). Free for re-use, but nice to cite.</p>
<p><a href="/images/Meltdown-presentation-without-stops.pdf">This version</a> is just full slides, with no interruptions.</p>
<p><a href="/images/Meltdown-presentation.pdf">This version</a> is slides with sentences and pictures gradually coming in, with room for explanations and interaction with the audience.</p>Endless Christmas X-MAS CTF Writeup2018-12-22T20:00:00+01:002018-12-22T20:00:00+01:00Stefano Ottolenghitag:thecrowned.org,2018-12-22:/endless-christmas-x-mas-ctf-writeup<p>This is a writeup for the <strong>Endless Christmas</strong> challenge, md5 hash 866c92038d6e9fc47db4424f71f6167a (<a href="https://www.thecrowned.org/wp-content/uploads/2018/12/866c92038d6e9fc47db4424f71f6167a.zip">download binary</a>). It appeared in the X-MAS CTF, and it's a <em>Reverse</em> challenge.</p>
<p>Using <code>afl</code> with Radare we can see there are calls …</p><p>This is a writeup for the <strong>Endless Christmas</strong> challenge, md5 hash 866c92038d6e9fc47db4424f71f6167a (<a href="https://www.thecrowned.org/wp-content/uploads/2018/12/866c92038d6e9fc47db4424f71f6167a.zip">download binary</a>). It appeared in the X-MAS CTF, and it's a <em>Reverse</em> challenge.</p>
<p>Using <code>afl</code> with Radare we can see there are calls to <code>write</code> and <code>execve</code>, both happening in <code>main</code>, a sign that this program creates (and maybe executes?) something else.</p>
<p><img alt="radare" src="http://www.thecrowned.org/wp-content/uploads/2018/12/endless-x-mas1.png"></p>
<p>Putting a breakpoint just before the <code>execve</code> happens will reveal what file is being loaded (looking into the rax register).</p>
<p><img alt="radare" src="http://www.thecrowned.org/wp-content/uploads/2018/12/endless-x-mas2.png"></p>
<p>I went down 60 rabbit holes disassembling this binary further, but the best thing we can do at this point is <em>change point of view</em>, step out of Radare, and launch the binary by itself - it certainly doesn't seem to be doing anything nasty up to this point.</p>
<p>It takes some time before any output is shown, so this may be a sign that <strong>some decoding happens</strong>. The program creates a good number of other binaries which all look identical, albeit different from the original one (as their size shows), but that are actually different upon closer inspection with their md5 hashes.</p>
<p><img alt="radare" src="http://www.thecrowned.org/wp-content/uploads/2018/12/endless-x-mas3.png"></p>
<p>So of course, let's look at what's inside them.</p>
<p><img alt="radare" src="http://www.thecrowned.org/wp-content/uploads/2018/12/endless-x-mas4.png"></p>
<p>Opening one at random, we see that function names are different and more self-explanatory, but still not so helpful. Also, there are signs that this may be a scam, as <code>sym.encode_85</code> is never called (discover it by running <code>axt sym.encode_85</code>). So it looks like it could be decoding the huge string the program stores (obtainable with a simple call to <code>strings</code>) in base 85, but trying to manually decode it doesn't yield much results. So far so bad. Uhm. Remember that <em>all files are different, but they all look the same</em>, and there is no immediate clue pointing at which should be the interesting one.</p>
<p>This is again a time in which you need to take a deep breath and consider stepping out of what you are currently doing. Launching the original binary with <code>ltrace</code> reveals that <strong>several</strong> calls to <code>execve</code> are made, and hints us at the fact that maybe only the last executed binary is interesting.</p>
<p><img alt="radare" src="http://www.thecrowned.org/wp-content/uploads/2018/12/endless-x-mas5.png"></p>
<p>But how to discover its filename, since <code>ltrace</code> only shows pointers? Let's run it with <code>strace</code> and find the relevant line amidst all the noise... There it is, right at the top of the window!</p>
<p><img alt="radare" src="http://www.thecrowned.org/wp-content/uploads/2018/12/endless-x-mas6.png"></p>
<p><img alt="radare" src="http://www.thecrowned.org/wp-content/uploads/2018/12/endless-x-mas7.png"></p>
<p><strong>Bingo!</strong> This file has no <code>decode/encode_85</code> functions and similar, but only a big <code>main</code>. This main now really contains the behavior that we observe in the program, asking for a flag and all.</p>
<p>However, we soon realize that this is a scam again - there is no way to get to the successful "MERRY CHRISTMAS!" message. One significant hint is that the buffer that is read from input is never really used in any purposeful way. <strong>What the program does</strong> is scan a string of his own char by char, <code>xor</code> each char with <code>0xd</code>, and then check if <code>al</code> is the same as <code>dl</code>. This is never the case, and can never be, and in fact if we try to debug that routine, we see that the program always exits after the first iteration.</p>
<p><img alt="radare" src="http://www.thecrowned.org/wp-content/uploads/2018/12/endless-x-mas8.png"></p>
<p>But we already have all that we need! Remember that the program xored each char of its string with <code>0xd</code> - maybe that is the solution? Let's write a small program to reproduce this behavior:</p>
<div class="highlight"><pre><span></span><code><span class="cp">#include</span><span class="w"> </span><span class="cpf"><stdio.h></span><span class="cp"></span>
<span class="cp">#include</span><span class="w"> </span><span class="cpf"><string.h></span><span class="cp"></span>
<span class="kt">int</span><span class="w"> </span><span class="nf">main</span><span class="p">(){</span><span class="w"></span>
<span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">i</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="n">str</span><span class="p">[]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">"U @L^vi>n=i>R9;9<cR9ciR9;9<cR9ciR9;9<cR9ciR9;9<cR9ciRka9;p"</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="n">output</span><span class="p">[</span><span class="mi">200</span><span class="p">];</span><span class="w"></span>
<span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="n">i</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span><span class="w"> </span><span class="n">i</span><span class="o"><</span><span class="n">strlen</span><span class="p">(</span><span class="n">str</span><span class="p">);</span><span class="w"> </span><span class="n">i</span><span class="o">++</span><span class="p">)</span><span class="w"></span>
<span class="w"> </span><span class="n">output</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">str</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="w"> </span><span class="o">^</span><span class="w"> </span><span class="mi">13</span><span class="p">;</span><span class="w"> </span><span class="c1">//13 = 0xd</span>
<span class="w"> </span><span class="n">output</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="sc">'\0'</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="n">printf</span><span class="p">(</span><span class="s">"</span><span class="se">\n</span><span class="s"> %s </span><span class="se">\n\n</span><span class="s">"</span><span class="p">,</span><span class="w"> </span><span class="n">output</span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>
<p>And booom! We get the flag!</p>
<p><img alt="terminal" src="http://www.thecrowned.org/wp-content/uploads/2018/12/endless-x-mas9.png"></p>Is every piece of software doomed to become bloated?2018-12-15T23:14:00+01:002018-12-15T23:14:00+01:00Stefano Ottolenghitag:thecrowned.org,2018-12-15:/is-every-software-bloated<p>One of the very few things I learnt in art class is what role Jackson Pollock played in his art. Because, we were asked, what is the role of the artist, if the only thing …</p><p>One of the very few things I learnt in art class is what role Jackson Pollock played in his art. Because, we were asked, what is the role of the artist, if the only thing he does is let paint drop on a canvas?</p>
<p><img src="http://www.thecrowned.org/wp-content/uploads/2018/12/pollock-300x225.jpg" style="width:300px; float:right;" /></p>
<p><em>His role is to decide when the work is complete</em>.</p>
<p>This is something we most often overlook in computer science: <strong>there comes a time when a project,</strong> or a feature, <strong>is complete, and</strong> any more improvements, <strong>any more work put into it is likely to decrease its value and ruin all the good work</strong>. Too often we want <em>progress</em> in our applications, without realizing that it's actually destroying them. Sometimes it's just better to move on and work on something else. Even if a solution is 10 years old, it doesn't mean it <em>has</em> to be updated because <em>progress</em> requires it.</p>
<p>Let me present a couple examples.</p>
<h2 id="the-gutenberg-editor-in-wordpress">The Gutenberg editor in WordPress</h2>
<p>WordPress 5 introduced the new Gutenberg editor, a project that <a href="https://wordpress.org/support/plugin/gutenberg/reviews/">has been rated</a> with 2 stars out of 5 with a total of around 2000 reviews at the time of writing. It's a product that is so buggy and un-usable that it is bewildering that it made it into Core, but whatever (in 10 minutes of usage, I found 7 crucial and unreported bugs just 4 months prior to release - <a href="https://wordpress.org/support/topic/no-not-in-core-please/">see my review</a>).</p>
<p><img src="http://www.thecrowned.org/wp-content/uploads/2018/12/Screenshot-from-2018-12-15-23-04-57.png" style="float:right; width: 350px" /></p>
<p>Let us pause and ponder <em>why it was introduced</em>. Any apology of Gutenberg will say that is because the classic editor felt <em>old</em>. It looked so much like Office 2003, and it's 2018, they say! You see, they say, 15 years in computer science is a huge deal!</p>
<p>But, you see, what is the main purpose of an editor? To write. And to that it must be apt. Gutenberg shifted the focus from writing content to designing a page, effectively forcing a progress in the wrong direction. Not much has changed in writing since Office 2003 came around: we still use bold, italic, headings, text alignment and little more. Anything else requires the careful crafting of a designer and writing of some HTML, as it should be. Nothing else is needed, really, when it comes to writing. But, they say, you cannot even create a table with the classic editor! And I say, that's right, it should be possible! But that doesn't require trashing a whole editor and building a cumbersome React-y thing just so that we can have tables, does it?</p>
<p>But, they say, <em>this way you don't need a designer to design your pages anymore</em>. Of course, people must be really stupid if they have been paying web-developers/designers to put up their websites for the last 25 years, of course! So stupid of us! There, instead of hiring a professional photographer to shoot at your weeding, just give a compact camera to your uncle, since technology and progress have enabled you to do so. Because it really is just the same. When I was a kid, websites designed with Dreamweaver were looked down on, and anybody who wanted a real site should have hired a professional. Not it looks like everybody can do everything - expect that, uhm, they can't.</p>
<p>Too often <strong>the right questions</strong> are not asked and carefully considered. Those are the most basic ones: <em>do we really need this thing?</em> How difficult is it to build it? <em>Is it really worth it? What is the impact</em> it will have on users/market? Does it add something really useful and needed without breaking anything else?</p>
<p>If some answers to these questions are not fully positive, then it's likely we are doing something just because it <em>can</em> be done. But the fact that it <em>can</em> be done doesn't mean that it <em>should</em>. We all know how bad Windows is getting by just feeling like <em>they have to</em> completely change their style every 4 years. (When not on Ubuntu,) I run Windows 7 and I don't feel any urge to jump forward, and I have got clients that ask me to install it on their brand new laptop, even if it's a 10 years old system. Maybe all that came after it was not needed?</p>
<h2 id="co-authors-plus-and-guest-authors">Co-Authors Plus and guest authors</h2>
<p>The <a href="https://it.wordpress.org/plugins/co-authors-plus/">Co-Authors Plus</a> plugin allows WordPress posts to have more than one author. It's a useful feature. I have made significant contributions to that plugin during my internship at Automattic, so I have come to know the plugin very well.</p>
<p>This plugin is now almost 10 years old, and in its basic form, it allowed to add two WordPress users as authors to the same post. This is already a significantly difficult task from a technical point of view. But then, 3 years from its first release, plugin authors introduced the <em>Guest authors</em> feature, which allowed to create virtual user profiles for people who did not have an account on the site but had to be added as co-authors.</p>
<p>God, how bad that has been. <em>Virtual user profile</em> means that it's a not a WordPress user, so in order to add it as co-author, a bunch of horrible things need to be done. It introduced so many issues and possible bugs that even now, in 2018, we are still fixing them. At the end of the day, the database was littered with so many redundant records to make the whole thing work, and the logic of the software so more difficult, that it's doubtful that the feature was good (not to mention that there was no real uninstall routine, so all that garbage stayed there forever).</p>
<p>And now pause and ponder: <em>why did we need that? What is the sense</em> of creating virtual user profiles? Does it really add something important, given the issues and badness it gives rise to? Most importantly, <strong>what is the sense</strong> of creating a virtual user profile that is <em>not</em> a real user but <em>can</em> be added as co-author? Can't we just create a real user profile and give it the Subscriber role, aka the lowest set of permissions? What is wrong with this idea? Nothing - but you see, if we always took the right decision then we wouldn't need a fork of Co-Authors Plus that only retains the useful features, and it would be too easy.</p>A day tour in Cork, Ireland: the best things to see and do!2018-11-23T23:23:00+01:002018-11-23T23:23:00+01:00Stefano Ottolenghitag:thecrowned.org,2018-11-23:/a-day-tour-cork-ireland-best-things-to-see-do<p>After spending two months in Cork, Ireland, I feel like I can provide good advice for a nice day tour in Cork. I stayed in July-August, so some of my comments may be affected (by …</p><p>After spending two months in Cork, Ireland, I feel like I can provide good advice for a nice day tour in Cork. I stayed in July-August, so some of my comments may be affected (by good weather, mostly). Here is the <a href="https://www.flickr.com/photos/ste_95/sets/72157671717882318">photo album of the whole time</a>.</p>
<p>I think the best way to go around is on foot, as you can really enjoy the town, but there are the Coca Cola bikes that can be rent for 3 days: you just need to register online, pay a few euros, and then pick a bike in the many bike points scattered around Cork, and return it in any other one.</p>
<h2 id="a-day-tour-in-cork">A day tour in Cork</h2>
<p>If you want to go to the tourist office, for example to take a map or ask for some information, the best time to do so in when you get in town. In fact, the office is near both the bus station and the CityLink stops. In the building just in front of the tourist office, at the second floor, there are free toilets, handy in any circumstance!</p>
<p>In Shandon (St. Anne's Church) you can ring the church bells! It really is amazing! Music sheets are provided with popular songs (Hey Jude, Lord of the rings soundtrack, and many more) and the bells sequence to be played. It is a bit out of the centre, but it does even go through nice streets. For students, the entrance is just 4 euros, and you can also get to the top of the bell tower and see Cork from above. Beware that the last entry is early in the afternoon, at around 16. This church is also called The Four Liars because, with strong winds, the four clocks on its sides are said to never display the same time.</p>
<p><img alt="The four liars, St. Anne's Church, Shandon" src="http://www.thecrowned.org/wp-content/uploads/2018/11/43932468651_7ab4a5b3a4_k.jpg">
The four liars, St. Anne's Church, Shandon</p>
<p>Exactly on the other bank of the river there is Elizabeth Fort. It is not much, they are basically some high walking paths, but again you can see Cork from above (although not as high as the Church), and it is free! Closes at around 17. Near here there is also St. Fin Barre's Church, which is patron saint of the city. The church surroundings are not bad, the church is huge (but entry is not free) and there is a very disappointing labyrinth.</p>
<p>On a clear day, Fitzgerald Park is a beautiful place. It's the biggest park of the city, there are often events in the weekend (music/festivals). There is even a bar that makes nice launches for reasonable prices (\~5 euros for a sandwich), and you can then eat on the grass on the river banks. It's a bit out of town, like 20 minutes on foot, but you can go there basically all through pedestrian-only streets, and there is a walking route that goes along the river bank on the other side: it starts from Shandon's side and is called something like Banks of Lee Walkway.</p>
<p><img alt="Fitzgerald Park, Cork" src="http://www.thecrowned.org/wp-content/uploads/2018/11/29115132037_0a2600087c_k.jpg">
Fitzgerald Park, Cork</p>
<p>Even if you don't need any groceries, the English Market is a great place to visit. Besides all the stalls that sell sweets & chocolate, there are all local farmers and it's very nice to go around. It is located right in the centre and has several paths in and out.</p>
<p>While going around the centre, my favorite narrow street can be accessed by North Main Street: it's a tiny hole going towards the river: you will go through a narrow path all painted by local guys. A spot of love.</p>
<p><img alt="A beautiful hidden narrow street in Cork" src="http://www.thecrowned.org/wp-content/uploads/2018/11/30184292348_e3e8776537_k.jpg">
A beautiful hidden narrow street in Cork</p>
<p>For a snack, there is an awesome place called Sticky Fingers that serves a stunning variety of donuts for 2/3 euros, and they are really good! It's just in the centre, opposite to the Opera House, besides a Costa Coffee.</p>
<p>If you happen to be in town later than 18, the "Sin è" pub always has some great live music and a lively atmosphere, peculiar of Irish pubs, and is definitely worth a visit. If it is crowded, you can get into for the music and don't even take anything to drink. This pub really offers the true Irish Pub experience that I have not felt in many other more mainstream places (like The Oliver Plunkett). What I have found to be the best burger in town is offered by West Cork Burgers, 2 minutes from the centre. A nice place to hang out and have a pizza is Tom Barry's - the pizza is actually quite good, and is not expensive - lots of young people there. Finally, a good italian option is Ristorante Rossini.</p>
<p>Also, always go around and look for street art: it's everywhere! Rubbish bins, houses, electrical boxes, walls... anything that can be decorated is likely to actually be so!</p>
<p><img alt="Street art on house in Cork" src="http://www.thecrowned.org/wp-content/uploads/2018/11/43962372082_d37c040f2c_k.jpg">
Street art on house in Cork</p>
<p>A nice bookshop, both for second hand and new books, is called Vibes and Scribes, just 5 minutes out of the centre.</p>
<p>If you happen to be in town at sunset, and it's a good day, on the river walk that heads away from the centre starting from Fitzgerald Park you may be lucky enough to appreciate one of the most beautiful sunsets. There, I witnessed one of the best sunsets I have even seen. The walk will take you at least a full hour to complete.</p>
<p><img alt="Sunset on Lee River Walk, Cork" src="http://www.thecrowned.org/wp-content/uploads/2018/11/43104809705_2e3470118f_k.jpg">
Sunset on Lee River Walk, Cork</p>
<h2 id="trips-from-cork">Trips from Cork</h2>
<p>If you are in town for more time, the trips I can recommend are:</p>
<ol>
<li>
<p><strong>Galway, the Aran Islands and Cliffs of Moher</strong>. This is probably the best I had, plan for 4-5 days. Inishmore is the biggest of the Aran Islands and is amazing. You can get a bike for around 10€ for the full day and go around it. My strategy has been: every time I came at a junction, to where fewer people went. I ended up in the middle of nowhere, truly breathing the island atmosphere. Be sure to bring your lunch and anything you need for the whole day. Go to Dun Aengus as well: if you can't make it to Cliffs of Moher, you will see some real irish cliffs here! <br />
<br />
<img alt="aran" src="http://www.thecrowned.org/wp-content/uploads/2018/11/30189893298_aa57c8c5fe_k.jpg">
Inishmore, Aran Islands<br />
<br />
The Cliffs of Moher is another beautiful place, but packed with tourists. I took a hostel in Lisdoonvarna, a desert place, then took a bus to Doolin early in the morning and walked from there to Cliffs of Moher, slowly rising and climbing up to the top of them. Don't get fooled in the day trips that take you to the tourist center of the cliffs and back - have the full experience.<br />
<br />
<img alt="moher" src="http://www.thecrowned.org/wp-content/uploads/2018/11/43104778395_3416fdc681_k.jpg">
Cliffs of Moher<br />
<br />
Connemara is another nice region around Galway, but I didn't have time to explore it much. And Galway is nice too, although quite touristic. The Aran Sweater Market is a great place to buy real Irish wool, very warm indeed!</p>
</li>
<li>
<p><strong>The Beara Way</strong>. A ring that goes all around the Beara peninsula. It crosses some very nice villages, all quite still living the real irish life, and full of so many kind people. Be warned that getting good weather in that place is really a jackpot: most of my days there were spent walking in the mud looking at my feet, cursing at all the goats that seemed to at ease in that inhospitable land. Beautiful sceneries when the sun was out, though.<br />
<br />
<img alt="beara" src="http://www.thecrowned.org/wp-content/uploads/2018/11/42515331510_3eea96f78b_k.jpg">
Allihies, Beara Way</p>
</li>
<li>
<p><strong>Killarney and its National Park</strong>. This was a bit disappointing for me. I expected a big national park, and I found the part I went into packed with tourists. The city of Killarney was full of tourists and of shops for them. I stayed one night and went to Ross Castle, Muckross Abbey and Torc Waterfalls, but they were all full of people and nothing really exciting.<br />
<br />
I have had the feeling that the nice part of the park started <em>after</em> Torc Waterfalls, which is the farthest I could go without missing my bus back home. Overall, I did not find it much worth for a day visit, but I met a canadian guy who had walked the Ring of Kerry, which goes deep into the park, and was amazingly happy!<br />
<br />
<img alt="killarney" src="http://www.thecrowned.org/wp-content/uploads/2018/11/30079585208_f76d5d6905_k.jpg">
Killarney National Park</p>
</li>
<li>
<p><strong>Kinsale</strong>. A nice small sailors village on the see to the south, just an hour bus from Cork. There are some nice shops, while the beaches and nice nature is a bit away from the city (around 30 minutes walking at least). May be worth going to Hold Head Castle, but is pretty far from the town itself and really requires a means of transport (around 20 km, mostly on concrete).<br />
<br />
<img alt="kinsale" src="http://www.thecrowned.org/wp-content/uploads/2018/11/28943288127_4a0000b9fd_k.jpg">
Kinsale, Cork</p>
</li>
</ol>Tasks un-owned are task that go forgotten2018-08-11T22:02:00+02:002018-08-11T22:02:00+02:00Stefano Ottolenghitag:thecrowned.org,2018-08-11:/tasks-un-owned-task-go-forgotten<p>If you are a tech company, and your people commit code, then you probably have some code review policy. And if you do not, you definitely should: you want to have an extra pair of …</p><p>If you are a tech company, and your people commit code, then you probably have some code review policy. And if you do not, you definitely should: you want to have an extra pair of eyes on the code that goes live. You certainly do not want a mistake to break things. And that is why you do pull requests to contribute to GitHub repos, and why Google employees must have a certain degree of maturity to commit code without review.</p>
<p>BUT, as long as that is a good idea, we must be careful to implement it the right way. Just enforcing reviews is not enough. <strong>You want to make the time between the <em>code is sent for review</em> and the <em>code is deployed</em> as short as possible</strong>. The longer the review time span is, the more work will be needed when the review comes. That is because:</p>
<ol>
<li>Who wrote the code simply does not have it fresh in their mind anymore. The context switch between the current task and the code he wrote days/weeks ago is just more demanding;</li>
<li>Conflicts are more likely to arise, and then more work is needed in solving them;</li>
<li>Other issues may depend on the code being held for review. Other people may spend (waste) time debugging an issue for which a solution is already available;</li>
<li>If the repo is public, it makes more difficult for other to jump in and contribute, because they also have to be aware of all the pending code.</li>
</ol>
<h2 id="the-right-way-to-implement-a-code-review-system-in-a-tech-company">The right way to implement a code review system in a tech company</h2>
<p>I think the best way to implement a code review system is to:</p>
<ol>
<li>Assign each code review to a particular member of the team. If everybody owns a task, then nobody does as well. That is why you want that particular review to be a responsibility of someone specific. An automated system can randomly assign a review to a team member.</li>
<li>Each code review comes with a deadline. That's it: code reviews are as important as any other task - basically because every other task often generates a code review at some point, so if we lag on those, nothing gets carried to the end and we are getting no work done at all! We may have different priorities associated with different deadlines, but we want each review to expire at some point (with the longest being a couple days)!</li>
<li>Team members can turn down their assignments, but only if they have a good reason to. Again, if code does not get reviewed, it cannot go live, and thus the work has been done for nothing. Reviews are important and must be considered as such.</li>
<li>Then just track how it goes: who is turning down most assignments? Is the weight uniformly distributed across the team?</li>
</ol>WP CLI custom commands not working2018-06-19T02:36:00+02:002018-06-19T02:36:00+02:00Stefano Ottolenghitag:thecrowned.org,2018-06-19:/wp-cli-custom-commands-not-working<p>Make sure to add them in active code. Adding a spare php file with the WP-CLI command definition in /wordpress won't work, because that code won't be loaded by WP. Dropping the file into wp-content …</p><p>Make sure to add them in active code. Adding a spare php file with the WP-CLI command definition in /wordpress won't work, because that code won't be loaded by WP. Dropping the file into wp-content/plugins won't work as well. Make it part of an active plugin, or use your theme's functions.php.</p>A tale in topology - The large clovers meadow2018-04-18T10:27:00+02:002018-04-18T10:27:00+02:00Stefano Ottolenghitag:thecrowned.org,2018-04-18:/tale-topology-large-clovers-meadow<blockquote>
<p>A small tale with a topological soul, with the aim of providing a very high level intuition for the notion of density and dense set in topology.</p>
</blockquote>
<hr>
<p>Smally Open was a cheeky youngster of the …</p><blockquote>
<p>A small tale with a topological soul, with the aim of providing a very high level intuition for the notion of density and dense set in topology.</p>
</blockquote>
<hr>
<p>Smally Open was a cheeky youngster of the Open family who lived in a large meadow. It was a very nice and green meadow, well cared for and abundant in clovers. Smally Open liked it much because he found it magic. Oh, I wish you were there as well, my smally readers: Smally Open climbed the highest trees, went as far as he could from his home, went down below the blades of grass, but there was no way: anyway he was, Smally Open would always see his verdant meadow, as if it moved with him and changed its look. Anyway he looked, Smaly Open would always see a large green meadow.</p>
<p>You will wonder, my dears, if Smally Open did not get bored to death in always seeing the very same meadow anywhere he went... well, I hope you do not believe that it was just all alike! The meadow looked all the same if you looked from afar and without attention, but sometimes there was a butterfly here, some other time there was an amazingly beautiful red leaf there: at any rate, there was always some new thing to fascinate him.</p>
<p>Anyway, when Smally Open did get bored, there always was a nice way he liked to use to spend his time: go and bother Mr. Dense-D. He was pretty a weird and extraordinary sir, in fact he had a lot of heads (how many, you ask? Try to count the stars in a cloudless night: Mr Dense-D had at least three-times-twice-one-thousand-times-that-number-of-heads!) You should not however think of D-Dense as a green weird martian with several scary heads, going away pecking at children's ears, no! D-Dense was a very distinguished man, very kind and happy.</p>
<p>Indeed, D-Dense was the happiness of the meadow in which he lived together with Smally Open, because he was its clovers. But not a few crumpled and frail clovers, oh well stop with these prejudices towards Mr. D-Dense! No, those were beautiful clovers, big and comfortable, and above all there was one in any part of the meadow you would look for. When Smally Open was tired, he would look around and immediately find one of the heads of Mr. D-Dense, upon which he could rest under the sunlight.</p>
<p>Apertolo, paying, would often jump from one blade of grass to another. When he would lose his balance and fall in the void he was always lucky that Mr. D-Dense lived in his same meadow, because were it not for his many heads that would catch him, falling on the ground he would at least get a big bruise! But Smally Open sometimes take advantage of Mr. D-Dense's kindness: he would jump on his heads from tree tops on purpose, he would eat on the clovers, crumbling on D-Dense's heads, or he would ruffle all his hair intentionally. At any rate, he would really bother him! Mr. D-Dense was a patient man, but one day he was fed up and said: "Enough, I am old and tired: I need a quiet holiday. I am going to leave." And so he started lifting himself.</p>
<p>First, Smally Open fell on the ground. Then he heard a great rumble, as if the whole land was shaking, and saw all the clovers moving and lifting, with mountains of soil up in the air, until Mr. D-Dense headed with all his clover-heads towards the horizon.</p>
<hr>
<p>Days went by since Mr. D-Dense had left, and Smally Open did not know what to do. The meadow was not as nice as before: now small holes could be seen everywhere, left by Mr. D-Dense's heads, and, from any perspective he looked the meadow at, it now looked sad and battered, as if something was missing and it was not the magic meadow he so much liked anymore. Moreover, when Smally Open was tired, he could but sit on the ground, in the shade: he missed so much the comfortable and cozy clovers couch. To have fun again, he tried to jump from one blade of grass to another, but this time, when he fell, Mr. D-Dense was not there to grab him and he got himself a big bruise. Smally Open was not able to build anything nice and funny as when Mr. D-Dense was his playmate.</p>
<p>By now, Smally Open spent several hours on the top of the highest tree, with the hope of seeing Mr. D-Dense coming back. Just when he was about to lose all hopes, there he caught a glimpse of a shadow, far far away. It just looked the shadow of someone with lots of heads... and who could it have been if not Mr. D-Dense? Smally Open got down the tree and run towards him. Indeed, it was Mr. D-Dense! Smally Open went closer to one of his heads, patted a delicate knock-knock, and said: "I am sorry Mr. D-Dense for jumping on your heads and for eating on your heads and for ruffling your hair, I will not do it again. But will you come back? Mr. D-Dense took Smally Open on one of his clover heads and told him: "Of course I will come back. Come, I have got to tell you about the friends I met again in this holiday of mine: the waves of the sea, the dunes of the desert, the stars of the sky..."</p>Una storia topologica - Il grande prato dei trifogli2018-04-18T00:00:00+02:002018-04-18T00:00:00+02:00Stefano Ottolenghitag:thecrowned.org,2018-04-18:/it/toplogia-storia-il-grande-prato-dei-trifogli<blockquote>
<p>Una piccola storia dall'animo topologico, con lo scopo di dare un'intuizione di alto livello per la nozione topologica di densità e di insieme denso.</p>
</blockquote>
<hr>
<p>Apertolo era un birichino membro della famiglia degli Aperti che viveva …</p><blockquote>
<p>Una piccola storia dall'animo topologico, con lo scopo di dare un'intuizione di alto livello per la nozione topologica di densità e di insieme denso.</p>
</blockquote>
<hr>
<p>Apertolo era un birichino membro della famiglia degli Aperti che viveva in un grandissimo prato. Era un prato molto bello e verde, ben curato e ricco di trifogli. Ad Apertolo piaceva tanto perché lo trovava magico. Oh vorrei che foste stati lì anche voi, miei piccoli lettori: Apertolo saliva sugli alberi più alti, si allontanava quanto più poteva dalla sua casa, scendeva in basso in basso sotto i fili d'erba, ma non c'era verso: dovunque si trovasse, Apertolo vedeva sempre lo stesso verdeggiante prato, come se il prato si muovesse insieme a lui e cambiasse il suo aspetto. Dovunque guardasse, Apertolo vedeva un grande prato verde.</p>
<p>Vi chiederete, miei cari, se Apertolo non si annoiasse a morte a vedere sempre lo stesso identico prato dovunque andasse… ebbene, non credete che fosse proprio tutto uguale! Il prato sembrava uguale a chi guardava da lontano e senza attenzione, ma una volta c'era una farfalla qui, un'altra volta c'era una bellissima foglia rossa là: insomma, c'era sempre qualche novità che lo affascinava.</p>
<p>Ad ogni modo, quando ad Apertolo capitava di annoiarsi, c'era sempre un passatempo che non gli mancava: andare a infastidire il Signor D-Denso. Egli era un signore piuttosto inusuale e alquanto straordinario, infatti aveva tantissime teste ("quante", mi chiedete? Provate a contare le stelle in una nottata serena: il Signor D-Denso aveva almeno il-triplo-del-doppio-di-mille-volte-quel-numero di teste!) Non dovete però immaginare il Signor D-Denso come uno strambo marziano verde dalle tante spaventose teste che va in giro a mangiucchiare le orecchie ai bambini, no accipicchia! D-Denso era un signore ben distinto, sempre tirato a lucido, molto gentile e sprizzante allegria.</p>
<p>In effetti, D-Denso era la gioia del prato in cui viveva insieme ad Apertolo, perché ne era i trifogli! Ma non pochi sgualciti e caduchi trifogli in un prato malconcio, oibò basta con questi pregiudizi verso il Signor D-Denso! No, erano bellissimi trifogli, larghi e comodi e di un verde bellissimo, e soprattutto ce n'era uno in qualunque parte del prato voi lo cercaste. Quando Apertolo era stanco, si guardava intorno e trovava subito una delle teste del signor D-Denso su cui riposarsi alla luce del sole.</p>
<p>Apertolo, per gioco, spesso saltellava da un filo d'erba all'altro. Quando perdeva l'equilibrio e cascava nel vuoto gli andava sempre bene che il signor D-Denso viveva nel suo stesso prato, perché se non ci fossero state le sue teste a prenderlo, cadendo a terra si sarebbe fatto almeno un bel bernoccolo! Ma Apertolo a volte abusava della gentilezza e disponibilità del Signor D-Denso, e si gettava di proposito sulle sue teste dalle cime degli alberi, oppure si portava da mangiare sui trifogli, sbriciolando sulle teste del Signor D-Denso, oppure ancora gli arruffava tutti i capelli apposta: insomma gliene combinava proprio di tutti i colori! Il Signor D-Denso era paziente, ma un giorno non ce la fece più e disse: "Basta, sono vecchio e stanco: ho bisogno di una vacanza. Andrò via." E allora fece per alzarsi.</p>
<p>Per prima cosa Apertolo cadde a terra. Poi udì un grande rombo, come se tutta la terra tremasse, e vide tutti i trifogli che si muovevano e si sollevavano, con un mucchio di terra che finiva per aria, finché alla fine il signor D-Denso si incamminò con tutte le sue teste-trifogli al seguito verso l'orizzonte.</p>
<hr>
<p>Passavano i giorni da quando il Signor D-Denso se n'era andato, e Apertolo non sapeva più che fare. Il prato non era più bello come prima: ora si intravedevano ovunque piccoli buchi, lasciati dalle teste del Signor D-Denso, e da dovunque guardasse il prato Apertolo, ora gli sembrava triste e malconcio, come se mancasse qualcosa e non fosse più il prato magico che a lui tanto piaceva. E poi ora quando Apertolo era stanco non poteva far altro che sedersi a terra, all'ombra: come gli mancava il comodo e accogliente giaciglio dei trifogli! Per divertirsi di nuovo, provò a saltellare da un filo d'erba all'altro, ma questa volta quando cadde non c'era il signor D-Denso a recuperarlo e si fece un bel bernoccolo e anche qualche livido. Apertolo cercava di inventarsi nuovi passatempi, ma non riusciva a costruire nulla di bello e divertente come quando il Signor D-Denso era suo compagno di avventure.</p>
<p>Ormai Apertolo passava tanto tempo sulla cima dell'albero più alto, con la speranza di veder ricomparire il Signor D-Denso all'orizzonte. Proprio quando stava per perdere le speranze, ecco che intravide una grande ombra molto distante, sembrava proprio qualcuno con molte teste… e chi poteva essere se non il Signor D-Denso? Apertolo scese dall'albero e gli corse incontro. E sì era proprio il Signor D-Denso! Apertolo si avvicinò a una delle sue teste, fece delicatamente toc-toc e disse: "Scusami signor D-Denso per esserti saltato sulle teste e averti sbriciolato sulle teste e averti arruffato i capelli, non lo farò più. Tu però torni?" Il Signor D-Denso si caricò Apertolo su una testa-trifoglio e gli disse: "Ma certo che torno. Andiamo, devo raccontarti degli amici che ho rivisto in questa vacanza: le onde del mare, le dune del deserto, le stelle del cielo..."</p>A/B testing on WordPress: a framework for developers and tutorial2018-04-13T08:52:00+02:002018-04-13T08:52:00+02:00Stefano Ottolenghitag:thecrowned.org,2018-04-13:/a-b-testing-on-wordpress-a-framework-for-developers-and-tutorial<p>Some months ago, I changed one link in the menu in my website <a href="https://postpaycounter.com">postpaycounter.com</a>. After that, it looked to me more people were purchasing products, i.e. the conversion rate had increased. But how …</p><p>Some months ago, I changed one link in the menu in my website <a href="https://postpaycounter.com">postpaycounter.com</a>. After that, it looked to me more people were purchasing products, i.e. the conversion rate had increased. But how to check whther that was really the case, or if it was just an accident/impression? Use an A/B test, I told myself!</p>
<p><strong>With an A/B test</strong>, half of the users are served one version of the page, the one with the old link, and half of them another version of it, the one with the new link in place. When a sale happens, we may then log that as a success for the kind of page that was used, be it the A version or the B one.</p>
<p>In my case, the two versions of the page simply consisted of two different links in the menu, while I wanted the success to be logged when the user purchased something (I use <em>Easy Digital Downloads</em> to handle purchases).</p>
<p>I could find a bunch of plugins that allowed to set up A/B tests, but they all seemed pretty difficult to customize from a developer perspective, and I was already seeing myself wrestling with someone else's code that provide tons of features useless to me, but through which was nearly impossible to interact with Easy Digital Downloads. So I decided to build <strong>my own, simple implementation, with the aim of it being tailored to developers rather than users who needed an interface.</strong></p>
<h2 id="an-ab-test-implementation-example">An A/B test implementation example</h2>
<p>This is an example of how to use the little framework. To set up a test, you only need to provide <strong>two functions</strong>:</p>
<ul>
<li><code>A_function</code> specifies what should happen to the page that is served to the first half of visitors. In my case, I attach a filter to edit the navigation menu with a custom function.</li>
<li><code>B_function</code> works the same for specifying what should happen to the second version of the page. Again, I attach a filter to the navigation menu.</li>
</ul>
<p>At this point, you can set up a test object, through which you will be able to retrieve the almost-unique alphanumeric test ID (it's a MD5 hash of the setup arguments).</p>
<p>Finally, you need a <strong>third function</strong> that is going to log successes. You should probably have it attached to some action or filter. In my case, I used an action that fires when a purchase through Easy Digital Downloads happens. The <strong>only requirement</strong> about the test function is that it should call <code>AB_log_success( $test_id )</code>, that will handle the under-the-hood success logging, basically updating the <code>wp_option</code> (<code>AB_tests</code>) containing the stats.</p>
<div class="highlight"><pre><span></span><code><span class="cp"><?php</span>
<span class="k">global</span> <span class="nv">$test_id</span><span class="p">;</span>
<span class="nv">$args</span> <span class="o">=</span> <span class="k">array</span><span class="p">(</span>
<span class="s1">'label'</span> <span class="o">=></span> <span class="s1">'Menu link'</span><span class="p">,</span>
<span class="s1">'A_function'</span> <span class="o">=></span> <span class="s1">'test_A_function'</span><span class="p">,</span>
<span class="s1">'B_function'</span> <span class="o">=></span> <span class="s1">'test_B_function'</span>
<span class="p">);</span>
<span class="nv">$test</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">AB_test</span><span class="p">(</span> <span class="nv">$args</span> <span class="p">);</span>
<span class="nv">$test_id</span> <span class="o">=</span> <span class="nv">$test</span><span class="o">-></span><span class="na">get_ABT_id</span><span class="p">();</span>
<span class="k">function</span> <span class="nf">test_A_function</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">add_filter</span><span class="p">(</span> <span class="s1">'wp_get_nav_menu_items'</span><span class="p">,</span> <span class="s1">'leave_untouched'</span><span class="p">,</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">3</span> <span class="p">);</span>
<span class="p">}</span>
<span class="k">function</span> <span class="nf">test_B_function</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">add_filter</span><span class="p">(</span> <span class="s1">'wp_get_nav_menu_items'</span><span class="p">,</span> <span class="s1">'change_menu'</span><span class="p">,</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">3</span> <span class="p">);</span>
<span class="p">}</span>
<span class="k">function</span> <span class="nf">log_success</span><span class="p">(</span> <span class="nv">$download_id</span><span class="p">,</span> <span class="nv">$payment_id</span><span class="p">,</span> <span class="nv">$download_type</span> <span class="p">)</span> <span class="p">{</span>
<span class="k">global</span> <span class="nv">$test_id</span><span class="p">;</span>
<span class="nx">AB_log_success</span><span class="p">(</span> <span class="nv">$test_id</span> <span class="p">);</span>
<span class="p">}</span>
<span class="nx">add_action</span><span class="p">(</span> <span class="s1">'edd_complete_download_purchase'</span><span class="p">,</span> <span class="s1">'log_success'</span><span class="p">,</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">3</span> <span class="p">);</span>
</code></pre></div>
<p>For the full code, see the <a href="https://github.com/TheCrowned/A-B-Testing-for-WordPress-Developers">GitHub repo!</a></p>Numpy histogram density does not sum to 12018-03-19T08:49:00+01:002018-03-19T08:49:00+01:00Stefano Ottolenghitag:thecrowned.org,2018-03-19:/numpy-histogram-density-not-sum-1<p>During a Computational Vision lab, while comparing histograms, I stumbled upon a peculiar behavior. The histograms pairwise kernel matrix - which is just a fancy name for the matrix holding <em>histograms correlations one with another</em> - did …</p><p>During a Computational Vision lab, while comparing histograms, I stumbled upon a peculiar behavior. The histograms pairwise kernel matrix - which is just a fancy name for the matrix holding <em>histograms correlations one with another</em> - did not have ones on the diagonal. This means that one histogram was not fully correlated to itself, which is weird.</p>
<p><img alt="numpy histogram integral not 1" src="http://www.thecrowned.org/wp-content/uploads/2018/03/histogram-integral-1.png">
The comparison metric I was using is the simple histogram intersection one, defined as
</p>
<div class="math">$$K_{hi} = \sum^{d}_{m=1} min(x_m,y_m)$$</div>
<p>And histograms were generated basing on this call schema:</p>
<div class="highlight"><pre><span></span><code><span class="n">bins_num</span> <span class="o">=</span> <span class="mi">35</span>
<span class="n">red</span><span class="p">,</span> <span class="n">binsred</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">histogram</span><span class="p">(</span><span class="n">np</span><span class="o">.</span><span class="n">ndarray</span><span class="o">.</span><span class="n">flatten</span><span class="p">(</span><span class="n">I</span><span class="p">[:,:,</span><span class="mi">0</span><span class="p">]),</span> <span class="n">bins_num</span><span class="p">,</span> <span class="n">density</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="n">green</span><span class="p">,</span> <span class="n">binsgreen</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">histogram</span><span class="p">(</span><span class="n">np</span><span class="o">.</span><span class="n">ndarray</span><span class="o">.</span><span class="n">flatten</span><span class="p">(</span><span class="n">I</span><span class="p">[:,:,</span><span class="mi">1</span><span class="p">]),</span> <span class="n">bins_num</span><span class="p">,</span> <span class="n">density</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="n">blue</span><span class="p">,</span> <span class="n">binsblue</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">histogram</span><span class="p">(</span><span class="n">np</span><span class="o">.</span><span class="n">ndarray</span><span class="o">.</span><span class="n">flatten</span><span class="p">(</span><span class="n">I</span><span class="p">[:,:,</span><span class="mi">2</span><span class="p">]),</span> <span class="n">bins_num</span><span class="p">,</span> <span class="n">density</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="n">h</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">concatenate</span><span class="p">((</span><span class="n">red</span><span class="p">,</span> <span class="n">green</span><span class="p">,</span> <span class="n">blue</span><span class="p">),</span> <span class="n">axis</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span>
</code></pre></div>
<p>So I started digging into the code I was provided to find where the issue lay. I noticed that sum(h) was not 1, so this was really the issue. Even considering one histogram as a whole, its sum was not what it was supposed to be.</p>
<p>Looking at the doc for <a href="https://docs.scipy.org/doc/numpy-1.14.0/reference/generated/numpy.histogram.html">numpy.histogram,</a> I found this remark for the <em>density</em> parameter:</p>
<blockquote>
<p>If <code>True</code>, the result is the value of the <em>probability density function</em> at the bin, normalized such that the integral over the range is 1. Note that the sum of the histogram values will not be equal to 1 unless<em> bins of unity width</em> are chosen.</p>
</blockquote>
<p>Indeed, trying as bins number 256/3, sum(h) = 1, and the pairwise matrix had ones on the diagonal. I first used 256 as bins number, which seemed like a more correct choice, but it sum(h) = 3 that way, because of the RGB decomposition.</p>
<p><strong>One better solution</strong>, independent from the choice of the size of the bins, is to slightly <strong>change the metric <em>hik</em> function</strong> to account for the size of the bins.</p>
<p>In fact, instead of simply doing</p>
<div class="highlight"><pre><span></span><code><span class="n">hi</span> <span class="o">+=</span> <span class="nb">min</span><span class="p">(</span><span class="n">x</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="n">y</span><span class="p">[</span><span class="n">i</span><span class="p">])</span>
</code></pre></div>
<p>I tried</p>
<div class="highlight"><pre><span></span><code><span class="n">hi</span> <span class="o">+=</span> <span class="p">(</span><span class="nb">min</span><span class="p">(</span><span class="n">x</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="n">y</span><span class="p">[</span><span class="n">i</span><span class="p">])</span><span class="o">*</span><span class="p">(</span><span class="mi">256</span><span class="o">/</span><span class="nb">len</span><span class="p">(</span><span class="n">x</span><span class="p">)))</span>
</code></pre></div>
<p>so that it scales the output depending on the number of bins. This way, all histograms sum to one regardless of the number of bins chosen!</p>
<p><img alt="numpy histogram integral not 1" src="http://www.thecrowned.org/wp-content/uploads/2018/03/histogram-integral-2.png"></p>
<p>And here is the final metric definition:</p>
<p><img alt="numpy histogram integral not 1" src="http://www.thecrowned.org/wp-content/uploads/2018/03/histogram-integral-3.png"></p>
<script type="text/javascript">if (!document.getElementById('mathjaxscript_pelican_#%@#$@#')) {
var align = "center",
indent = "0em",
linebreak = "false";
if (false) {
align = (screen.width < 768) ? "left" : align;
indent = (screen.width < 768) ? "0em" : indent;
linebreak = (screen.width < 768) ? 'true' : linebreak;
}
var mathjaxscript = document.createElement('script');
mathjaxscript.id = 'mathjaxscript_pelican_#%@#$@#';
mathjaxscript.type = 'text/javascript';
mathjaxscript.src = 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.3/latest.js?config=TeX-AMS-MML_HTMLorMML';
var configscript = document.createElement('script');
configscript.type = 'text/x-mathjax-config';
configscript[(window.opera ? "innerHTML" : "text")] =
"MathJax.Hub.Config({" +
" config: ['MMLorHTML.js']," +
" TeX: { extensions: ['AMSmath.js','AMSsymbols.js','noErrors.js','noUndefined.js'], equationNumbers: { autoNumber: 'none' } }," +
" jax: ['input/TeX','input/MathML','output/HTML-CSS']," +
" extensions: ['tex2jax.js','mml2jax.js','MathMenu.js','MathZoom.js']," +
" displayAlign: '"+ align +"'," +
" displayIndent: '"+ indent +"'," +
" showMathMenu: true," +
" messageStyle: 'normal'," +
" tex2jax: { " +
" inlineMath: [ ['\\\\(','\\\\)'] ], " +
" displayMath: [ ['$$','$$'] ]," +
" processEscapes: true," +
" preview: 'TeX'," +
" }, " +
" 'HTML-CSS': { " +
" availableFonts: ['STIX', 'TeX']," +
" preferredFont: 'STIX'," +
" styles: { '.MathJax_Display, .MathJax .mo, .MathJax .mi, .MathJax .mn': {color: 'inherit ! important'} }," +
" linebreaks: { automatic: "+ linebreak +", width: '90% container' }," +
" }, " +
"}); " +
"if ('default' !== 'default') {" +
"MathJax.Hub.Register.StartupHook('HTML-CSS Jax Ready',function () {" +
"var VARIANT = MathJax.OutputJax['HTML-CSS'].FONTDATA.VARIANT;" +
"VARIANT['normal'].fonts.unshift('MathJax_default');" +
"VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
"VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
"VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
"});" +
"MathJax.Hub.Register.StartupHook('SVG Jax Ready',function () {" +
"var VARIANT = MathJax.OutputJax.SVG.FONTDATA.VARIANT;" +
"VARIANT['normal'].fonts.unshift('MathJax_default');" +
"VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
"VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
"VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
"});" +
"}";
(document.body || document.getElementsByTagName('head')[0]).appendChild(configscript);
(document.body || document.getElementsByTagName('head')[0]).appendChild(mathjaxscript);
}
</script>The Distributional Hypothesis: semantic models in theory and practice2018-03-03T05:07:00+01:002018-03-03T05:07:00+01:00Stefano Ottolenghitag:thecrowned.org,2018-03-03:/the-distributional-hypothesis-semantic-models-in-theory-and-practice<blockquote>
<p>This was the final project for the Data Semantics course at university - A report on distributional semantics and Latent Semantic Analysis.</p>
<p>Here is the <a href="https://www.thecrowned.org/wp-content/uploads/2018/03/Stefano-Ottolenghi-Distributional-Hypothesis-report.pdf">nicely-formatted pdf version</a> (with references).</p>
</blockquote>
<h1 id="what-is-the-distributional-hypothesis">What is the Distributional Hypothesis</h1>
<p>When …</p><blockquote>
<p>This was the final project for the Data Semantics course at university - A report on distributional semantics and Latent Semantic Analysis.</p>
<p>Here is the <a href="https://www.thecrowned.org/wp-content/uploads/2018/03/Stefano-Ottolenghi-Distributional-Hypothesis-report.pdf">nicely-formatted pdf version</a> (with references).</p>
</blockquote>
<h1 id="what-is-the-distributional-hypothesis">What is the Distributional Hypothesis</h1>
<p>When it comes to Distributional Semantics and the Distributional Hypothesis, the slogan is often "You shall know a word by the company it keeps" (J.R. Firth).</p>
<p>The idea of the Distributional Hypothesis is that the distribution of words in a text holds a relationship with their corresponding meanings. More specifically, the more semantically similar two words are, the more they will tend to show up in similar contexts and with similar distributions. Stating the idea the other way round may be helpful: given two morphemes with different semantical meaning, their distribution is likely to be different.</p>
<p>For example, <em>fire</em> and <em>dog</em> are two words unrelated in their meaning, and in fact they are not often used in the same sentence. On the other hand, the words <em>dog</em> and <em>cat</em> are sometimes seen together, so they may share some aspect of meaning.</p>
<p>Mimicking the way children learn, Distributional Semantics relies on huge text corpora, the parsing of which would allow to gather enough information about words distribution to make some inference. These corpora are treated with statistical analysis techniques and linear algebra methods to extract information. This is similar to the way humans learn to use words: by seeing how they are used (i.e. coming across several examples in which a specific word is used).</p>
<p>The fundamental difference between human learning and the learning a distributional semantic algorithm could achieve is mostly related to the fact that humans have a concrete, practical experience to rely on. This allows them not only to learn the <em>usage</em> of a word, but to eventually <em>understand</em> its meaning. However, the way word meaning is inferred is still an open research problem in psychology and cognitive science.</p>
<h2 id="the-weak-and-strong-hypothesis">The weak and strong hypothesis</h2>
<p>Before going any further, it is important to keep in mind that there are two versions of the distributional hypothesis, as pointed out by A. Lenci.</p>
<p>In the weak version of the hypothesis, the distribution of words is believed to be a way of inferring their semantic behaviour. Let us stress the term <em>behaviour</em> in the previous sentence. The weak hypothesis does not state at any rate that the distribution of words will, in any way, tell something about the <em>meaning</em> of words. In fact, we may only hope (and this would already be a success!) to unveil the ways a word is <em>used</em>.</p>
<p>The idea here is that the semantic meaning of words has a specific effect on their distribution. By observing the distribution, we may then hope to infer something about the meaning. The distribution is thus seen as a <em>latent variable</em> of the semantic meaning: observing the former may give hints about the latter.</p>
<p>The strong form of the hypothesis, on the other hand, states that words distribution have a <em>causal relationship</em> with the way meaning is derived from the text. In fact, this theory entails that the statistical distribution of a word causes the semantic representation humans have of the corresponding idea.</p>
<p>In other words, while the weak version of the distributional hypothesis states that there is a relationship such that</p>
<div class="math">$$\text{meaning } \rightarrow \text{ distribution}$$</div>
<p>but does not say anything about the inverse, the strong version believes that to hold as well.</p>
<hr>
<p>Perhaps not surprisingly, the weak version is accepted by far more researchers of the (computational) linguistics fields than the strong one. It is not difficult to believe that meaning has a strong influence on the distribution of words, but it is not so easy to believe that distributions shape meaning by themselves.</p>
<p>For example, consider the following sentence:</p>
<blockquote>
<p>"I hiked 2000 metres uphill in the mountains to have a swim in the lake".</p>
</blockquote>
<p>Knowing the meaning of the word <em>sea</em>, humans (hopefully) know that the word <em>lake</em> cannot just be replaced with the word <em>sea</em> in the sentence above, even if there is the word <em>swim</em>, which is often seen together with <em>sea</em>. However, if we believed that distribution shapes meaning, then words with supposedly very similar distributions such as <em>lake</em> and <em>sea</em> could be swapped one with the other. This is not the case, although this is only clear if the meaning of the context is understood.</p>
<p>As we will see, the relationship between distribution and meaning with respect to the distributional hypothesis is a difficult one, and we will discuss it in more depth later on.</p>
<h1 id="how-to-deploy-the-idea">How to deploy the idea</h1>
<p>In order to investigate lexical meaning, we first need to set up a way to measure semantic similarity between words. When are two words similar? What does it even mean for two words to be <em>similar</em>? These are important questions, as word similarity is believed to affect the way terms are processed in mental lexicon. We may define synonymy as being related to word interchangeability, but this is not a computationally implementable definition yet.</p>
<p>Thus, a way to make explicit the functional dependance between word distribution and semantic is needed.</p>
<p>Distributional semantics models rely on statistical analysis and linear algebra tools to implement its theory. We will now discuss in more depth Latent Semantic Analysis and hint at further developments.</p>
<h2 id="latent-semantic-analysis-lsa">Latent Semantic Analysis (LSA)</h2>
<p><img alt="distributional hypothesis" src="http://www.thecrowned.org/wp-content/uploads/2018/03/lsa-angles.png" title="distributional hypothesis"></p>
<p>Figure 1 - Dot products between words (Evert)</p>
<p>Latent Semantic Analysis (LSA) is arguably <strong>the</strong> mathematical tool of distributional semantics. In its basic form, it allows to parse several texts and analyze similarities between them. It is an unsupervised learning method (i.e. it requires no human-preprocessed data). Although it is not the only possible method, others are roughly based on the same mathematics that LSA employs, so exploring the latter is enough to understand other approaches.</p>
<p>The idea of LSA is to regard words as points in a so-called <em>distributional space</em>. The distributional space is simply a vector space (most often <span class="math">\(\mathbb{R}^n\)</span>), where vectors represent words. The vector contains an average of all possible different words usages deduced from the input text corpora.</p>
<p>However simple this construction may seem, it has a very nice property: that distances between points correspond to semantic distances between words. In particular, measuring angles between vectors (which is as simple as calculating dot products) is a proxy for measuring the similarity of the corresponding words (see Figure 1).</p>
<p>An example of the result of text corpora processing through Latent Semantic Analysis is shown in Figure 2.</p>
<p>As an aside, notice that the name of this technique comes from the idea that semantic may be thought of as a latent variable responsible for the observed linguistic distributions.</p>
<p><img alt="distributional hypothesis" src="http://www.thecrowned.org/wp-content/uploads/2018/03/lsa.jpg" title="distributional hypothesis"></p>
<p>Figure 2 - Latent Semantic Analysis-based grouping of documents basing on their keywords (Wikipedia)</p>
<h2 id="how-it-is-done">How it is done</h2>
<p>We briefly go through how Latent Semantic Analysis works from a technical point of view.</p>
<p><img src="http://www.thecrowned.org/wp-content/uploads/2018/03/matrix1-300x242.jpg" alt="distributional hypothesis"></p>
<p>Figure 3 - Matrix example of LSA.</p>
<p>Consider <span class="math">\(n\)</span> documents, which contain <span class="math">\(m\)</span> different words all together. LSA starts by building a matrix of size <span class="math">\(m \times n\)</span>:</p>
<div class="math">$$\begin{matrix}
& \textbf{d}_j \\
& \downarrow \\
\textbf{t}_i^T \rightarrow &
\begin{bmatrix}
x_{1,1} & \dots & x_{1,j} & \dots & x_{1,n} \\
\vdots & \ddots & \vdots & \ddots & \vdots \\
x_{i,1} & \dots & x_{i,j} & \dots & x_{i,n} \\
\vdots & \ddots & \vdots & \ddots & \vdots \\
x_{m,1} & \dots & x_{m,j} & \dots & x_{m,n} \\
\end{bmatrix}
\end{matrix}$$</div>
<p>Each row <span class="math">\(t_i^T\)</span> represents a word, while each column <span class="math">\(d_j\)</span> represents a document. The matrix entry <span class="math">\(x_{i,j}\)</span> holds the occurrence count (or frequency) of the word <span class="math">\(i\)</span> in the document <span class="math">\(j\)</span>.</p>
<p>It is then possible to calculate similarity both between terms and between documents. In fact, the dot product <span class="math">\(t_i^T t_p\)</span> computes the similarity between the word vectors <span class="math">\(i\)</span> and <span class="math">\(p\)</span>, whereas <span class="math">\(d_j d_k^T\)</span> measures similarity between documents <span class="math">\(j\)</span> and <span class="math">\(k\)</span>.</p>
<p>It is easy to generate matrices of co-occurrence both for words and for documents, through the computation respectively of <span class="math">\(X^TX\)</span> and of <span class="math">\(XX^T\)</span>. In particular, <span class="math">\(X^TX\)</span> will contain information about the similarity of words, basing on the provided corpora; <span class="math">\(XX^T\)</span> will contain similar information about the documents.</p>
<p>However, the matrix generated by LSA is likely to be huge in size, and thus computationally difficult to handle. Furthermore, a good part of it may well consist of useless data: noise, synonyms and other information that may be stripped away without any concrete loss.</p>
<p>For these reasons, Singular Value Decomposition is applied to the matrix to extract the most meaningful pieces of information from it. In this way, similarity measures will still be computable, though with less computation effort.</p>
<p>However, the drawback is that the new dimensions are very unlikely to relate to any comprehensible concept: they will just be linear combinations of rows and columns.</p>
<p>To make this concern clearer, consider the following example.</p>
<p><code>{(car), (bottle), (flower)}</code> <span class="math">\(\rightarrow\)</span> <code>{(1.3452 \* car + 0.2828 \* bottle), (flower)}</code></p>
<p>Here, two words are merged in one new joint dimension, while the third is retained as it is. However, although the interpretation of car and bottle is clear, it is not clear at all how the joint dimension should be thought of.</p>
<h2 id="critiques-and-more-sophisticated-approaches">Critiques and more sophisticated approaches</h2>
<p>As we have seen, LSA is able to generate co-occurrence patterns from the input it is provided. However, the idea that meaning may consist of abstractions from purely linguistic co-occurrence patterns has raised many critiques. As already pointed out, the biggest one is that co-occurrence information may only allow to infer the usage of a word, but not its meaning.</p>
<p>Moreover, one concern is understanding whether the current limits and weak points of the distributional semantics approaches are due to the implementations and current models. On the other hand, it may be possible that they are inherent to the distributional hypothesis itself.</p>
<p>When using Latent Semantic Analysis, further rules may be enforced. In fact, although text corpora may be used as they are in their raw form (<em>naive</em> approaches), syntactically-savy models are possible as well. In these latter models, the distribution is analysed by keeping in mind the syntactic configurations words should obey to. In this way it should be possible to get closer to the ways terms are used in reality.</p>
<p>It is worth pointing out that LSA-based methods are often the starting point of distributional semantic investigations, but more complex methods have been devised on top of it.</p>
<p>One example of further technique consists on focusing on the notion of property of a concept, to try to capture the essence of its meaning. In this model, <em>animal</em> would be identified as property for <em>dog</em>, and <em>wheels</em> as property for <em>car</em>.</p>
<p>Another possible approach is that of semantic association. In this fashion, <em>swim</em> would find a relationship with <em>water</em>, as the former calls to mind the latter.</p>
<h1 id="critiques-to-the-distributional-approach">Critiques to the distributional approach</h1>
<p>As we have seen, the distributional hypothesis in its weak form is usually accepted by field experts, but there is strong disagreement over the strong formulation.</p>
<p>One fundamental critique to the distributional hypothesis is that, as far as it could go, distributional semantic models may not go beyond learning word usage. In fact, learning words meaning is totally out of their scope. This is well conveyed by the <em>Chinese Room</em> thought experiment, first proposed by Searle in 1980:</p>
<blockquote>
<p>Imagine to be locked in a room and to receive batches of Chinese characters. Crucially, you do not know Chinese and for you these characters are just meaningless symbols. You also receive "rules" (in English) on how to combine these characters, and how to respond to Chinese characters with other Chinese characters. Suppose that, after a certain amount of training, you have been able to learn to combine Chinese characters and to reply to Chinese messages in such a way that your answers are de facto indistinguishable from those given by Chinese native speakers. The key point is that even in this case it would still be true that you do not understand Chinese, even though you may be said to "speak it".</p>
</blockquote>
<p>Another point of difficulty is that of composivity. By learning the meaning of the parts (i.e. the single words that make up a sentence), is it possible to learn the meaning of the whole sentence?</p>
<p>As we have seen, technical implementations of the theory (Latent Semantic Analysis) usually rely on basic linear algebra and vector spaces. In these models, each word is conceived as a point. However, the sum of the points representing the words of a sentence is an absolutely inadequate definition for the meaning of the sentence. This is because vector summation is commutative, whereas word summation should not.</p>
<p>In fact, consider the following examples:</p>
<blockquote>
<p>1. The dog ate a cake.</p>
<p>2. The cake ate a dog.</p>
<p>3. The a cake dog ate.</p>
</blockquote>
<p>If word-vector summation would be enough to define sentence meaning, then the three sentences would be believed to all have the same meaning, although this is clearly not the case. Remarkably, the third sentence does not even have any real meaning at all, although it is believed to be equal to the other two. The point here is that while swapping vectors in a sum is fine, swapping words in a sentence obviously is not.</p>
<p>More sophisticated models that account for non-commutativity in words usage have been developed, but this still looks like an open problem.</p>
<p>One further question, although weakly related to that of non-commutativity, is that of asymmetric relationships. For example, sentences like:</p>
<blockquote>
<p>A bus has wheels.</p>
</blockquote>
<p>allow to learn that there is a strict relationship between <em>buses</em> and <em>wheels</em>, but it is not clear how to learn the fact that it is an asymmetric link. Indeed, it is difficult to find a bus without wheels, but it is not difficult at all to find wheels without a bus.</p>
<p>Moreover, experts argue that meaning is most often influenced by factors that cannot be learnt just by the textual representation of content. In fact, the speaker's intentions, the speaker's own way of expression and the context in which the content fits into all shape the meaning of the words it contains. The statistical methods employed by distributional semantics are not able to capture these factors, as they lay beyond its scope.</p>
<p>Also, keep in mind that up to now we have only considered well-formed language instantions, i.e. sentences in which the meaning exactly corresponds to the words used to express it. However, consider the following sentence:</p>
<blockquote>
<p>I borrow books and sometimes they do not come back to me.</p>
</blockquote>
<p>A human skilled enough with the English language easily understands that the speaker mistaked the word <em>borrow</em> with the word <em>lend</em>, but would a computer be able to do this?</p>
<p>Ultimately, making inferences given a group of facts is the final goal of learning the meaning of words. However, it is unclear how this could be achieved without a real understanding of semantic meaning. In fact, one of the main critiques to distributional semantics is precisely this: that it cannot provide meaning for words.</p>
<p>Finally, it should be noted that the distributional semantics debate is just an instance of a broader debate. The two contrasting theories in this regard are the Abstract Cognition Hypothesis (ACH) and the Embodied Cognition Hypothesis (ECH).</p>
<p>Standing to Abstract Cognition Hypothesis, concepts and meanings are represented in human cognition by formal symbols. This stance supports the Distributional Hypothesis. On the other hand, the Embodied Cognition Hypothesis believes that meanings are stored in the same perceptual system from which its instance is experienced. This means that knowing the meaning of the word <em>spider</em> implies being able to run some sort of internal simulator that re-enacts out concrete experience with spiders. This theory, of course, is in sharp contrast with the distributional hypothesis and its tools, since requires a deeper understanding of words, that goes beyond their usage.</p>
<h1 id="final-remarks">Final remarks</h1>
<p>As we have discussed at length, even if distributional-based semantic models could tell us something interesting about the language and words usage, it would reveal absolutely nothing about meanings. In fact, one could build graphs, buckets and at any rate classify words in fashionable and complex ways, but it seems pretty unlikely that the meaning of a word could be extracted (just) from those kind of analysis. This is <strong>the</strong> vital problem of distributional semantics.</p>
<p>However, even though philosophical debates are far from settled, distributional semantics models have allowed good results in real-world applications, and so are used notwithstanding the critiques. Indeed, using vocabulary tests to compare the performance of Latent Semantic Analysis based models with the learning achieved through reading by school children, it was found that they scored similarly.</p>
<p>After all, it is important to realize what our goals are. If we would like a machine to understand ideas, concepts and words as a human would do, then there is probably no technique that would allow this in the current state of things. However, if we only require a machine to process and respond to linguistic queries, then distributional models can be used with a good success rate.</p>
<p>To make an analogy with another field, let us notice that we do not know how (artificial) neural networks work, although we use them and rely on them in more and more contexts. Crucially, we can not state that they are able to <em>think</em> or <em>understand</em> the work they do. For example, when searching for <em>tieleman orchestra</em>, Google suggests <em>thielemann orchestra</em> instead. Do Google's algorithms know who Thielemann is, or that he is a director? Do they know what a director even is?</p>
<p>All of this is to say: then, why should distributional models not be used, if they have proved to be good enough to infer words usage, which is what matters in practice? Our lives lay on top of automated systems that work, although they do not understand their work. Distributional semantic models may well become one of these tools.</p>
<p>The difficulty of devising computational models that would extract meanings from terms and contexts is mostly related to the fact that we ourselves do not yet understand how we do it. It is unlikely, of course, that we will ever be able to teach a machine to do it without understanding it ourselves first.</p>
<p>At any rate, is seems unlikely (at least as of now) to ever be able to teach stupid machinery to learn meanings and concepts.</p>
<p>The fact that we are able to <em>understand</em> the word circle<em>, for</em> example, has undoubtedly to do with our past experience and interaction with the real word: we have seen round things, balls and other different geometric shapes, and have an <em>intuitive idea</em> of what a circle is. This allows us not only to <em>use</em> the word, but to have a real understanding of, which stands at a higher level. In fact, one may argue that the understanding that an 8-year-old could have of <em>circle</em> would be at a superior level than any distributional model!</p>
<p>In other words, it is just difficult to believe that a simple algorithm/technique may be able to compete with the human conceptualization of the world. After all, just observing the statistical occurrence of the word <em>idea</em> may make it look like it has a lot to do with <em>lamps</em> and <em>lights</em>.</p>
<p>As Wittgenstein said, "the meaning of a word lies in its use", and looking at usage may be enough to replicate usage patterns. However, grasping and understanding that meaning looks like a totally different matter!</p>
<h2 id="references">[References]</h2>
<p>[1] Lenci Alessandro 2008. Distributional semantics in linguistic and cognitive research. Rivista di Linguistica 20.1, pp. 1-31</p>
<p>[2] Evert Stefan – Distributional Semantic Models (last accessed: 2018/02/26)
http://esslli2016.unibz.it/wp-content/uploads/2015/10/dsm_tutorial_part1.slides.pdf</p>
<p>[3] Wikipedia – Latent Semantic Analysis (last accessed: 2018/02/26)
https://en.wikipedia.org/wiki/Latent_semantic_analysis</p>
<p>[4] Landauer Thomas K., Susan T. Dumais 1997. A solution to Plato’s problem: the latent semantic analysis theory of acquisition, induction and
representation of knowledge. Psychological Review CIV/2. 211-240</p>
<p>[5] Searle John 1980. Minds, brains and programs. Behavioural and Brain Sciences III. 417-424</p>
<p>[6] Wittgenstein Ludwig 1953. Philosophical Investigations. Oxford: Blackwell</p>
<p>[7] Firth John R. 1957. Papers in Linguistics. London, Oxford University
Press</p>
<script type="text/javascript">if (!document.getElementById('mathjaxscript_pelican_#%@#$@#')) {
var align = "center",
indent = "0em",
linebreak = "false";
if (false) {
align = (screen.width < 768) ? "left" : align;
indent = (screen.width < 768) ? "0em" : indent;
linebreak = (screen.width < 768) ? 'true' : linebreak;
}
var mathjaxscript = document.createElement('script');
mathjaxscript.id = 'mathjaxscript_pelican_#%@#$@#';
mathjaxscript.type = 'text/javascript';
mathjaxscript.src = 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.3/latest.js?config=TeX-AMS-MML_HTMLorMML';
var configscript = document.createElement('script');
configscript.type = 'text/x-mathjax-config';
configscript[(window.opera ? "innerHTML" : "text")] =
"MathJax.Hub.Config({" +
" config: ['MMLorHTML.js']," +
" TeX: { extensions: ['AMSmath.js','AMSsymbols.js','noErrors.js','noUndefined.js'], equationNumbers: { autoNumber: 'none' } }," +
" jax: ['input/TeX','input/MathML','output/HTML-CSS']," +
" extensions: ['tex2jax.js','mml2jax.js','MathMenu.js','MathZoom.js']," +
" displayAlign: '"+ align +"'," +
" displayIndent: '"+ indent +"'," +
" showMathMenu: true," +
" messageStyle: 'normal'," +
" tex2jax: { " +
" inlineMath: [ ['\\\\(','\\\\)'] ], " +
" displayMath: [ ['$$','$$'] ]," +
" processEscapes: true," +
" preview: 'TeX'," +
" }, " +
" 'HTML-CSS': { " +
" availableFonts: ['STIX', 'TeX']," +
" preferredFont: 'STIX'," +
" styles: { '.MathJax_Display, .MathJax .mo, .MathJax .mi, .MathJax .mn': {color: 'inherit ! important'} }," +
" linebreaks: { automatic: "+ linebreak +", width: '90% container' }," +
" }, " +
"}); " +
"if ('default' !== 'default') {" +
"MathJax.Hub.Register.StartupHook('HTML-CSS Jax Ready',function () {" +
"var VARIANT = MathJax.OutputJax['HTML-CSS'].FONTDATA.VARIANT;" +
"VARIANT['normal'].fonts.unshift('MathJax_default');" +
"VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
"VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
"VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
"});" +
"MathJax.Hub.Register.StartupHook('SVG Jax Ready',function () {" +
"var VARIANT = MathJax.OutputJax.SVG.FONTDATA.VARIANT;" +
"VARIANT['normal'].fonts.unshift('MathJax_default');" +
"VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
"VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
"VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
"});" +
"}";
(document.body || document.getElementsByTagName('head')[0]).appendChild(configscript);
(document.body || document.getElementsByTagName('head')[0]).appendChild(mathjaxscript);
}
</script>Brute force a crackme file password with Python2018-02-13T10:06:00+01:002018-02-13T10:06:00+01:00Stefano Ottolenghitag:thecrowned.org,2018-02-13:/brute-force-crackme-file-password-python<p>I was to <strong>reverse a file</strong> for a challenge, MD5 hash 85c9feed0cb0f240a62b1e50d1ab0419.</p>
<p>The challenge was called <em>mio cuggino</em>, purposefully <strong>misspelled</strong> with two g letters. It asks for three numbers. The challenge <strong>led me to a …</strong></p><p>I was to <strong>reverse a file</strong> for a challenge, MD5 hash 85c9feed0cb0f240a62b1e50d1ab0419.</p>
<p>The challenge was called <em>mio cuggino</em>, purposefully <strong>misspelled</strong> with two g letters. It asks for three numbers. The challenge <strong>led me to a brute force</strong> of the password with a Python script, learning how to interact with a subprocess stdin and stdout (<em>SKIP to next section if you don't care about context but only want the code</em>).</p>
<p><strong>Looking at the assembly</strong> with Radare, the first thing it does is to check that the numbers are non-negative and in increasing order. In details, it checks that:</p>
<ol>
<li>exactly three inputs have been provided;</li>
<li>the first two are non-negative;</li>
<li>the third is bigger than the second;</li>
<li>the second is bigger than the first;</li>
<li>the third is non-negative.</li>
</ol>
<p><img alt="radare" src="http://www.thecrowned.org/wp-content/uploads/2018/02/mio-cuggino1.png"></p>
<p>Very good, <strong>so the input pattern is three non-negative integers in increasing order</strong>. Fine. No clue about what those numbers should be though, yet.</p>
<p>Scroll the assembly just enough to unravel the magic.</p>
<p><img alt="radare" src="http://www.thecrowned.org/wp-content/uploads/2018/02/mio-cuggino2.png"></p>
<p>A (pointer to) string is loaded into ebx, which contains the following Italian sentence:</p>
<blockquote>
<p>Mi ha detto mio cuggino che una volta e' stato co' una che poi gli ha scritto sullo specchio benvenuto nell'AIDS, mio cuggino mio cuggino</p>
</blockquote>
<p>The assembly basically takes the characters in the string that correspond to the first and second input (for ex, 0 as first input would map to the first char, <em>M</em>) and checks whether they are equal. If this is not satisfied, a <em>Nope</em> message is shown and the binary returns.</p>
<p>If this is satisfied, the same check is repeated with the third input (with the first one, although this doesn't matter). If this is satisfied as well, a tricky <em>sub.puts_640</em> function is called (with 5 inputs), and a <em>Uhm</em> message is shown.</p>
<p>Going to looking into that routine is absolutely useless as it's completely unreadable, and even makes a bunch of additional calls that are further jumbled.</p>
<p><img alt="radare" src="http://www.thecrowned.org/wp-content/uploads/2018/02/mio-cuggino3.png"></p>
<p>So let us summarize what we know up to here: <strong>three non-negative integers, in increasing order, with the corresponding chars in the italian string being equal</strong>. Seems easy: just try with the space character! Pick integers whose position in the string are spaces, such as <em>2 5 11</em>. <strong>No luck</strong>, we just get a <em>Uhm</em>.</p>
<p>At this point I must say I was stupid enough not to catch the hint, because I really was close to the solution! <em>Cuggino</em> is purposefully misspelled in the italian sentence: it has two <em>g </em>s! So the g was probably the letter to go... But I had been banging my head on this binary for a couple of hours after dealing with other challenges and the competition was going to end soon, so I really was tired.</p>
<h2 id="brute-forcing-a-binary-file-input-with-python">Brute forcing a binary file input with Python</h2>
<p>Later on, I was told that a brute-force approach could have worked as well, since the string length was not prohibitive. Indeed, in the worst scenario and with the most naive technique, I would have had to try out <span class="math">\(136^3 = 2515456\)</span> inputs (with 136 being the string length). Not small, but not prohibitive.</p>
<p>So back home, I tried this approach. I wrote a <strong>small Python script</strong> that would generate all possible triplets of numbers (picking them at random) and feeding them as inputs one by one, looking at the output of the file to see when the flag was output. Sure enough, in <strong>roughly 20 minutes</strong> the flag was found! <strong>The correct input was indeed <em>18 19 63</em></strong>, corresponding to three g letters in the string.</p>
<p><img alt="radare" src="http://www.thecrowned.org/wp-content/uploads/2018/02/mio-cuggino4.png"></p>
<p>In 1247 seconds, (more than) 1316248 attempts were made, scoring a rate of <strong>1055 attempts/second</strong>. I was running this on an Intel i3 so this was not particularly powerful.</p>
<p>There was not a ready made script online to do this, so I though I would share it. In particular, <strong>running a file, feeding it an input NOT through arguments</strong> (i.e. writing to its stdin) and reading its output did not seem easy.</p>
<p>The core of the script is the following three lines</p>
<div class="highlight"><pre><span></span><code><span class="n">test</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span> <span class="n">randint</span><span class="p">(</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">136</span> <span class="p">)</span> <span class="p">)</span> <span class="o">+</span> <span class="s1">' '</span> <span class="o">+</span> <span class="nb">str</span><span class="p">(</span> <span class="n">randint</span><span class="p">(</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">136</span> <span class="p">)</span> <span class="p">)</span> <span class="o">+</span> <span class="s1">' '</span> <span class="o">+</span> <span class="nb">str</span><span class="p">(</span> <span class="n">randint</span><span class="p">(</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">136</span> <span class="p">)</span> <span class="p">)</span>
<span class="n">p</span> <span class="o">=</span> <span class="n">subprocess</span><span class="o">.</span><span class="n">Popen</span><span class="p">([</span><span class="s2">"/home/stefano/Binary Reverse/mio_cuggino_85c9feed0cb0f240a62b1e50d1ab0419"</span><span class="p">],</span> <span class="n">stdin</span><span class="o">=</span><span class="n">subprocess</span><span class="o">.</span><span class="n">PIPE</span><span class="p">,</span> <span class="n">stdout</span><span class="o">=</span><span class="n">subprocess</span><span class="o">.</span><span class="n">PIPE</span><span class="p">)</span>
<span class="n">stdout</span> <span class="o">=</span> <span class="n">p</span><span class="o">.</span><span class="n">communicate</span><span class="p">(</span><span class="nb">input</span> <span class="o">=</span> <span class="n">test</span><span class="o">.</span><span class="n">encode</span><span class="p">())[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">decode</span><span class="p">()</span>
</code></pre></div>
<p>First, input to be tested is built, picking three integers at random. Second, a process is spawned calling the file to crack. The two subsequent arguments to that call are needed to be able to interact with the process input and output spaces. The object of this call is stored into the <em>p</em> var. Finally, we communicate with that project passing as input the bytearray version of the test string (<em>test.encode()</em>), reading the output as the first entry of the result.</p>
<p>So stdout contains the output string of the program, and we can look at what it contains to see whether the attempt was successful or not.</p>
<p><strong>More sophisticated approaches</strong> could be tried, such as keeping a log of the already tried inputs and not replaying them. This, however, was 10 times <strong>slower</strong> than the naive approach (took 1+ hour). Because of the random nature of the script, launching several instances of it could yield to success quicker than with just one.</p>
<p>One successful attempt was to restrict the random numbers range in <span class="math">\([0, 90]\)</span>, hoping that a valid triplet was found in that space - and it worked! Just keep in mind that we are definitely over-optimizing something that should be "just good enough" to work, and we already have a decently working script.</p>
<p>Here is the full Python script code for brute forcing a password:</p>
<div class="highlight"><pre><span></span><code><span class="kn">from</span> <span class="nn">random</span> <span class="kn">import</span> <span class="n">randint</span>
<span class="kn">import</span> <span class="nn">subprocess</span>
<span class="kn">import</span> <span class="nn">time</span>
<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">"__main__"</span><span class="p">:</span>
<span class="n">found</span> <span class="o">=</span> <span class="mi">0</span>
<span class="n">n</span> <span class="o">=</span> <span class="mi">0</span>
<span class="n">start_time</span> <span class="o">=</span> <span class="n">time</span><span class="o">.</span><span class="n">time</span><span class="p">()</span>
<span class="k">while</span> <span class="n">found</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
<span class="n">n</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="n">test</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span> <span class="n">randint</span><span class="p">(</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">136</span> <span class="p">)</span> <span class="p">)</span> <span class="o">+</span> <span class="s1">' '</span> <span class="o">+</span> <span class="nb">str</span><span class="p">(</span> <span class="n">randint</span><span class="p">(</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">136</span> <span class="p">)</span> <span class="p">)</span> <span class="o">+</span> <span class="s1">' '</span> <span class="o">+</span> <span class="nb">str</span><span class="p">(</span> <span class="n">randint</span><span class="p">(</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">136</span> <span class="p">)</span> <span class="p">)</span>
<span class="n">p</span> <span class="o">=</span> <span class="n">subprocess</span><span class="o">.</span><span class="n">Popen</span><span class="p">([</span><span class="s2">"/home/stefano/Binary Reverse/mio_cuggino_85c9feed0cb0f240a62b1e50d1ab0419"</span><span class="p">],</span> <span class="n">stdin</span><span class="o">=</span><span class="n">subprocess</span><span class="o">.</span><span class="n">PIPE</span><span class="p">,</span> <span class="n">stdout</span><span class="o">=</span><span class="n">subprocess</span><span class="o">.</span><span class="n">PIPE</span><span class="p">)</span>
<span class="n">stdout</span> <span class="o">=</span> <span class="n">p</span><span class="o">.</span><span class="n">communicate</span><span class="p">(</span><span class="nb">input</span> <span class="o">=</span> <span class="n">test</span><span class="o">.</span><span class="n">encode</span><span class="p">())[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">decode</span><span class="p">()</span>
<span class="k">if</span> <span class="s1">'Uhm'</span> <span class="ow">in</span> <span class="n">stdout</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s1">'ATTEMPT : '</span> <span class="o">+</span> <span class="nb">str</span><span class="p">(</span> <span class="n">n</span> <span class="p">))</span>
<span class="nb">print</span><span class="p">(</span><span class="s1">'TESTING : '</span> <span class="o">+</span> <span class="n">test</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="s1">'OUTPUT : '</span> <span class="o">+</span> <span class="n">stdout</span><span class="p">)</span>
<span class="k">if</span> <span class="s1">'flag'</span> <span class="ow">in</span> <span class="n">stdout</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span> <span class="s1">' ~~~~~~ FOUND!!!! ~~~~~'</span> <span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="n">test</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="n">stdout</span><span class="p">)</span>
<span class="n">found</span> <span class="o">=</span> <span class="mi">1</span>
<span class="k">break</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"--- </span><span class="si">%s</span><span class="s2"> seconds ---"</span> <span class="o">%</span> <span class="p">(</span><span class="n">time</span><span class="o">.</span><span class="n">time</span><span class="p">()</span> <span class="o">-</span> <span class="n">start_time</span><span class="p">))</span>
</code></pre></div>
<script type="text/javascript">if (!document.getElementById('mathjaxscript_pelican_#%@#$@#')) {
var align = "center",
indent = "0em",
linebreak = "false";
if (false) {
align = (screen.width < 768) ? "left" : align;
indent = (screen.width < 768) ? "0em" : indent;
linebreak = (screen.width < 768) ? 'true' : linebreak;
}
var mathjaxscript = document.createElement('script');
mathjaxscript.id = 'mathjaxscript_pelican_#%@#$@#';
mathjaxscript.type = 'text/javascript';
mathjaxscript.src = 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.3/latest.js?config=TeX-AMS-MML_HTMLorMML';
var configscript = document.createElement('script');
configscript.type = 'text/x-mathjax-config';
configscript[(window.opera ? "innerHTML" : "text")] =
"MathJax.Hub.Config({" +
" config: ['MMLorHTML.js']," +
" TeX: { extensions: ['AMSmath.js','AMSsymbols.js','noErrors.js','noUndefined.js'], equationNumbers: { autoNumber: 'none' } }," +
" jax: ['input/TeX','input/MathML','output/HTML-CSS']," +
" extensions: ['tex2jax.js','mml2jax.js','MathMenu.js','MathZoom.js']," +
" displayAlign: '"+ align +"'," +
" displayIndent: '"+ indent +"'," +
" showMathMenu: true," +
" messageStyle: 'normal'," +
" tex2jax: { " +
" inlineMath: [ ['\\\\(','\\\\)'] ], " +
" displayMath: [ ['$$','$$'] ]," +
" processEscapes: true," +
" preview: 'TeX'," +
" }, " +
" 'HTML-CSS': { " +
" availableFonts: ['STIX', 'TeX']," +
" preferredFont: 'STIX'," +
" styles: { '.MathJax_Display, .MathJax .mo, .MathJax .mi, .MathJax .mn': {color: 'inherit ! important'} }," +
" linebreaks: { automatic: "+ linebreak +", width: '90% container' }," +
" }, " +
"}); " +
"if ('default' !== 'default') {" +
"MathJax.Hub.Register.StartupHook('HTML-CSS Jax Ready',function () {" +
"var VARIANT = MathJax.OutputJax['HTML-CSS'].FONTDATA.VARIANT;" +
"VARIANT['normal'].fonts.unshift('MathJax_default');" +
"VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
"VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
"VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
"});" +
"MathJax.Hub.Register.StartupHook('SVG Jax Ready',function () {" +
"var VARIANT = MathJax.OutputJax.SVG.FONTDATA.VARIANT;" +
"VARIANT['normal'].fonts.unshift('MathJax_default');" +
"VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
"VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
"VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
"});" +
"}";
(document.body || document.getElementsByTagName('head')[0]).appendChild(configscript);
(document.body || document.getElementsByTagName('head')[0]).appendChild(mathjaxscript);
}
</script>The one time pad and the many time pad vulnerability2018-01-18T09:25:00+01:002018-01-18T09:25:00+01:00Stefano Ottolenghitag:thecrowned.org,2018-01-18:/the-one-time-pad-and-the-many-time-pad-vulnerability<p>The scope of this article is to present the one time pad cipher method and its biggest vulnerability: the many time pad attack.</p>
<h2 id="the-one-time-pad-what-it-is-and-how-it-works">The one time pad: what it is and how it works</h2>
<p>The …</p><p>The scope of this article is to present the one time pad cipher method and its biggest vulnerability: the many time pad attack.</p>
<h2 id="the-one-time-pad-what-it-is-and-how-it-works">The one time pad: what it is and how it works</h2>
<p>The one time pad is the archetype of the idea of stream cipher. It's very simple: if you want to make a message unintelligible to an eavesdropper, just change each character of the original message in a way that you can revert, but that looks random to another person.</p>
<p><strong>The way the one time pad works is the following</strong>. Suppose <span class="math">\(\mathcal{M}\)</span> is the clear-text message you would like to send securely, of length <span class="math">\(|\mathcal{M}| = s\)</span>. First, you need to generate a string <span class="math">\(\mathcal{K}\)</span> of equal length <span class="math">\(|\mathcal{K}| = s\)</span>. Then, you can obtain a cipher-text version of your message by computing the bitwise XOR of the two strings:</p>
<div class="math">$$\mathcal{C} = \mathcal{M} \oplus \mathcal{K}$$</div>
<p>The best thing is that decoding is just the same as encoding, as the XOR operator has the property that <span class="math">\(\mathcal{X} \oplus \mathcal{X} = 0 \ \forall X\)</span> (and that <span class="math">\(\mathcal{X} \oplus 0 = \mathcal{X} \ \forall \mathcal{X}\)</span>). The only difference is that the cipher-text is involved in the XOR, rather than the clear-text:</p>
<div class="math">$$\mathcal{C} \oplus \mathcal{K} = \mathcal{M} \oplus \mathcal{K} \oplus \mathcal{K} = \mathcal{M} \oplus 0 = \mathcal{M}$$</div>
<p>Below is an example of the one time pad encoding achieved with Python, with a made-up pad string.</p>
<p><img alt="terminal" src="http://www.thecrowned.org/wp-content/uploads/2018/01/many-time-pad-1.png"></p>
<p>In the first section, <em>result</em> holds the XOR result. In the second part, the <em>result</em> and <em>one_time_pad</em> variables are XORed together to obtain the original plain-text message again.</p>
<p>Since XOR is a fundamental logical operation, and the only other element we used is the string <span class="math">\(\mathcal{K}\)</span> (which is the <code>pad</code> the technique takes its name from), it's clear that the algorithm's strength must lie in <span class="math">\(\mathcal{K}\)</span>. If an attacker gains access to <span class="math">\(\mathcal{K}\)</span>, then it's oviously game over, as we've seen that decryption is straightforward then.</p>
<p>We said one requirement of <span class="math">\(\mathcal{K}\)</span> is that it should be of the same size of the message, otherwise there are not enough bytes to XOR with, and part of the message is revealed. <strong>But the other, more subtle but still fundamental, requirement is that each <span class="math">\(\mathcal{K}\)</span> must be used once</strong> and once only (that's where the <code>one-time</code> comes from). Understanding why this second requisite is important is the scope of this article.</p>
<h2 id="the-many-time-pad-vulnerability">The many time pad vulnerability</h2>
<p>If the same key is used more than once, bad things will happen and you will go to hell. As soon as you have encoded a message with a given pad, throw it away immediately and for good! That's what makes the One-Time Pad so tricky: when keys still laid on stacks of paper in different continents, people had to be scrupulous in burning away the key sheet they had just used, to ensure their stack was always exactly in the same state as their peer's. The one time pad is simple, and yet one little bit of carelessness can break it!</p>
<p>Let's look at what happens if the same key <span class="math">\(\mathcal{K}\)</span> is used more than once. Suppose an attacker intercepted some of the cipher-text messages, but did not have the key used to encode them. Could the attacker get something out of them, if they had been encrypted with the same key? Suppose we have two ciphertexts at our disposal:</p>
<div class="math">$$\mathcal{C}\_1 = \mathcal{M}\_1 \oplus \mathcal{K}$$</div>
<div class="math">$$\mathcal{C}\_2 = \mathcal{M}\_2 \oplus \mathcal{K}$$</div>
<div class="math">$$\mathcal{C}\_1 \oplus \mathcal{C}\_2 =\mathcal{M}\_1 \oplus\mathcal{K} \oplus \mathcal{M}\_2 \oplus \mathcal{K} = \mathcal{M}\_1 \oplus \mathcal{M}\_2$$</div>
<p>where in the last line we have used the fact that the XOR is a commutative operator, and that <span class="math">\(\mathcal{K} \oplus \mathcal{K} = 0\)</span>.</p>
<p>But behold! <strong>Given only two cipher-text, we retrieved a combination of the original plain-text messages!</strong> We may have not recovered the original messages yet, but the way is short from here.</p>
<p>For the sake of simplicity, let's assume that plain-text messages are made up only of letters (both lower and uppercase) and spaces. What we need is just one stroke of <em>genius</em>. The question we now need to ask is: <strong>what happens when two characters from the plain-texts are XORed together?</strong></p>
<p>Using the ASCII encoding, we can build the following (symmetric) table:</p>
<table>
<thead>
<tr>
<th><span class="math">\(\oplus\)</span></th>
<th>A-Z <em>(65-90)</em></th>
<th>a-z <em>(97-122)</em></th>
<th>Space <em>(32)</em></th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>A-Z <em>(65-90)</em></strong></td>
<td><span class="math">\(\leq 32\)</span></td>
<td><span class="math">\(\leq 64\)</span></td>
<td><span class="math">\(\geq 65\)</span></td>
</tr>
<tr>
<td><strong>a-z <em>(97-122)</em></strong></td>
<td><span class="math">\(\leq 64\)</span></td>
<td><span class="math">\(\leq 32\)</span></td>
<td><span class="math">\(\geq 65\)</span></td>
</tr>
<tr>
<td><strong>Space <em>(32)</em></strong></td>
<td><span class="math">\(\geq 65\)</span></td>
<td><span class="math">\(\geq 65\)</span></td>
<td>0</td>
</tr>
</tbody>
</table>
<p>The crucial part is the last line of the table. <strong>When XORing a space with any of the upper/lowercase letters, a number <span class="math">\(\geq 65\)</span> is obtained</strong>. And notice, that is the only case when this happens!</p>
<p>Imagine to have the three following plaintexts:</p>
<div class="highlight"><pre><span></span><code>Attack now
Withdraw tomorrow
Come today
</code></pre></div>
<p>and suppose we know they have been encrypted with the same one time pad. As seen above, we don't need to consider the key <span class="math">\(\mathcal{K}\)</span>, as that can be made to vanish if used multiple times. Let's look at the messages split in columns:</p>
<table>
<thead>
<tr>
<th></th>
<th><strong>0</strong></th>
<th><strong>1</strong></th>
<th><strong>2</strong></th>
<th><strong>3</strong></th>
<th><strong>4</strong></th>
<th><strong>5</strong></th>
<th><strong>6</strong></th>
<th><strong>7</strong></th>
<th><strong>8</strong></th>
<th><strong>9</strong></th>
<th><strong>10</strong></th>
<th><strong>11</strong></th>
<th><strong>12</strong></th>
<th><strong>13</strong></th>
<th><strong>14</strong></th>
<th><strong>15</strong></th>
<th><strong>16</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td><span class="math">\(\mathcal{M}_0\)</span></td>
<td>A</td>
<td>t</td>
<td>t</td>
<td>a</td>
<td>c</td>
<td>k</td>
<td></td>
<td>n</td>
<td>o</td>
<td>w</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td><span class="math">\(\mathcal{M}_1\)</span></td>
<td>W</td>
<td>i</td>
<td>t</td>
<td>h</td>
<td>d</td>
<td>r</td>
<td>a</td>
<td>w</td>
<td></td>
<td>t</td>
<td>o</td>
<td>m</td>
<td>o</td>
<td>r</td>
<td>r</td>
<td>o</td>
<td>w</td>
</tr>
<tr>
<td><span class="math">\(\mathcal{M}_2\)</span></td>
<td>C</td>
<td>o</td>
<td>m</td>
<td>e</td>
<td></td>
<td>t</td>
<td>o</td>
<td>d</td>
<td>a</td>
<td>y</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</tbody>
</table>
<p>I will treat messages as arrays: the first char of the first message will be <span class="math">\(\mathcal{M}\_0[0]\)</span>, the second char of the second message will be <span class="math">\(\mathcal{M}\_1[1]\)</span>, and so on.</p>
<p>Let's look at the 7th column, where the first message has a space. What happens if we XOR <span class="math">\(\mathcal{M}\_0[6]\)</span> with <span class="math">\(\mathcal{M}\_1[6]\)</span> and <span class="math">\(\mathcal{M}\_2[6]\)</span>? As expected, we get:</p>
<div class="math">$$\mathcal{M}\_0[6] \oplus \mathcal{M}\_1[6] = \mathcal{C}\_0[6] \oplus \mathcal{C}\_1[6] = 65 $$</div>
<div class="math">$$\mathcal{M}\_0[6] \oplus \mathcal{M}\_2[6] = \mathcal{C}\_0[6] \oplus \mathcal{C}\_1[6] = 79 $$</div>
<p>So it seems we can pretty confidently assert that <span class="math">\(\mathcal{M}\_0[6]\)</span> is a space. But since <span class="math">\(\mathcal{M}\_0[6] \oplus \mathcal{M}\_1[6]\)</span> is known, we can easily recover <span class="math">\(\mathcal{M}\_1[6]\)</span> as well! And, of course, we can do the same with <span class="math">\(\mathcal{M}\_2[6]\)</span>. Boom!</p>
<p>But the best is yet to come! We know that <span class="math">\(\mathcal{C}\_0 = \mathcal{M}\_0 \oplus \mathcal{K}\)</span>. Thus, <strong>we can now recover one char of the key itself:</strong></p>
<div class="math">$$\mathcal{K}[6] = \mathcal{M}\_0[6] \oplus \mathcal{C}\_0[6]$$</div>
<p>Now we are able to decode any 7th character of any message encoded with the same <span class="math">\(\mathcal{K}\)</span>! If we intercept enough ciphertexts, all encoded with the same one time pad key, it's likely that there will be enough spaces spread throughout all columns to allow a fair amount of decryption with this method. Even if that would not be the case, enough spaces would probably be present to assure at least a partial decryption, that may then be completed by hand.</p>
<p><strong>To sum up, the idea of the many time pad is that:</strong></p>
<div class="math">$$\text{If } \mathcal{M}\_t[k] \oplus \mathcal{M}\_s[k] \geq 65 \ \forall s \neq t \Rightarrow \mathcal{M}\_t[k] \text{ is a space}$$</div>
<p>It is surprising how much one can do with seemingly useless information. It becomes clear now why cryptographic protocols should never leak any information whatsoever!</p>
<h2 id="python-code-for-many-time-pad">Python code for many time pad</h2>
<p>The many time pad vulnerability is pretty powerful. It is not difficult to understand, and not so difficult to implement. Below is an almost-working Python code that you may draw inspiration from.</p>
<div class="highlight"><pre><span></span><code><span class="n">key</span> <span class="o">=</span> <span class="nb">bytearray</span><span class="p">(</span><span class="sa">b</span><span class="s1">'?'</span> <span class="o">*</span> <span class="n">max_length</span><span class="p">)</span>
<span class="c1">#Key decrypting routine</span>
<span class="k">for</span> <span class="n">k</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">max</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">c</span><span class="p">)</span> <span class="k">for</span> <span class="n">c</span> <span class="ow">in</span> <span class="n">ciphertexts</span><span class="p">)):</span>
<span class="n">cts</span> <span class="o">=</span> <span class="p">[</span><span class="n">c</span> <span class="k">for</span> <span class="n">c</span> <span class="ow">in</span> <span class="n">ciphertexts</span> <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">c</span><span class="p">)</span> <span class="o">></span> <span class="n">k</span><span class="p">]</span> <span class="c1">#selects all cipher texts long at least k</span>
<span class="n">guess</span> <span class="o">=</span> <span class="nb">ord</span><span class="p">(</span><span class="s1">'?'</span><span class="p">)</span> <span class="c1">#holds guess for current key char</span>
<span class="k">for</span> <span class="n">curs1</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">cts</span><span class="p">)):</span>
<span class="k">for</span> <span class="n">curs2</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">cts</span><span class="p">)):</span>
<span class="k">if</span> <span class="n">curs2</span> <span class="o">==</span> <span class="n">curs1</span><span class="p">:</span>
<span class="k">continue</span>
<span class="n">xor</span> <span class="o">=</span> <span class="nb">ord</span><span class="p">(</span><span class="n">cts</span><span class="p">[</span><span class="n">curs1</span><span class="p">][</span><span class="n">k</span><span class="p">])</span> <span class="o">^</span> <span class="nb">ord</span><span class="p">(</span><span class="n">cts</span><span class="p">[</span><span class="n">curs2</span><span class="p">][</span><span class="n">k</span><span class="p">])</span>
<span class="k">if</span> <span class="mi">0</span> <span class="o"><</span> <span class="n">xor</span> <span class="o"><</span> <span class="mi">65</span><span class="p">:</span>
<span class="n">guess</span> <span class="o">=</span> <span class="nb">ord</span><span class="p">(</span><span class="s1">'?'</span><span class="p">)</span>
<span class="k">break</span>
<span class="n">guess</span> <span class="o">=</span> <span class="n">SPACE</span>
<span class="k">if</span> <span class="n">guess</span> <span class="o">==</span> <span class="n">SPACE</span><span class="p">:</span> <span class="c1">#we can use the space to recover one key char and decrypt the whole column!</span>
<span class="n">key</span><span class="p">[</span><span class="n">k</span><span class="p">]</span> <span class="o">=</span> <span class="nb">ord</span><span class="p">(</span><span class="n">cts</span><span class="p">[</span><span class="n">curs1</span><span class="p">][</span><span class="n">k</span><span class="p">])</span> <span class="o">^</span> <span class="n">SPACE</span>
<span class="k">break</span>
<span class="c1">#Decode messages</span>
<span class="k">for</span> <span class="n">k</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">key</span><span class="p">)):</span>
<span class="k">for</span> <span class="n">curs</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">cleartexts</span><span class="p">)):</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">cleartexts</span><span class="p">[</span><span class="n">curs</span><span class="p">])</span> <span class="o">></span> <span class="n">k</span><span class="p">:</span>
<span class="n">cleartexts</span><span class="p">[</span><span class="n">curs</span><span class="p">][</span><span class="n">k</span><span class="p">]</span> <span class="o">=</span> <span class="n">key</span><span class="p">[</span><span class="n">k</span><span class="p">]</span> <span class="o">^</span> <span class="nb">ord</span><span class="p">(</span><span class="n">ciphertexts</span><span class="p">[</span><span class="n">curs</span><span class="p">][</span><span class="n">k</span><span class="p">])</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"</span><span class="se">\n</span><span class="s2">"</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">c</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s1">'ascii'</span><span class="p">)</span> <span class="k">for</span> <span class="n">c</span> <span class="ow">in</span> <span class="n">cleartexts</span><span class="p">))</span>
</code></pre></div>
<script type="text/javascript">if (!document.getElementById('mathjaxscript_pelican_#%@#$@#')) {
var align = "center",
indent = "0em",
linebreak = "false";
if (false) {
align = (screen.width < 768) ? "left" : align;
indent = (screen.width < 768) ? "0em" : indent;
linebreak = (screen.width < 768) ? 'true' : linebreak;
}
var mathjaxscript = document.createElement('script');
mathjaxscript.id = 'mathjaxscript_pelican_#%@#$@#';
mathjaxscript.type = 'text/javascript';
mathjaxscript.src = 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.3/latest.js?config=TeX-AMS-MML_HTMLorMML';
var configscript = document.createElement('script');
configscript.type = 'text/x-mathjax-config';
configscript[(window.opera ? "innerHTML" : "text")] =
"MathJax.Hub.Config({" +
" config: ['MMLorHTML.js']," +
" TeX: { extensions: ['AMSmath.js','AMSsymbols.js','noErrors.js','noUndefined.js'], equationNumbers: { autoNumber: 'none' } }," +
" jax: ['input/TeX','input/MathML','output/HTML-CSS']," +
" extensions: ['tex2jax.js','mml2jax.js','MathMenu.js','MathZoom.js']," +
" displayAlign: '"+ align +"'," +
" displayIndent: '"+ indent +"'," +
" showMathMenu: true," +
" messageStyle: 'normal'," +
" tex2jax: { " +
" inlineMath: [ ['\\\\(','\\\\)'] ], " +
" displayMath: [ ['$$','$$'] ]," +
" processEscapes: true," +
" preview: 'TeX'," +
" }, " +
" 'HTML-CSS': { " +
" availableFonts: ['STIX', 'TeX']," +
" preferredFont: 'STIX'," +
" styles: { '.MathJax_Display, .MathJax .mo, .MathJax .mi, .MathJax .mn': {color: 'inherit ! important'} }," +
" linebreaks: { automatic: "+ linebreak +", width: '90% container' }," +
" }, " +
"}); " +
"if ('default' !== 'default') {" +
"MathJax.Hub.Register.StartupHook('HTML-CSS Jax Ready',function () {" +
"var VARIANT = MathJax.OutputJax['HTML-CSS'].FONTDATA.VARIANT;" +
"VARIANT['normal'].fonts.unshift('MathJax_default');" +
"VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
"VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
"VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
"});" +
"MathJax.Hub.Register.StartupHook('SVG Jax Ready',function () {" +
"var VARIANT = MathJax.OutputJax.SVG.FONTDATA.VARIANT;" +
"VARIANT['normal'].fonts.unshift('MathJax_default');" +
"VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
"VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
"VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
"});" +
"}";
(document.body || document.getElementsByTagName('head')[0]).appendChild(configscript);
(document.body || document.getElementsByTagName('head')[0]).appendChild(mathjaxscript);
}
</script>Getting started with Binary reverse engineering: an example2018-01-13T14:09:00+01:002018-01-13T14:09:00+01:00Stefano Ottolenghitag:thecrowned.org,2018-01-13:/getting-started-binary-reverse-engineering-example<p>For a challenge in a university security class, I was given this file to crack: <a href="http://www.thecrowned.org/wp-content/uploads/2018/01/reverse1.zip">reverse1</a>. I started with <a href="http://www.thecrowned.org/wp-content/uploads/2018/01/reverse0.zip">reverse0</a>, which was considerably easier than the second one. In this post I will briefly explain …</p><p>For a challenge in a university security class, I was given this file to crack: <a href="http://www.thecrowned.org/wp-content/uploads/2018/01/reverse1.zip">reverse1</a>. I started with <a href="http://www.thecrowned.org/wp-content/uploads/2018/01/reverse0.zip">reverse0</a>, which was considerably easier than the second one. In this post I will briefly explain how I tackled reverse1. I provided the files so you can you try on your own and then came back for hints if you are stuck! If you are new to this business, as I relatively am, I advise you to start from reverse0 and crack that first.</p>
<p><strong>Hashes of reverse1 file: </strong><br>
<strong>MD5</strong> - c22c985acb7ca0f373b7279138213158<br>
<strong>SHA256</strong> - cd56541a75657630a2a0c23724e55f70e7f4f77300faf18e8228cd2cffe8248e</p>
<h2 id="disassembling-and-hoping-for-the-best">Disassembling and hoping for the best</h2>
<p>The first thing I did was to disassemble the file with <a href="https://github.com/radare/radare2">Radare</a> to have a look at the code.</p>
<div class="highlight"><pre><span></span><code><span class="c1">#In a terminal</span>
r2 -A ./reverse1
<span class="c1">#In Radare</span>
s main
Vp
</code></pre></div>
<p><img alt="radare" src="http://www.thecrowned.org/wp-content/uploads/2018/01/reverse1-1.png"></p>
<p>The assembly is quite jumbled up, and difficult to analyse all together. A quick look tells us that <strong>trying to crack the file just by reversing the assembly is no easy task</strong>, and actually a silly idea to begin with. There's a cycle after the password is read from standard input, then some other instructions, then another cycle... it's difficult to get what is going on...</p>
<p><strong>Instead, let's seek the <em>Bad password</em> print section, and see what should happen for the code to jump there</strong>. If we are lucky enough, we may find a bunch of final checks that will send over to the <em>Bad password</em> section. If we can find those, we may then look at those bits of assembly to understand how to avoid going there.</p>
<p>Scroll down enough, and down at the bottom I can see the <em>Bad password</em> part, starting at <code>0x080484f0</code>.</p>
<p><img alt="radare" src="http://www.thecrowned.org/wp-content/uploads/2018/01/reverse1-2.png"></p>
<p>Radare helps in showing two different arrows going into this address. The related comparisons are the following:</p>
<p><img alt="radare" src="http://www.thecrowned.org/wp-content/uploads/2018/01/reverse1-3.png"></p>
<p>So at this point, we want to make sure that <code>esi</code> is zero and that the current byte in the address printed at by <code>edi</code> is zero as well. If those conditions are not met, we'll see the <em>Bad password</em> message.</p>
<p><strong>So we should ask: where is <code>esi</code> changed throughout the execution? What's in <code>esi</code>? What's in <code>edi</code>? What is important to realize is that answers to these questions are the only thing we care about at the moment</strong>. The program may be doing very weird things at the beginning, but, as of now, the only important things to crack it are what's in <code>esi</code> and in <code>edi</code>. Period. <strong>Don't overshoot!</strong></p>
<p>So, it turns out the answer to these questions can be found in the lines just above the checks on <code>edi</code> and <code>esi</code>, starting at <code>0x08048483</code>.</p>
<p><img alt="radare" src="http://www.thecrowned.org/wp-content/uploads/2018/01/reverse1-4.png"></p>
<p>Here, <code>esi</code> is first set to zero, and <code>edi</code> contains the pointer to the user-entered password. Then there's a cycle, in which <code>edi</code> (i.e. entered password) is read byte by byte. Each iteration features a call to strtol (convert string to long integer) and strlen (get string length). We see that esi is incremented with results of xors with function returns, and we assume the result of the xors should be zero, but who knows what the password is? You may try to debug the program to see what the inputs and outputs of those calls are, but it's really going to be a pain.</p>
<h2 id="ltrace-to-the-rescue">ltrace to the rescue</h2>
<p>Instead, <strong>using <code>ltrace</code></strong> can save your life:</p>
<p><img alt="radare" src="http://www.thecrowned.org/wp-content/uploads/2018/01/reverse1-5.png"></p>
<p>First, try to run ltrace several times providing different input passwords. The output is always the same: the same calls are done over and over, regardless of the password we enter!</p>
<p>Now we can dive into understanding the assembly we saw earlier, the one featuring the calls to strtol and strlen.</p>
<p><img alt="radare" src="http://www.thecrowned.org/wp-content/uploads/2018/01/reverse1-6.png"></p>
<p>From ltrace, we see that the first strtol call returns <code>97</code>, which goes into <code>eax</code> (as all return values usually do). That is then xored with <code>local_1ch</code>, which contains the first char of the input password. The result is used to increment <code>esi</code>. <strong>So here we go! This is vital!</strong> We want <code>esi</code> to be 0 at the end, so it should never be incremented. The result of the xor should thus be zero, which means that the two xor values should be the same. Eureka! We know the output of strtol is 97, so we should provide that same value as first char of the password! 97 is <code>a</code> in ascii, so the password starts with <code>a</code>.</p>
<p><img alt="radare" src="http://www.thecrowned.org/wp-content/uploads/2018/01/reverse1-7.png"></p>
<p>The same reasoning can be applied to subsequent calls to strtol details provided by ltrace to find that the second char <code>s</code>, <code>c</code>, then <code>i</code> and finally <code>i</code> again.</p>
<p><strong>What are the strlen calls for?</strong> They are used as cycle condition: as soon as a string longer than 3 chars is found, the cycle ends and the two conditions on <code>esi</code> and <code>edi</code> are evaluated. The byte condition on <code>edi</code> basically checks that the password is exactly the one it should be, and does not only start with the expected password. That's why it checks for a zero byte, meaning string termination.</p>
<p>And there you go: password cracked!</p>
<p><img alt="terminal" src="http://www.thecrowned.org/wp-content/uploads/2018/01/reverse1-8.png"></p>Base conversion in Ubuntu (decimal to binary)2018-01-11T11:29:00+01:002018-01-11T11:29:00+01:00Stefano Ottolenghitag:thecrowned.org,2018-01-11:/base-conversion-ubuntu-decimal-binary<p>Need to convert a base 10 integer in a base 2 one? Or, at any rate, convert a number from one numeration system to another? In Ubuntu, the <a href="https://packages.ubuntu.com/xenial/bc">bc utility</a> already integrates these features. It …</p><p>Need to convert a base 10 integer in a base 2 one? Or, at any rate, convert a number from one numeration system to another? In Ubuntu, the <a href="https://packages.ubuntu.com/xenial/bc">bc utility</a> already integrates these features. It is usually already installed, so you don't have to anything special.</p>
<p><strong>Simply run <code>bc</code>, and enter the following commands:</strong></p>
<div class="highlight"><pre><span></span><code><span class="nv">ibase</span><span class="o">=</span><span class="m">10</span>
<span class="nv">obase</span><span class="o">=</span><span class="m">2</span>
</code></pre></div>
<p>Then, all subsequent number inputs will be simply converted to their base-2 representation.</p>
<p><img alt="terminal" src="http://www.thecrowned.org/wp-content/uploads/2018/01/bc-base-conversion-1.png"></p>
<p>If you want to get a conversion straight ahead, without going through the opening of bc, just enter the following from a terminal:</p>
<div class="highlight"><pre><span></span><code><span class="nb">echo</span> <span class="s1">'obase=2; ibase=10; 123'</span> <span class="p">|</span> bc
</code></pre></div>
<p>which will convert the number 123 from base 10 to base 2.</p>
<p><img alt="terminal" src="http://www.thecrowned.org/wp-content/uploads/2018/01/bc-base-conversion-2.png"></p>
<p>Of course, 2 and 10 can be replaced with any other possible base!</p>Does C++ delete operator really free memory?2017-09-20T09:48:00+02:002017-09-20T09:48:00+02:00Stefano Ottolenghitag:thecrowned.org,2017-09-20:/c-delete-operator-really-frees-memory<p>Well, I have been wondering about this for quite a while now, and I have tried to run some tests to better understand what's going on under the hood. The standard answer is that after …</p><p>Well, I have been wondering about this for quite a while now, and I have tried to run some tests to better understand what's going on under the hood. The standard answer is that after you call delete you should not expect anything good from accessing that memory spot. However, this did not seem enough to me. What is it really happening when calling <code>delete(ptr)</code>? Even though there no standard behavior, what could happen, anyway? Here's what I've found. I'm using g++ on Ubuntu 16.04, so this may play a role in the results.</p>
<p>What I first expected when using the delete operator was <strong>that the freed memory would be handed back to the system</strong> for usage in other processes. Let me say this <strong>does not happen under any of the circumstances I have tried</strong>.</p>
<p>Memory released with <code>delete</code> still seem to be allocated to the program it first allocated it with new. I have tried, and there is no memory usage decrease after calling delete. I had a software which allocated around 30MB of lists through new calls, and then released them with subsequent delete calls. What happened is that, looking at the System monitor while the program was running, even a long sleep after the delete calls, memory consumption my the program was the same. No decrease! This means that delete does not release memory to the system.</p>
<p>In fact, it looks like memory allocated by a program is his forever! However, the point is that, <strong>if deallocated, memory can be used again by the same program without having to allocate any more</strong>. I tried to allocate 15MB, freeing them, and then allocating another 15MB of data after, and the program never used 30MB. System monitor always showed it around 15MB. What I did, in respect to the previous test, was just to change the order in which things happened: half allocation, half deallocation, other half of allocation.</p>
<p><strong>So, apparently memory used by a program can increase, but never shrink</strong>. I thought that maybe memory would really be released for other processes in critical situations, such as when there is no more memory available. After all, what sense would it make to let a program keep its own memory forever, when other processes are asking for it? So I allocated the 30MB again, and while deallocating them I run a memtester with as much physical memory I could. I expected to see my software hand out its memory to memtester. But guess it, it did not happen!</p>
<p>I've made up a short screencast that shows the thing in action:</p>
<p><img alt="Delete operator C++" src="http://www.thecrowned.org/wp-content/uploads/2017/09/delete.gif"></p>
<p>To be 100% honest, there was a situation in which something happened. When I tried memtester with more than the available physical memory in the middle of the deallocation process of my program, the memory used by my program dropped to around 3MB. The memtester process was killed automatically though, and what happened was even more surprising! The memory usage of my program increased with each delete call! It was just as if Ubuntu was restoring all its memory back after the memtester incident.</p>
<h2 id="a-situation-in-which-freeing-memory-worked">A situation in which freeing memory worked</h2>
<p>So, I've said that free calls don't seem to release memory, and I've backed that claim with details and a gif screencast of my tests. However, there was a situation in which <code>free</code> did actually free memory!</p>
<p>This happened in my <a href="https://github.com/TheCrowned/Hilbert-Image-to-Sound">Hilbert Image to Sound script</a>. That code loads an image into memory, does some processing to it creating a new matrix of equal size and then a couple arrays of quite some length. I tried freeing memory and sleeping for a while after each free call, <strong>and indeed the memory usage of the app did decrease</strong>!</p>
<p>I can't provide an explanation for why this happened in this script but not in the other one, but if any of you know, please comment!</p>Tips and advice on being a freelance in Information Technology2017-02-14T11:04:00+01:002017-02-14T11:04:00+01:00admintag:thecrowned.org,2017-02-14:/tips-and-advice-being-freelance-information-technology<blockquote>
<p>What follows is Javier Silva's interview to me. The interview is mostly focused on how what it is like to be a freelance in the IT field and how to start as a programmer (and …</p></blockquote><blockquote>
<p>What follows is Javier Silva's interview to me. The interview is mostly focused on how what it is like to be a freelance in the IT field and how to start as a programmer (and how that may evolve into a business). It was first <a href="https://www.wpescuela.com/entrevista-al-creador-del-plugin-post-pay-counter/">published on his blog in Spanish</a>. He also did a <a href="https://www.wpescuela.com/plugin-post-pay-counter-multi-autor-wordpress/">small review of</a> my <a href="https://postpaycounter.com/">Post Pay Counter plugin</a>.</p>
</blockquote>
<h2 id="please-introduce-yourself">Please, Introduce yourself!</h2>
<p>I'm Stefano from Italy. I study mathematics, but there are very few things I am not interested into. I am a web developer, a walker, a reader, and an amateur photographer. Those are just the things that take most of my time, but don't believe I don't do anything else!</p>
<h2 id="you-work-as-web-developer-where-did-you-study-it-or-how-did-you-learn-it">You work as web developer... where did you study it? or how did you learn it?</h2>
<p>I've never taken any classes on web developing or on any IT subject. I have just always been into computers and technology, and by reading/replying on forums, experimenting and lots of tutorials I have learnt all that I know. When I was 12, at school we were covering divisors, prime numbers, factorization, and the like, and… you know, homework was boring as hell! I was just tired of having to figure out whether a number was prime, or what its divisors were, so I wrote a little script that did it for me. That evolved in writing more complex software and slowly learning to write decent-quality code.</p>
<blockquote>
<p>Homework was boring as hell! I was just tired of having to figure out whether a number was prime, or what its divisors were, so I wrote a little script that did it for me.</p>
</blockquote>
<p>There are a lot of resources out there for people willing to learn. I believe the key is to play around and experiment. And, as always, a lot of practice is important.</p>
<h2 id="what-is-your-point-of-view-about-the-programming-career-is-it-a-competitive-profession">What is your point of view about the "programming career"? Is it a competitive profession?</h2>
<p>I believe it definitely is. Unless you aim at working within your own city, meeting customers face to face, you really face a lot of competition. If I am hiring someone to develop something for me, and I don't require they live in my same city, then I can pick anyone from all over the world. And good luck to convince me that you are the best developer, and that I really want <em>you</em></p>
<p>In the end, freelancing (at least in programming) has a lot to do with cultivating relationships and caring for your customers, or they will just find someone else to do your work. However, if you do work well, the word will spread and you may thus earn more work. It is a fact that building your network takes time, though. It's not like opening a shop and waiting until passers-by stop and get in, it's completely different.</p>
<h2 id="let-us-know-about-your-career-are-you-a-full-time-freelance-or-do-you-have-a-current-job">Let us know about your career... Are you a full-time freelance or do you have a current job?</h2>
<p>I am neither: I am a less-than-part-time freelance. As I said, I'm a student, and for that reason I tend to spend most of my time studying. Of course, that is what I should be doing, but it often happens that a critical bug needs to be addressed, or that I have a deadline for a paid project, or whatnot, and I put aside university for a while and care about my software developing and my customers.</p>
<blockquote>
<p>Practical skills are only learnt through doing, and the sooner you start screwing things up the sooner you will get experienced enough.</p>
</blockquote>
<p>As I said, building your network takes time (and with time, I mean years!). I do not believe it is wise to go through university without doing any work, and then expect that customers, good ideas and success will rain down the sky. Practical skills are only learnt through doing, and the sooner you start screwing things up (because you will, of course you will!), the sooner you will get experienced enough to at least avoid the worst mistakes.</p>
<h2 id="and-what-do-you-think-about-freelance">And what do you think about Freelance?</h2>
<p>I believe it is very hard. For this time, let's put aside the talks about discipline (although it is certainly important). The good thing about freelancing, obviously, is that you get all the money your customer will pay, as opposed to being employed. Money aside, the best thing about freelancing is the freedom. I can pick the projects I want to work on, and if you are a really bad customer, I can just turn you down and save me tons of anger and frustration.</p>
<p>However, you also get a harsh thing: you have to find your work by yourself. Nobody provides you stuff to work on which you will get paid regardless of its success. If you spend one year developing a product no-one wants… then you have lost a full year! Of course you may have learnt a lot from it, but it's still a lot of wasted time, when it comes to money.</p>
<p>On the other hand, if you don't sell any products but just develop software on demand, then it's even harder to build your network and find your work. How are people supposed to know you exist and provide quality services if they are not linked to you in any way? I get several requests from people who would like me to develop their plugin or platform or whatever, and they all come from people who use <em>my</em> plugins, either free or paid ones. The usually say: "We really like your plugin and its quality, and we'd like you to build our own", and it's not difficult to believe that they wouldn't even know I <em>existed</em> if they didn't use my plugins.</p>
<h2 id="you-have-some-products-wordpress-plugins-on-sell-by-yourself-how-is-it-going-are-they-fulfilling-your-expectations">You have some products (WordPress plugins) on sell by yourself. How is it going? Are they fulfilling your expectations?</h2>
<p><a href="https://profiles.wordpress.org/ste_95/#content-plugins">My plugins</a> have all been free for a long time. Just a couple years ago, when I really put a lot of effort and time in re-writing from scratch the <a href="https://postpaycounter.com">Post Pay Counter</a> plugin, I believed the product was finally sound enough to be worth people's money. Since it was my first paid product, I wasn't really expecting anything, I was quite open. I was still in my last year of high school and I did not even thought about not going to university, so I did not feel the pressure of being successful with the product. I guess not feeling the stress of <em>having</em> to earn a salary certainly helps at the beginning: flunking will not be pleasing, but it will not be such a big deal. In my case, I was going through the routine go-to-university-then-find-a-job routine anyway.</p>
<p>Today, <a href="https://postpaycounter.com/addons">Post Pay Counter addons</a> provide me a consistent revenue, customers are satisfied and I can think about the next product I would like to build. So I guess it is going pretty well!</p>
<h2 id="what-is-the-best-and-worst-part-of-developing-a-plugin">What is the best and worst part of developing a plugin?</h2>
<p>For me, the best is user feedback and reviews. Every time a user or a customer takes the time to publicly write an appreciation of my plugin, I am happy. I like that because it means I have done something useful and well done.</p>
<p>As for the worst, this is probably highly subjective, but for me it's the marketing part. I am the only man behind all my plugins, including the website design and management, tutorials writing, and everything. Sometimes I just wish someone else would take care of the marketing part on my behalf.</p>
<h2 class="western" id="what-can-you-tell-us-about-the-plugin-process-development-time-design-issues-etc" lang="en-GB">What can you tell us about the plugin process development? Time, design, issues, etc?</h2>
<p>My personal experience is that developing the very first release of a plugin takes a lot of time, but really a lot. It may take several months before you have a decent product you can safely release publicly. Post Pay Counter took me almost a year. I did even release a beta version (and I had to look for beta testers) and of course there were lots of things that needed improvement, some that did not work at all, and some features users really felt the need for and were not in the plugin already. So it's likely that when you think you are done, you are kind of halfway through actually.</p>
<blockquote>
<p>If you just skip the whole process of designing your product, you are likely to get to a point where improving your product is not possible any more.</p>
</blockquote>
<p>However, as with all digital products, this initial stage is crucial. If you just skip the whole process of designing your product, you are likely to get to a point where improving your product is not possible any more because the choices you made at the beginning were bad, or hurried.</p>
<p>I believe this is best understood with a real example. When Post Pay Counter was first released, it allowed you to pay authors with 4 different criteria (such as per word and per visit). Of course, adding more payment criteria was technically possible, but every time I wanted to do that, especially in a separate addon, it was a nightmare. I had to write a lot of code, most of which was just copied from other payment criteria (which is really a bad thing to do). It was very easy for me to forget about some little piece of code, or when a bug showed up in that code, I had to fix it several times, one for each addon. When I decided it was too much, I had to re-write the whole part that handles payment criteria in the plugin, which took me around a month, and I then had to update all addons with the changes. Developing an addon now takes me way less time, but I would have saved even a lot more time had I been more careful in the first place.</p>
<p>However, once you have a plugin that works fine, your work is nearly done. Of course you will have to spend some time fixing bugs, or implementing new features, but in terms of time this does not even compare with the first development process. Providing support to users is likely to be the most time-consuming thing, but I can tell you the very steep path is developing the first release, then it's all downhill.</p>
<h2 id="the-truth-about-the-myth-can-you-live-doing-plugins-themes-or-support">The truth about the myth! Can you live doing plugins, themes or support?</h2>
<p>I do believe so. There are several people doing that, with quite some success. I am thinking about Pippin Williamson, for example. However, the competition is so fierce out there, that is not easy to be noticed and have success. It requires the right product at the right time, and even then it will take a couple years before your product become renown. Most people who develop plugins also do consulting jobs and customization, which is another good source of income.</p>
<h2 id="what-usually-clients-say-or-request-to-you">What usually clients say or request to you?</h2>
<p>They usually tell me they have a brilliant idea for a plugin, or for a specific feature to be added to my plugins, which is surely going to be helpful to other people. Most often it is just something very specific for their own setup, that probably nobody else would benefit from. I guess they just hope I would do a paid work for free. It is just like going to IKEA, seeing a piece of furniture that is kind of what you need but not exactly, and tell the salesman if they could just make it more like this and that, surely a lot more people would buy it. Of course, sometimes the idea is a good one, but most of the time you would just reply "go and pay a carpenter".</p>
<p>Fortunately, I do also get the same requests from people who are human and reasonable enough to offer to pay for my time and services.</p>
<h2 id="you-maybe-know-about-marketplaces-what-do-you-think-about-this-new-way-of-commerce">You maybe know about Marketplaces... What do you think about this new way of commerce?</h2>
<p>I guess it's a way for customers to know they have their back covered. If you purchase a plugin from a private website, and it was a scam, then you are 99% to have lost your money. But if you purchase on CodeCanyon and I don't reply to emails, then you can tell CodeCanyon about it, and they will probably refund you.</p>
<h2 id="are-you-involved-in-any-marketplace">Are you involved in any marketplace?</h2>
<p>No, not for now at least. CodeCanyon is the only worthy marketplace for paid WordPress plugins, but they take at least around 25% of your income, which I believe is just so unfair! I understand they are putting in the brand and allowing me an exposure I wouldn't gain otherwise, but that's not equivalent to a forth of my work!</p>
<h2 id="how-do-you-see-the-future-of-marketplaces-we-know-the-wordpress-plugin-repository-is-a-kind-of-marketplace-and-you-are-there">How do you see the future of marketplaces? (We know the Wordpress Plugin Repository is a kind of Marketplace and you are there).</h2>
<p>It is clear to me that every successful platform of the latest years has been such thanks to its marketplace. Look at Android, iOS, WordPress… even Linux, to an extent. On the other hand, Windows Phone doesn't have a flourishing app store, and what's its market share?</p>
<p>If we consider marketplaces even free ones, then it looks clear to me that any platform wanting to have success should care about having an active and huge one.</p>
<h2 id="any-advice-for-new-guys-trying-to-get-into-the-web-developer-world">Any advice for new guys trying to get into the Web Developer World?</h2>
<p>Start early. And it does not mean that you should already be working at 18 and stop caring about everything else you like, hell no! Take all the time you want, study whatever interests you, even if it's not developing-related, but commit to little projects here and there whenever you can. This is sure to repay you in the future.</p>
<p>You will make mistakes, nobody does not. But, you know, if you make them when you are 15, who cares? But then, at 22 you will not make the same mistakes (hopefully), and there will be much more at stake at that age (the success of your product and whether you get fired or not, maybe!)</p>
<h2 id="thats-enough-about-profession-let-know-us-the-person-behind-what-do-you-do-in-your-free-time">Thats enough about profession... let know us the person behind! What do you do in your free-time?</h2>
<p>I really like walking. On sunny sundays I often go hiking somewhere. And then I read, I read a lot (both fiction and essays).</p>
<h2 id="what-hobbies-do-you-like-to-practice">What hobbies do you like to practice?</h2>
<p>I play table tennis and practice yoga. I do also love light and try to capture it through photography.</p>
<h2 id="a-book-and-why">A book? and why...</h2>
<p>This is always the toughest question… I'd say Surely you're joking, Mr. Feynman, by Richard Feynman. Nowadays, very few people really understand what science is about, and what a scientist really is: Feynman is the best person to show us (spoiler: being a scientist is not about knowing scientific facts).</p>
<h2 id="a-movie-and-why">A movie? and why...</h2>
<p>Harvey is probably the best non-popular movie I can recommend. We should never impose our beliefs unto other people, and there is no warranty that our beliefs are better than other people's ones. If you look carefully, Harvey will teach you that. Apply that in your life, and I believe all your relationships will improve.</p>
<blockquote>
<p>You don't have to prove anything to anyone. Just do what you believe is good, and always be open to questions.</p>
</blockquote>
<h2 id="a-song-and-why">A song? and why...</h2>
<p>Eclipse, by Pink Floyd. It may already be well known, but both the music and the lyrics are perfect in a way I have never yet found again.</p>
<h2 id="a-country-would-you-like-to-visit">A country would you like to visit?</h2>
<p>Sweden, or any of the Scandinavian ones. I'd also really like to go to Australia and to the poles, one day.</p>
<h2 id="a-person-would-you-like-to-meet-and-a-question-would-you-like-to-ask">A person would you like to meet? and a question would you like to ask...</h2>
<p>Shaun Tan. I would never have the courage, but I'd ask him if he wanted to draw my children stories.</p>
<h2 id="what-cartoon-or-superhero-would-you-like-to-be-in-a-parallel-world">What cartoon or superhero would you like to be in a parallel world?</h2>
<p>Iron Man, because I could ask Jarvis stuff.</p>
<h2 id="a-dream">A dream?</h2>
<p>That my small Italian not-for-profit which helps kids explore and learn about science in a fun and entertaining way would become my full-time future.</p>
<h2 id="a-memory-you-will-never-forget">A memory you will never forget?</h2>
<p>I was helping a little kid building some sort of origami. When we started out, he didn't want to tell me his name, nor would he reply to questions or remarks. He was like mute. He had his own toy-bulldozer and wouldn't let it go. After he finished building his origami, he still wouldn't talk nor tell me his name or let go of his bulldozer, hell no! But he accepted to give me a high-five, which really made my day.</p>
<h2 id="finally-do-you-want-say-something-else-regards-a-message-or-phrase">Finally, do you want say something else... regards, a message or phrase!</h2>
<p>You don't have to prove anything to anyone. Just do what you believe is good, and always be open to questions.</p>WordPress function url_to_postid not working with Anspress2017-01-23T22:04:00+01:002017-01-23T22:04:00+01:00Stefano Ottolenghitag:thecrowned.org,2017-01-23:/wordpress-function-url_to_postid-not-working-with-anspress<p>With the Anspress theme/plugin, using the WordPress native function <code>url_to_postid()</code> on a question permalink returns the page ID of the base page. For example,</p>
<div class="highlight"><pre><span></span><code>url_to_postid( "www.website.com/questions/question/this-is-the-question");
</code></pre></div>
<p>would return the …</p><p>With the Anspress theme/plugin, using the WordPress native function <code>url_to_postid()</code> on a question permalink returns the page ID of the base page. For example,</p>
<div class="highlight"><pre><span></span><code>url_to_postid( "www.website.com/questions/question/this-is-the-question");
</code></pre></div>
<p>would return the page ID of the <code>/questions</code> page.</p>
<p>To get the WP question ID of <code>this-is-the-question</code>, use the following:</p>
<div class="highlight"><pre><span></span><code>get_page_by_path( "/this-is-the-question", "ARRAY_A", get_post_types() );
</code></pre></div>
<p>(using <code>get_post_types()</code> instead of <code>'questions'</code> allows the call to work even with permalinks of non-question contents.)</p>bbPress - Anonymous Subscriptions2016-02-15T07:28:00+01:002016-02-15T07:28:00+01:00Stefano Ottolenghitag:thecrowned.org,2016-02-15:/bbpress-anonymous-subscriptions<p>This add-on plugin for bbPress will allow anonymous users to subscribe to topics and get email notifications when a new reply is posted. The notification email includes an unsubscribe link.</p>
<p>bbPress notifications will keep to …</p><p>This add-on plugin for bbPress will allow anonymous users to subscribe to topics and get email notifications when a new reply is posted. The notification email includes an unsubscribe link.</p>
<p>bbPress notifications will keep to go out to registered users, this plugin only extends the thing to anonymous posters as well!</p>
<h3 id="download-its-free"><a href="https://downloads.wordpress.org/plugin/bbp-anonymous-subscriptions.latest-stable.zip">Download (it's free!)</a></h3>
<p><img alt="bbPress - Anonymous Subscriptions" src="http://www.thecrowned.org/wp-content/uploads/2016/02/bbpress-anonymous-subscriptions-1024x766.png"></p>
<h2 id="a-case-example-with-100-subscription-rate">A case example with >100% subscription rate</h2>
<p>This is vital for support forums, for example. On <a href="https://postpaycounter.com">Post Pay Counter</a> support forums, I did not want customers to sign-up: I wanted them to be able to request support in a matter of minutes, without any hassle. I liked the idea of "enter your name and email and you're done!" But I also felt like they needed to be notified when someone replied to help. It was not compulsory, of course, but I would have wanted it as a customer.</p>
<p>Now, after a year from bbPress - Anonymous Subscriptions release, we have seen a <strong>139% subscription rate</strong>! On a total of 117 topics posted since the plugin started working, we have had 163 subscriptions to topics. However, it often happened that more than one user subscribed to a topic. On average, we had <strong>1.7 users subscribed per topic</strong>.</p>
<p>How is it possible that we registered a >100% subscription rate? Because <strong>users also subscribed to older topics</strong>!</p>
<p>I would never go back without having this plugin on a support forum, or anyway on a public forum in which most posts are from anonymous users.</p>A method for calculating pattern relevance in a text2016-02-06T19:03:00+01:002016-02-06T19:03:00+01:00Stefano Ottolenghitag:thecrowned.org,2016-02-06:/method-for-pattern-relevance-in-text<p>This article aims at presenting a method for computing the relevance of a given string (pattern) in a text. This algorithm is at the core of my WordPress plugin <a href="https://wordpress.org/plugins/smart-tag-insert/">Smart Tag Insert</a>.</p>
<p>First of all …</p><p>This article aims at presenting a method for computing the relevance of a given string (pattern) in a text. This algorithm is at the core of my WordPress plugin <a href="https://wordpress.org/plugins/smart-tag-insert/">Smart Tag Insert</a>.</p>
<p>First of all, there is a difference between a simple pattern matching and computing text pattern relevance. The question we are trying to address here is the following: I have a string, and <strong>I would like to know how much that string is relevant for a specific text</strong>. For example, let's say we have "download music" as the string of which relevance we are interested into. How can we determine how much relevant it is for a specific article?</p>
<h2 id="the-simple-approach">The simple approach</h2>
<p>The easy thing one could try is run a pattern match of "download music" in the article text. That is okay, but suppose the article contained strings like "download the music", or "download some music", or "downloading music", or "download good quality music". It is clear that, to a human, all these strings are equivalent when trying to understand what the article is about: it is about downloading music, regardless of whether it is good, bad, a lot or little.</p>
<p><strong>A simple pattern match would fall short</strong>, because it would exclude all those other strings and make it look like the content is not very much about downloading music, just because "download music" was never found exactly that way.</p>
<p>So the first point we need to acknowledge if we want to try to teach a machine to compute text pattern relevance, is that <strong>we need to find a way</strong>, at least a rough way, <strong>to teach it to grasp the meaning of the content</strong>.</p>
<p>(One thing that should be clear from the beginning is that we are not going to teach a machine to <em>understand</em> what a text is about, but rather whether the subject of the text is the one we are suggesting (for example, from a list). We are not asking what a text is about, we are asking whether the given text is related to "download music", and how we can assign a number to that relevance.)</p>
<h2 id="a-more-sophisticated-approach">A more sophisticated approach</h2>
<p>So we have a couple of things we need to think about:</p>
<ol>
<li>how do we run a <em>gentle</em> pattern match, so that "downloading music" gets recognized as well? However, we still don't want the match to be completely ineffective. If a text starts with "download" and ends with "music", and has 3k words in between, that match should value pretty low (or nothing). So the next problem is:</li>
<li>how do we effectively quantify the value of a single match?</li>
</ol>
<h2 id="the-gentleflexible-pattern-match">The gentle/flexible pattern match</h2>
<p>Point one is rather technical. We can build a regex that does the text pattern match the way we want like this:</p>
<div class="highlight"><pre><span></span><code><span class="o">^</span>\<span class="n">bdownload</span><span class="p">(</span><span class="o">.*</span><span class="err">?</span><span class="p">)</span>\<span class="n">bmusic</span><span class="o">^</span><span class="n">i</span><span class="w"></span>
</code></pre></div>
<p>People better at dealing with regular expressions than I am may notice that this regex does what we wished, but has a <strong>limitation</strong>: it will match fine "download some music" and even "downloading some musicality" (whatever it means), but it will miss "redownload music" and "download discomusic". The choice here is due to the fact that prefixes change word meanings more often than post-fixes do (for example, imagine a text about the "new york run": allowing prefixes as well as post-fixes would make "new york forerunner" seem related to the text, while it is not).</p>
<p>Anyway, in a human language our regex reads: match strings that</p>
<ul>
<li>start with <code>download</code>. No exceptions. Don't allow anything before (this assures us a real match is starting);</li>
<li>at some point (even 1k chars away) have <code>music</code>. Again, not <code>discomusic</code> or any other prefix to <code>music</code>;</li>
<li>end after <code>music</code>;</li>
<li>do the above case-insensitively.</li>
</ul>
<p>Of course, one can have regex for complex strings, however complex they may be. For example, if one wishes to match "download good music fast", the regex would be</p>
<div class="highlight"><pre><span></span><code><span class="o">^</span>\<span class="n">bdownload</span><span class="p">(</span><span class="o">.*</span><span class="err">?</span><span class="p">)</span>\<span class="n">bgood</span><span class="p">(</span><span class="o">.*</span><span class="err">?</span><span class="p">)</span>\<span class="n">bmusic</span><span class="p">(</span><span class="o">.*</span><span class="err">?</span><span class="p">)</span>\<span class="n">bfast</span><span class="o">^</span><span class="n">i</span><span class="w"></span>
</code></pre></div>
<p>A good idea would be to <strong>discard matches with full stops in them</strong>, and we will do that while processing the matches.</p>
<h2 id="determining-a-relevance-score">Determining a relevance score</h2>
<p>Once we find a match, we need to assign a score to it. The idea is that if "download music" is found exactly, then it Is a perfect match, if "download good and nice music" is found, then yes it should be a match, but we do not want it to have the same score as a perfect match, and if "downloading an antivirus is important so that your computer can't be hurt by fake music" is found, then it should have a pretty low score.</p>
<p>The idea is to <strong>rely on the number of characters that are between each word of the string to match</strong>. This is a rough method, but it is fine enough to grant decent relevance scores. Mathematically speaking, we want the relevance of a match to be a (strictly) <strong>decreasing function of the number of characters</strong> that stand in between of the match words.</p>
<p>The formula we are going to use can be schematized like this:</p>
<blockquote>
<p>relevance of a match = relevance of a perfect match - decrease of relevance due to in between chars</p>
</blockquote>
<p>This expression still has two unknown values. We thus have two questions to address now:</p>
<ol>
<li><strong>What should the relevance of a perfect match be?</strong> My answer comes from the following (arguable) assumption: <strong>perfectly finding 10 times the match in a 360 words text means 100% relevance</strong> of the given string. This is actually kind of reasonable: if you consider that an average sentence can be of 30 words, then to find a perfect match in each sentence would need 12 matches in the whole text. We could use 12 instead of 10, but 12 relates to an unlikely scenario, and would probably make posts reach lower scores than their real ones. So 10 is my choice.</li>
<li><strong>How much should relevance decrease, given the number of chars in between?</strong> This is 100% empirical, based on my expectations on test texts, but it looked like <span class="math">\(x^\frac{4}{5}\)</span> is fine. It plays well on small numbers (so that "download the music" has still quite high a relevance) but gets bigger as soon as numbers grow a little bit, so that it drops the relevance of matches with words too far apart.</li>
</ol>
<h2 id="share-your-knowledge">Share your knowledge!</h2>
<p>Now, I am sure there are better ways to compute pattern relevance in a text. This article wants to be a starting point for methods of this kind, since I am quite confident that the ideas and the questions I have tried to address here do have some value. <strong>Getting to the root of the questions that we need to answer to solve a specific problem is more important than the answers one can provide</strong>, and are anyway fundamental in finding the right answers. So if you have any idea to improve this method, or have something completely different in mind, please do share it in the comments below, I would really like to know!</p>
<h2 id="the-complete-algorithm-for-text-pattern-relevance">The complete algorithm for text pattern relevance</h2>
<p>At this point, we have everything to build the algorithm that will calculate the relevance given a string (needle) and a text (haystack). What follows is written in PHP.</p>
<div class="highlight"><pre><span></span><code><span class="cp"><?php</span>
<span class="k">function</span> <span class="nf">get_tag_relevance</span><span class="p">(</span> <span class="nv">$needle</span><span class="p">,</span> <span class="nv">$haystack</span><span class="p">,</span> <span class="o">&</span><span class="nv">$overall_relevance</span> <span class="p">)</span> <span class="p">{</span>
<span class="k">if</span><span class="p">(</span> <span class="nb">strlen</span><span class="p">(</span> <span class="nv">$haystack</span> <span class="p">)</span> <span class="o">==</span> <span class="mi">0</span> <span class="p">)</span> <span class="p">{</span> <span class="nv">$overall_relevance</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="k">return</span><span class="p">;</span> <span class="p">}</span> <span class="c1">//empty text -> 0 relevance for everything</span>
<span class="c1">//Break the pattern in single words</span>
<span class="nv">$exploded_needle</span> <span class="o">=</span> <span class="nb">explode</span><span class="p">(</span> <span class="s2">" "</span><span class="p">,</span> <span class="nv">$needle</span> <span class="p">);</span>
<span class="nv">$explode_count</span> <span class="o">=</span> <span class="nb">count</span><span class="p">(</span> <span class="nv">$exploded_needle</span> <span class="p">);</span>
<span class="c1">//Strip special chars from text and count text words</span>
<span class="nv">$purged_haystack</span> <span class="o">=</span> <span class="nb">preg_replace</span><span class="p">(</span> <span class="s1">'/[(),;:!?%#$"_+=\\/-]+/'</span><span class="p">,</span> <span class="s1">''</span><span class="p">,</span> <span class="nb">trim</span><span class="p">(</span> <span class="nb">preg_replace</span><span class="p">(</span> <span class="s1">'/\'|&nbsp;|&#160;|\r|\n|\r\n|\s+/'</span><span class="p">,</span> <span class="s1">' '</span><span class="p">,</span> <span class="nb">strip_tags</span><span class="p">(</span> <span class="nv">$haystack</span> <span class="p">)</span> <span class="p">)</span> <span class="p">)</span> <span class="p">);</span> <span class="c1">//need to trim to remove final new lines</span>
<span class="nv">$haystack_words</span> <span class="o">=</span> <span class="nb">count</span><span class="p">(</span> <span class="nb">preg_split</span><span class="p">(</span> <span class="s1">'/\s+/'</span><span class="p">,</span> <span class="nv">$purged_haystack</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="nx">PREG_SPLIT_NO_EMPTY</span> <span class="p">)</span> <span class="p">);</span>
<span class="c1">//Calculate how much a perfect occurrence should weigh</span>
<span class="nv">$occurrence_weigth</span> <span class="o">=</span> <span class="mi">10</span><span class="o">*</span><span class="mi">350</span><span class="o">/</span><span class="nv">$haystack_words</span><span class="p">;</span> <span class="c1">//we consider perfectly finding 10 times the tag in a 360-words text as 100% relevance</span>
<span class="c1">//Build the regex pattern</span>
<span class="nv">$pattern</span> <span class="o">=</span> <span class="s2">"^\b"</span><span class="p">;</span>
<span class="nv">$n</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="k">foreach</span><span class="p">(</span> <span class="nv">$exploded_needle</span> <span class="k">as</span> <span class="nv">$single</span> <span class="p">)</span> <span class="p">{</span>
<span class="nv">$pattern</span> <span class="o">.=</span> <span class="nv">$single</span><span class="p">;</span>
<span class="k">if</span><span class="p">(</span> <span class="nv">$n</span> <span class="o"><</span> <span class="nv">$explode_count</span> <span class="p">)</span>
<span class="nv">$pattern</span> <span class="o">.=</span> <span class="s2">"(.*?)\b"</span><span class="p">;</span>
<span class="o">++</span><span class="nv">$n</span><span class="p">;</span>
<span class="p">}</span>
<span class="nv">$pattern</span> <span class="o">.=</span> <span class="s2">"^i"</span><span class="p">;</span> <span class="c1">//case insensitive</span>
<span class="nv">$output</span> <span class="o">=</span> <span class="k">array</span><span class="p">();</span>
<span class="nv">$overall_relevance</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="nb">preg_match_all</span><span class="p">(</span> <span class="nv">$pattern</span><span class="p">,</span> <span class="nv">$haystack</span><span class="p">,</span> <span class="nv">$occurrences</span><span class="p">,</span> <span class="nx">PREG_PATTERN_ORDER</span> <span class="p">);</span>
<span class="c1">//preg_match_all( $pattern, $haystack, $occurrences_offsets, PREG_OFFSET_CAPTURE ); //this can be useful in computing relevance, maybe early matches should weigh more than later ones</span>
<span class="k">if</span><span class="p">(</span> <span class="nb">count</span><span class="p">(</span> <span class="nv">$occurrences</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="p">)</span> <span class="o">==</span> <span class="mi">0</span> <span class="p">)</span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="c1">//If the string to match is just one word, its relevance is its weigth * times it occurres</span>
<span class="k">if</span><span class="p">(</span> <span class="nv">$explode_count</span> <span class="o">==</span> <span class="mi">1</span> <span class="p">)</span> <span class="p">{</span>
<span class="nv">$overall_relevance</span> <span class="o">=</span> <span class="nb">count</span><span class="p">(</span> <span class="nv">$occurrences</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="p">)</span><span class="o">*</span><span class="nv">$occurrence_weigth</span><span class="p">;</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="nv">$n</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">foreach</span><span class="p">(</span> <span class="nv">$occurrences</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="k">as</span> <span class="nv">$single</span> <span class="p">)</span> <span class="p">{</span>
<span class="c1">//If pattern words are separated by a full stop, this shouldn't count as a match</span>
<span class="k">if</span><span class="p">(</span> <span class="nb">strpos</span><span class="p">(</span> <span class="nv">$single</span><span class="p">,</span> <span class="s1">'.'</span> <span class="p">)</span> <span class="o">!==</span> <span class="k">false</span> <span class="p">)</span> <span class="k">continue</span><span class="p">;</span>
<span class="c1">//Calculate how much this occurrence contributes to overall relevance</span>
<span class="nv">$single</span> <span class="o">=</span> <span class="nb">trim</span><span class="p">(</span> <span class="nv">$single</span> <span class="p">);</span>
<span class="nv">$relevance</span> <span class="o">=</span> <span class="nv">$occurrence_weigth</span> <span class="o">-</span> <span class="p">(</span> <span class="nb">pow</span><span class="p">(</span> <span class="nb">strlen</span><span class="p">(</span> <span class="nv">$single</span> <span class="p">),</span> <span class="mi">4</span><span class="o">/</span><span class="mi">5</span> <span class="p">)</span> <span class="p">);</span>
<span class="nv">$output</span><span class="p">[]</span> <span class="o">=</span> <span class="k">array</span><span class="p">(</span>
<span class="s2">"text_in_between"</span> <span class="o">=></span> <span class="nv">$single</span><span class="p">,</span>
<span class="s2">"relevance"</span> <span class="o">=></span> <span class="nv">$relevance</span><span class="cm">/*,</span>
<span class="cm"> "offset" => $occurrences_offsets[0][$n][1]*/</span>
<span class="p">);</span>
<span class="nv">$overall_relevance</span> <span class="o">+=</span> <span class="nv">$relevance</span><span class="p">;</span>
<span class="o">++</span><span class="nv">$n</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">if</span><span class="p">(</span> <span class="nv">$overall_relevance</span> <span class="o">></span> <span class="mi">100</span> <span class="p">)</span>
<span class="nv">$overall_relevance</span> <span class="o">=</span> <span class="mi">100</span><span class="p">;</span>
<span class="k">return</span> <span class="nv">$output</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>
<script type="text/javascript">if (!document.getElementById('mathjaxscript_pelican_#%@#$@#')) {
var align = "center",
indent = "0em",
linebreak = "false";
if (false) {
align = (screen.width < 768) ? "left" : align;
indent = (screen.width < 768) ? "0em" : indent;
linebreak = (screen.width < 768) ? 'true' : linebreak;
}
var mathjaxscript = document.createElement('script');
mathjaxscript.id = 'mathjaxscript_pelican_#%@#$@#';
mathjaxscript.type = 'text/javascript';
mathjaxscript.src = 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.3/latest.js?config=TeX-AMS-MML_HTMLorMML';
var configscript = document.createElement('script');
configscript.type = 'text/x-mathjax-config';
configscript[(window.opera ? "innerHTML" : "text")] =
"MathJax.Hub.Config({" +
" config: ['MMLorHTML.js']," +
" TeX: { extensions: ['AMSmath.js','AMSsymbols.js','noErrors.js','noUndefined.js'], equationNumbers: { autoNumber: 'none' } }," +
" jax: ['input/TeX','input/MathML','output/HTML-CSS']," +
" extensions: ['tex2jax.js','mml2jax.js','MathMenu.js','MathZoom.js']," +
" displayAlign: '"+ align +"'," +
" displayIndent: '"+ indent +"'," +
" showMathMenu: true," +
" messageStyle: 'normal'," +
" tex2jax: { " +
" inlineMath: [ ['\\\\(','\\\\)'] ], " +
" displayMath: [ ['$$','$$'] ]," +
" processEscapes: true," +
" preview: 'TeX'," +
" }, " +
" 'HTML-CSS': { " +
" availableFonts: ['STIX', 'TeX']," +
" preferredFont: 'STIX'," +
" styles: { '.MathJax_Display, .MathJax .mo, .MathJax .mi, .MathJax .mn': {color: 'inherit ! important'} }," +
" linebreaks: { automatic: "+ linebreak +", width: '90% container' }," +
" }, " +
"}); " +
"if ('default' !== 'default') {" +
"MathJax.Hub.Register.StartupHook('HTML-CSS Jax Ready',function () {" +
"var VARIANT = MathJax.OutputJax['HTML-CSS'].FONTDATA.VARIANT;" +
"VARIANT['normal'].fonts.unshift('MathJax_default');" +
"VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
"VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
"VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
"});" +
"MathJax.Hub.Register.StartupHook('SVG Jax Ready',function () {" +
"var VARIANT = MathJax.OutputJax.SVG.FONTDATA.VARIANT;" +
"VARIANT['normal'].fonts.unshift('MathJax_default');" +
"VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
"VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
"VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
"});" +
"}";
(document.body || document.getElementsByTagName('head')[0]).appendChild(configscript);
(document.body || document.getElementsByTagName('head')[0]).appendChild(mathjaxscript);
}
</script>New System76 Lemur 2016 review (6th gen Intel core)2016-01-11T12:05:00+01:002016-01-11T12:05:00+01:00Stefano Ottolenghitag:thecrowned.org,2016-01-11:/new-system76-lemur-6th-gen-intel-core-review<p>On December 2016 I purchased the <strong><a href="https://system76.com/laptops/lemur">System76 6th gen Lemur</a></strong>. These are more of impressions than a real review, but may be helpful if you're interested in buying a new laptop and were considering the …</p><p>On December 2016 I purchased the <strong><a href="https://system76.com/laptops/lemur">System76 6th gen Lemur</a></strong>. These are more of impressions than a real review, but may be helpful if you're interested in buying a new laptop and were considering the new System76 Lemur. This is the review of the laptop released in December 2015. I purchased the version with <strong>Intel Core 6th gen i3 6100U, 8GB DDR3 and 256 GB SSD</strong>.</p>
<blockquote>
<p><strong>NOTE to European people</strong>: I've just discovered the British <a href="http://pcspecialist.co.uk">PC Specialist</a> does a similar service with very little shipping costs to Europe. My girlfriend purchased basically the very same laptop I have, but costed $350 less in shipping!</p>
</blockquote>
<h2 id="things-i-dont-like-about-system76-lemur">THINGS I DON'T LIKE ABOUT SYSTEM76 LEMUR</h2>
<blockquote>
<p>August 2016 update: <strong>my screen turned to a shade of pink</strong>. All of a sudden, while working, colors flickered and it's just as if a pink filter is in front of it now. Some times it flickers back to white, but most of the time it's pinkish. Of course System76 offered to fix the issue for free, but it would have costed me at least $400 in shipping, so clearly this was not an option.</p>
<p>January 2017 update: <strong>my Lemur died</strong>. In previous weeks, sometimes it would happen that the screen would go black and that the fan would start spinning as hell, at which point the only thing I could do is forcefully shut it down. However, the behavior started to show up more often, until it was the only it would do. After some troubleshooting with System76, it turned out that it was probably a motherboard issue. The PC was a couple of months out of warranty, and the quote was of around $500 to ship, repair and ship back. I ditched System76 and bought a new, almost equal laptop from PC Specialist for roughly the same amount (€500). It's working fine so far. All that follows still applies, as the PC Specialist laptop is basically the same product as the Lemur, just built and shipped from the UK. To this day, I would rather buy a Dell XPS Developer Edition or a Lenovo Thinkpad X1 Carbon: higher price tag, but definitely sturdier and more reliable.</p>
</blockquote>
<p><img src="http://www.thecrowned.org/wp-content/uploads/2016/01/Lemur_feature4-e1451740982457-300x175.png" style="float:right" /></p>
<ul>
<li>The screen has a <strong>poor opening angle</strong> (I'd say around 120 degrees). This is not a problem if you put it on a desk with a proper chair, but as soon as you put it on your lap or if you are higher than your table, you'd immediately like the screen to open some more.</li>
<li>The SD card reader <strong>doesn't work with Sandisk Extreme</strong> 90MB/s or even 60 MB/s. System76 said these are extremely new and high speed cards, but this is not a justification to me as my 10-years-old card reader handles them just fine.</li>
<li><strong>Build quality is cheap</strong>. It's all bendy plastic. I've got light scratches everywhere, and some deep ones. Just laying copybooks on my laptop shouldn't make deep scratches.</li>
<li><strong>Screen brightness is too high</strong>, even at its minima. In normally (and even poor) lit environments this is fine, but when you are in the dark you have the feeling a lamp is being pointed at you.</li>
<li><strong>Volume is still a bit low</strong>, even at its maxima. This may be due to the fact that speakers are located under the laptop.</li>
</ul>
<h2 id="reasons-why-id-purchase-the-system76-lemur-again">REASONS WHY I'D PURCHASE THE SYSTEM76 LEMUR AGAIN</h2>
<blockquote>
<p>I still stand behind everything I had written below, but given the screen fault and other shortcomings, and that PC Specialist is offering such a discounted service (at least to me, in Europe), I'd buy from them rather than from System76.</p>
</blockquote>
<p>It's great. It just works. Beautifully. It's reliable, and quiet. And when I say quiet, I really mean that I've heard the fan start going a couple of times in 6 months.</p>
<p>I've been asked several times why I didn't purchase a <em>normal</em> laptop from a <em>normal</em> company. There are two reasons:</p>
<ol>
<li><strong>Normal companies don't make laptop for normal people</strong>.</li>
<li><strong>Normal laptop</strong> from normal companies <strong>are not built to last</strong>.</li>
</ol>
<p>It's quite clear to me that the laptops you find in a shop are built so that 4 years later you will need to buy a new one. Just look at the average configuration of a decent laptop (which would be around $500): Intel i5 CPU, 4 GB RAM, 1 TB HDD, some dedicated graphics card. Now, how much of that my mother needs? Little nothing, and it's lacking what she'd really need.</p>
<ol>
<li>Graphics card are completely useless to the average user. Highest graphical need is the Windows GUI, why waste $80+ on something needless?</li>
<li>Intel i5 CPU - right, okay, it's gonna be fast, but how much does it need to be fast a laptop to surf the internet and watch a film?</li>
<li>1 TB HDD? My whole film collection takes less than that. Like, really. Put in a SSD of 256 GB and stop this fuss of having bigger drives.</li>
<li>4 GB RAM is fine. But it's <em>just</em> fine. Ubuntu with Chrome (5 tabs) open takes 1,7 GB. My mother has Windows, an antivirus, dropbox, 15 tabs open, and generally forgets to close anything she opens. Now suppose she has 3 GB busy. Just let the next Windows come out and it won't fit on her machine, and voilà: you need a new laptop.</li>
<li>If you look for a higher-end laptop, they'll put in an Intel i7. What on earth is my mother supposed to do with a 4-core 3 GHz? It would just drain the battery in an hour! Oh yes, I forgot to say, My <strong>new Lemur's battery lasts five damn hours</strong>!</li>
</ol>
<p>The wonderful thing with <strong>System76</strong> is that it <strong>gives you the choice</strong> of the PC you want to have: you start with a basic setup and you can tweak it to fit your needs. They're <strong>providing a service to users</strong>, not just requesting them money. Plus, it <strong>costs around the same</strong> amount as a normal computer from a normal company, so why not? (I spent a hell lot of money to get it to my house, but I'm not from the USA) Not to mention the <strong>quality support and availability of the System76 team</strong>, something which you wouldn't get from a normal company, not even in a million years.</p>
<p>System76 does good work for little the right money and probably reduces technology waste over time. I think it's <strong>our responsability as tech guys to feed this kind of companies</strong> instead of careless giants.</p>
<p>Are you considering buying a System76 Lemur? Or, if you own one, what has been your experience like?</p>IPSC 2014: Imagination is more important than knowledge2014-05-18T15:59:00+02:002014-05-18T15:59:00+02:00Stefano Ottolenghitag:thecrowned.org,2014-05-18:/ipsc-2014-imagination-important-knowledge<p><em>From 12th to 17th May I took part to the <a href="https://www.esu.org/our-work/international-public-speaking-competition">International Public Speaking Competition (IPSC) 2014</a> in London, representing my country, Italy. The topic was </em><em>Imagination is more important than knowledge</em><em>, and the title of …</em></p><p><em>From 12th to 17th May I took part to the <a href="https://www.esu.org/our-work/international-public-speaking-competition">International Public Speaking Competition (IPSC) 2014</a> in London, representing my country, Italy. The topic was </em><em>Imagination is more important than knowledge</em><em>, and the title of my speech, which follows, was </em><em>The largest dancing floor</em><em>. The video of the italian version is <a href="https://www.youtube.com/watch?v=mNKlkwdChh0" title="La più grande pista da ballo - IPSC">available on YouTube</a> or at the bottom here.</em></p>
<h1 id="the-largest-dancing-floor-italian-candidate-speech">THE LARGEST DANCING FLOOR (Italian candidate speech)</h1>
<p>What I propose today is a journey. A journey with our imagination, through physics, to disclose a picture of the world far more appreciable than the one we usually have.</p>
<p>Have you ever thought about how fake the reality we see is? Looking around us, we see all kinds of different things. And yet, it looks to me that we give for granted the most basic assumption, which is that these things exist as we see them. I mean, we see a penguin and we think that yeah, that's the classic example of the capital-P Penguin, of the "<em>penguiness</em>". But where is this "penguiness" to be seen? Can we think of it? Can we say it is real?</p>
<p><img src="http://www.thecrowned.org/wp-content/uploads/2014/05/10330341_10152163570733576_8255782982304475793_n.jpg" style="float:right" /></p>
<p>Wondering at what is real and what is not means digging really deep into philosophy, which would probably lead us nowhere, but if we stick to physics… what do we get? We do get something interesting indeed. Physics tells us that all things, ranging from stars to snails, are all made of the same stuff, which is atoms, little balls that we, as limited beings, are not allowed to see. These balls arrange one beside the other for tremendous lengths and give rise to our beloved penguins. Now, even though different atoms exist, they come in a finite number. What makes up all the infinity of beautiful things around us is the way atoms put together, the way they have metaphorical sex. Atoms are really nothing more than the alphabet of nature: with a set of finite items, countless combinations are possible. So the first lesson of our journey is that, sadly, penguins do not exist as penguins, but should be imagined as groups of atoms.</p>
<p>Perhaps, a more surprising fact is that atoms are mostly empty. What we call the nucleus in the atom, takes way less than 1% of it, all that is left is empty space, the space where electrons live. This means that what appears to us as rock solid, as completely full in some way, is actually almost completely empty. And this rises a number of super-intriguing questions among which: what does it mean to "<em>touch</em>"?</p>
<p>What touch is, has a very interesting explanation. We are used to seeing things touching each other, (which happens when the distance between two objects becomes null). But can something like this really happen? Actually, it can't. It is not possible for two atoms to come in contact or be squeezed together. What happens is that electrons get very jealous of their homes around the nucleus: they have lived there all their lives and are not willing to give them up to foreigners. So when another atom comes near, electrons start fighting for their own space and prevent it from coming close. In this sense, electrons are very bad hosts. You can picture the clapping of your hands as a struggle between electrons, in which each of them fiercely defends its own space, and the more you push your hands together, the harder the combat gets.</p>
<p>These electron-fights are just one example of the bustle of nature, of the activity that goes on under the hood even though we cannot see it directly. What we can do, is imagine it. Think of a glass of water. It may appear like the most uninteresting, still and dull thing, so quiet it may not even be worth considering. But atoms are never still, they can never be. They are the guests of a never-ending worldwide party in which music never stops and they never get tired of dancing. They are always giggling and buzzing, and sometimes their dancing becomes so frenetic that they jump away from the dance floor and start floating in the air. This is what we call evaporating, and now <em>this</em> is a dull name for an exciting phenomenon.</p>
<p>Now, what's the point? First, these things are fascinating to me. Every time I am feeling down or bored, I always wear my special imaginary glasses to look at reality the way I have described it, to remind myself how interesting and amazing our world is. But the point is that you can know all these facts, and yet look at the world as the darkest and dullest place ever. Most people who know these things do not look at the world as the largest dancing floor, in which a huge number of excited dancers move around making up the objects that we see, and that is because they are not using their imagination. Knowledge is helpful, indeed it is needed, for imagination to work. But facts alone are nothing, we need imagination to understand their meaning. The key to a happier, more interesting and fascinating world is imagination.</p>Tutorial: Use PayPal Adaptive Payments API (with Embedded Lightbox)2013-02-20T14:42:00+01:002013-02-20T14:42:00+01:00admintag:thecrowned.org,2013-02-20:/tutorial-paypal-adaptive-payments-api-embedded<p>I am working on integrating my <a href="https://postpaycounter.com" title="Post Pay Counter – The final WordPress payment manager">Post Pay Counter</a> Wordpress plugin with PayPal, so that site administrators can pay their writers directly from their blog pages, without having to head to the PayPal website.</p>
<p>PayPal …</p><p>I am working on integrating my <a href="https://postpaycounter.com" title="Post Pay Counter – The final WordPress payment manager">Post Pay Counter</a> Wordpress plugin with PayPal, so that site administrators can pay their writers directly from their blog pages, without having to head to the PayPal website.</p>
<p>PayPal APIs have the usual shitty documentation we have grown accustomed to in the tech world, so I am sharing what I have come up with until now (which works pretty well, actually), scheduling a second part of the tutorial for when the job will be completed.</p>
<h2 id="paypal-adaptive-payments-api-what-it-is-for">PayPal Adaptive Payments API: what it is for</h2>
<blockquote>
<p>Adaptive payments handles payments between a <em>sender</em> of a payment and one or more <em>receivers</em> of the payment. You are an <em>application owner</em>, such as a merchant that owns a website, the owner of a widget on a social networking site, the provider of a payment application on mobile phones, and so on. Your application is the caller of Adaptive Payments API operations.</p>
</blockquote>
<p>Standing to what I have been able to discover about PayPal's different payment mechanisms over summer, Adaptive Payments represents the <strong>most flexible way</strong> to transfer funds from one account to another. Also, it seems the only method you can effectively integrate PayPal in your application. Adaptive Payments is in fact for those applications in which your account, as application developer, is not the one you are drawing funds from. <strong>Shortly, you need to move money on behalf of someone, and your application is the intermediary</strong>.</p>
<p><a href="http://www.thecrowned.org/wp-content/uploads/2012/10/AdapPymntRolesApp_B.gif" title="AdapPymntRolesApp_B">AdapPymntRolesApp_B</a>]</p>
<p>In my specific case, I needed a way to let administrators put their credentials into my plugin and have PayPal let me get money from their accounts and transfer it to their writers' ones. Adaptive Payments method was really suitable because <strong>it allows six transactions per each request</strong>, so that it is possible to send different amounts to several people with only one API request. As bottom line (which I did not need and did not care to dive into), it also allows <em>Chained Payments</em>, in which the primary receiver passes part of the payment to other receivers, splitting the original amount.</p>
<p>We are almost beginning. Just let me give you the link to the complete, detailed, give-a-lot-for-granted <a href="https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_api_APIntro" title="Adaptive Payments documentation">PayPal official documentation</a>. There is also a brilliant PHP class I have used (and that will be used in this tutorial) which is <a href="http://www.angelleye.com/paypal-payments-pro-php-class/" title="Angell EYE's PayPal Payments Pro PHP Class">Angell EYE's PayPal Payments Pro PHP Class</a>. Apart from Adaptive Payments, you can execute almost any API-related PHP task with that class.</p>
<h2 id="application-workflow">Application Workflow</h2>
<p>This is how the PayPal-related part of the application will work:</p>
<ol>
<li>A form will contain all the data we need to complete the transaction (i.e. amount(s) and receiver(s) address).</li>
<li>The transaction must then be prepared. When the submit button of the form is clicked, Javascript collects all the data and an AJAX request is issued to a PHP background page that takes them in and arranges the PayPal API request. <strong>PayPal will return a Pay Key</strong>, that our PHP page will return as result of the AJAX results. At this point we are still on the first page, nothing seems to have happened.</li>
<li>Having the PayPal Pay Key makes it possible to execute the transaction, finally. Thus, the user is shown the PayPal forms and confirmation stuff that let them complete the payment.</li>
</ol>
<p><img alt="AdapPymntSrvcFlow_A" src="http://www.thecrowned.org/wp-content/uploads/2012/10/AdapPymntSrvcFlow_A.gif" title="AdapPymntSrvcFlow_A">]</p>
<h2 id="getting-started-with-adaptive-payments-the-front-end">Getting started with Adaptive Payments: the front-end</h2>
<p>The first thing we are going to set up is the front end. There are two ways you can have it:</p>
<ol>
<li><strong>In a new window</strong>. This is the simplest method, which you will get to work pretty easily. When the user clicks to execute the payment, a new window/tab is opened where the transaction is completed. User are then momentarily forced to leave your website, to which they will be automatically redirected when the payment is done.</li>
<li><strong>In a lightboxed iframe</strong>. Although this is a bit harder, it will make the user experience a lot better. Users never leave your website: the transaction happens on top of your page, while the content gets dark. This is what this tutorial walks you through.</li>
</ol>
<p>First of all, we need two forms, either having its own submit button:</p>
<div class="highlight"><pre><span></span><code><span class="p"><</span><span class="nt">form</span> <span class="na">id</span><span class="o">=</span><span class="s">"prepare_payment_form"</span> <span class="na">action</span><span class="o">=</span><span class="s">"#"</span> <span class="na">method</span><span class="o">=</span><span class="s">"post"</span><span class="p">></span>
<span class="p"><</span><span class="nt">input</span> <span class="na">id</span><span class="o">=</span><span class="s">"prepare_payment"</span> <span class="na">name</span><span class="o">=</span><span class="s">"prepare_payment"</span> <span class="na">type</span><span class="o">=</span><span class="s">"submit"</span> <span class="na">value</span><span class="o">=</span><span class="s">"Prepare payment with PayPal"</span> <span class="p">/></span>
<span class="p"></</span><span class="nt">form</span><span class="p">></span>
<span class="p"><</span><span class="nt">form</span> <span class="na">id</span><span class="o">=</span><span class="s">"execute_payment_form"</span> <span class="na">action</span><span class="o">=</span><span class="s">"#"</span> <span class="na">method</span><span class="o">=</span><span class="s">"post"</span> <span class="na">target</span><span class="o">=</span><span class="s">"PPDGFrame"</span><span class="p">></span>
<span class="p"><</span><span class="nt">input</span> <span class="na">id</span><span class="o">=</span><span class="s">"execute_payment"</span> <span class="na">disabled</span><span class="o">=</span><span class="s">"disabled"</span> <span class="na">name</span><span class="o">=</span><span class="s">"execute_payment"</span> <span class="na">type</span><span class="o">=</span><span class="s">"submit"</span> <span class="na">value</span><span class="o">=</span><span class="s">"Execute payment with PayPal"</span> <span class="p">/></span>
<span class="p"></</span><span class="nt">form</span><span class="p">></span>
</code></pre></div>
<p>Note the <code>target="PPDGFrame"</code> for the second form. The <code>execute_payment</code> submit button is disabled because we do not want users to execute the payment before they have actually prepared it. I have the preparation handled by AJAX, so that no reload is needed. I guess that if you are smart and masochist enough to be willing to deal with PayPal APIs and read this tutorial, you will also know how to Javascript-enable the <code>execute_payment</code> button and the like stuff (I am lazy enough not to provide you with that code, sorry).</p>
<p>You may also want to add somewhere a space for errors to be displayed, that will be invisible until some error comes up.</p>
<p>The first form, apart from its submit button, will also contain the data needed to fulfill the PayPal request. I am really keeping this simple, putting two email addresses and two amounts directly as form data, but you can fetch your data the way you need (e.g. store in the form only the user ID and then pulling from some database the other data when the request will be prepared by PHP).</p>
<div class="highlight"><pre><span></span><code><span class="p"><</span><span class="nt">form</span> <span class="na">id</span><span class="o">=</span><span class="s">"prepare_payment_form"</span> <span class="na">action</span><span class="o">=</span><span class="s">"#"</span> <span class="na">method</span><span class="o">=</span><span class="s">"post"</span><span class="p">></span>
<span class="p"><</span><span class="nt">input</span> <span class="na">name</span><span class="o">=</span><span class="s">"payment_1"</span> <span class="na">type</span><span class="o">=</span><span class="s">"text"</span> <span class="na">value</span><span class="o">=</span><span class="s">"50"</span> <span class="p">/></span>
<span class="p"><</span><span class="nt">input</span> <span class="na">name</span><span class="o">=</span><span class="s">"user_address_1"</span> <span class="na">type</span><span class="o">=</span><span class="s">"text"</span> <span class="na">value</span><span class="o">=</span><span class="s">"my_first_address@domain.com"</span> <span class="p">/></span>
<span class="p"><</span><span class="nt">input</span> <span class="na">name</span><span class="o">=</span><span class="s">"payment_2"</span> <span class="na">type</span><span class="o">=</span><span class="s">"text"</span> <span class="na">value</span><span class="o">=</span><span class="s">"25"</span> <span class="p">/></span>
<span class="p"><</span><span class="nt">input</span> <span class="na">name</span><span class="o">=</span><span class="s">"user_address_2"</span> <span class="na">type</span><span class="o">=</span><span class="s">"text"</span> <span class="na">value</span><span class="o">=</span><span class="s">"my_second_address@domain.com"</span> <span class="p">/></span>
<span class="p"><</span><span class="nt">input</span> <span class="na">id</span><span class="o">=</span><span class="s">"prepare_payment"</span> <span class="na">name</span><span class="o">=</span><span class="s">"prepare_payment"</span> <span class="na">type</span><span class="o">=</span><span class="s">"submit"</span> <span class="na">value</span><span class="o">=</span><span class="s">"Prepare payment with PayPal"</span> <span class="p">/></span>
<span class="p"></</span><span class="nt">form</span><span class="p">></span>
</code></pre></div>
<p>Now, when the form is submitted, we will need to grab those data and send them to a secondary PHP page that will handle the whole thing.</p>
<div class="highlight"><pre><span></span><code><span class="nx">jQuery</span><span class="p">(</span><span class="nb">document</span><span class="p">).</span><span class="nx">ready</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">$</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="nx">javascript</span><span class="w"></span>
<span class="w"> </span><span class="nx">$</span><span class="p">(</span><span class="s2">"#prepare_payment"</span><span class="p">).</span><span class="nx">unbind</span><span class="p">(</span><span class="s1">'click'</span><span class="p">).</span><span class="nx">click</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">e</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="nx">e</span><span class="p">.</span><span class="nx">preventDefault</span><span class="p">();</span><span class="w"></span>
</code></pre></div>
<p>So, from Javascript to English, when the <em>prepare_payment</em> button is clicked (row 3), the default action is prevented, so that the form is not sent and we allow AJAX to take over (row 4). As for the unbinding and subsequent binding, I have no clue why this happens, but it prevents the event from firing multiple times.</p>
<div class="highlight"><pre><span></span><code><span class="kd">var</span><span class="w"> </span><span class="nx">data</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="nx">action</span><span class="o">:</span><span class="w"> </span><span class="s1">'the_php_page_that_will_handle_the_request.php'</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nx">payment_data</span><span class="o">:</span><span class="w"> </span><span class="nx">$</span><span class="p">(</span><span class="s1">'#prepare_payment_form'</span><span class="p">).</span><span class="nx">serialize</span><span class="p">()</span><span class="w"></span>
<span class="p">};</span><span class="w"></span>
</code></pre></div>
<p>Quite easy: preparing the AJAX request data, which is actually simply a serialization of the form data. If you need more stuff, you can add more indexes to the array. You may also want to add a confirmation step, which is what I did:</p>
<div class="highlight"><pre><span></span><code><span class="kd">var</span><span class="w"> </span><span class="nx">agree</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">confirm</span><span class="p">(</span><span class="s1">'You are just about to ask the Post Pay Counter to prepare a PayPal payment on your behalf.'</span><span class="p">);</span><span class="w"></span>
<span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="o">!</span><span class="nx">agree</span><span class="p">)</span><span class="w"></span>
<span class="k">return</span><span class="w"> </span><span class="kc">false</span><span class="p">;</span><span class="w"></span>
</code></pre></div>
<p>From the moment the payment is being prepared on the related button can not be clicked anymore. Moreover, to prevent changes to the form, all the text inputs are disabled:</p>
<div class="highlight"><pre><span></span><code><span class="nx">$</span><span class="p">(</span><span class="s1">'#prepare_payment'</span><span class="p">).</span><span class="nx">attr</span><span class="p">(</span><span class="s2">"disabled"</span><span class="p">,</span><span class="w"> </span><span class="s2">"true"</span><span class="p">);</span><span class="w"></span>
<span class="nx">jQuery</span><span class="p">(</span><span class="s2">"#prepare_payment_form :text"</span><span class="p">).</span><span class="nx">each</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="nx">jQuery</span><span class="p">(</span><span class="k">this</span><span class="p">).</span><span class="nx">attr</span><span class="p">(</span><span class="s2">"disabled"</span><span class="p">,</span><span class="w"> </span><span class="s2">"true"</span><span class="p">);</span><span class="w"></span>
<span class="p">});</span><span class="w"></span>
</code></pre></div>
<p>Then we finally fire the AJAX request to get the Pay Key:</p>
<div class="highlight"><pre><span></span><code><span class="nx">$</span><span class="p">.</span><span class="nx">post</span><span class="p">(</span><span class="nx">ajaxurl</span><span class="p">,</span><span class="w"> </span><span class="nx">data</span><span class="p">,</span><span class="w"> </span><span class="kd">function</span><span class="p">(</span><span class="nx">response</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="k">if</span><span class="p">(</span><span class="nx">response</span><span class="p">.</span><span class="nx">indexOf</span><span class="p">(</span><span class="s2">"Error: "</span><span class="p">)</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="o">-</span><span class="mf">1</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="nx">$</span><span class="p">(</span><span class="s2">"#paypal_error"</span><span class="p">).</span><span class="nx">css</span><span class="p">(</span><span class="s2">"display"</span><span class="p">,</span><span class="w"> </span><span class="s2">"block"</span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="nx">$</span><span class="p">(</span><span class="s2">"#paypal_error"</span><span class="p">).</span><span class="nx">html</span><span class="p">(</span><span class="nx">response</span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="nx">$</span><span class="p">(</span><span class="s1">'#prepare_payment'</span><span class="p">).</span><span class="nx">removeAttr</span><span class="p">(</span><span class="s2">"disabled"</span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="nx">jQuery</span><span class="p">(</span><span class="s2">"#prepare_payment_form :text"</span><span class="p">).</span><span class="nx">each</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="nx">jQuery</span><span class="p">(</span><span class="k">this</span><span class="p">).</span><span class="nx">removeAttr</span><span class="p">(</span><span class="s2">"disabled"</span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="p">});</span><span class="w"></span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="kc">false</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="w"> </span><span class="c1">//Initialize PayPal embedded payment flow. Not loading it on document ready so that we only have it if user prepares payment, not just loads the page…</span><span class="w"></span>
<span class="w"> </span><span class="kd">var</span><span class="w"> </span><span class="nx">dgFlow</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="ow">new</span><span class="w"> </span><span class="nx">PAYPAL</span><span class="p">.</span><span class="nx">apps</span><span class="p">.</span><span class="nx">DGFlow</span><span class="p">({</span><span class="nx">trigger</span><span class="o">:</span><span class="w"> </span><span class="s1">'execute_payment'</span><span class="p">});</span><span class="w"></span>
<span class="w"> </span><span class="c1">//Store PayKey in the form action and enable execute payment button</span><span class="w"></span>
<span class="w"> </span><span class="nx">$</span><span class="p">(</span><span class="s2">"#execute_payment_form"</span><span class="p">).</span><span class="nx">attr</span><span class="p">(</span><span class="s2">"action"</span><span class="p">,</span><span class="w"> </span><span class="s2">"https://www.sandbox.paypal.com/webapps/adaptivepayment/flow/pay?expType=light&payKey="</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="nx">response</span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="nx">$</span><span class="p">(</span><span class="s1">'#execute_payment'</span><span class="p">).</span><span class="nx">removeAttr</span><span class="p">(</span><span class="s2">"disabled"</span><span class="p">);</span><span class="w"></span>
<span class="p">});</span><span class="w"></span>
</code></pre></div>
<p>Inside the AJAX request, the first block is for error handling: if something bad happens the form button and inputs are clickable and editable again and the error is shown in the proper div.</p>
<p>If the request is successful, The PayPal flow is initialized, using as trigger parameter the HTML ID of the submit button of the second form, the execute_payment one.
Finally, the action of the execute payment form is updated with the PayPal URL and the Pay Key received as results of the AJAX request, and the submit button is enabled, so that the payment can be executed. (When that will happen, PayPal libraries will take over and do the job).</p>
<p>There is one more thing to add to the document.ready of the page:</p>
<div class="highlight"><pre><span></span><code><span class="nx">jQuery</span><span class="p">(</span><span class="nb">document</span><span class="p">).</span><span class="nx">ready</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">$</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nb">window</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="nx">top</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="nx">top</span><span class="p">.</span><span class="nx">location</span><span class="p">.</span><span class="nx">replace</span><span class="p">(</span><span class="nb">document</span><span class="p">.</span><span class="nx">location</span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="p">}</span><span class="w"></span>
</code></pre></div>
<p>This will ensure that when/if the transaction is over, either because the user canceled it or because it was complete, the iframe gets closed automatically. I have not found a better way to handle this, so if you Javascript guys have some better ways, I do really long to get rid of that crappy redirect.</p>
<h2 id="getting-started-with-paypal-adaptive-payments-the-back-end">Getting started with PayPal Adaptive Payments: the back-end</h2>
<p>That was the front-end. Not let's focus on the PHP page to which the AJAX request is issued.</p>
<div class="highlight"><pre><span></span><code><span class="cp"><?php</span>
<span class="k">include</span><span class="p">(</span> <span class="s1">'angell_eye_class_dir/Pay.php'</span> <span class="p">);</span>
<span class="c1">//Payment data is passed via AJAX serialized. We explode them by & (dividing fields from each other) and then for each of them we explode again, this time by = (separating fields names and fields values). I know it looks awful, but it is not that hard...</span>
<span class="nv">$receivers</span> <span class="o">=</span> <span class="k">array</span><span class="p">();</span>
<span class="nv">$payment_data</span> <span class="o">=</span> <span class="k">array</span><span class="p">();</span>
<span class="nv">$payment_data_temp</span> <span class="o">=</span> <span class="nb">explode</span><span class="p">(</span> <span class="s1">'&'</span><span class="p">,</span> <span class="nv">$_POST</span><span class="p">[</span><span class="s1">'payment_data'</span><span class="p">]</span> <span class="p">);</span>
<span class="k">foreach</span><span class="p">(</span> <span class="nv">$payment_data_temp</span> <span class="k">as</span> <span class="nv">$single</span> <span class="p">)</span> <span class="p">{</span>
<span class="nv">$yet_another_temp</span> <span class="o">=</span> <span class="nb">explode</span><span class="p">(</span> <span class="s1">'='</span><span class="p">,</span> <span class="nv">$single</span> <span class="p">);</span>
<span class="nv">$userid</span> <span class="o">=</span> <span class="nb">substr</span><span class="p">(</span> <span class="nv">$yet_another_temp</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="o">-</span><span class="mi">1</span> <span class="p">);</span>
<span class="nv">$receivers</span><span class="p">[]</span> <span class="o">=</span> <span class="k">array</span><span class="p">(</span>
<span class="s1">'Amount'</span> <span class="o">=></span> <span class="nv">$single</span><span class="p">[</span><span class="s1">'payment'</span><span class="o">.</span><span class="nv">$userid</span><span class="p">],</span> <span class="c1">// Required. Amount to be paid to the receiver.</span>
<span class="s1">'Email'</span> <span class="o">=></span> <span class="nv">$single</span><span class="p">[</span><span class="s1">'user_address'</span><span class="o">.</span><span class="nv">$userid</span><span class="p">],</span> <span class="c1">// Receiver's email address. 127 char max.</span>
<span class="s1">'InvoiceID'</span> <span class="o">=></span> <span class="s1">''</span><span class="p">,</span> <span class="c1">// The invoice number for the payment. 127 char max.</span>
<span class="s1">'PaymentType'</span> <span class="o">=></span> <span class="s1">'PERSONAL'</span><span class="p">,</span> <span class="c1">// Transaction type. Values are: GOODS, SERVICE, PERSONAL, CASHADVANCE, DIGITALGOODS</span>
<span class="s1">'PaymentSubType'</span> <span class="o">=></span> <span class="s1">''</span><span class="p">,</span> <span class="c1">// The transaction subtype for the payment.</span>
<span class="s1">'Phone'</span> <span class="o">=></span> <span class="k">array</span><span class="p">(</span><span class="s1">'CountryCode'</span> <span class="o">=></span> <span class="s1">''</span><span class="p">,</span> <span class="s1">'PhoneNumber'</span> <span class="o">=></span> <span class="s1">''</span><span class="p">,</span> <span class="s1">'Extension'</span> <span class="o">=></span> <span class="s1">''</span><span class="p">),</span> <span class="c1">// Receiver's phone number. Numbers only.</span>
<span class="s1">'Primary'</span> <span class="o">=></span> <span class="s1">''</span> <span class="c1">// Whether this receiver is the primary receiver. Values are boolean: TRUE, FALSE</span>
<span class="p">);</span>
<span class="p">}</span>
<span class="nv">$paypal_result</span> <span class="o">=</span> <span class="nx">execute_payment</span><span class="p">(</span> <span class="k">TRUE</span><span class="p">,</span> <span class="nv">$paypal_API_email</span><span class="p">,</span> <span class="nv">$paypal_API_password</span><span class="p">,</span> <span class="nv">$paypal_API_signature</span><span class="p">,</span> <span class="nv">$paypal_currency_code</span><span class="p">,</span> <span class="s1">'SENDER'</span><span class="p">,</span> <span class="nv">$receivers</span><span class="p">,</span> <span class="nv">$_POST</span><span class="p">[</span><span class="s1">'current_page'</span><span class="p">]</span> <span class="p">);</span>
<span class="k">if</span><span class="p">(</span> <span class="nv">$paypal_result</span><span class="p">[</span><span class="s1">'Ack'</span><span class="p">]</span> <span class="o">==</span> <span class="s1">'Failure'</span> <span class="p">)</span>
<span class="k">die</span><span class="p">(</span> <span class="s1">'Error: '</span><span class="o">.</span><span class="nv">$paypal_result</span><span class="p">[</span><span class="s1">'Errors'</span><span class="p">][</span><span class="mi">0</span><span class="p">][</span><span class="s1">'Message'</span><span class="p">]</span> <span class="p">);</span>
<span class="k">die</span><span class="p">(</span><span class="nv">$paypal_result</span><span class="p">[</span><span class="s1">'PayKey'</span><span class="p">]);</span>
</code></pre></div>
<p>An array containing all the payment receivers (up to 6) is created, required fields are only <em>Amount</em>, <em>Email</em> and <em>PaymentType</em>. At this point, the only thing missing is the execute_payment function, which has the following parameters:</p>
<ul>
<li>Whether you want to work in the PayPal sandbox for testing (TRUE) or not (FALSE);</li>
<li>The PayPal API email address of the account <strong>you are drawing funds from</strong>. For example, in my plugin it is the PayPal API email of the blog administrator;</li>
<li>The PayPal API password of the account you are drawing funds from;</li>
<li>The PayPal API signature of the account you are drawing funds from;</li>
<li>The code for the currency you want to use for payments, as <em>EUR, USD, GBP</em>...</li>
<li>Who will pay for PayPal fees, <em>SENDER</em> or <em>RECEIVER</em>;</li>
<li>The page you want the user to be redirected when the Adaptive Payment will be done (or canceled).</li>
</ul>
<p>That function consists in a customized version of the Angel EYE's class Pay.php file as follows (I am stripping it of all the inline comments):</p>
<div class="highlight"><pre><span></span><code><span class="cp"><?php</span>
<span class="k">include_once</span><span class="p">(</span><span class="s1">'paypal.class.php'</span><span class="p">);</span>
<span class="k">function</span> <span class="nf">execute_payment</span><span class="p">(</span> <span class="nv">$sandbox</span><span class="p">,</span> <span class="nv">$api_username</span><span class="p">,</span> <span class="nv">$api_password</span><span class="p">,</span> <span class="nv">$api_signature</span><span class="p">,</span> <span class="nv">$currency</span><span class="p">,</span> <span class="nv">$fees_payer</span><span class="p">,</span> <span class="nv">$receivers</span><span class="p">,</span> <span class="nv">$return_page</span> <span class="p">)</span> <span class="p">{</span>
<span class="c1">// Create PayPal object.</span>
<span class="nv">$PayPalConfig</span> <span class="o">=</span> <span class="k">array</span><span class="p">(</span>
<span class="s1">'Sandbox'</span> <span class="o">=></span> <span class="nv">$sandbox</span><span class="p">,</span>
<span class="s1">'DeveloperAccountEmail'</span> <span class="o">=></span> <span class="s1">''</span><span class="p">,</span>
<span class="s1">'ApplicationID'</span> <span class="o">=></span>
<span class="s1">'APP-80W284485P519543T'</span><span class="p">,</span>
<span class="s1">'DeviceID'</span> <span class="o">=></span> <span class="s1">''</span><span class="p">,</span>
<span class="s1">'IPAddress'</span> <span class="o">=></span> <span class="nv">$_SERVER</span><span class="p">[</span><span class="s1">'REMOTE_ADDR'</span><span class="p">],</span>
<span class="s1">'APIUsername'</span> <span class="o">=></span> <span class="nv">$api_username</span><span class="p">,</span>
<span class="s1">'APIPassword'</span> <span class="o">=></span> <span class="nv">$api_password</span><span class="p">,</span>
<span class="s1">'APISignature'</span> <span class="o">=></span> <span class="nv">$api_signature</span><span class="p">,</span>
<span class="s1">'APISubject'</span> <span class="o">=></span> <span class="s1">''</span>
<span class="p">);</span>
<span class="nv">$PayPal</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">PayPal_Adaptive</span><span class="p">(</span><span class="nv">$PayPalConfig</span><span class="p">);</span>
<span class="c1">// Prepare request arrays</span>
<span class="nv">$PayRequestFields</span> <span class="o">=</span> <span class="k">array</span><span class="p">(</span>
<span class="s1">'ActionType'</span> <span class="o">=></span> <span class="s1">'CREATE'</span><span class="p">,</span>
<span class="s1">'CancelURL'</span> <span class="o">=></span> <span class="nv">$return_page</span><span class="p">,</span>
<span class="s1">'CurrencyCode'</span> <span class="o">=></span> <span class="nv">$currency</span><span class="p">,</span>
<span class="s1">'FeesPayer'</span> <span class="o">=></span> <span class="nv">$fees_payer</span><span class="p">,</span>
<span class="s1">'IPNNotificationURL'</span> <span class="o">=></span> <span class="s1">''</span><span class="p">,</span>
<span class="s1">'Memo'</span> <span class="o">=></span> <span class="s1">''</span><span class="p">,</span>
<span class="s1">'Pin'</span> <span class="o">=></span> <span class="s1">''</span><span class="p">,</span>
<span class="s1">'PreapprovalKey'</span> <span class="o">=></span> <span class="s1">''</span><span class="p">,</span>
<span class="s1">'ReturnURL'</span> <span class="o">=></span> <span class="nv">$current_page</span><span class="p">,</span>
<span class="s1">'ReverseAllParallelPaymentsOnError'</span> <span class="o">=></span> <span class="s1">''</span><span class="p">,</span>
<span class="s1">'SenderEmail'</span> <span class="o">=></span> <span class="s1">''</span><span class="p">,</span>
<span class="s1">'TrackingID'</span> <span class="o">=></span> <span class="s1">''</span>
<span class="p">);</span>
<span class="nv">$ClientDetailsFields</span> <span class="o">=</span> <span class="k">array</span><span class="p">(</span>
<span class="s1">'CustomerID'</span> <span class="o">=></span> <span class="s1">''</span><span class="p">,</span>
<span class="s1">'CustomerType'</span> <span class="o">=></span> <span class="s1">''</span><span class="p">,</span>
<span class="s1">'GeoLocation'</span> <span class="o">=></span> <span class="s1">''</span><span class="p">,</span>
<span class="s1">'Model'</span> <span class="o">=></span> <span class="s1">''</span><span class="p">,</span>
<span class="s1">'PartnerName'</span> <span class="o">=></span> <span class="s1">'Always Give Back'</span>
<span class="p">);</span>
<span class="nv">$FundingTypes</span> <span class="o">=</span> <span class="k">array</span><span class="p">(</span><span class="s1">'ECHECK'</span><span class="p">,</span> <span class="s1">'BALANCE'</span><span class="p">,</span> <span class="s1">'CREDITCARD'</span><span class="p">);</span>
<span class="nv">$SenderIdentifierFields</span> <span class="o">=</span> <span class="k">array</span><span class="p">(</span> <span class="s1">'UseCredentials'</span> <span class="o">=></span> <span class="s1">''</span> <span class="p">);</span>
<span class="nv">$AccountIdentifierFields</span> <span class="o">=</span> <span class="k">array</span><span class="p">(</span>
<span class="s1">'Email'</span> <span class="o">=></span> <span class="s1">''</span><span class="p">,</span>
<span class="s1">'Phone'</span> <span class="o">=></span> <span class="k">array</span><span class="p">(</span><span class="s1">'CountryCode'</span> <span class="o">=></span> <span class="s1">''</span><span class="p">,</span> <span class="s1">'PhoneNumber'</span> <span class="o">=></span> <span class="s1">''</span><span class="p">,</span> <span class="s1">'Extension'</span> <span class="o">=></span> <span class="s1">''</span><span class="p">)</span>
<span class="p">);</span>
<span class="nv">$PayPalRequestData</span> <span class="o">=</span> <span class="k">array</span><span class="p">(</span> <span class="s1">'PayRequestFields'</span> <span class="o">=></span> <span class="nv">$PayRequestFields</span><span class="p">,</span> <span class="s1">'ClientDetailsFields'</span> <span class="o">=></span> <span class="nv">$ClientDetailsFields</span><span class="p">,</span> <span class="s1">'FundingTypes'</span> <span class="o">=></span> <span class="nv">$FundingTypes</span><span class="p">,</span> <span class="s1">'Receivers'</span> <span class="o">=></span> <span class="nv">$receivers</span><span class="p">,</span> <span class="s1">'SenderIdentifierFields'</span> <span class="o">=></span> <span class="nv">$SenderIdentifierFields</span><span class="p">,</span> <span class="s1">'AccountIdentifierFields'</span> <span class="o">=></span> <span class="nv">$AccountIdentifierFields</span> <span class="p">);</span>
<span class="nv">$PayPalResult</span> <span class="o">=</span> <span class="nv">$PayPal</span><span class="o">-></span><span class="na">Pay</span><span class="p">(</span><span class="nv">$PayPalRequestData</span><span class="p">);</span>
<span class="k">return</span> <span class="nv">$PayPalResult</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>
<p>It will probably need some tweaking on your part, but I have hopefully explained how the hell PayPal Adaptive Payments are supposed to work!</p>Posts To-Do List: keep track of writing ideas2012-09-10T17:47:00+02:002012-09-10T17:47:00+02:00admintag:thecrowned.org,2012-09-10:/wordpress-plugin-posts-to-do-list<p>I worked for several blogs and websites. In most of them, we often felt the need to share hints and ideas about future posts, about what each of us stumbled upon while surfing. Unfortunately, there …</p><p>I worked for several blogs and websites. In most of them, we often felt the need to share hints and ideas about future posts, about what each of us stumbled upon while surfing. Unfortunately, there was not a great way to fulfill that need, and we often told each other via email, or emailed the admin who would in turn forward to all the writers. That is the reason why I wanted to build a Wordpress tool that could simplify this sharing process.</p>
<p><strong>That Wordpress tool is a plugin and is called Posts To Do List. </strong></p>
<p><strong>By a convenient box in the posts editing page, everyone will be able to share the posts they think are worth writing</strong>. You have this little box, where you put the URL of the page where you read that great post, and the plugin will fetch the title by itself. You will then be able to change the retrieved title, suggest a keyword and add other notes, set a priority and assign the post to some user of the blog. And if you want to leave everything blank but the title field, leaving a suggestion that anyone can catch and deepen... well, you can!</p>
<p>You, as the administrator, want everything in the power of your hands? No problem, you can decide what user roles can add new posts to the to do list and what user roles can delete already added items. You want your users to stick to the post you assigned to them? You can hide the posts you have assigned to other users from their view. From a simple stats page it will be immediately clear how many posts you have already assigned and how many of them are still to do, so that it will be easy to understand how much your writers have done and how many posts you have still to assign. Almost every action is powered by AJAX, so that no page reloads are needed and you do not even notice it is happening, it just works. What do you want more? Cause we even have cookies!</p>
<h1 id="download-posts-to-do-list"><a href="http://wordpress.org/extend/plugins/posts-to-do-list/" title="Posts To Do List">Download Posts To Do List</a></h1>When you start a new project, think big!2012-04-03T08:00:00+02:002012-04-03T08:00:00+02:00admintag:thecrowned.org,2012-04-03:/when-you-start-a-new-project-think-big<p>As you may know, I am the happy developer of a Wordpress plugin called <a href="https://postpaycounter.com">Post Pay Counter</a>. I dare say that that has been my first serious coding project, well thought and well written. It …</p><p>As you may know, I am the happy developer of a Wordpress plugin called <a href="https://postpaycounter.com">Post Pay Counter</a>. I dare say that that has been my first serious coding project, well thought and well written. It taught me a lot, above all the concept that when you start a new project, you should think as if it was going to become a huge one. This is what I'd like to share with you today.</p>
<p>When I started developing the Post Pay Counter project it was two years ago. I took it up because a friend of mine needed it, and asked me if I could set up a plugin that could simplify writers payments. That was how the project started. Looking back at that time, I can clearly identify an error that came along with me and, partly, is still with me today: the idea that was not going to be an important plugin, that I would not have edited it much after, that I would not have added almost anything... that so it was, so it would have remained. What did this practically mean?</p>
<p>Well, first, it meant that I had chosen a way to save options that was not convenient at all. I had very few settings to store and I went for an option in the database, using two different separators to distinguish them. It was quite resource-cheap, but when it came to coding, it was a mess. My files were filled up with nested explode() functions and I was driven mad to remember which array I should use and which index.</p>
<p>Second, as a result, my whole code (although it was only half a thousand lines long overall) was just like a puzzle. Few functions, badly organized and slowly working. I was scared that the puzzle could break for every more piece I added. The problem was that when I designed it, I did not thought of adding stuff later, I just regarded it as done at the first release. So, basically, when I wanted to implement a new feature, I did not know how and where to put it simply because I was not supposed to do it!</p>
<p>Third, I was so eager to finish it, that I did not even stop to think about the name. The very first release of the plugin, in fact, held a different name than the current one: Monthly Post Counter. I am sure I made other mistakes more, but I think these were the major ones.</p>
<p><img src="http://www.thecrowned.org/wp-content/uploads/2011/10/schulz-charles-peanuts-think-big.jpg" style="float:right" /></p>
<p>The consequence of this all, is that after a year, when I had a whole summer in front of me, I decided that, if I were to keep updating the plugin and keep adding new features, I had to rewrite it from scratch. And so I did. But this time with a brand new idea in my head: this is going to become something big, I cannot predict what I am going to do with it nor whether I will keep adding stuff in a year or two, so let's do things well since the beginning.</p>
<p>And so I started, again. I did not touch the computer in the first place, instead I reached for some paper. It was a week of thought and notes, no coding. And then there was the real, concrete start: there were classes, and methods, and performance, and speed, and graphics. I got rid of that horrible wp_option value and built a new independent table to store my data.</p>
<p>I committed just one more mistake: the names I chose for my MySQL fields were stupid and my whole table was not thought to welcome almost sixty fields, as they are in the current release indeed. But I did not need to work too much to make that work as I wanted, since everything else was working fine...</p>
<p>I acknowledge what I have written is more like the diary of a plugin developer, but the message is "this what happens when you do not think big, so do think big since the beginning!".</p>Have I told you we should abolish Facebook?2012-03-22T08:00:00+01:002012-03-22T08:00:00+01:00Stefano Ottolenghitag:thecrowned.org,2012-03-22:/have-i-told-you-we-should-abolish-facebook<p>Yesterday it was my birthday.</p>
<p>My family phoned me or just met me. Then there has been a handful of people who texted me on my mobile, mostly in the morning. I do not care …</p><p>Yesterday it was my birthday.</p>
<p>My family phoned me or just met me. Then there has been a handful of people who texted me on my mobile, mostly in the morning. I do not care how they remembered my birthday's date, what I care about is that they took the time and the money (for those who paid) to text me. I am not ashamed to admit I use myself both Facebook and my mobile phone agenda to keep track of birthdays, apart from certain ones that are quite stuck in my head. But what I liked is that those people, the ones who texted me, tried to think of something nice to write and they actually came up with something sweet, something you would fancy reading after waking up. Those were the people closer to me. Some of them even told me that it was Facebook that remembered them it was my birthday, but they actually sent me an SMS and did not use Facebook to write me.</p>
<p><img src="http://www.thecrowned.org/wp-content/uploads/2012/03/happy-birthday.jpg" style="float:right" /></p>
<p>Next there was the Facebook world. Usually, who is friends with me on Facebook also has my phone number, so they could have easily sent me a message with their greetings, or wrote me an email as some did indeed. A lot of people, instead, wrote on my Facebook wall for my birthday. There were schoolmates of 5 years or so ago, people from my middle school and even guys who are still with me at school and that, although they met me in the morning at school (either in class or through the corridors), wrote on Facebook instead of just telling me "Happy birthday" face to face. Strangely enough, the people who wished me a happy birthday by voice wrote it on Facebook too. There were even people I never met with whom I am only friend in a virtual manner, the people I usually see once a year at the seaside and do not really spend time with, people I knew in afternoon classes years ago with whom I did not actually have any real relationship.</p>
<p>You know, I do not really question their wishes, there is nothing bad in wishes as long as they are true and sincere. There were some people from whom I was glad to receive wishes, people whose messages contained my name and even some sort of joke that could kind of show we were friends, that we shared something. But that was a minority, the vast majority simply wrote "Happy birthday". Nothing more. Something a stranger could have easily said. And that is the point: if we are not much more that strangers I do not expect you to wish me happy birthday, it is something you are not supposed to do and for which I will not blame you.</p>
<p>I have already talked about how <a href="http://www.thecrowned.org/how-facebook-is-killing-real-relationships">Facebook is killing real relationiships</a>, and if you wanted a proof here it is. It is ok if you use Facebook just to keep track of your friends' birthdays, what is not fine is to use it to wish them happy birthday when you could use other, more intimate means like walking to him, texting him or writing some kind of email or private chat.</p>
<p>Do you like being wished happy birthday on Facebook?</p>The fourth way: the digital knowledge2011-12-24T00:57:00+01:002011-12-24T00:57:00+01:00admintag:thecrowned.org,2011-12-24:/the-fourth-way-the-digital-knowledge<p>Amazon.com, great pioneer of the digital book, declared that, starting from April 2011, electronic papers/hardback sales have overtaken the paper ones 105 to 100: small achievements, you will probably grin, but if it …</p><p>Amazon.com, great pioneer of the digital book, declared that, starting from April 2011, electronic papers/hardback sales have overtaken the paper ones 105 to 100: small achievements, you will probably grin, but if it is not this that hints us the future evolution of multimedia, what else?</p>
<p>Nowadays, one's cultural baggage is discernible by the size of the bookcases available in their house - and obviously by the quality of the papers there stored -, but in the future? Sooner or later, like it or not, all this is going to change: all the contents we keep in a physical form, so CDs for music, DVDs for films and books for literary works, tomorrow will not be recorded on medias, but they will be much more ethereal and impalpable indeed.</p>
<p>It is a phenomenon which, after all, has started years ago and keeps evolving during time. Apple's iTunes Store has been, in 2003, the first attempt of music sales in a digital form, featured by a very positive feedback. In recent times, Amazon.com has taken and extended the idea in a significant way, offering not only music purchase under a download form, but also software, films and books. All the bought items were then to build a personal shelf which could be accessed from any location. Google as well, with its Google Music project, is testing its new music purchasing and selling service.</p>
<p>Do not imagine a future in which, instead of novels, our bookcases will be crammed with hard disks of huge amounts where all these digital contents will stay, it would not be of any use to anyone. What is more likely to happen is a turnaround: if today we are used to keeping terabytes and terabytes of data in our machines, in the future Internet connections will be fast enough to avoid contents local storage and shift the center of gravity towards streaming.</p>
<p>In plain terms: the facts that the holders which today keep contents will vanish, will lead the consumer to lose the property of these (contents). Bought a film, a novel or a musical track, in fact, the user will not take possession of the content, they will not be able to download it, but they will have to go and look for it in their digital shelf. We are practically talking of centralization: the vendors own the content and consumers refer to them to enjoy those contents. Not only the physical good is not owned anymore, but even the digital one is lost.</p>
<p>The result is that every device will evolve in mere interfaces to access the contents bought e will therefore be equipped with memories far smaller than the ones we are used today and with connections much faster. The drawback - surely appreciated by content industries - implies a drastic reduction of piracy (share a file which you own is easy, but if you do not have it things get much harder...).</p>
<p>And then, all the ones which were the physical shelves for books and disks vanish to become virtual. We will then have the novel shelf, the scientific texts one, the rock and pop music one, the action movies one... Like in real life, just... virtual, without space limitations and with the ability of search.</p>
<p>The soial backlash will be very violent. Besides Facebook (or whatever will take its place in 10 to 30 years), these digital shelves will become highly powerful means of knowing people: what better way to make up an idea about a person than to find out the books they have read, the music they listen to, the films they have seen..? Not everybody is allowed to enter others' bedrooms and lurk through the shelves, maybe even for an intimacy question, but talking about the net, at least nowadays, the key work is lightness.</p>
<p>Granting access to your own digital library seems far less important than it could seem, though it means to show other people your education and, by reflex, your own personality.</p>
<p>And here it is, the digital knowledge.</p>When YouTube pages load with no CSS: how to solve2011-11-18T18:57:00+01:002011-11-18T18:57:00+01:00Stefano Ottolenghitag:thecrowned.org,2011-11-18:/youtube-pages-load-with-no-css-how-to-solve<p>I have been unable to watch YouTube videos for a couple of months. There were some people on the Google Forums reporting the same problem, also posting screenshots, but no one seemed to be able …</p><p>I have been unable to watch YouTube videos for a couple of months. There were some people on the Google Forums reporting the same problem, also posting screenshots, but no one seemed to be able to fix it.</p>
<p>Finally, checking with despair my Chrome Dev Tools, I found the problem! In my rush to block ads and avoid the slowing down produced by all the requests to external domains, I ended up redirecting <em>s.ytimg.com</em> to my own PC. Sadly, it seems YouTube is taking some CSSes and other stuff from there, reason why I was unable to watch any video and load the home page with a decent view.</p>
<p><img alt="editor" src="http://www.thecrowned.org/wp-content/uploads/2011/11/Immagine.png"></p>
<p>Simply get rid of that row in your hosts file (or wherever you may have blocked it), save, and you are done!</p>How Facebook is killing real relationships2011-10-11T22:39:00+02:002011-10-11T22:39:00+02:00Stefano Ottolenghitag:thecrowned.org,2011-10-11:/how-facebook-is-killing-real-relationships<p>I've always had strong opinions about the Facebook phenomenon and all that virtual social stuff, I have always said they are ruining our life and actually destroying our real social interactions. Today I think that's …</p><p>I've always had strong opinions about the Facebook phenomenon and all that virtual social stuff, I have always said they are ruining our life and actually destroying our real social interactions. Today I think that's true more than ever, and I will tell you why.</p>
<p>My opinion is that Facebook is <strong>something to pop up at 9 in the evening</strong>, when after a 14 hours day you are too tired to do anything else. You open it and just start <strong>lurking around</strong> new friends embarassing photos (like grils' bikini ones), laughing at childish status updates and links and browsing your groups recent activities. At least, that is how I use that damn website, <strong>publishing hironyc and nonsense content without really telling people nothing serious about myself</strong>. Quotes are the farthest thing I do. Instead, <strong>people use to put there all their lives</strong> - their interests, hobbys, wants, likes and dislikes and, most important, friends interactions. We've all seen that "John is engaged with Melissa" with around 15 likes under it and as many comments saying "man enjoy it" or "sis I'm so happy about that!". Few people actually realize <strong>they are losing the real social interaction that piece of news may have generated</strong>. Remember the old style way - just to wait to really meet the people you care about and tell them orally or, better, organize an appointment?</p>
<p><img src="http://www.thecrowned.org/wp-content/uploads/2011/10/modern_media_illustration.jpg" style="float:right" /></p>
<p>A lot of people <strong>just use Facebook as a replacement for real life while</strong>, at the very same time, <strong>considering it a different reality</strong>. I know it sounds weird, but that is really how it appears to me. It is definitely kind of odd considering a different reality something they spend 1-2 hours a day on, yet being the place where they get in touch with their friends. My fellows do not seem to understand that every tiny piece of information, the tiniest indeed, they put on Facebook (being it a status update, link, photo or whatever) is somehting that mirrors their personality. Therefore, it may mean <strong>there may be people who can form an idea on how a person is just looking at their Facebook profile</strong>. Sadly, it is this way. And it is shuddering. If you listen to certain music you are more likely to be a certain type of person, and it is the same for the poses you take in photos, the kind of links you share, whether you share your sentimental status or not, how often you engage and break up... <strong>Literally everything on Facebook mirrors the writer</strong>. Not good. It is dangerous not to acknowledge and remember this. Unfortunately, I come to be into that really small group of few people who can join all these information together. That is the reason why <strong>I stopped asking and accepting Facebook friendships from people I do not know well enough and who I plan to know thoroughly in person</strong>. This is because <strong>saying yes to a Facebook friendship request is saying no to a real relationship</strong>.</p>
<p>Guys, the pleasure of friendship is <strong>to know the people you spend time with, not having virtual interactions with them</strong>. Think about this: it is much more likely you will have a deep conversation with someone on a Facebook chat than in person. This has always happened and probably will happen forever. Wanna advice? Avoid that. Even though Facebook make people show involuntarily their characters, it also reveals the essence of them. <strong>People chatting with you will be themselves in such a way that they won't</strong>. When you have the possibility to write instead of speak, you have all the time you may need to think about what to say, and you rarely end up saying the same things as speaking. <strong>Does that reveal the essence of a person? Yes. Is that the person you would know in public? No</strong>. So <strong>you start knowing a person that virtually does not exist</strong>. You may get a crush for that nonexistent person (and tell them your love in a chat) or even hate that person. I am afraid of saying that in the web half measures rarely exist. Also remember that writing hides facial expressions and gestures, so that half of the communication is lost. If you are smart enough to look at your speaker's eyes when in person, you can not deny that the look is part of the conversation.</p>
<p><img src="http://www.thecrowned.org/wp-content/uploads/2011/10/Facebook-Relationships.jpg" style="float:right" /></p>
<p><strong>Having too much information</strong> in bulk about a person through its Facebook profile <strong>may actually make people go away</strong>. Have you ever thought that? Let's put this to real life, shall we? Say you are a boy who likes rock music, wears Converse All Star, watches horror films and reads historical books. You have met a girl, a pretty one, who, after a first talk, you regard as interesting. You wanna now her better. What would you do - What is the dreadfully wrong thing you would do? Open Facebook, of course. And there, in her profile, you learn she likes pop music, wears boots, watch love films and reads thrillers. <strong>Would you still think she is worth knowing?</strong> I guess no. And yet, that could be the girl of your life! Maybe, knowing her piece by piece, as time passed by, you may have loved her.</p>
<p>This year, in high school, my class was joined with another one of the same year just because we were two courses of about 14 people, too few... Now that we are together,<strong>we just can not get in touch</strong>. Since the first day there is one class sitting on the left part of the room, and the other one staying on the right. We seldom interact (though with some exceptions) and even when doing PE we do not really know how to cope with each other. The first day I thought we would get on well, it was just a matter of time before we knew and liked. But now, for how things are turning out, I am not that sure about it anymore. After about a month of school I noticed all the guys in my class are friends on Facebook, even between the once two different courses. But hey, that is weird, <strong>they have never spoken with each other</strong>, nor that I know at least! They surely have not gone beyond the polite "hello" or "what's for tomorrow?". But <strong>yet they are friends online</strong>. I can not get it, maybe I am missing something, but I really can not get it. I mean, what is the purpose of this all? <strong>I fear those people will never know really</strong>.</p>
<p>Facebook is a very large and interesting topic, and I will probably write about it again. It is a fantastic platform when it is up to communication, but nothing more. Communicating is what Facebook is good for. <strong>Forgetting this means leaving real life behind and giving Facebook a primary role</strong> even though it should not be this way. Some people prefer to declare themselves (as I did until an year ago) against Facebook, saying they will never sign-up. I think this is as bad as using it all day because it kind of cuts you out.<strong>There is nothing terrible on being on Facebook, there are only a few things to remember.</strong></p>How Windows 8 is the clear proof that Microsoft can't lead2011-09-19T17:23:00+02:002011-09-19T17:23:00+02:00admintag:thecrowned.org,2011-09-19:/how-windows-8-is-the-clear-proof-that-microsoft-cant-lead<p>I've just had a <strong>quick look at the Windows 8 developer preview</strong>. Not screenshots, rumors or reviews around, I've actually installed it on VirtualBox and tried it myself. Yeah, it's still early beta, developer preview …</p><p>I've just had a <strong>quick look at the Windows 8 developer preview</strong>. Not screenshots, rumors or reviews around, I've actually installed it on VirtualBox and tried it myself. Yeah, it's still early beta, developer preview, whatever you want... but <strong>what a crappy thing</strong>!! It's not really up to bugs, instability and this kind of stuff, <strong>it's all about the concept, that's doesn't make any sense at all</strong>! With this, I think Microsoft showed the last -though more evident- evidence <strong>they can't lead - they must follow</strong> others' footsteps. Apple has already said that eventually iOS and Mac OS will converge somehow but it's not speeding this change up, it's obviously something that will take time and, of course, PCs will never disappear. Anyway, slowly, it's fine to develop new stuff for the mobile ecosystem and leaving the desktop a little behind because that's where the average user will move, and the mass market with it. What's not ok -actually, <strong>it's foolish</strong>- is <strong>to think that the same operating system can provide a great user experience on both small and large screens</strong>. On portable and desktop devices. On 7/10" and 25" monitors. How could you possibly think that?!</p>
<p><img alt="Start screen" src="http://www.thecrowned.org/wp-content/uploads/2011/09/Start-Screen-1024x575.png" title="Start-Screen">]</p>
<p>I don't doubt the Metro interface looks amazing on tablets, but... you open Windows Explorer from there, and the interface slides to the old style. What?! <strong>Is Metro just a showcase that actually links to old styled apps</strong>? Is that the revolutionary OS you thought? And then in that explorer you have some really big buttons (which are absolutely awful) and <strong>some really small ones</strong> (like the one to hide the ribbon bar). It's like a puzzle. And then, it's hilarious, <strong>you can even bring up a cmd prompt. From a tablet</strong>! I'm sure out there there are plenty of people looking forward to execute commands from a black prompt on their new shiny tablet. Plenty.</p>
<p>The <em>strength</em> (doule-italics) of Windows 8 is the ability to switch from Metro to desktop in a click. So basically, you'll potentially have this <strong>continuous flipping of screens</strong> that's not really going to please users (or me at least). <strong>Everything is doubled and provide a different user experience, but it seems the tablet prospective can't live without the desktop one</strong>. Without the old interface (not built for the mobile world) it wouldn't be possible to browse disks, view flash content on the web and God now what else!</p>
<p>But that's not all, Microsoft wanted to go farther, wanted to change even the desktop experience. And what did they do? <strong>They've substantially made harder the desktop actions we all were used to</strong>. Or sometimes they even got rid of them. <strong>What about the Start Menu</strong>? What's that shitty black-and-white thing that pops out when hovering on the bottom-left corner? 4 items? I used to have 25, or something like that! Are you telling me every time I'll need to open a software, I'll either have to do it by a desktop link or via the Metro interface? Moreover, it seems to me like <strong>there's no way to close applications</strong>, they just pile up in a way the user doesn't see. And then, when you won't be able to close them, you may want to switch the whole machine off. But to realize how to shut the computer, you need to look up some tutorial on the web. In fact, there's no button like "Shutdown" or similars. It's hidden is a field nobody would actually think of: in the "Settings" list. I think Steve would be better off learning the meaning of <em>settings</em>, that isn't linked to Shutdown <strong>in ANY way</strong>.</p>
<p><img alt="Figure" src="http://www.thecrowned.org/wp-content/uploads/2011/09/4300.Figure-9-Home-tab-crop_thumb.png" title="4300.Figure 9 - Home tab crop_thumb">]</p>
<blockquote>
<p>With each update, no matter how much they intend to change things, they always end up cramming in the baggage of operating systems' past. This leads to a <strong>weird amalgamation that's neither new nor old</strong>. And quite often, worse than both.</p>
</blockquote>
<p>This is what <a href="http://techcrunch.com/2011/08/30/we-need-an-invert-selection-button/">MG Siegler</a> stated about the new explorer and it's kind of a summary of my whole point of view. They have developed a <strong>tablet concept that's not ready at all</strong> <strong>and</strong> that, for how it is now, <strong>poses no concerns about Android leadership</strong>. Then they have a <strong>desktop concept that is basically a much more confusing Windows 7</strong>, I'd dare say it's not the Windows we know anymore. It's just a clumsy attempt to put a foot in the mobile shoe and to keep the other one into the old-style-desktop shoe. The problem is that the former sacrifices the latter, leaving the whole system far behind my own expectations.</p>
<p>Yeah... It's a developer preview, it's still early beta (I'd better say <em>early alpha</em>)... but hey, <strong>if an OS is not decent and presentable, there's no reason to release a preview and push the press to cover it</strong>. Keep working silently and let some screenshots and blog posts leak if you don't have anything to show. This is not the first negative opinion, a lot of english blogs have already talked about it and they weren't really kind words.</p>
<p>I don't know whether <strong>Microsoft tried to anticipate Apple</strong> or not, but anyway that's now the right way of acting. <strong>Everyone's shifting to the mobile world as if there weren't already a lot of players there</strong>. HP tried to shift completely and failed. Microsoft is not doing it fully, but just putting its hands forward. <strong>Who is going to look after the desktop world?</strong> Does everybody really think it's going to die soon? Why the hell are you leaving it? Why don't you focus on it and assure your product is the best one so everyone will buy it so you are going to make money?</p>
<p>I feel like it has been something like a decade <strong>Microsoft has been needing a flop to build a good product</strong>. You know, Windows Millenium was criticized by most, while XP widely appreciated. Vista sucked, and then 7 came and was great and a lot of people bought and is still buying it. The point is that <strong>I'm not sure they'll have another chance if Windows 8 will be a huge fail</strong>. <strong>It seems that they can't innovate</strong>, introduce new things, new concepts, they can <strong>just implement already tested features</strong> and ideas into easy to use products. But take care: <strong>IT is changing and the market with it, dear Redmond, consumers will not wait 3 more years for you to understand how to do things, so plan your moves carefully</strong>.</p>