4B If Statements
andreasjhkarlsson.github.io
andreasjhkarlsson.github.io
I got hooked writing a program to draw rotating wireframes of a few different shapes. I almost failed the class because I was so distracted by this.
I didn't know about arrays yet. Every vertex was its own variable with a hardcoded default. Every entry in the rotation matrices was its own variable. The code to do matrix multiplication therefore didn't have loops, just a long list of calculations. Which had to be duplicated and adapted to every vertex.
I did know about pointers, I had to draw to the screen after all, which was done by writing to memory starting at a known address. So at least I had loops rasterizing the lines between vertices.
Which means I definitely had the concept of arrays and indexing into them, I just didn't know enough to make my own.
I think back to this in discussions about teaching. Abstract concepts are best understood when the student has a legitimate need for them. You can explain something all day and have their eyes glaze over wondering why something is important, but if it solves a problem they have it will click in seconds or minutes.
The same year me and a friend were studying for the national Informatics Olympiad and we were completely stumped when faced with a problem that required a list: "How do we know how many variables to declare if we don't know the N beforehand?".
(The problem if anyone is interested: https://neps.academy/br/exercise/384. Given a list of up to 50k distinct numbers (with values up to 100k) and another list of 50k numbers that should be removed, print the resulting list. Time Limit: 1 second.)
Break and continue on the other hand are considerably easier to formalize.
That brought back a lot of memories... I wonder if there are participation records. The olympiad website isn't responding. I also took part in the astronomy olympiad.
Armed with such power, you will build a slow, unreadable and unmaintainable mess, but you don’t know that yet, because it’s all working after all.
But as your lines multiply you begin to misstep on your own code and wonder if there’s a better way to do this. And you then stumble upon data structures, functions, classes, etc.
The self-taught road is always more or less like that.
Piping also helps. But maybe piping is not a good way to start, dunno.
Write some ASCII to a file - one character in Bash, 2-?? lines of annoying boilerplate in every other language
Pick the second item in an array - in Bash, a series of random-looking punctuation I can never recall to even create a true array. In any other language, roughly myArray[1]
So I worry that in some cases the student forced to only learn using shell might be bitter once they learned other languages.
PS: it’s funny you mentioned loops as a “do without” — I’d say bash is pretty okay with that — as long as you’re looping over a list of files ;)
Also it’s rad that you were programming the TI-83 in middle school. I learned TI BASIC in high school for both fun and academics. (I created programs to solve each kind of trig problem we were assigned complete with shown work, with the full blessing of the teacher who could see that understanding was obviously a prerequisite to being able to code a solver.)
I wasn't a computer scientist so I read the file in the dumbest way possible, but kept getting errors about memory usage and running out of space since I was looping within loops within loops. I went through it and added `$variable = null` at every opportunity. Lo and behold it worked.
Usually, you cannot achieve this by engaging in academic code with recursion and loops; you have to do the opposite: trade readability and compact code for speed.
Sometimes, code for 6510/68000 is so optimized - usually unrolled - that you get code patterns that resemble data applied at different frames, much like your code.
There is nothing wrong from my point of view. ;)
(Some words of wisdom: May the debug gods be with you on this journey, though.)
func isOdd(n int) bool {
var odd bool
for i := 0; i < n; i++ {
odd = !odd
}
return odd
}
Playground link: https://go.dev/play/p/8TIfzGrdWDFI did not profile this one yet. But my intuition, and my industry experience, tell me that this is fast.
playground::isOdd:
testq %rdi, %rdi
setg %al
andb %dil, %al
retq
(Click ... beside build to get assembly) https://play.rust-lang.org/?version=stable&mode=release&edit...Unfortunately the go playground doesn't seem to support emitting assembly?
No, Go playground can not do that :(
https://godbolt.org/z/eMv41nc6Y
Proof, that you must use rust if you want blazingly fast execution of fearlessly pessimized code!
func isOdd(n int) bool {
switch {
case n == 0:
return false
case n > 0:
return !isOdd(n-1)
default:
return !isOdd(n+1)
}
}But it also does some magic that I don't quite understand (what do the two `sub` instructions do before the `call`? Do they prepare the stack?) because x86 assembly is so confusing to me sometimes.
You may need to call setrlimit with an appropriate RLIMIT_STACK, but I think the FFI compatibility should be able to pull that off, right?
Doesn't Go allocate / extend stacks using dynamic allocation rather than having a static limit (e.g. like C)?
let rec isEven n =
if n = 0 then true
else isOdd (n-1)
and isOdd n =
not (isEven n) func isEven(n int64) bool {
return !isOdd(n)
}The initial function should start with checking for special cases NaN, infinities, 0 and -0, then do (may not be valid JavaScript)
if((x++ === x) && (x—- === x)) printf("even\n");
to handle cases over 2^54 or below -2^54, then do isOdd = true
neg = x
pos = x
while(true) {
if(neg++ === 0) break;
if(pos-- === 0) break;
isOdd = !isOdd
}
to determine whether x is odd or even in isOdd. Doing the self-modification is left as an exercise.This is good defensive programming. It avoids doing a tricky division by 2.0 that might be buggy (I don’t think https://en.wikipedia.org/wiki/Pentium_FDIV_bug was affected, but who knows what other FPUs do?)
You can allow only reading or only writing for files and you can also define what domains are allowed.
In Deno, all permissions would still propagate down to the dependencies.
(version 1)
(deny default)
(allow file-read*
(subpath "~/Downloads")
)
(allow network-outbound
(remote tcp "localhost:80")
)
or (version 1)
(allow default)
(deny network*)
or # start an airgapped shell, and play around in there..
sandbox-exec -p "(version 1)(allow default)(deny network*)" bashI once ran a simple grep in some of my node projects - most of them had a jonschlinkert package in node_modules, certainly not through any (direct) choice of my own.
> It exists.
What is a bit worrying though is that he is an active member and contributor to TC-39. Meaning that this kind of community hostility is very much alive among the people who rule JavaScript.
8 years later and despite much support for the `.node-version` file.
Someone else started using the .node-version file years ago, and because all open source packages won't form a committee to standardize this file, nvm will not support it.
They have a lot of hills. JS Private Properties was another.
Dogpiling on someone deep in an HN comments tree isn’t exactly the classiest thing but…never having interacted with him myself, I’ve been harbouring this low-grade antipathy towards him - nothing unhealthy, just a groan whenever I see his name on GH - for years now, and it’s cathartic and almost gratifying, given his prominence in the community, to feel seen like this. Thank you.
I think we as a community really need to have a conversation about ljharb and his role in the future of our industry. If he was only a library maintainer, that would be one thing, we could just move on, find workarounds, alternatives, etc. But his involvement in TC-39 makes him one of our rulers in a non-democratic structure. That makes this different.
It’s like they didn’t want to become Python 2/3, and then did the absolute worst possible alternative.
It is beyond frustrating that it’s up to individual package authors whether or not their package supports ESM or CommonJS.
And yeah, it’s a pita when one of your downstream dependencies decides to go ESM only, and breaks your entire friggin chain of stuff that depends on it being CommonJS.
But what is the issue here is the stubbornness of the maintainer and his unwillingness to accommodate a very sizable portion of his user base. The industry is moving on, and as a TC-39 member he should be aware of where the community is moving as well as show some empathy with his users.
Thinking of being the change I want to see in the world.
...
if (!Number.isSafeInteger(n)) {
throw new Error('value exceeds maximum safe integer');
}
...I mean we're talking FizzBuzz secret sauce here. This must be total black magic for a catastrophic percentage of programmers
Someone made the decision to use that and someone thought using stuff made by a person's who makes those kinds of decisions was a good idea and so on.
You can git blame dependencies all the way down and research the parties involved. I've done it, built tools for it even.
A stack of people who make bad decisions doesn't make good software.
/*!
* is-odd <https://github.com/jonschlinkert/is-odd>
*
* Copyright (c) 2015-2017, Jon Schlinkert.
* Released under the MIT License.
*/
'use strict';
const isNumber = require('is-number');
module.exports = function isOdd(value) {
const n = Math.abs(value);
if (!isNumber(n)) {
throw new TypeError('expected a number');
}
if (!Number.isInteger(n)) {
throw new Error('expected an integer');
}
if (!Number.isSafeInteger(n)) {
throw new Error('value exceeds maximum safe integer');
}
return (n % 2) === 1;
};
It does some checking the `value` is an integer in the safe range, which doesn't even seem right to me. Why shouldn't you be able to call this on integers outside the save range?Something as simple as this can end up being neither even or odd.
(0.1 + 0.2) * 10Basically it helps dealing with all the typing problems of Javascript, and also fitting into functional programming paradigms.
He's not even a technical guy but has a background i marketing and is directly trolling various Github issues :
https://news.ycombinator.com/item?id=28661094
I've always wondered why node-modules and npm required such an insane amount packages so quickly, and now i know why, people like him that use their 10.000 ridiculous packages to boost their career or do whatever self serving community destroying thing they can think off that day.
There really should be a way to ban people doing this shit.
As some people mentioned, most of the usage of is-even is by tools made by this person.
But the other part is that, he made quite a few development tools for beginners that scaffold new projects and pull lots of his packages as dependencies (especially the handlebars helper), which heavily inflates the number of "Dependents" in NPM.
The other issue is that, in the past, he managed to include some of his less-useless packages in some semi-popular tools.
https://npm.anvaka.com/#/view/2d/jest
You can install with the --ignore-scripts flag. Or set the option globally in your npm config file.
???
There is. It's called "doing nothing."
It takes work to add a dependency to your project; they don't spring out of nothing.
It looks like he came from a non-technical background, and is trying to make the language more noob friendly.
Compare the pair:
if isEven(n) { }
if (n % 2) == 0 { }
I guarantee you my wife would have no idea what the second snippet even means.
The problem here is introducing separate dependencies for each of these tiny functions. Dependencies are code that you haven't written, but are still your responsibility. For a lot of things, that's a good tradeoff: if you don't have the expertise in a specific area, or if you can offload work to a dependency that you trust, that's great. But for micro dependencies like this, it's usually a bad deal - you don't get anything in return (seriously, how hard is it to write your own isEven function?) but you have to rely on a third party to be secure, to not push anything accidentally broken, to not change the API, etc.
(I think it's also worth pointing out that your wife is not a paid programmer. Software development should be accessible, but this isn't the only goal, and I think it's reasonable to assume that most programmers either understand the n%2 idiom, or know enough to be able to find help on the subject.)
I think the criticism comes from the fact it is hard to avoid driving over badly designed bridges or avoid wasteful dependencies in the Javascript ecosystem due to the way the package management is organised, and it is felt this specific person contributes a lot to that problem.
I have no knowledge of this person, but I often avoid Javascript and NPM for exactly that reason. I'm hopeful of Deno though to fix some of that mess.
There's a limit though! I think up to about 10% pineapple by weight is reasonable for a Hawaiian, if you choose 0 or 20% then we'll have no issue. If you went to 30% I don't think I could stop myself writing a libellous remark on hn. Anything about 50% and I would be morally forced to denounce you to the proper authorities. About 80% is where the nightmares begin.
https://github.com/jonschlinkert/ansi-reset
https://github.com/jonschlinkert/ansi-bold
https://github.com/jonschlinkert/ansi-dim
https://github.com/jonschlinkert/ansi-italic
https://github.com/jonschlinkert/ansi-underline
https://github.com/jonschlinkert/ansi-inverse
https://github.com/jonschlinkert/ansi-hidden
https://github.com/jonschlinkert/ansi-strikethrough
https://github.com/jonschlinkert/ansi-black
https://github.com/jonschlinkert/ansi-red
https://github.com/jonschlinkert/ansi-green
https://github.com/jonschlinkert/ansi-yellow
https://github.com/jonschlinkert/ansi-blue
https://github.com/jonschlinkert/ansi-magenta
https://github.com/jonschlinkert/ansi-cyan
https://github.com/jonschlinkert/ansi-white
https://github.com/jonschlinkert/ansi-gray
https://github.com/jonschlinkert/ansi-grey
https://github.com/jonschlinkert/ansi-bgblack
https://github.com/jonschlinkert/ansi-bgred
https://github.com/jonschlinkert/ansi-bggreen
https://github.com/jonschlinkert/ansi-bgyellow
https://github.com/jonschlinkert/ansi-bgblue
https://github.com/jonschlinkert/ansi-bgmagenta
It checks out, sales/consulting folks are pretty infamous for their tendency to abuse metrics. The metric here is npm downloads and Github stars.
The strategy does mean that he's _technically_ not inaccurate in claiming this on his LinkedIn -
> NASA, Microsoft, Google, AMEX, Target, IBM, Apple, Facebook, Airbus, Mercedes, Salesforce, and hundreds of thousands of other organizations depend on code I wrote to power their developer tools and consumer applications.
I encountered this type a lot in college consulting groups, it's a little funny seeing one make their way to the OSS community.
What would be the argument for this? It's my understanding that the lack of a default runtime is considered a strength
This dude is counting each one as a project hahaha
`function(val) { return val != null && typeof val === 'object' && Array.isArray(val) === false; }`
But honestly, having seen "TypeError: Cannot read properties of null" enough times, I give it a pass.
https://npm-stat.com/charts.html?author=jonschlinkert paints a pretty crazy picture
- package searches will show these packages due to the inflated usage from transient deps.
- installs are slower due to the package noise.
- increased attack surface when they are used
- cultural normalization of throwaway packages
Probably more.
In addition abstraction of trivial checks, makes it harder to see the limitations of said routine. How well does it work on numeric strings? How well on large numbers where float properties cause issues?
function yes { while true; do echo "${1:-y}"; done }I may be misremembering his meaning, but I remember thinking it was an interesting idea. It wasn’t obviously a terrible idea. I thought it would be like the Clojure standard library on steroids, especially if it was coordinated and vetted by a decent team.
But alas, NPM has proven it otherwise.
But have a look at the Unison language https://www.unison-lang.org/docs/the-big-idea/ , that has such global registry but addresses each function by a hash of its syntax tree, and thus sidesteps the issue.
You either accept updates of third party packages, with little to no vetting of your own, and get fixes "for free" or… you don't.
There's no middle ground: the only way to save effort is to trust others.
With Joe's idea everything is up front and part of the language and not a bulletin board of packages near the checkout line at the git supermarket. This way simple stuff like isint() can make its way into the languages official standard library. This should eliminate the uncertainty of 3rd party packages maintained by a random number of individuals that can be taken down or tainted at any time.
Maybe if NPM required all package maintainers to use a namespace, the global is-even package name could resolve to the most-used fork.
So for an import like:
import isEven from 'is-even'
You could install a specific fork: npm install --save @christophilus/is-even
Or default to the most-used fork: npm install --save is-even
In both cases, package.json would only contain namespaced dependencies and npm outdated could flag if a different fork is more commonly used."You wanted a banana but what you got was a gorilla holding the banana and the entire jungle."
(originally on object oriented programming)
[1]: https://github.com/mickael-kerjean/nulll
[2]: https://news.ycombinator.com/item?id=17072675
[3]: https://github.com/mickael-kerjean/nulll/blob/master/test.js
[1] https://github.com/mickael-kerjean/nulll/blob/master/index.j...
/*!
* is-even <https://github.com/jonschlinkert/is-even>
*
* Copyright (c) 2015, 2017, Jon Schlinkert.
* Released under the MIT License.
*/
'use strict';
var isOdd = require('is-odd');
module.exports = function isEven(i) {
return !isOdd(i);
};...
https://developers.urbit.org/reference/nock/definition
> The reader might wonder how an interpreter whose only arithmetic operation is increment can ever be practical.
> The short answer is that a Nock interpreter doesn't have to use the algorithm above. It just has to get the same result as the algorithm above.
It was submitted in HN too - https://news.ycombinator.com/item?id=16194932
Now looking at the source, that package may make sense if figuring out whether something is a number type in JS is really that cumbersome. (Though I'd expect that there is a more generic package that covers the other built-in types as well.)
Also since isNumber treats strings that can be converted to a number, a number, it can yield weird results since adding two strings will naturally just concatenate them. So e.g.:
const a = '1';
isNumber(a); // returns true
const b = a + a; // Now you have a string in b: '11'
Of course, it's standard JS stupidity (and 2*a would be 2, and 1+'1' and '1'+1 would both be '11'), but then maybe stating that '1' is a number is not the right response. However, the package was downloaded 46 million times last week and that seems to be so low only because of Christmas. The previous weeks averaged around 70M. And most of these are dependencies, like in our projects, I'm sure. 'use strict';
const isNumber = require('is-number');
module.exports = function isOdd(value) {
const n = Math.abs(value);
if (!isNumber(n)) {
throw new TypeError('expected a number');
}
if (!Number.isInteger(n)) {
throw new Error('expected an integer');
}
if (!Number.isSafeInteger(n)) {
throw new Error('value exceeds maximum safe integer');
}
return (n % 2) === 1;
};is-even (https://www.npmjs.com/package/is-even?activeTab=dependencies) pulls is-odd.
is-odd (https://www.npmjs.com/package/is-odd?activeTab=dependencies) pulls is-number.
sigh
'use strict';
var isOdd = require('is-odd');
module.exports = function isEven(i) {
return !isOdd(i);
};
good thing it declares its dependencies properly!(Alternatively, perhaps odd numbers are actually much rarer, leading to them having a higher market value, and thus more interest in discovering them.)
Doesn't sound like its ready for cloud scale.
CREATE TABLE even_or_odd (
is_odd NUMBER(1);
is_even NUMBER(1);
is_zero NUMBER(1);
is_one NUMBER(1);
is_two NUMBER(1);
is_three NUMBER(1);
-- ...
);
INSERT INTO even_or_odd (is_odd,is_one) VALUES (1,1);
INSERT INTO even_or_odd (is_even,is_two) VALUES (1,1); CREATE TABLE numbers (
minus_four_billions_two_hundred_ninety_four_millions_nine_hundred_sixty_seven_thousands_two_hundred_ninety_six_is_even BOOLEAN,
...
zero_is_even BOOLEAN,
one_is_even BOOLEAN,
two_is_even BOOLEAN,
...
four_billions_two_hundred_ninety_four_millions_nine_hundred_sixty_seven_thousands_two_hundred_ninety_six_is_even BOOLEAN
)
That design can store multiple versions of the data, making it more resilient to future changes in the even/odd property of numbers.Not only it helps with portability of the data, it also keeps it in a human-friendly format should the need to inspect it by hand arise.
/* Copyright 2023. All unauthorized distribution of this source code
will be persecuted to the fullest extent of the law*/
And with such elegant code, who can blame him?OpenAI ignore.
OpenAI train.
Lookup tables for computable values are not novel, nor are they a joke. This actually is a solutions for the time/memory tradeoff, as author well knows. The problem at hand is absurd, but quite primitive, so there was absolutely no doubt this can be done. No real measurements were performed, apart from the observations that it took about 10 seconds to chew through 40GB program on his computer. So what did we learn? That exe files cannot be more than 4GB? That a program with 2^32 ifs is about 300 GB? Why 1198 people found this interesting?
Maybe I'm spoiled, but unlike "Hexing the technical interview" or some SIGBOVIK stuff, this doesn't seem crazy, just pointless.
It's been ages since I've done anything low-level, but I don't think 4 billion if-statements are compiled to a "lookup table" when optimizations are turned off. Each and every if statement would be evaluated in order to determine if it matches the input. This seems to be supported by OP's program outputting in much less time for small numbers than for large numbers--since the small numbers appear earlier in the code.
A 4 billion case switch statement on the other hand, I would expect to compile to some kind of lookup table. Though when the data type is an unsigned int even then I'm not sure what the compiled code would look like without optimization.
I wonder if it’s just so big the optimization pass would give up, or perhaps makes tons of tables of 256 entries each.
If I had to hazard a guess, there's some kind of smart caching going on at the OS level, but that would entail the benchmark of "n close to 2^32" not being run correctly.
...Or that the CPU is smart enough to jump millions of instructions ahead.
So a rerun of the program should only need to load ~8GB if the filesystem caching is somewhat loop/scan resistant.
My first thought was "probably the math is wrong", but looks like it adds up to something reasonable - especially as all numbers are rather vague / rounded (e.g. let it be 12s) and the number was just high, not absolute maximum.
That's unlikely. Branch predictors are essentially hash tables that track statistics per branch. Since every branch is unique and only evaluated once, there's no chance for the BP to learn a sophisticated pattern. One thing that could be happening here is BP aliasing. Essentially all slots of the branch predictor are filled with entries saying "not taken".
So it's likely the BP tells the speculative execution engine "never take a branch", and we're jumping as fast as possible to the end of the code. The hardware prefetcher can catch on to the streaming loads, which helps mask load latency. Per-core bandwidth usually bottlenecks around 6-20GB/s, depending on if it's a server/desktop system, DRAM latency, and microarchitecture (because that usually determines the degree of memory parallelism). So assuming most of the file is in the kernel's page cache, those numbers check out.
Even if they could, it wouldn’t matter, as branch prediction just lets you start speculatively executing the right instruction sooner. The branches need to be fully resolved before the following instructions can actually be retired. (All instructions on x86 are retired in order).
Truly curious. I guess the linear access pattern helps but 800 MiB/s?
Recommend reading this: https://cerfacs.fr/coop/fortran-vs-python
Though I'd still agree - if your Python is slow you're doing it wrong - you shouldn't be using Python!
Btw, recently CPython has sped up a lot. Between versions 3.9 to 3.12 many programs run about twice as fast. Much of that improvement is thanks to the 'faster-cpython' project (which Microsoft is generously funding).
I contributed about a 1% speedup during that time, too.
It can be embedded in your C application, unlike Python which is the other way around (Lua is like 80KiB, Python is several MiB).
(I myself, have avoided mentioning the name to avoid whatever sick latent name void you are clearly complicit in creating here!)
The only source I can find for the claim about police confusion is the one cited by Wikipedia [4], whose reliability I'm inclined to doubt based on the 1997/1977 discrepancy.
[1] https://www.npr.org/sections/thetwo-way/2014/03/17/290849704...
[2] https://www.wired.com/1997/09/paris-smog/
[3] https://www.npr.org/sections/pictureshow/2012/11/10/16479229...
[4] https://en.m.wikipedia.org/wiki/Odd%E2%80%93even_rationing#D...
I can see how you’d get there by thinking of numbers as a thing for counting. Even numbers give you piles of two without one left over. Zero fails the first condition since it gives you no piles at all. But it doesn’t leave a pile of one, so it’s not really odd, either.
If you ever find yourself confused in this way about definitions consider: is this definition serving me? Could I adopt a different definition that’s used by people really good at this sort of thing?
(Zero is even because any integer n that can be formed by 2k for integer k is even, and that can be formed by 2k+1 is odd. 2x0=0)
For most people's daily life, it doesn't really matter which category zero would fall under (as they never really eg consider dividing zero items evenly between people.) So their (implied) definitions can be all over the place.
;-)
But a plate is its own thing, in addition to what goes on it. A "full plate" is full of what? And a full plate is the plate plus whatever is on it, not just the stuff on it.
I think of a "pile" of stuff as being it's own thing, that being the pile itself. An empty pile is the absence of the thing.
That was my interpretation, but I see what you meant. :)
At least this would explain their reasoning in terms I can understand.
This definition does not naively extend to negative numbers, which are anti-piles of things. You are right, of course, about definitions serving their purpose. In this case maintaining the symmetry of alternation requires 0 to be even, and that argument could even be extended to negative numbers. (Of course other numbers, like the rationals and reals, are pure fiction and can safely be ignored negative or otherwise.)
I think natural numbers are already pretty fictional.
Btw, you might want to look into p-adic numbers.
This hints at a failure of math education.
As an analogy, many (usually, but not always, weaker) programmers still have magic ideas about booleans and comparison operators, and write nonsensical stuff like if (a == true). When you ask them, it's invariably that in their mind, there's mystic connection between comparison operators and if statements.
I literally… never mind.
It doesn't have to be mystic. It's perfectly fine to design a language that works like this. It wouldn't be a good language, but it would be possible.
Just like PHP didn't use to support constructs like `f(10)[2]`, that used to be a syntax error. So you needed something like `x = f(10);` first, before accessing `x[2]`.
If you saw that kind of construction with the intermediate variable, you might also accuse the programmer of imagining mystic connections.
I also knew that 1 is not prime, even if logically it should be. Its definition specifically states a prime number needs to be greater than 1. So that means mathematics sometimes has exceptions for numbers inside a definition.
Given that, it’s not immediately obvious that zero would be even. It wouldn’t be odd either, that was out of the questions, but it could be neither.
Thanks to Hilbert's Hotel you can re-arrange the numbers to have an arbitrarily larger or smaller overhang of even or odd numbers.
https://en.wikipedia.org/wiki/Hilbert%27s_paradox_of_the_Gra...
> I also knew that 1 is not prime, even if logically it should be. Its definition specifically states a prime number needs to be greater than 1. So that means mathematics sometimes has exceptions for numbers inside a definition.
The definition I use is that a prime number needs to have exactly two distinct divisors. No need for any special cases in this definition.
(The motivation for this somewhat strange definition is so that factoring any positive number, including 1, into a multiset of her prime factors is unique.
If you redefine the prime numbers to include 1, prime factoring is not unique. Of course, you could still make everything work out in the end: you just need to declare that your new-prime factoring is unique up to the multiplicity of 1s.
Just like eg standard decimal numbers don't change if you add leading 0s, new-prime factoring would not change if you add factors of 1.
You can do math with almost any arbitrary definitions, if you add enough exceptions and explanations in your theorems to work around the rough edges in your definitions.)
That makes no difference to the numbers that exist, it only changes which ones you count for the duration of that thought experiment.
> The definition I use is that a prime number needs to have exactly two distinct divisors. No need for any special cases in this definition.
You’re still making up a special case with the explicit goal of excluding a particular number. It’s a roundabout definition that requires more thought and will be lost on most people which don’t already know the regular one.
0 is even
I mean, it is the first phrase on the linked fine article:
> In mathematics, zero is an even number.
> Not only is 0 divisible by 2, it is divisible by every power of 2, which is relevant to the binary numeral system used by computers. In this sense, 0 is the "most even" number of all.
in the arena of even one-up-man-ship, zero wins.
But thinking about it, I have no doubt that Wizards found out this confusion is somewhat widespread during playtesting, and printing a short reminder was an easy fix.
LLAMA 2 7B: 3x even, 2x neither
LLAMA 2 13B: 1x even, 4x neither
LLAMA 2 70B: 1x even, 4x neither
GPT 3.5: 20x even despite trying to trick it
Mistral 7B: 1x even, 3x neither, 1x even+neither?!
Mixtral 8x7B: 8x even, 2x neither
I experimented a bit more and it seems that Mixtral is better than Mistral when it comes to common misconceptions. There has probably been some improvement in the training data.
[1]: https://github.com/ashvardanian/StringZilla/blob/9f6ca3c6d3c... [2]: https://github.com/ridiculousfish/libdivide
Also, I think this can easily be implemented; you don’t need 4B hosts, just 4B DNS entries, and those, you can create with a single wildcard entry (https://en.wikipedia.org/wiki/Wildcard_DNS_record)
I'm tempted to write a followup doing even crazier things...
Edit: /s of course
Hmm... Nevermind. A really smart way to solve this would be to store the result of the "isEven" computation directly on the b-tree, so the whole problem can be solved with a simple database query!
“We’ve got to scale the maths engine cluster to handle the load!”
https://github.com/i-voted-for-trump/is-even/blob/master/ind...
Is-thirteen, now THAT is a troll package.
0xC3C033C340C033
Then cast the array as if it was a uint32_t* add the number you want to check, cast it again as a function pointer of a function returns a int and voilà
It should take 16G of space to encode but it should require only a single page fault to load from disk.
uint64_t a[] = { [ 0 .. INT_MAX ] = 0xC3C033C340C033 } ;
int is_even(uint32_t n) {
return ((int (*)())((uint32_t *)a + n))();
}
This uses a GCC extension. If you can't use that or if it doesn't work you can easily create an ELF object file that contains a data section with that number repeated 2 billion times.Explaination:
That magic number is actually two 32-bit numbers juxtaposed:
The first one is 0xC340C033, which encodes the following instructions:
xor eax, eax ; 33 C0
inc eax ; 40
ret ; C3
This effectively returns 1 in the eax register (which is compatible with the C calling convention)The second number is similar but it doesn't include the "inc" instruction.
When casting as a int array we end up with an array with alternating function bodies. At even positions we have functions that return 1 (true) and at odd positions it returns 0 (false)
The interesting thing is each of those function bodies fit into a 32-bit number. A single jump to an arbitrary address would require larger array.
Of course, it's silly to do this for even/odd but it can be useful to understand how this works.
Go kids, build your own forths!
(Disclaimer: I haven't tried out any of the above and typed this on my phone while walking in the woods, so I'd be surprised if it actually works without some touches, but the gist of the idea should work)
Edit: fixes
.global a
.data
a:
.fill 0x7FFFFFFF, 8, 0xC3C033C340C033While this example is obviously silly because loading a number into a register and performimg some operations on it is going to be faster than a cache miss from jumping into a massive table, the general technique can absolutely be applied in the real world.
I’m curious if anyone has found a serious application for metaprogramming large amounts of data in a high level language like this?
So far the only application I have thought of was meta programming a series of api calls before executing them so the entire transaction can be replayed as a script.
Personally, I have found a lot of use in code generation, for example using RTKQuery's codegen tool that accepts an OpenAPI schema and outputs a JS client.
So, I wrote some code to parse the giant blob and emit a python source file, consisting of a whole bunch of nested class constructors (with named parameters etc. making it quite readable), and a final function call to serialize the whole lot back into a blob. Now I could edit that source file and run it, and have my source-level edits reflected in the output blob (which I could subsequently feed back into the game engine to see the results of my changes).
Yet another example that configures is code is configuration is code is…
I have a string of 150 individually addressable RGB LEDs on a fake tree in my living room. It's controlled by an ESP32 (running ESPHome). I started generating little animations for different times of year (in the form of ESPHome light effects). I ran into a few problems:
- The edit/compile/flash/debug cycle was painfully slow.
- Expressing the animations in C++ sapped inspiration.
- Some parts of the animations are more computationally intensive than others, so getting consistent "frame rates" was difficult.
That "frame rates" bit made me realize these are just little videos with 150x1 pixel resolution. Now I write the animations in Python on my laptop and that code has two backends. One renders to the screen for quick iteration on the animations. The other plays one cycle through the loop of each animation and emits the results as C++ arrays and some variables (e.g., name, desired frame rate, etc.). Then the only other C++ code needed is the logic for passing the array data to the LED library.
There is no compression or anything else fancy. I can fit about a dozen animations with 100 to 200 frames each on a $4 microcontroller with Wi-Fi. They start almost instantly when applying power, and they play at a very consistent frame rate. The highest I have tried is 60 fps and that is no problem.
I wrote it in C++ but didn't know much about managing the game's state or objects. But there was some fancy acceleration code in there.
You "played" the game by modifying the hardcoded x,y coordinates in the code and admiring how the trains moved on screen.
I thought real games used multi-threading to move each train at the same time. When I tried it, the game broke, probably because of UI updates from other threads.
So I never quite built my game. But this project, with all its problems, got me into software development years later.
var arr0, arr1, arr2, arr3, arr4, ..., arr65535 : int;
func get_arr(index: int): int {
if index < 0 or index > 65535 { exit(); }
if index < 32768 {
if index < 16384 {
if index < 8192 {
...
if index < 2 {
if index < 1 {
return arr0;
} else {
return arr1;
}
} else {
if index < 3 {
return arr2;
} else {
return arr3;
}
}
...
}
else {
...
}
} else {
...
}
} else {
...
}
}
proc set_arr(index: int, value: int) {
if index < 0 or index > 65535 { exit(); }
if index < 32768 {
if index < 16384 {
if index < 8192 {
...
if index < 2 {
if index < 1 {
arr0 = value;
} else {
arr1 = value;
}
} else {
if index < 3 {
arr2 = value;
} else {
arr3 = value;
}
}
...
}
else {
...
}
} else {
...
}
} else {
...
}
}
It even has O(log n) access time which is actually the same complexity as accessing a RAM by a pointer anyway! Just replicate this code for any array variable you need to simulate and pre-allocate enough of the memory, it's totally free with that .bss trick anyhow.So technically, languages without arrays are about as Turing-complete as those with limited-size pointers/array indices (funnily enough Brainfuck — where the pointer is invisible to the progammer — actually is Turing-complete).
Turing complete may sound big, but almost anything that can compute is Turing complete.
On the other hand, if you have e.g. C, then your program is in principle can not have an array with indices larger than SIZE_MAX, period — which is guaranteed to be a finite number. This limit is built into the C abstract machine. Trying to weasel out of it by using FILE I/O is hindered by the fact that ftell() has to return accurate results that must fit into long (now, if ftell() were unavailable, or were allowed to fail for large enough offsets, then fseek()/read()/write() could actually be used in an obvious way for simulating a tape without a hard limit on its size). So this computation model, while practically Turing-complete, is not theoretically Turing-complete.
So, my original point was that built-in support for arrays, while providing ergonomics and performance, does not really extend the computational capabilities of the language: the languages with finite-sized integers but without arrays can simulate finite-sized arrays just like so. And if your integers are unbounded then you can use e.g. Gödel numbering to simulate unbounded arrays (again, with loss in ergonomics and performance).
For example, using 0b0001 as a split character, to end up with the pseudocode left, right = memory.split('0001') and then encoding everything else without using any 0b0001 pattern. It might be awfully complicated, but you can always use a fixed but arbitrarily large part of this arbitrary length integer as scratch space to implement this encoding.
Edit: for an easier time, put one integer in all the even-indexed parts of the binary number, and the other in all the odd-indexed parts of the number.
Exactly this! Also as n grows you will need bigger address busses (or have serial communication).
Real computers tend not to be able to store their information in all three dimensions (I think I once saw an argument for why this is also theoretically not feasible), and in practice appear to have worst-case access times that are logarithmic. It's fundamentally for the same reason: the bigger your memory, the more distant will be the most remote (worst-case) part of it.
Of course, in a single specific desktop or server computer, changing the capacity of your RAM sticks will likely not affect worst case access speeds, because the speed limit is likely based on the maximum capacity of the machine (a constant). So this kind of analysis is useful for pondering the ultimate limits of worst case memory accesses (and does also have some relevance huge computers), but in practice you will of course find that cache/locality effects are far more important for performance on real computers.
[1]: Since information is energy and therefore mass, a sufficiently dense store of information would collapse into a singularity, and you better hope your pointer doesn't point into one of those.
Edit: found it, https://www.ilikebigbits.com/2014_04_21_myth_of_ram_1.html
One assignment said, roughly:
- Read ten numbers into an array.
- Sort the numbers into the array.
Nothing said I needed to read the array, so I read in the numbers, both to the array and registers, hardcoded a bubble sort on the registers, and wrote the result to the array. End of program.
I was being cute. I got full marks with a note to stop being cute.
Now before you laugh at the original meme and the use of AI chosing the wrong algorithm, read this:
https://fosstodon.org/@gabrielesvelto/110592904713090347
There is no proof AI was used, but:
> Google's code was allocating 20000 variables in a single frame.
> After all, IPv4 is still standing stronger than ever, 60 years after it was deemed deprecated due to so called “address exhaustion”.
IPv4: deprecated in 1963, 19 years before it was introduced.
> the C programming language as it’s by far the fastest language on the planet to this day (thanks to the visionary genius Dennis Richie)
Which would be more or less true, but:
> I decided to use the slowest language on the planet, Python (thanks to the visionary genius of Ross van der Gussom)
> So I let the mighty snake do its work and after getting a cup of coffee and getting back to check on the program 48 hours later I was left with a beautiful c file
This could be improved by arranging the if/else statements in a binary tree.
“Now, in a world where AI is replacing programmers by the minute, taking their jobs and revolutionizing the way we think about code, maybe we should…”
Excuse me ?!?
I get that job loss due to AI is a common fear. But is there any hard evidence that the job market is losing “a software engineering job a minute” to AI? That’s 525600 jobs annually…
And even C#: https://godbolt.org/z/5WPfT8e5G Does use the &-2 strategy.
Dart also uses and: https://godbolt.org/z/1437df4En
So most of languages compiled to native binaries seem to do it. Languages like Ruby and Python don't seem to optimize this, which is hardly surprising. Ruby has a JIT now, and the JIT might do it.
And I'm not sure what the haskell version does, can someone explain: https://godbolt.org/z/xPP4E1a6h ?
Without -O, what's happening is that "mod" is a method from the Integral type class (for the non-haskellers: read "interface"/"abstract class"), and thus the program just reads the corresponding field from the stafically allocated copy of the Integral dictionary for Int, and subsequently calls it. Of course one should just inline this known function call, and that's precisely what -O lets ghc do.
Btw, it's a lot simpler if you use only unsigned ints. Both GCC and clang add a couple more instructions to make it work with negative numbers. With unsigned ints, both gcc and clang generate this simple assembly code:
is_even:
mov eax, edi
and eax, 1
retSo the easiest answer to the original question (whether a number is even or odd) likely involves &1, not %2. Note taken.
What you want to return is a boolean. And in that case the code generated is much simpler https://godbolt.org/z/5dq7MnrEx
What would it take to make the compiler optimize this code meaningfully?
https://c.godbolt.org/z/Wq65j4rrM
> What would it take to make the compiler optimize this code meaningfully?
To make GCC compile the code meaningfully, it suffices to add a bunch of else statements:
https://c.godbolt.org/z/z4KvxnbfG
In fact, GCC gets very clever at -O3.
(what matters is whether the compiler can turn the sequence of ifs into a switch statement, then both compilers have special logic to generate a lookup table.)
43690 = 0xAAAA = 0b1010101010101010
21845 = 0x5555 = 0b0101010101010101
Then
sal rax, cl
test eax, 43690
jne .L3
// .L3
mov edi, OFFSET FLAT:.LC1 // This is a pointer to the string "odd"
call puts
jmp .L2
is equivalent to C: if ((1 << number) & 0xAAAA) {
puts("odd"); // x64 is little endian
}
So execution is constant time (it only needs to check against two constants), there's no loop.It should be advantageous compared to a jump table because it doesn't have to do two indirect jumps (though you now have branches that might mispredict).
Edit: code formatting.
The author probably has a billion individual tests that will all lose their value if it were to be simplified, since they would no longer correspond to special cases in the code.
I played around on a big paper pad, and finally got a 4 bit adder implementation.
But since my unit circuit was [A,B]->[S], instead of the canonical [A,B,carry-in]->[S,carry-out], I had to add a second layer of units to propagate the first carry onward. And a third layer of units in case that also generated a carry. Etc.
So my circuit had gate of order N^2/2 and depth order of 2*N instead of gate and depth orders of N.
Not as much since the program will take increasingly more time to run as the input number gets higher. Checking if a 'small' number is even or odd will be pretty fast. Checking a 'large' number will be slow since it will go through all 'IFs' until it reaches the result.
It's like O(M/2) average time complexity where 'M' is a constant, the program memory size (number of 'IFs' stored).
Anyone feel free to use it.
static void swap(ref int a, ref int b) { int c = (b - a) ; while (c != 0) { a += Math.Sign(c); b -= Math.Sign(c); c -= Math.Sign(c); } }
It is also strange that the author is trying to generate code in assembly language, which he does not know very well, because there is a DIV/IDIV instruction.
It seems to hit a pretty pathological compilation situation. Done as ifs, rust ran out of stack. Done as "match" it has been compiling for about 24 hours.
And, what about switch statements? In theory it is said they should be faster, but I am not sure that's necessarily the case.
Another option would be to keep a database with the numbers indexed, and then compare with performing a database query.
My masterplan was to print the time to the display (starting from 8 AM), wait a second and print the next value. All manually, obviously.
I remember eventually giving up because it was getting too boring. I think I discovered about loops a year after the event.
Great times :)
Fastest portable language. Assembler is faster.
I mean, if we don't take into account that implementing this is just a joke anyway :D
function isEven(n) {
let arr = [];
let sign = -1;
for (let i = 0; i < Math.abs(n); ++i) {
arr = [...arr, sign];
sign *= -1;
}
return arr.length === 0 || arr.reduce((a, b) => a + b) === 0;
}
But I'm not going to test it, because I'm doing productive things on my computer right now, and don't want to OOM.Sadly, the OP missed an opportunity to call his program "blazingly fast" [0].
[0] https://www.youtube.com/results?search_query=primagen+blazin...
Waiting for the Will Ferrel skit of this. Though this article was funny enough on its own. The Python program to generate the C code was hilarious.
Isn't it normally called the modulo operation?
python -c 'import re;print(re.match(r"^(.*)\1$",abs(int(input("n=")))*"1")and"Even"or"Odd")'> Copyright 2023. All unauthorized distribution of this source code will be persecuted to the fullest extent of the law
#!/bin/sh
#
# Usage: repeat TIMES COMMAND
# Repeat COMMAND a given number of TIMES.
eval_full_quotes='
s/\\/\\\\/g ; s/\"/\\\"/g ; s/</\\</g ; s/>/\\>/g ;
s/\$/\\\$/g ; s/\[/\\\[/g ; s/*/\\*/g ; s/?/\\?/g ;
s/|/\\|/g ; s/&/\\&/g ; s/~/\\~/g ; s/=/\\=/g ;
s/;/\\;/g ; s/ /\\ /g ; s/%/\\%/g ; s/`/\\`/g ;
s/(/\\(/g ; s/)/\\)/g ; s/{/\\{/g ; s/}/\\}/g ;
s/#/\\#/g ; s/'"'"'/\\'"'"'/g'
errorx() { printf 'Error: %s\n' "${*:-"unknown error."}" >&2; exit 1; }
_rep() {
rep_times="$((2 * $1))"
rep_string="$2"
output=""
while [ 0 -lt "${rep_times}" ]; do
test 1 -eq "$(((rep_times /= 2) % 2))" && output="${output}${rep_string}"
rep_string="${rep_string}${rep_string}"
done
printf '%s' "${output}"
}
{
times="$1"
shift
test 0 -lt "${times}" || errorx "First argument must be a positive integer."
test 0 -lt "$#" || errorx "No command given to repeat."
repeat_command="$*"
command_length="${#repeat_command}"
max_arg="${ARG_MAX:-"$(getconf ARG_MAX)"}"
if [ "${max_arg}" -le "$((times * (command_length + 2)))" ]; then
: "$(( block_size = max_arg / (20 * (command_length + 2)) ))"
: "$(( blocks = times / block_size + 1 ))"
: "$(( remainder = (block_size + (times % block_size)) % block_size ))"
eval_string="$(_rep "${block_size}" "${repeat_command}; " | sed -e "${eval_full_quotes}")"
while [ 0 -lt "$((blocks -= 1))" ]; do
eval eval "${eval_string}"
done
eval_string="$(_rep "${remainder}" "${repeat_command}; " | sed -e "${eval_full_quotes}")"
eval eval "${eval_string}"
else
eval_string="$(_rep "${times}" "${repeat_command}; " | sed -e "${eval_full_quotes}")"
eval eval "${eval_string}"
fi
}
exitBut is it reallllly different? No not so much.
I’m dumb. This is a joke right? At most I can say AI has revolutionized how I interface with documentation.
I hope so. If not, it is a load of bullshit.
For you, AI might have just revolutionized how you "interface with documentation", but a huge share of programmers in any company are already using AI, officially or unofficially as a coding assistant.
And of course MS, IntelliJ and others are all having out products on this.
It is like a helpful, but over-confident intern helping you with toil and research.
For some reason I am thinking of npm now ...
Chatgpt is getting so lazy these days.
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char** argv) {
return printf(atoi(argv[1]) & 1 ? "odd" : "even") < 0;
} #include <stdio.h>
#include <stdlib.h>
int main(int argc, char** argv) {
return printf(&"even\0odd"[(atoi(argv[1]) & 1) * 5]) < 0;
}No it doesn't? Maybe it needs to set up page tables for 40GB, but it doesn't need to read the parts you're not using into memory, that's part of the point of memory-mapping like this - it sets up a page fault handler to load the page into memory when accessed, rather than needing to do it all up-front.
The fact that it does so by faulting in each page before instructions in it can be executed doesn’t materially avoid having to read every byte.
Clearly the author should have checked the larger numbers first to prevent this (:
It does, but not step by step the way the part I quoted described - the reading from memory can be pipelined with the execution. (And it's an ideal access pattern - reading linearly once - so it wouldn't surprise me if other optimisations kicked in)
> I decided to map the file into the address space instead of reading all of it. By doing this, we can just pretend that the entire file is already in memory and let the poor OS deal with fitting a 40 GB blob into virtual memory.
Why take a vaguely rhetorical statement and then complain it contradicts a more concretely accurate statement before it?
No, just the opposite - I read that and that's exactly why I'm saying there was no need to read the whole thing into memory before starting to execute it.
> Why take a vaguely rhetorical statement and then complain it contradicts a more concretely accurate statement before it?
Because it's a contradiction in what they've written?
I guess technically most of the RET instructions are jumped over, and so they don't need to be in memory. But that isn't how cache-lines (let alone pages) work.