Create separate classes, cache math answers
This commit is contained in:
parent
a4dbea4141
commit
aaebd736dd
|
@ -0,0 +1,73 @@
|
||||||
|
import {CachedPrimeMath, PrimeMath} from "./PrimeMath";
|
||||||
|
|
||||||
|
|
||||||
|
const width: number = 100;
|
||||||
|
const height: number = 100;
|
||||||
|
|
||||||
|
|
||||||
|
export class Display {
|
||||||
|
private readonly scale: number = 15;
|
||||||
|
|
||||||
|
private readonly canvas: HTMLCanvasElement;
|
||||||
|
private readonly primeMath: PrimeMath;
|
||||||
|
|
||||||
|
|
||||||
|
constructor(canvas: HTMLCanvasElement) {
|
||||||
|
this.canvas = canvas;
|
||||||
|
this.canvas.width = this.scale * width;
|
||||||
|
this.canvas.height = this.scale * height;
|
||||||
|
|
||||||
|
this.primeMath = CachedPrimeMath.getInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public startDrawLoop(): void {
|
||||||
|
const cb = () => {
|
||||||
|
this.draw();
|
||||||
|
window.requestAnimationFrame(cb);
|
||||||
|
};
|
||||||
|
window.requestAnimationFrame(cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private draw(): void {
|
||||||
|
const ctx = this.canvas.getContext("2d")!;
|
||||||
|
|
||||||
|
this.clearCanvas(ctx);
|
||||||
|
this.drawGrid(ctx);
|
||||||
|
this.drawPrimes(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
private clearCanvas(ctx: CanvasRenderingContext2D): void {
|
||||||
|
ctx.save();
|
||||||
|
ctx.fillStyle = "#ffffff";
|
||||||
|
ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
|
||||||
|
ctx.restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
private drawGrid(ctx: CanvasRenderingContext2D): void {
|
||||||
|
ctx.save();
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.strokeStyle = "#7b7b7b";
|
||||||
|
for (let x = 0; x <= width; x++) {
|
||||||
|
ctx.moveTo(x * this.scale, 0);
|
||||||
|
ctx.lineTo(x * this.scale, height * this.scale);
|
||||||
|
}
|
||||||
|
for (let y = 0; y <= height; y++) {
|
||||||
|
ctx.moveTo(0, y * this.scale);
|
||||||
|
ctx.lineTo(width * this.scale, y * this.scale);
|
||||||
|
}
|
||||||
|
ctx.stroke();
|
||||||
|
ctx.restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
private drawPrimes(ctx: CanvasRenderingContext2D): void {
|
||||||
|
ctx.save();
|
||||||
|
ctx.fillStyle = "#ff0000";
|
||||||
|
for (let i = 1; i <= width * height; i++) {
|
||||||
|
if (this.primeMath.isPrime(i))
|
||||||
|
ctx.fillRect(((i - 1) % width) * this.scale, Math.floor((i - 1) / width) * this.scale, this.scale, this.scale);
|
||||||
|
}
|
||||||
|
ctx.restore();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,8 @@
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const {$, doAfterLoad, footer, header, nav} = window.fwdekker;
|
const {$, doAfterLoad, footer, header, nav} = window.fwdekker;
|
||||||
|
|
||||||
|
import {Display} from "./Display";
|
||||||
|
|
||||||
|
|
||||||
// Set up template
|
// Set up template
|
||||||
doAfterLoad(() => {
|
doAfterLoad(() => {
|
||||||
|
@ -17,87 +19,10 @@ doAfterLoad(() => {
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
const scale: number = 15;
|
|
||||||
const width: number = 100;
|
|
||||||
const height: number = 100;
|
|
||||||
|
|
||||||
|
|
||||||
function isPrime(n: number): boolean {
|
|
||||||
if (!Number.isInteger(n)) return false;
|
|
||||||
|
|
||||||
if (n < 2) return false;
|
|
||||||
if (n === 2) return true;
|
|
||||||
if (n % 2 === 0) return false;
|
|
||||||
|
|
||||||
const limit = Math.floor(Math.sqrt(n));
|
|
||||||
for (let i = 3; i <= limit; i += 2)
|
|
||||||
if (n % i === 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function decompose(n: number): number[] {
|
|
||||||
let factors: number[] = [];
|
|
||||||
|
|
||||||
let target = n;
|
|
||||||
for (let i = 2; target !== 1; i = i === 2 ? 3 : i + 2) {
|
|
||||||
while (target % i === 0) {
|
|
||||||
factors.push(i);
|
|
||||||
target = target / i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return factors;
|
|
||||||
}
|
|
||||||
|
|
||||||
function draw() {
|
|
||||||
const canvas = $("#canvas");
|
|
||||||
const ctx = canvas.getContext("2d")!;
|
|
||||||
|
|
||||||
function clearCanvas(ctx: CanvasRenderingContext2D): void {
|
|
||||||
ctx.save();
|
|
||||||
ctx.fillStyle = "#ffffff";
|
|
||||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
||||||
ctx.restore();
|
|
||||||
}
|
|
||||||
|
|
||||||
clearCanvas(ctx);
|
|
||||||
|
|
||||||
ctx.save();
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.strokeStyle = "#7b7b7b";
|
|
||||||
for (let x = 0; x <= width; x++) {
|
|
||||||
ctx.moveTo(x * scale, 0);
|
|
||||||
ctx.lineTo(x * scale, height * scale);
|
|
||||||
}
|
|
||||||
for (let y = 0; y <= height; y++) {
|
|
||||||
ctx.moveTo(0, y * scale);
|
|
||||||
ctx.lineTo(width * scale, y * scale);
|
|
||||||
}
|
|
||||||
ctx.stroke();
|
|
||||||
ctx.restore();
|
|
||||||
|
|
||||||
ctx.save();
|
|
||||||
ctx.fillStyle = "#ff0000";
|
|
||||||
for (let i = 1; i <= width * height; i++) {
|
|
||||||
if (isPrime(i))
|
|
||||||
ctx.fillRect(((i - 1) % width) * scale, Math.floor((i - 1) / width) * scale, scale, scale);
|
|
||||||
}
|
|
||||||
ctx.restore();
|
|
||||||
}
|
|
||||||
|
|
||||||
doAfterLoad(async () => {
|
doAfterLoad(async () => {
|
||||||
const canvas = $("#canvas");
|
const canvas = $("#canvas");
|
||||||
canvas.classList.remove("invisible");
|
canvas.classList.remove("invisible");
|
||||||
canvas.width = scale * width;
|
|
||||||
canvas.height = scale * height;
|
|
||||||
|
|
||||||
{
|
const display = new Display(canvas);
|
||||||
const cb = () => {
|
display.startDrawLoop();
|
||||||
draw();
|
|
||||||
window.requestAnimationFrame(cb);
|
|
||||||
};
|
|
||||||
window.requestAnimationFrame(cb);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,92 @@
|
||||||
|
export interface PrimeMath {
|
||||||
|
isPrime(n: number): boolean
|
||||||
|
|
||||||
|
decompose(n: number): number[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SimplePrimeMath implements PrimeMath {
|
||||||
|
private static instance: SimplePrimeMath;
|
||||||
|
|
||||||
|
|
||||||
|
private constructor() {
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
public static getInstance(): SimplePrimeMath {
|
||||||
|
if (!SimplePrimeMath.instance)
|
||||||
|
SimplePrimeMath.instance = new SimplePrimeMath();
|
||||||
|
|
||||||
|
return SimplePrimeMath.instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public isPrime(n: number): boolean {
|
||||||
|
if (!Number.isInteger(n)) throw Error("Cannot check non-integer number for primality.");
|
||||||
|
|
||||||
|
if (n < 2) return false;
|
||||||
|
if (n === 2) return true;
|
||||||
|
if (n % 2 === 0) return false;
|
||||||
|
|
||||||
|
const limit = Math.floor(Math.sqrt(n));
|
||||||
|
for (let i = 3; i <= limit; i += 2)
|
||||||
|
if (n % i === 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public decompose(n: number): number[] {
|
||||||
|
if (!Number.isInteger(n)) throw Error("Cannot find prime decomposition of non-integer number.");
|
||||||
|
|
||||||
|
let factors: number[] = [];
|
||||||
|
|
||||||
|
let target = n;
|
||||||
|
for (let i = 2; target !== 1; i = i === 2 ? 3 : i + 2) {
|
||||||
|
while (target % i === 0) {
|
||||||
|
factors.push(i);
|
||||||
|
target = target / i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return factors;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CachedPrimeMath implements PrimeMath {
|
||||||
|
private static instance: CachedPrimeMath;
|
||||||
|
private static simple: SimplePrimeMath = SimplePrimeMath.getInstance();
|
||||||
|
|
||||||
|
private isPrimeCache: Map<number, boolean> = new Map();
|
||||||
|
private decomposeCache: Map<number, number[]> = new Map();
|
||||||
|
|
||||||
|
|
||||||
|
private constructor() {
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
public static getInstance(): CachedPrimeMath {
|
||||||
|
if (!CachedPrimeMath.instance)
|
||||||
|
CachedPrimeMath.instance = new CachedPrimeMath();
|
||||||
|
|
||||||
|
return CachedPrimeMath.instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public isPrime(n: number): boolean {
|
||||||
|
return CachedPrimeMath.invokeCached(n, CachedPrimeMath.simple.isPrime, this.isPrimeCache);
|
||||||
|
}
|
||||||
|
|
||||||
|
public decompose(n: number): number[] {
|
||||||
|
return CachedPrimeMath.invokeCached(n, CachedPrimeMath.simple.decompose, this.decomposeCache);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static invokeCached<IN, OUT>(input: IN, fun: (_: IN) => OUT, cache: Map<IN, OUT>): OUT {
|
||||||
|
if (cache.has(input))
|
||||||
|
return cache.get(input)!!;
|
||||||
|
|
||||||
|
const output = fun(input);
|
||||||
|
cache.set(input, output);
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue