Skip to content

Commit 7181e71

Browse files
authored
Add square-root (#1587)
* Add `square-root` * Format files * Add custom metadata fields
1 parent 6913e3e commit 7181e71

18 files changed

+7031
-0
lines changed

config.json

+8
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,14 @@
416416
"time"
417417
]
418418
},
419+
{
420+
"slug": "square-root",
421+
"name": "Square Root",
422+
"uuid": "bf4a0802-334b-4ed7-9ade-a3ef5fa6878e",
423+
"practices": [],
424+
"prerequisites": [],
425+
"difficulty": 2
426+
},
419427
{
420428
"slug": "reverse-string",
421429
"name": "Reverse String",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Instructions
2+
3+
Your task is to calculate the square root of a given number.
4+
5+
- Try to avoid using the pre-existing math libraries of your language.
6+
- As input you'll be given a positive whole number, i.e. 1, 2, 3, 4…
7+
- You are only required to handle cases where the result is a positive whole number.
8+
9+
Some potential approaches:
10+
11+
- Linear or binary search for a number that gives the input number when squared.
12+
- Successive approximation using Newton's or Heron's method.
13+
- Calculating one digit at a time or one bit at a time.
14+
15+
You can check out the Wikipedia pages on [integer square root][integer-square-root] and [methods of computing square roots][computing-square-roots] to help with choosing a method of calculation.
16+
17+
[integer-square-root]: https://en.wikipedia.org/wiki/Integer_square_root
18+
[computing-square-roots]: https://en.wikipedia.org/wiki/Methods_of_computing_square_roots
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Introduction
2+
3+
We are launching a deep space exploration rocket and we need a way to make sure the navigation system stays on target.
4+
5+
As the first step in our calculation, we take a target number and find its square root (that is, the number that when multiplied by itself equals the target number).
6+
7+
The journey will be very long.
8+
To make the batteries last as long as possible, we had to make our rocket's onboard computer very power efficient.
9+
Unfortunately that means that we can't rely on fancy math libraries and functions, as they use more power.
10+
Instead we want to implement our own square root calculation.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
"authors": [
3+
"BNAndras"
4+
],
5+
"files": {
6+
"solution": [
7+
"square-root.ts"
8+
],
9+
"test": [
10+
"square-root.test.ts"
11+
],
12+
"example": [
13+
".meta/proof.ci.ts"
14+
]
15+
},
16+
"blurb": "Given a natural radicand, return its square root.",
17+
"custom": {
18+
"version.tests.compatibility": "jest-29",
19+
"flag.tests.task-per-describe": false,
20+
"flag.tests.may-run-long": false,
21+
"flag.tests.includes-optional": false,
22+
"flag.tests.jest": true,
23+
"flag.tests.tstyche": false
24+
},
25+
"source": "wolf99",
26+
"source_url": "https://github.com/exercism/problem-specifications/pull/1582"
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
export function squareRoot(radicand: number): number {
2+
if (radicand === 1) {
3+
return 1
4+
}
5+
6+
let guess = Math.floor(radicand / 2)
7+
for (let i = 0; i < 10; i++) {
8+
guess = Math.floor((guess + radicand / guess) / 2)
9+
}
10+
11+
return guess
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# This is an auto-generated file.
2+
#
3+
# Regenerating this file via `configlet sync` will:
4+
# - Recreate every `description` key/value pair
5+
# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications
6+
# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion)
7+
# - Preserve any other key/value pair
8+
#
9+
# As user-added comments (using the # character) will be removed when this file
10+
# is regenerated, comments can be added via a `comment` key.
11+
12+
[9b748478-7b0a-490c-b87a-609dacf631fd]
13+
description = "root of 1"
14+
15+
[7d3aa9ba-9ac6-4e93-a18b-2e8b477139bb]
16+
description = "root of 4"
17+
18+
[6624aabf-3659-4ae0-a1c8-25ae7f33c6ef]
19+
description = "root of 25"
20+
21+
[93beac69-265e-4429-abb1-94506b431f81]
22+
description = "root of 81"
23+
24+
[fbddfeda-8c4f-4bc4-87ca-6991af35360e]
25+
description = "root of 196"
26+
27+
[c03d0532-8368-4734-a8e0-f96a9eb7fc1d]
28+
description = "root of 65025"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"recommendations": [
3+
"arcanis.vscode-zipfs",
4+
"dbaeumer.vscode-eslint",
5+
"esbenp.prettier-vscode"
6+
]
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"cSpell.words": ["exercism"],
3+
"search.exclude": {
4+
"**/.yarn": true,
5+
"**/.pnp.*": true
6+
}
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
compressionLevel: mixed
2+
3+
enableGlobalCache: true
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module.exports = {
2+
// eslint-disable-next-line @typescript-eslint/no-require-imports
3+
presets: [[require('@exercism/babel-preset-typescript'), { corejs: '3.38' }]],
4+
plugins: [],
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// @ts-check
2+
3+
import tsEslint from 'typescript-eslint'
4+
import config from '@exercism/eslint-config-typescript'
5+
import maintainersConfig from '@exercism/eslint-config-typescript/maintainers.mjs'
6+
7+
export default [
8+
...tsEslint.config(...config, {
9+
files: ['.meta/proof.ci.ts', '.meta/exemplar.ts', '*.test.ts'],
10+
extends: maintainersConfig,
11+
}),
12+
{
13+
ignores: [
14+
// # Protected or generated
15+
'.git/**/*',
16+
'.vscode/**/*',
17+
18+
//# When using npm
19+
'node_modules/**/*',
20+
21+
// # Configuration files
22+
'babel.config.cjs',
23+
'jest.config.cjs',
24+
],
25+
},
26+
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
module.exports = {
2+
verbose: true,
3+
projects: ['<rootDir>'],
4+
testMatch: [
5+
'**/__tests__/**/*.[jt]s?(x)',
6+
'**/test/**/*.[jt]s?(x)',
7+
'**/?(*.)+(spec|test).[jt]s?(x)',
8+
],
9+
testPathIgnorePatterns: [
10+
'/(?:production_)?node_modules/',
11+
'.d.ts$',
12+
'<rootDir>/test/fixtures',
13+
'<rootDir>/test/helpers',
14+
'__mocks__',
15+
],
16+
transform: {
17+
'^.+\\.[jt]sx?$': 'babel-jest',
18+
},
19+
moduleNameMapper: {
20+
'^(\\.\\/.+)\\.js$': '$1',
21+
},
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
{
2+
"name": "@exercism/typescript-square-root",
3+
"version": "1.0.0",
4+
"description": "Exercism exercises in Typescript.",
5+
"private": true,
6+
"repository": {
7+
"type": "git",
8+
"url": "https://github.com/exercism/typescript"
9+
},
10+
"type": "module",
11+
"engines": {
12+
"node": "^18.16.0 || >=20.0.0"
13+
},
14+
"devDependencies": {
15+
"@exercism/babel-preset-typescript": "^0.6.0",
16+
"@exercism/eslint-config-typescript": "^0.8.0",
17+
"@jest/globals": "^29.7.0",
18+
"@types/node": "~22.7.6",
19+
"babel-jest": "^29.7.0",
20+
"core-js": "~3.38.1",
21+
"eslint": "^9.12.0",
22+
"expect": "^29.7.0",
23+
"jest": "^29.7.0",
24+
"prettier": "^3.3.3",
25+
"tstyche": "^2.1.1",
26+
"typescript": "~5.6.3",
27+
"typescript-eslint": "^8.10.0"
28+
},
29+
"scripts": {
30+
"test": "corepack yarn node test-runner.mjs",
31+
"test:types": "corepack yarn tstyche",
32+
"test:implementation": "corepack yarn jest --no-cache --passWithNoTests",
33+
"lint": "corepack yarn lint:types && corepack yarn lint:ci",
34+
"lint:types": "corepack yarn tsc --noEmit -p .",
35+
"lint:ci": "corepack yarn eslint . --ext .tsx,.ts"
36+
},
37+
"packageManager": "[email protected]"
38+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { describe, it, expect, xit } from '@jest/globals'
2+
import { squareRoot } from './square-root.ts'
3+
4+
describe('Square Root', () => {
5+
// Root of 1
6+
it('root of 1', () => {
7+
expect(squareRoot(1)).toEqual(1)
8+
})
9+
10+
// Root of 4
11+
xit('root of 4', () => {
12+
expect(squareRoot(4)).toEqual(2)
13+
})
14+
15+
// Root of 25
16+
xit('root of 25', () => {
17+
expect(squareRoot(25)).toEqual(5)
18+
})
19+
20+
// Root of 81
21+
xit('root of 81', () => {
22+
expect(squareRoot(81)).toEqual(9)
23+
})
24+
25+
// Root of 196
26+
xit('root of 196', () => {
27+
expect(squareRoot(196)).toEqual(14)
28+
})
29+
30+
// Root of 65025
31+
xit('root of 65025', () => {
32+
expect(squareRoot(65025)).toEqual(255)
33+
})
34+
})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export function squareRoot(radicand: unknown): unknown {
2+
throw new Error('Remove this statement and implement this function')
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
#!/usr/bin/env node
2+
3+
/**
4+
* 👋🏽 Hello there reader,
5+
*
6+
* It looks like you are working on this solution using the Exercism CLI and
7+
* not the online editor. That's great! The file you are looking at executes
8+
* the various steps the online test-runner also takes.
9+
*
10+
* @see https://github.com/exercism/typescript-test-runner
11+
*
12+
* TypeScript track exercises generally consist of at least two out of three
13+
* types of tests to run.
14+
*
15+
* 1. tsc, the TypeScript compiler. This tests if the TypeScript code is valid
16+
* 2. tstyche, static analysis tests to see if the types used are expected
17+
* 3. jest, runtime implementation tests to see if the solution is correct
18+
*
19+
* If one of these three fails, this script terminates with -1, -2, or -3
20+
* respectively. If it succeeds, it terminates with exit code 0.
21+
*
22+
* @note you need corepack (bundled with node LTS) enabled in order for this
23+
* test runner to work as expected. Follow the installation and test
24+
* instructions if you see errors about corepack or pnp.
25+
*/
26+
27+
import { execSync } from 'node:child_process'
28+
import { existsSync, readFileSync } from 'node:fs'
29+
import { exit } from 'node:process'
30+
import { URL } from 'node:url'
31+
32+
/**
33+
* Before executing any tests, the test runner attempts to find the
34+
* exercise config.json file which has metadata about which types of tests
35+
* to run for this solution.
36+
*/
37+
const metaDirectory = new URL('./.meta/', import.meta.url)
38+
const exercismDirectory = new URL('./.exercism/', import.meta.url)
39+
const configDirectory = existsSync(metaDirectory)
40+
? metaDirectory
41+
: existsSync(exercismDirectory)
42+
? exercismDirectory
43+
: null
44+
45+
if (configDirectory === null) {
46+
throw new Error(
47+
'Expected .meta or .exercism directory to exist, but I cannot find it.'
48+
)
49+
}
50+
51+
const configFile = new URL('./config.json', configDirectory)
52+
if (!existsSync(configFile)) {
53+
throw new Error('Expected config.json to exist at ' + configFile.toString())
54+
}
55+
56+
// Experimental: import config from './config.json' with { type: 'json' }
57+
/** @type {import('./config.json') } */
58+
const config = JSON.parse(readFileSync(configFile))
59+
60+
const jest = !config.custom || config.custom['flag.tests.jest']
61+
const tstyche = config.custom?.['flag.tests.tstyche']
62+
console.log(
63+
`[tests] tsc: ✅, tstyche: ${tstyche ? '✅' : '❌'}, jest: ${jest ? '✅' : '❌'}, `
64+
)
65+
66+
/**
67+
* 1. tsc: the typescript compiler
68+
*/
69+
try {
70+
console.log('[tests] tsc (compile)')
71+
execSync('corepack yarn lint:types', {
72+
stdio: 'inherit',
73+
cwd: process.cwd(),
74+
})
75+
} catch {
76+
exit(-1)
77+
}
78+
79+
/**
80+
* 2. tstyche: type tests
81+
*/
82+
if (tstyche) {
83+
try {
84+
console.log('[tests] tstyche (type tests)')
85+
execSync('corepack yarn test:types', {
86+
stdio: 'inherit',
87+
cwd: process.cwd(),
88+
})
89+
} catch {
90+
exit(-2)
91+
}
92+
}
93+
94+
/**
95+
* 3. jest: implementation tests
96+
*/
97+
if (jest) {
98+
try {
99+
console.log('[tests] tstyche (implementation tests)')
100+
execSync('corepack yarn test:implementation', {
101+
stdio: 'inherit',
102+
cwd: process.cwd(),
103+
})
104+
} catch {
105+
exit(-3)
106+
}
107+
}
108+
109+
/**
110+
* Done! 🥳
111+
*/

0 commit comments

Comments
 (0)