Skip to content

Instantly share code, notes, and snippets.

@voltrevo
Last active January 1, 2017 10:30
Show Gist options
  • Save voltrevo/70996fea7c2aba13f080eff054d5e1df to your computer and use it in GitHub Desktop.
Save voltrevo/70996fea7c2aba13f080eff054d5e1df to your computer and use it in GitHub Desktop.
Goldbach's Comet
// Ported to TypeScript from https://github.com/kayellpeee/hsl_rgb_converter/blob/master/converter.js
export default function hslToRgb(hue: number, saturation: number, lightness: number): [number, number, number] {
// based on algorithm from http://en.wikipedia.org/wiki/HSL_and_HSV#Converting_to_RGB
if( hue == undefined ){
return [0, 0, 0];
}
var chroma = (1 - Math.abs((2 * lightness) - 1)) * saturation;
var huePrime = hue / 60;
var secondComponent = chroma * (1 - Math.abs((huePrime % 2) - 1));
huePrime = Math.floor(huePrime);
var red;
var green;
var blue;
if( huePrime === 0 ){
red = chroma;
green = secondComponent;
blue = 0;
}else if( huePrime === 1 ){
red = secondComponent;
green = chroma;
blue = 0;
}else if( huePrime === 2 ){
red = 0;
green = chroma;
blue = secondComponent;
}else if( huePrime === 3 ){
red = 0;
green = secondComponent;
blue = chroma;
}else if( huePrime === 4 ){
red = secondComponent;
green = 0;
blue = chroma;
}else if( huePrime === 5 ){
red = chroma;
green = 0;
blue = secondComponent;
}
var lightnessAdjustment = lightness - (chroma / 2);
red += lightnessAdjustment;
green += lightnessAdjustment;
blue += lightnessAdjustment;
return [Math.round(red * 255), Math.round(green * 255), Math.round(blue * 255)];
};
import hslToRgb from './hslToRgb';
function eratosthenesFlags(n: number) {
const start = Date.now();
const flags: boolean[] = (new Array(n)).fill(true);
flags[0] = false;
flags[1] = false;
let p = 2;
while (p * p < n) {
let kp = 2 * p;
while (kp < n) {
flags[kp] = false;
kp += p;
}
p++;
while (p < n && !flags[p]) {
p++;
}
}
console.log(`eratosthenesFlags: ${Date.now() - start}`);
return flags;
}
function eratosthenesPrimes(n: number) {
const flags = eratosthenesFlags(n);
const primes = [];
for (let i = 0; i < n; i++) {
if (flags[i]) {
primes.push(i);
}
}
return primes;
}
function goldbachTallies(n: number): number[] {
const start = Date.now();
const primes = eratosthenesPrimes(n);
const tallies: number[] = (new Array(n)).fill(0);
const primeLen = primes.length;
for (let i = 0; i < primeLen; i++) {
for (let j = i; j < primeLen; j++) {
const sum = primes[i] + primes[j];
if (sum >= n) {
break;
}
tallies[sum]++;
}
}
console.log(`goldbachTallies: ${Date.now() - start}`);
return tallies;
}
const draw = (() => {
let canvas: HTMLCanvasElement | null = null;
let ctx: CanvasRenderingContext2D | null = null;
window.addEventListener('load', () => {
document.body.style.backgroundColor = 'black';
const div = document.createElement('div');
const pratio = window.devicePixelRatio;
const width = window.innerWidth;
const height = window.innerHeight;
div.innerHTML = `<canvas ` +
`width=${pratio * width} ` +
`height=${pratio * height} ` +
`style='` +
`position: absolute; ` +
`left: 0; ` +
`top: 0; ` +
`width: ${width}px; ` +
`height: ${height}px;` +
`'` +
`></canvas>`;
canvas = div.firstChild as HTMLCanvasElement;
ctx = canvas.getContext('2d');
document.body.appendChild(canvas);
});
let queuedCb: ((imgData: ImageData) => ImageData) | null = null;
return (cb: (imgData: ImageData) => ImageData) => {
queuedCb = cb;
window.requestAnimationFrame(() => {
if (!queuedCb || !ctx || !canvas) {
return;
}
const imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);
ctx.putImageData(queuedCb(imgData), 0, 0);
queuedCb = null;
});
};
})();
function drawPoints(points: [number, number][], [rLevels, gLevels, bLevels]: [number, number, number]) {
let minX = Infinity;
let maxX = -Infinity;
let minY = Infinity;
let maxY = -Infinity;
for (const [x, y] of points) {
minX = Math.min(x, minX);
maxX = Math.max(x, maxX);
minY = Math.min(y, minY);
maxY = Math.max(y, maxY);
}
draw(imgData => {
const start = Date.now();
const pixelTallies: number[] = (new Array(imgData.width * imgData.height)).fill(0);
for (const [x, y] of points) {
const i = Math.floor(imgData.height * (1 - (y - minY) / (maxY - minY)));
const j = Math.floor(imgData.width * (x - minX) / (maxX - minX));
const idx = (imgData.width * i + j);
if (idx >= pixelTallies.length) {
continue;
}
pixelTallies[idx]++;
}
// const pixelTallyMax = pixelTallies.reduce((x, y) => Math.max(x, y));
const sortedPixelTallies = pixelTallies.filter(i => i !== 0).sort();
for (let i = 0; i < pixelTallies.length; i++) {
if (pixelTallies[i] === 0) {
continue;
}
const percentile = sortedPixelTallies.indexOf(pixelTallies[i]) / sortedPixelTallies.length;
const [r, g, b] = hslToRgb(percentile * 270, 1, 0.5);
imgData.data[4 * i + 0] = r;
imgData.data[4 * i + 1] = g;
imgData.data[4 * i + 2] = b;
imgData.data[4 * i + 3] = 255;
}
console.log(`imgData assignment: ${Date.now() - start}`);
return imgData;
});
}
const n = 10000000;
const start = Date.now();
const tallies = goldbachTallies(n);
const points = tallies!.map((x, i) => [i, x] as [number, number]);
console.log(`n: ${n}, t: ${Date.now() - start}`);
drawPoints(points, [1, 20, 60]);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment