Skip to content

Commit

Permalink
Merge pull request #45 from hildjj/speedup
Browse files Browse the repository at this point in the history
Special case counters of numbers, to avoid stringification and hashing
  • Loading branch information
hildjj authored Dec 23, 2024
2 parents 7b1cfea + 72e64de commit 6e21d38
Show file tree
Hide file tree
Showing 5 changed files with 189 additions and 28 deletions.
4 changes: 2 additions & 2 deletions day1.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { type MainArgs, parseFile } from './lib/utils.ts';
import { Counter } from './lib/counter.ts';
import { NumberCounter } from './lib/counter.ts';
import { Sequence } from './lib/sequence.ts';

function part1(left: number[], right: number[]): number {
Expand All @@ -11,7 +11,7 @@ function part1(left: number[], right: number[]): number {
}

function part2(left: number[], right: number[]): number {
const count = new Counter<number>();
const count = new NumberCounter();
count.addAll(right);
return left.reduce((t, v) => t + (v * count.get(v)), 0);
}
Expand Down
6 changes: 3 additions & 3 deletions day14.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Point } from './lib/rect.ts';
import { div, type MainArgs, mod, parseFile } from './lib/utils.ts';
import { Counter } from './lib/counter.ts';
import { NumberCounter } from './lib/counter.ts';

type Parsed = [px: number, py: number, vx: number, vy: number][];

Expand Down Expand Up @@ -53,8 +53,8 @@ function part2(inp: Parsed): number {
// I found the setpoints by writing images to the terminal and fiddling with
// the constants. Used Jimp and term-img.
for (let i = 0; i < Infinity; i++) {
const xc = new Counter<number>();
const yc = new Counter<number>();
const xc = new NumberCounter();
const yc = new NumberCounter();
for (let j = 0; j < pos.length; j++) {
const p = pos[j];
const [_px, _py, vx, vy] = inp[j];
Expand Down
31 changes: 15 additions & 16 deletions day22.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { type MainArgs, parseFile } from './lib/utils.ts';
import { Counter } from './lib/counter.ts';
import { Ring } from './lib/ring.ts';
import { NumberCounter } from './lib/counter.ts';

type Parsed = bigint[];

Expand All @@ -25,25 +24,25 @@ function part1(inp: Parsed): bigint {
}

function part2(inp: Parsed): number {
const patterns = new Counter();
const patterns = new NumberCounter();
for (let n of inp) {
const c = new Ring<bigint>(4);
const seen = new Set<string>();
let cost = n % 10n;
let last4 = 0;
const seen = new Set<number>();
let cost = Number(n % 10n);

for (let i = 0; i < 2000; i++) {
const nextN = nextSecret(n);
const nextCost = nextN % 10n;
c.push(nextCost - cost);

if (c.full) {
const key = c.get().join(',');
if (!seen.has(key)) {
patterns.sum(Number(nextCost), key);
seen.add(key);
}
const nextCost = Number(nextN % 10n);
// Input is -9..9. Normalize to 0..18, which takes 5 bits.
// Put the newest one at the top chunk, moving all of the others towards 0.
last4 = ((nextCost - cost + 9) << 15) | (last4 >> 5);

if ((i > 2) && !seen.has(last4)) {
patterns.sum(nextCost, last4);
seen.add(last4);
}
[n, cost] = [nextN, nextCost];
n = nextN;
cost = nextCost;
}
}

Expand Down
136 changes: 133 additions & 3 deletions lib/counter.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
type countFilter = (n: number, s: string) => number | boolean;
type numberCountFilter = (n: number, s: number) => number | boolean;
const totalCount: countFilter = (n: number): number => n;
const numberTotalCount: numberCountFilter = (n: number): number => n;

/**
* Count all the things!
Expand Down Expand Up @@ -110,7 +112,7 @@ export class Counter<T = string> {
*
* @returns the [key, value] of the maximum value, or null if empty.
*/
max(): [string, number] | null {
max(): [string, number] | undefined {
let mv = -Infinity;
let mk: string | null = null;
for (const [k, v] of this) {
Expand All @@ -119,15 +121,143 @@ export class Counter<T = string> {
mk = k;
}
}
return (mk === null) ? mk : [mk, mv];
return (mk === null) ? undefined : [mk, mv];
}

/**
* How many unique things have been added?
*
* @returns The count.
*/
size(): number {
get size(): number {
return Object.keys(this.points).length;
}
}

/**
* Count all the things!
* (When the things are single numbers)
*/
export class NumberCounter {
points = new Map<number, number>();

/**
* Iterate over the entries in points.
*/
*[Symbol.iterator](): Generator<[number, number], void, undefined> {
yield* this.points.entries();
}

/**
* Get the current value of the given key.
*
* @param vals - The thing.
* @returns - The current total for this thing, or zero if thing doesn't exist.
*/
get(val: number): number {
return this.points.get(val) ?? 0;
}

/**
* Get the current keys in the counter. Returns the concatenated string
* values, which isn't useful unless the keys are plain strings.
*
* @returns - All of the current keys
*/
keys(): MapIterator<number> {
return this.points.keys();
}

/**
* Current values.
*
* @returns - All of the current values
*/
values(): MapIterator<number> {
return this.points.values();
}

/**
* Add a thing.
*
* @param vals - The list of values that describe the thing.
* @returns - The current total for this thing.
*/
add(key: number): number {
const val = (this.points.get(key) ?? 0) + 1;
this.points.set(key, val);
return val;
}

/**
* Assuming that each value is a simple non-array, add each.
*
* @param vals
* @returns
*/
addAll(vals: Iterable<number>): this {
for (const v of vals) {
this.add(v);
}
return this;
}

/**
* Add something other than one.
*
* @param count - The amount to add.
* @param vals - The list of values that describe the thing.
* @returns number - The current total for this thing.
*/
sum(count: number, key: number): number {
const val = (this.points.get(key) ?? 0) + count;
this.points.set(key, val);
return val;
}

/**
* Count the total number of things, possibly filtered.
*
* @param fn - A filter function. If it returns boolean, count 1 if true.
* If number, counts that many.
* @returns The count of all of the things that match the filter.
*/
total(fn: numberCountFilter = numberTotalCount): number {
return this
.points
.entries()
.reduce((t, [s, v]) => {
const res = fn(v, s);
if (typeof res === 'boolean') {
return t + (res ? 1 : 0);
}
return t + res;
}, 0);
}

/**
* The maximum entry.
*
* @returns the [key, value] of the maximum value, or null if empty.
*/
max(): [number, number] | undefined {
let mv = -Infinity;
let mk = NaN;
for (const [k, v] of this.points) {
if (v > mv) {
mv = v;
mk = k;
}
}
return isNaN(mk) ? undefined : [mk, mv];
}

/**
* How many unique things have been added?
*
* @returns The count.
*/
get size(): number {
return this.points.size;
}
}
40 changes: 36 additions & 4 deletions lib/test/counter.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { Counter } from '../counter.ts';
import { Counter, NumberCounter } from '../counter.ts';
import { assertEquals } from '@std/assert';

Deno.test('Counter', () => {
const c = new Counter<number>();
assertEquals(c.max(), null);
assertEquals(c.size(), 0);
assertEquals(c.max(), undefined);
assertEquals(c.size, 0);

c.add(1, 2); // 1
assertEquals(c.get(1, 2), 1);
Expand All @@ -24,5 +24,37 @@ Deno.test('Counter', () => {
}
assertEquals(count, -8);
assertEquals(c.max(), ['1,2', 2]);
assertEquals(c.size(), 3);
assertEquals(c.size, 3);

c.addAll([10, 11, 12]);
assertEquals(c.size, 6);
});

Deno.test('NumberCounter', () => {
const c = new NumberCounter();
assertEquals(c.max(), undefined);
assertEquals(c.size, 0);

c.add(1); // 1
assertEquals(c.get(1), 1);
assertEquals(c.get(33), 0);
assertEquals([...c.values()], [1]);
c.add(1); // 2
c.add(3); // 1
assertEquals([...c.keys()], [1, 3]);
c.sum(-5, 3); // -4
c.sum(-6, 4); // -6
assertEquals(c.total(), -8);
assertEquals(c.total((p) => p > 0), 1);
assertEquals(c.total((p) => p), -8);
let count = 0;
for (const [_, p] of c) {
count += p;
}
assertEquals(count, -8);
assertEquals(c.max(), [1, 2]);
assertEquals(c.size, 3);

c.addAll([10, 11, 12]);
assertEquals(c.size, 6);
});

0 comments on commit 6e21d38

Please sign in to comment.