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
Empty file removed src/problem1/.keep
Empty file.
74 changes: 74 additions & 0 deletions src/problem1/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# Problem 1 - `sum_to_n`

Three unique implementations of a function that returns the summation to `n`,
e.g. `sum_to_n(5) === 1 + 2 + 3 + 4 + 5 === 15`.

## Problem statement

> Provide 3 unique implementations of the following 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`.

The module (`sum_to_n.mjs`) is a side-effect-free ES module that exports all
three functions (`sum_to_n_a`, `sum_to_n_b`, `sum_to_n_c`), so it can be
imported cleanly from other code.

## The three approaches

| Fn | Approach | Time | Space |
| ------------ | ---------------------------- | ------ | ------------ |
| `sum_to_n_a` | Iterative `for` loop | `O(n)` | `O(1)` |
| `sum_to_n_b` | Closed-form Gauss formula | `O(1)` | `O(1)` |
| `sum_to_n_c` | Recursion (functional style) | `O(n)` | `O(n)` stack |

### a) Iterative loop

Accumulates a running total, walking `1..n` for positive `n` or `n..-1` for
negative `n`.

### b) Closed-form (Gauss)

Uses the triangular-number identity. Important detail: the bare
`n*(n+1)/2` does **not** satisfy the negative convention - for `n = -3` it
returns `3`, not `-6`. The sum of `n..-1` for `n < 0` equals the triangular
number of `|n|`, negated. The implementation therefore uses the sign-aware
closed form:

```
sign(n) * |n| * (|n| + 1) / 2
```

which gives `15` for `n=5`, `0` for `n=0`, and `-6` for `n=-3`, all in constant
time.

### c) Recursion

Peels one term off toward `0` per call: `f(n) = n + f(n ∓ 1)`. Clear and
functional, but uses `O(n)` call-stack space (so very large `|n|` can overflow
the stack - a trade-off documented here for completeness).

## Negative-`n` assumption

Since `n` may be any integer, the three implementations agree on the following
consistent behaviour:

| `n` | result | meaning |
| ---- | ------ | ---------------------------------- |
| `5` | `15` | sum of `1..5` |
| `0` | `0` | empty sum |
| `-3` | `-6` | sum of `-3, -2, -1` (i.e. `n..-1`) |

## Run the demo

```bash
node sum_to_n.mjs
```

A clean run prints `all checks passed`. The file contains `console.assert`
sanity checks (behind an `import.meta` main-module guard) that stay silent on
success and log a message on failure. Requires **Node 20+**, no dependencies to
install.
116 changes: 116 additions & 0 deletions src/problem1/sum_to_n.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/**
* Problem 1: Three unique implementations of `sum_to_n`.
*
* Task:
* Input: n - any integer (result assumed < Number.MAX_SAFE_INTEGER).
* Output: summation to n, e.g. sum_to_n(5) === 1 + 2 + 3 + 4 + 5 === 15.
*
* Negative-n convention (applied CONSISTENTLY across all three):
* - sum_to_n(0) === 0
* - For n > 0: sum of 1..n -> sum_to_n(5) === 15
* - For n < 0: sum of n..-1 -> sum_to_n(-3) === (-3)+(-2)+(-1) === -6
* All three implementations honour this convention. Note the closed form
* needs a sign-aware variant (see sum_to_n_b) because the bare n*(n+1)/2
* does not produce -6 for n = -3.
*
* Complexity summary:
* a) iterative for-loop : O(n) time, O(1) space
* b) closed-form (Gauss): O(1) time, O(1) space
* c) recursion : O(n) time, O(n) stack space
*
* This module has NO top-level side effects, so it is safe to import from
* other modules. A tiny runnable demo lives behind the
* `import.meta` main-module guard at the bottom of the file.
*/

/**
* a) Iterative for-loop.
*
* Walks either 1..n (n > 0) or n..-1 (n < 0), accumulating a running sum.
* O(n) time, O(1) space.
*
* @param {number} n - any integer
* @returns {number} the summation to n
*/
export const sum_to_n_a = function (n) {
let sum = 0;
if (n >= 0) {
for (let i = 1; i <= n; i++) sum += i;
} else {
for (let i = n; i <= -1; i++) sum += i;
}
return sum;
};

/**
* b) Closed-form Gauss formula.
*
* For n >= 0 this is the classic triangular number n*(n+1)/2.
* NOTE: the *plain* formula n*(n+1)/2 does NOT match our negative
* convention -- for n = -3 it gives (-3)*(-2)/2 = 3, but we want -6.
* The convention "sum of n..-1" for n < 0 equals -(|n|*(|n|+1)/2), i.e.
* the same triangular number negated. So the sign-aware closed form is:
* sign(n) * |n| * (|n| + 1) / 2
* which yields 15 for n=5, 0 for n=0, and -6 for n=-3. O(1) time & space.
*
* @param {number} n - any integer
* @returns {number} the summation to n
*/
export const sum_to_n_b = function (n) {
const m = Math.abs(n);
return (Math.sign(n) * (m * (m + 1))) / 2;
};

/**
* c) Recursive / functional approach.
*
* Peels one term off toward 0 each call: f(n) = n + f(n -/+ 1).
* O(n) time, O(n) call-stack space.
*
* @param {number} n - any integer
* @returns {number} the summation to n
*/
export const sum_to_n_c = function (n) {
if (n === 0) return 0;
if (n > 0) return n + sum_to_n_c(n - 1);
return n + sum_to_n_c(n + 1);
};

// --- Runnable demo --------------------------------------------------------
// The self-test lives in a function so the caller at the bottom can be
// adjusted freely. Keeps the module free of import-time side effects.
function runSelfTest() {
const cases = [
[5, 15],
[1, 1],
[0, 0],
[10, 55],
[100, 5050],
[-1, -1],
[-3, -6],
[-100, -5050],
];

for (const [n, expected] of cases) {
console.assert(sum_to_n_a(n) === expected, `a(${n}) => ${sum_to_n_a(n)}, want ${expected}`);
console.assert(sum_to_n_b(n) === expected, `b(${n}) => ${sum_to_n_b(n)}, want ${expected}`);
console.assert(sum_to_n_c(n) === expected, `c(${n}) => ${sum_to_n_c(n)}, want ${expected}`);
console.assert(
sum_to_n_a(n) === sum_to_n_b(n) && sum_to_n_b(n) === sum_to_n_c(n),
`disagreement at n=${n}`,
);
}

console.log("all checks passed");
}





const n = 5;

runSelfTest();
console.log(`sum_to_n_a(${n}) =`, sum_to_n_a(n));
console.log(`sum_to_n_b(${n}) =`, sum_to_n_b(n));
console.log(`sum_to_n_c(${n}) =`, sum_to_n_c(n));
28 changes: 28 additions & 0 deletions src/problem2/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
*.local
*.tsbuildinfo

# Test coverage
coverage

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
Loading