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
15 changes: 14 additions & 1 deletion fraction.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
import { roundTo } from "./utils.ts";
import { gcdEuclid } from "./gcd.ts";

export class Fraction {
constructor(
private numerator: number,
private denominator: number,
) {}
) {
this.cancel();
}

public add(other: Fraction) {
const newNumerator =
this.numerator * other.denominator + other.numerator * this.denominator;
const newDenominator = this.denominator * other.denominator;
this.numerator = newNumerator;
this.denominator = newDenominator;
this.cancel();
}

public subtract(other: Fraction) {
Expand All @@ -20,20 +24,29 @@ export class Fraction {
const newDenominator = this.denominator * other.denominator;
this.numerator = newNumerator;
this.denominator = newDenominator;
this.cancel();
}

public multiply(other: Fraction) {
const newNumerator = this.numerator * other.numerator;
const newDenominator = this.denominator * other.denominator;
this.numerator = newNumerator;
this.denominator = newDenominator;
this.cancel();
}

public divide(other: Fraction) {
const newNumerator = this.numerator * other.denominator;
const newDenominator = this.denominator * other.numerator;
this.numerator = newNumerator;
this.denominator = newDenominator;
this.cancel();
}

public cancel() {
const gcd = gcdEuclid(this.numerator, this.denominator);
this.numerator = this.numerator / gcd;
this.denominator = this.denominator / gcd;
}

public toFloat(precision: number): number {
Expand Down
134 changes: 133 additions & 1 deletion fraction_test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { assertAlmostEquals, assertEquals } from "@std/assert";
import { assertAlmostEquals, assertEquals, assertThrows } from "@std/assert";
import { Fraction } from "./fraction.ts";

Deno.test("fraction of 1/1 is 1.0", () => {
Expand All @@ -23,6 +23,14 @@ Deno.test("fraction of 2/3 is roughly 0.67", () => {
assertAlmostEquals(float, 0.67);
});

Deno.test("constructor reduces 2/4 to 1/2 automatically", () => {
// Arrange & Act
const fraction = new Fraction(2, 4);

// Assert
assertEquals(fraction.toString(), "1/2");
});

Deno.test("1/3 + 2/6 = 2/3 is roughly 0.67", () => {
// Arrange
const left = new Fraction(1, 3);
Expand All @@ -34,3 +42,127 @@ Deno.test("1/3 + 2/6 = 2/3 is roughly 0.67", () => {
// Assert
assertAlmostEquals(left.toFloat(0.01), 0.67);
});

Deno.test("addition result is reduced automatically", () => {
// Arrange
const left = new Fraction(1, 2);
const right = new Fraction(1, 3);

// Act
left.add(right);

// Assert
assertEquals(left.toString(), "5/6");
});

Deno.test("1/3 - 2/3 = -1/3 is roughly -0.33", () => {
// Arrange
const left = new Fraction(1, 3);
const right = new Fraction(2, 3);

// Act
left.subtract(right);

// Assert
assertAlmostEquals(left.toFloat(0.01), -0.33);
});

Deno.test("1/3 * 2/3 = 2/9 is roughly 0.22", () => {
// Arrange
const left = new Fraction(1, 3);
const right = new Fraction(2, 3);

// Act
left.multiply(right);

// Assert
assertAlmostEquals(left.toFloat(0.01), 0.22);
});

Deno.test("1/3 / 1/6 = 6/3 is roughly 2.00", () => {
// Arrange
const left = new Fraction(1, 3);
const right = new Fraction(1, 6);

// Act
left.divide(right);

// Assert
assertAlmostEquals(left.toFloat(0.01), 2.00);
});

Deno.test("2/4 can be canceled to 1/2", () => {
// Arrange
const fraction = new Fraction(2, 4);

// Act
fraction.cancel();

// Assert
assertEquals(fraction.toString(), "1/2");
});

Deno.test("already reduced fraction remains unchanged after cancel", () => {
// Arrange
const fraction = new Fraction(1, 3);

// Act
fraction.cancel();

// Assert
assertEquals(fraction.toString(), "1/3");
});

Deno.test("toString method returns correct string representation", () => {
// Arrange
const fraction = new Fraction(1, 3);

// Act
const result = fraction.toString();

// Assert
assertEquals(result, "1/3");
});

Deno.test("parse method creates correct Fraction object", () => {
// Arrange
const expression = "2/3";

// Act
const fraction = Fraction.parse(expression);

// Assert
assertEquals(fraction.toString(), "2/3");
});

Deno.test("parse method reduces 4/8 to 1/2 automatically", () => {
// Arrange
const expression = "4/8";

// Act
const fraction = Fraction.parse(expression);

// Assert
assertEquals(fraction.toString(), "1/2");
});

Deno.test("parse method throws error for invalid syntax", () => {
// Arrange
const expression = "invalid";

// Act & Assert
assertThrows(() => {
Fraction.parse(expression);
}, Error, "illegal syntax: \"[numerator]/[denominator]\" required");
});

Deno.test("parse method throws error for non-numeric values", () => {
// Arrange
const expression = "a/b";

// Act & Assert
assertThrows(() => {
Fraction.parse(expression);
}, Error, "non-numeric numerator/denominator");
});

18 changes: 18 additions & 0 deletions gcd.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@

export function gcdBruteForce(a: number, b: number): number {
let gcd = 1;
for (let i = 1; i <= Math.min(a, b); i++) {
if (a % i === 0 && b % i === 0) {
gcd = i;
}
}
return gcd;
}


export function gcdEuclid(a: number, b: number): number {
if (b === 0) {
return a;
}
return gcdEuclid(b, a % b);
}
22 changes: 22 additions & 0 deletions gcd_test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { assertEquals } from "@std/assert";
import { gcdBruteForce, gcdEuclid } from "./gcd.ts";

Deno.test("gcd of 48 and 18 is 6", () => {
// Given
const a = 48;
const b = 18;
// When
const actual = gcdBruteForce(a, b);
// Then
assertEquals(actual, 6);
});

Deno.test("gcd of 48 and 18 is 6 (Euclid)", () => {
// Given
const a = 48;
const b = 18;
// When
const actual = gcdEuclid(a, b);
// Then
assertEquals(actual, 6);
});
3 changes: 2 additions & 1 deletion geometry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export class Rectangle implements Shape {
) {}

circumference(): number {
return 2 * (this.width() + 2 * this.height());
return 2 * (this.width() + this.height());
}

area(): number {
Expand All @@ -61,3 +61,4 @@ export class Rectangle implements Shape {
return this.topRight.y - this.bottomLeft.y;
}
}

61 changes: 58 additions & 3 deletions geometry_test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { assertAlmostEquals } from "@std/assert";
import { Circle, Point2D } from "./geometry.ts";
import { assertAlmostEquals, assertEquals } from "@std/assert";
import { Circle, Point2D, Rectangle } from "./geometry.ts";

Deno.test("circumference of a circle with radius 5 is roughtly 31.416", () => {
Deno.test("circumference of a circle with radius 5 is roughly 31.416", () => {
// Given
const circle = new Circle(new Point2D(3, 4), 5);

Expand All @@ -11,3 +11,58 @@ Deno.test("circumference of a circle with radius 5 is roughtly 31.416", () => {
// Then
assertAlmostEquals(actual, 31.416, 0.01);
});

Deno.test("area of a circle with radius 5 is roughly 78.54", () => {
// Given
const circle = new Circle(new Point2D(3, 4), 5);

// When
const actual = circle.area();

// Then
assertAlmostEquals(actual, 78.54, 0.01);
});

Deno.test("diameter of a circle with radius 5 is 10", () => {
// Given
const circle = new Circle(new Point2D(3, 4), 5);

// When
const actual = circle.diameter();

// Then
assertAlmostEquals(actual, 10, 0.01);
});

Deno.test("diagonal of a rectangle with bottom-left (0, 0) and top-right (3, 4) is 5", () => {
// Given
const rectangle = new Rectangle(new Point2D(0, 0), new Point2D(3, 4));

// When
const actual = rectangle.diagonal();

// Then
assertAlmostEquals(actual, 5, 0.01);
});

Deno.test("area of a rectangle with bottom-left (0, 0) and top-right (3, 4) is 12", () => {
// Given
const rectangle = new Rectangle(new Point2D(0, 0), new Point2D(3, 4));

// When
const actual = rectangle.area();

// Then
assertEquals(actual, 12);
});

Deno.test("circumference of a rectangle with bottom-left (0, 0) and top-right (3, 4) is 14", () => {
// Given
const rectangle = new Rectangle(new Point2D(0, 0), new Point2D(3, 4));

// When
const actual = rectangle.circumference();

// Then
assertEquals(actual, 14);
});