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

export class Fraction {
constructor(
private numerator: number,
private denominator: number,
) {}
) {
if (denominator === 0) {
throw new Error("Denominator cannot be 0");
}
this.cancel();
}

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

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 +33,23 @@ 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 toFloat(precision: number): number {
Expand Down
111 changes: 99 additions & 12 deletions fraction_test.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,123 @@
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", () => {
// Arrange
const fraction = new Fraction(1, 1);

// Act
const float = fraction.toFloat(0.1);

// Assert
assertEquals(float, 1.0);
});

Deno.test("fraction of 2/3 is roughly 0.67", () => {
// Arrange
const fraction = new Fraction(2, 3);

// Act
const float = fraction.toFloat(0.01);

// Assert
assertAlmostEquals(float, 0.67);
});

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

left.add(right);

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

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

left.subtract(right);

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

Deno.test("2/3 * 3/4 = 1/2", () => {
const left = new Fraction(2, 3);
const right = new Fraction(3, 4);

left.multiply(right);

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

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

left.divide(right);

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

Deno.test("toString gives correct format", () => {
const fraction = new Fraction(3, 4);

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

Deno.test("parse valid fraction", () => {
const fraction = Fraction.parse("5 / 6");

assertAlmostEquals(fraction.toFloat(0.01), 0.83);
});

Deno.test("4/8 wird automatisch zu 1/2 gekuerzt", () => {
const fraction = new Fraction(4, 8);

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

Deno.test("6/9 wird automatisch zu 2/3 gekuerzt", () => {
const fraction = new Fraction(6, 9);

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

Deno.test("parse 10/4 wird zu 5/2 gekuerzt", () => {
const fraction = Fraction.parse("10 / 4");

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

Deno.test("cancel kuerzt 12/8 zu 3/2", () => {
const fraction = new Fraction(12, 8);

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

Deno.test("1/3 + 2/6 = 2/3 gekuerzt", () => {
const left = new Fraction(1, 3);
const right = new Fraction(2, 6);

// Act
left.add(right);

// Assert
assertAlmostEquals(left.toFloat(0.01), 0.67);
assertEquals(left.toString(), "2/3");
});

Deno.test("throws error when creating fraction with denominator 0", () => {
assertThrows(() => {
new Fraction(3, 0);
});
});

Deno.test("throws error when parsing 3/0", () => {
assertThrows(() => {
Fraction.parse("3 / 0");
});
});

Deno.test("throws error on invalid syntax", () => {
assertThrows(() => {
Fraction.parse("3-4");
});
});

Deno.test("throws error on non-numeric values", () => {
assertThrows(() => {
Fraction.parse("abc / def");
});
});
21 changes: 21 additions & 0 deletions gcd.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
export function gcdBruteForce(a: number, b: number): number {
let gcd = 1;
const min = Math.min(a, b);
for (let i = min; i >= 1; i--) {
if (a % i === 0 && b % i === 0) {
gcd = i;
break;
}
}
return gcd;
}

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

const gcdTests = [
{ a: 1, b: 1, gcd: 1 },
{ a: 1, b: 2, gcd: 1 },
{ a: 2, b: 2, gcd: 2 },
{ a: 3, b: 4, gcd: 1 },
{ a: 6, b: 9, gcd: 3 },
{ a: 81, b: 36, gcd: 9 },
{ a: 12, b: 8, gcd: 4 },
{ a: 100, b: 75, gcd: 25 },
{ a: 7, b: 13, gcd: 1 },
{ a: 48, b: 18, gcd: 6 },
];

Deno.test("gcdBruteForce", () => {
for (const { a, b, gcd } of gcdTests) {
const actual = gcdBruteForce(a, b);
assertEquals(actual, gcd);
}
});

Deno.test("gcdEuclid", () => {
for (const { a, b, gcd } of gcdTests) {
const actual = gcdEuclid(a, b);
assertEquals(actual, gcd);
}
});
60 changes: 54 additions & 6 deletions geometry_test.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,61 @@
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", () => {
// Given
Deno.test("distance between (0,0) and (3,4) is 5", () => {
const a = new Point2D(0, 0);
const b = new Point2D(3, 4);

assertAlmostEquals(a.distanceTo(b), 5);
});

Deno.test("distance between same point is 0", () => {
const a = new Point2D(2, 3);

assertEquals(a.distanceTo(a), 0);
});

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

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

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

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

const actual = circle.area();

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

Deno.test("diameter of circle with radius 5 is 10", () => {
const circle = new Circle(new Point2D(0, 0), 5);

assertAlmostEquals(circle.diameter(), 10);
});

Deno.test("area of rectangle (0,0) to (2,3) is 6", () => {
const rect = new Rectangle(new Point2D(0, 0), new Point2D(2, 3));

const actual = rect.area();

assertEquals(actual, 6);
});

Deno.test("circumference of rectangle (0,0) to (2,3)", () => {
const rect = new Rectangle(new Point2D(0, 0), new Point2D(2, 3));

const actual = rect.circumference();

assertEquals(actual, 16);
});

Deno.test("diagonal of rectangle (0,0) to (3,4) is 5", () => {
const rect = new Rectangle(new Point2D(0, 0), new Point2D(3, 4));

const actual = rect.diagonal();

assertAlmostEquals(actual, 5);
});