Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Dependencies
**/node_modules/
**/package-lock.json
**/yarn.lock
**/pnpm-lock.yaml

# Build outputs
**/dist/
**/build/
**/out/
**/.next/
**/.nuxt/
*.log

# Environment variables
**/.env
**/.env.local
**/.env.*.local

# IDE
.idea/
.vscode/
*.swp
*.swo
*~

# OS
.DS_Store
Thumbs.db

# Testing
**/coverage/
*.lcov

# Logs
**/logs/
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Runtime data
pids/
*.pid
*.seed
*.pid.lock

# TypeScript
**/*.tsbuildinfo
**/tsconfig.tsbuildinfo

# Optional npm cache directory
**/.npm

# Optional eslint cache
**/.eslintcache

# Misc
**/.turbo/
**/.cache/
**/.parcel-cache/
**/.webpack/
**/typings/
File renamed without changes.
43 changes: 43 additions & 0 deletions src/problem4/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Task

Provide 3 unique implementations of the following function in TypeScript.

- Comment on the complexity or efficiency of each function.

**Input**: `n` - any integer

*Assuming this input will always produce a result lesser than `Number.MAX_SAFE_INTEGER`*.

**Output**: `return` - summation to `n`, i.e. `sum_to_n(5) === 1 + 2 + 3 + 4 + 5 === 15`.

## Solution

Three implementations provided in `sum_to_n.ts`:

| Function | Approach | Time Complexity | Space Complexity |
|----------|----------|-----------------|------------------|
| `sumIterative` | For loop accumulation | O(n) | O(1) |
| `sumRecursive` | Recursive calls | O(n) | O(n) |
| `sumFormula` | Gauss's formula `n*(n+1)/2` | O(1) | O(1) |

## Design Decisions

- **All integers supported**: Positive, zero, and negative values handled correctly
- **Named exports**: Following TypeScript conventions over default exports
- **Descriptive naming**: `sumIterative`, `sumRecursive`, `sumFormula` for clarity
- **JSDoc comments**: Document time/space complexity for each function

## Trade-offs

- **Iterative**: Simple and memory-efficient, but requires O(n) time
- **Recursive**: Elegant but uses O(n) stack space; risks stack overflow for large n
- **Formula**: Optimal O(1) performance, but requires separate logic for negative numbers

## Testing

21 test cases in `sum_to_n.test.ts` covering:
- Positive numbers (1, 5, 100)
- Zero
- Negative numbers (-1, -2, -3)
- Large numbers

119 changes: 119 additions & 0 deletions src/problem4/sum_to_n.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import { describe, it, expect } from "vitest";
import { sumIterative, sumRecursive, sumFormula } from "./sum_to_n";

// Tests for iterative approach - handles large numbers but O(n) time
describe("sumIterative", () => {
it("should return 15 for n = 5", () => {
expect(sumIterative(5)).toBe(15);
});

it("should return 1 for n = 1", () => {
expect(sumIterative(1)).toBe(1);
});

it("should return 0 for n = 0", () => {
expect(sumIterative(0)).toBe(0);
});

it("should return 0 for n = -1", () => {
expect(sumIterative(-1)).toBe(0);
});

it("should return -2 for n = -2", () => {
expect(sumIterative(-2)).toBe(-2);
});

it("should return -5 for n = -3", () => {
expect(sumIterative(-3)).toBe(-5);
});

it("should return 5050 for n = 100", () => {
expect(sumIterative(100)).toBe(5050);
});

it("should handle large numbers without overflow", () => {
expect(sumIterative(100000)).toBe(5000050000);
});

it("should handle large negative numbers", () => {
expect(sumIterative(-100000)).toBe(-5000049999);
});
});

// Tests for recursive approach - uses trampoline to avoid stack overflow
describe("sumRecursive", () => {
it("should return 15 for n = 5", () => {
expect(sumRecursive(5)).toBe(15);
});

it("should return 1 for n = 1", () => {
expect(sumRecursive(1)).toBe(1);
});

it("should return 0 for n = 0", () => {
expect(sumRecursive(0)).toBe(0);
});

it("should return 0 for n = -1", () => {
expect(sumRecursive(-1)).toBe(0);
});

it("should return -2 for n = -2", () => {
expect(sumRecursive(-2)).toBe(-2);
});

it("should return -5 for n = -3", () => {
expect(sumRecursive(-3)).toBe(-5);
});

it("should return 5050 for n = 100", () => {
expect(sumRecursive(100)).toBe(5050);
});

it("should handle large numbers without stack overflow", () => {
expect(sumRecursive(100000)).toBe(5000050000);
});

it("should handle large negative numbers without stack overflow", () => {
expect(sumRecursive(-100000)).toBe(-5000049999);
});
});

// Tests for formula approach - uses BigInt for intermediate overflow protection
describe("sumFormula", () => {
it("should return 15 for n = 5", () => {
expect(sumFormula(5)).toBe(15);
});

it("should return 1 for n = 1", () => {
expect(sumFormula(1)).toBe(1);
});

it("should return 0 for n = 0", () => {
expect(sumFormula(0)).toBe(0);
});

it("should return 0 for n = -1", () => {
expect(sumFormula(-1)).toBe(0);
});

it("should return -2 for n = -2", () => {
expect(sumFormula(-2)).toBe(-2);
});

it("should return -5 for n = -3", () => {
expect(sumFormula(-3)).toBe(-5);
});

it("should return 5050 for n = 100", () => {
expect(sumFormula(100)).toBe(5050);
});

it("should handle large numbers with BigInt precision", () => {
expect(sumFormula(100000000)).toBe(5000000050000000);
});

it("should handle large negative numbers with BigInt precision", () => {
expect(sumFormula(-100000000)).toBe(-5000000049999999);
});
});
53 changes: 53 additions & 0 deletions src/problem4/sum_to_n.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/**
* Iterative approach using a for loop.
* Time Complexity: O(n) — iterates from 1 to n (or 1 down to n for negative)
* Space Complexity: O(1) — only uses a single accumulator variable
*/
export function sumIterative(n: number): number {
let sum = 0;
if (n >= 1) {
for (let i = 1; i <= n; i++) sum += i;
} else if (n <= -1) {
for (let i = 1; i >= n; i--) sum += i;
}
return sum;
}

type Thunk<T> = T | (() => Thunk<T>);

function trampoline<T>(fn: () => Thunk<T>): T {
let result: Thunk<T> = fn();
while (typeof result === "function") {
result = (result as () => Thunk<T>)();
}
return result;
}

function sumRecHelper(n: number, acc: number): Thunk<number> {
if (n === 0) return acc;
if (n > 0) return () => sumRecHelper(n - 1, acc + n);
if (n === -1) return acc;
return () => sumRecHelper(n + 1, acc + n);
}

/**
* Recursive approach with trampoline to avoid stack overflow.
* Time Complexity: O(|n|) — makes |n| recursive calls
* Space Complexity: O(1) — trampoline converts recursion to iteration
*/
export function sumRecursive(n: number): number {
return trampoline(() => sumRecHelper(n, 0));
}

/**
* Mathematical formula approach (Gauss's formula) using BigInt for precision.
* Time Complexity: O(1) — constant time computation
* Space Complexity: O(1) — no additional memory used
*/
export function sumFormula(n: number): number {
const bigN = BigInt(n);
if (n >= 0) {
return Number((bigN * (bigN + 1n)) / 2n);
}
return Number(1n + ((1n - bigN) * bigN) / 2n);
}
16 changes: 16 additions & 0 deletions src/problem5/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
node_modules
dist
.env
.env.*
.git
.gitignore
.dockerignore
docker-compose.yml
README.md
DECISIONS.md
__tests__
coverage
.vscode
.idea
*.md
*.log
13 changes: 13 additions & 0 deletions src/problem5/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
PORT=3000
NODE_ENV=development
LOG_LEVEL=info
SHUTDOWN_TIMEOUT_MS=10000

DB_HOST=localhost
DB_PORT=5432
DB_USER=postgres
DB_PASSWORD=postgres
DB_NAME=products_db
DB_MAX_CONNECTIONS=10
DB_CONNECTION_TIMEOUT_MS=5000
DB_QUERY_TIMEOUT_MS=10000
35 changes: 35 additions & 0 deletions src/problem5/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Dependencies
node_modules/

# Build outputs
dist/

# Environment variables
.env
.env.local
.env.*.local

# IDE
.idea/
.vscode/
*.swp
*.swo
*~

# OS
.DS_Store
Thumbs.db

# Testing
coverage/
*.lcov

# Logs
*.log
npm-debug.log*

# TypeScript
*.tsbuildinfo

# Docker volumes
postgres_data/
30 changes: 30 additions & 0 deletions src/problem5/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
FROM node:20-alpine AS builder

WORKDIR /app

COPY src/problem5/package*.json ./
RUN npm ci

COPY src/problem5/ .
RUN npm run build

FROM node:20-alpine

WORKDIR /app

RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001

COPY src/problem5/package*.json ./
RUN npm ci --only=production && npm cache clean --force

COPY --from=builder --chown=nodejs:nodejs /app/dist ./dist

USER nodejs

EXPOSE 3000

HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD node -e "require('http').get('http://localhost:3000/health/liveness', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"

CMD ["node", "dist/index.js"]
Loading