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
26 changes: 25 additions & 1 deletion fraction.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,24 @@
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 zero");
}
this._autoCancel();
}

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._autoCancel();
}

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

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

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

public toFloat(precision: number): number {
Expand All @@ -56,4 +66,18 @@ export class Fraction {
}
return new Fraction(numerator, denominator);
}


private _autoCancel() {
const divisor = gcdEuclid(this.numerator, this.denominator);
if (divisor !== 0 && divisor !== 1) {
this.numerator = this.numerator / divisor;
this.denominator = this.denominator / divisor;
}
}

public cancel(): Fraction {
const divisor = gcdEuclid(this.numerator, this.denominator);
return new Fraction(this.numerator / divisor, this.denominator / divisor);
}
}
134 changes: 134 additions & 0 deletions fraction_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,137 @@ Deno.test("1/3 + 2/6 = 2/3 is roughly 0.67", () => {
// Assert
assertAlmostEquals(left.toFloat(0.01), 0.67);
});

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

// Act
left.subtract(right);

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

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

// Act
left.multiply(right);

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

});

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

// Act
left.divide(right);

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

//String wird zerlegt
Deno.test("parse returns Fraction for '5/8'", () => {
// Act
const fraction = Fraction.parse("5/8");

// Assert
assertAlmostEquals(fraction.toFloat(0.01), 0.63);
});

//falsches Format
Deno.test("parse throws error for invalid format", () => {
try {
Fraction.parse("abc");
throw new Error("No error thrown");
} catch (e) {
const err = e as Error;
assertEquals(err.message, 'illegal syntax: "[numerator]/[denominator]" required');
}
});

//keine zahl
Deno.test("parse throws error for non-numeric values", () => {
try {
Fraction.parse("a/b");
throw new Error("No error thrown");
} catch (e) {
const err = e as Error;
assertEquals(err.message, "non-numeric numerator/denominator");
}
});

//zu einem string formatieren
Deno.test("toString returns correct value for 3/4", () => {
const fraction = new Fraction(3, 4);
assertEquals(fraction.toString(), "3/4");
});


//Nenner = 0 im Konstrukor
Deno.test("constructor throws error for 0", () => {
try {
new Fraction(3, 0);
throw new Error("No error thrown");
} catch (e) {
const err = e as Error;
assertEquals(err.message, "denominator cannot be zero");
}
});


// Nenner = 0 zuerst aber parsen
Deno.test("parse throws error for 0", () => {
try {
Fraction.parse("3 / 0");
throw new Error("No error thrown");
} catch (e) {
const err = e as Error;
assertEquals(err.message, "denominator cannot be zero");
}
});

Deno.test("Fraction.cancel() kürzt 2/4 zu 1/2", () => {
const f = new Fraction(2, 4);
const gekuerzt = f.cancel();
assertEquals(gekuerzt.toString(), "1/2");
});

Deno.test("Fraction.cancel() von 1/1 bleibt 1/1", () => {
const f = new Fraction(1, 1);
const gekuerzt = f.cancel();
assertEquals(gekuerzt.toString(), "1/1");
});

Deno.test("Fraction(2, 4) wird automatisch zu 1/2 gekürzt", () => {
const f = new Fraction(2, 4);
assertEquals(f.toString(), "1/2");
});

Deno.test("Fraction.parse('6/9') wird automatisch zu 2/3 gekürzt", () => {
const f = Fraction.parse("6/9");
assertEquals(f.toString(), "2/3");
});

Deno.test("1/3 + 2/6 ergibt automatisch 2/3 (gekürzt)", () => {
const left = new Fraction(1, 3);
const right = new Fraction(2, 6);
left.add(right);
assertEquals(left.toString(), "2/3");
});

Deno.test("1/2 * 2/4 ergibt automatisch 1/4 (gekürzt)", () => {
const left = new Fraction(1, 2);
const right = new Fraction(2, 4);
left.multiply(right);
assertEquals(left.toString(), "1/4");
});
20 changes: 20 additions & 0 deletions gcd.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
export function gcdBruteForce(a: number, b: number): number {
a = Math.abs(a);
b = Math.abs(b);
if (a === 0) return b;
if (b === 0) return a;
const min = Math.min(a, b);
for (let i = min; i >= 1; i--) {
if (a % i === 0 && b % i === 0) {
return i;
}
}
return 1;
}

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


Deno.test("gcdBruteForce(12, 18) should return 6", () => {
//Act
const bruteForce = gcdBruteForce(12,18)
//Act
assertEquals(bruteForce, 6.0);

});

Deno.test("gcdBruteForce(1, 1) should return 1", () => {
assertEquals(gcdBruteForce(1, 1), 1);
});

Deno.test("gcdBruteForce(2, 4) should return 2", () => {
assertEquals(gcdBruteForce(2, 4), 2);
});


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: 0, b: 5, gcd: 5 },
{ a: 5, b: 0, gcd: 5 },
{ a: 0, b: 0, gcd: 0 },
];

Deno.test("gcdEuclid table-driven", () => {
for (const { a, b, gcd } of gcdTests) {
assertEquals(gcdEuclid(a, b), gcd, `gcdEuclid(${a}, ${b})`);
}
});

Deno.test("gcdBruteForce table-driven", () => {
for (const { a, b, gcd } of gcdTests) {
assertEquals(gcdBruteForce(a, b), gcd, `gcdBruteForce(${a}, ${b})`);
}
});
2 changes: 1 addition & 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 Down
36 changes: 34 additions & 2 deletions geometry_test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { assertAlmostEquals } from "@std/assert";
import { Circle, Point2D } from "./geometry.ts";
import { assertAlmostEquals, assertEquals } from "@std/assert";
import { Rectangle, Circle, Point2D } from "./geometry.ts";

//Kreis Umfang
Deno.test("circumference of a circle with radius 5 is roughtly 31.416", () => {
// Given
const circle = new Circle(new Point2D(3, 4), 5);
Expand All @@ -11,3 +12,34 @@ Deno.test("circumference of a circle with radius 5 is roughtly 31.416", () => {
// Then
assertAlmostEquals(actual, 31.416, 0.01);
});

//Rechteck Fläche
Deno.test("area of rectangle (0,0)-(2,3) is 6", () => {
const rect = new Rectangle(new Point2D(0, 0), new Point2D(2, 3));
assertEquals(rect.area(), 6);
});

//Rechteck Umfang
Deno.test("circumference of rectangle (0,0)-(2,3) is 10", () => {
const rect = new Rectangle(new Point2D(0, 0), new Point2D(2, 3));
assertEquals(rect.circumference(), 10);
});

//Rechteck Diagonale
Deno.test("diagonal of rectangle (0,0)-(2,3) is roughly 3.61", () => {
const rect = new Rectangle(new Point2D(0, 0), new Point2D(2, 3));
assertAlmostEquals(rect.diagonal(), 3.61, 0.01);
});

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

//Kreis Fläche
Deno.test("area of circle with radius 5 is roughly 78.54", () => {
const circle = new Circle(new Point2D(0, 0), 5);
assertAlmostEquals(circle.area(), 78.54, 0.01);
});