Browse Source

move some logic to new class OrderedUniqueNumbers

Richard Köhl 3 years ago
parent
commit
8f9db4d797
2 changed files with 143 additions and 65 deletions
  1. 107 0
      src/ordered-unique-numbers.class.ts
  2. 36 65
      src/prime.class.ts

+ 107 - 0
src/ordered-unique-numbers.class.ts

@@ -0,0 +1,107 @@
+export class OrderedUniqueNumbers {
+  constructor(private numbers: number[] = []) {
+    if (!this.isValid) {
+      this.sort();
+      this.makeUnique();
+    }
+  }
+
+  private static filterUnique(
+    value: number,
+    index: number,
+    numbers: number[],
+  ): boolean {
+    return numbers.indexOf(value) === index;
+  }
+
+  public get list(): number[] {
+    return this.numbers;
+  }
+
+  public get max(): number {
+    return this.numbers.slice(-1)[0];
+  }
+
+  public contains(number: number): boolean {
+    return this.numbers.indexOf(number) !== -1;
+  }
+
+  public add(number: number): number[] {
+    const max = this.max;
+
+    this.numbers.push(number);
+    if (max < number) {
+      return this.list;
+    }
+
+    this.sort();
+    return this.list;
+  }
+
+  public remove(number: number): number[] {
+    this.numbers = this.list.filter((n) => n !== number);
+
+    return this.numbers;
+  }
+
+  public get isValid(): boolean {
+    for (const [index, number] of this.numbers.entries()) {
+      if (index > 0 && number < this.numbers[index - 1]) {
+        return false;
+      }
+    }
+
+    return this.isUnique;
+  }
+
+  public sort() {
+    return this.numbers.sort((a, b) => a - b);
+  }
+
+  public get isUnique(): boolean {
+    return this.numbers.filter(OrderedUniqueNumbers.filterUnique).length ===
+      this.numbers.length;
+  }
+
+  public makeUnique(): number[] {
+    this.numbers = this.numbers.filter(OrderedUniqueNumbers.filterUnique);
+
+    return this.numbers;
+  }
+
+  public findNextLargerValue(
+    number: number,
+    fromIndex = 0,
+    toIndex = this.numbers.length,
+  ): number {
+    if (this.list.length === 0) {
+      throw new Error(`the list is empty! nothing to find...`);
+    }
+
+    if (number > this.max) {
+      throw new Error(`there is no larger value than ${number} in this list!`);
+    }
+
+    if (number < this.numbers[0]) {
+      return this.numbers[0];
+    }
+
+    if (this.contains(number)) {
+      return this.numbers[this.numbers.indexOf(number) + 1];
+    }
+
+    const length = toIndex - fromIndex;
+
+    if (length > 1) {
+      const middle = fromIndex + Math.floor(length / 2);
+
+      return this.numbers[middle] > number
+        ? this.findNextLargerValue(number, fromIndex, middle)
+        : this.findNextLargerValue(number, middle + 1, toIndex);
+    }
+
+    return this.numbers[fromIndex] > number
+      ? this.numbers[fromIndex]
+      : this.numbers[fromIndex + 1];
+  }
+}

+ 36 - 65
src/prime.class.ts

@@ -1,68 +1,39 @@
-export class Prime {
-  private static values: number[] = [2, 3, 5, 7];
-
-  private static lastKnown(): number {
-    return Prime.values.slice(-1)[0];
-  }
-
-  private static searchNextKnownPrime(
-    number: number,
-    from = 0,
-    to: number = Prime.values.length,
-  ): number {
-    if (0 > number || number >= Prime.lastKnown()) {
-      throw new Error("we are wandering in unknown realms...");
-    }
-
-    // this is a binary search
-    // basically we are splitting the list into two halves and
-    // then discard the half that does not contain the input number
-    // and call the function again with the remaining half
-    // but instead of always compiling a new array we just use indices of base array
-
-    const length = to - from;
+import { OrderedUniqueNumbers } from "./ordered-unique-numbers.class.ts";
 
-    if (length > 1) {
-      const middle = from + Math.floor(length / 2);
-
-      return Prime.values[middle] > number
-        ? Prime.searchNextKnownPrime(number, from, middle)
-        : Prime.searchNextKnownPrime(number, middle + 1, to);
-    }
-
-    return Prime.values[from] > number
-      ? Prime.values[from]
-      : Prime.values[from + 1];
+export class Prime {
+  private static values: OrderedUniqueNumbers = new OrderedUniqueNumbers([
+    2,
+    3,
+    5,
+    7,
+  ]);
+
+  private static get lastKnown(): number {
+    return Prime.values.max;
   }
 
-  private static findNextUnknown(): number {
-    let number = Prime.lastKnown();
+  private static get findNextUnknown(): number {
+    let number = Prime.lastKnown;
 
     do {
       number += 2;
     } while (!Prime.isPrime(number));
 
-    Prime.values.push(number);
+    Prime.values.add(number);
     return number;
   }
 
   private static next(number: number): number {
-    if (number < Prime.lastKnown()) {
-      // if input is a prime number just return the next one in our list
-      const index = Prime.values.indexOf(number);
-      if (index !== -1) {
-        return Prime.values[index + 1];
-      }
-
+    if (number < Prime.lastKnown) {
       // input is not a prime number, so we have to find the next larger entry
-      return Prime.searchNextKnownPrime(number);
+      return Prime.values.findNextLargerValue(number);
     }
 
     // input is larger than last known prime number in our list
     // so we need to brute force the next one
     let next: number;
     do {
-      next = Prime.findNextUnknown();
+      next = Prime.findNextUnknown;
     } while (next <= number);
     return next;
   }
@@ -72,14 +43,14 @@ export class Prime {
       return false;
     }
 
-    if (number <= Prime.lastKnown()) {
-      return Prime.values.indexOf(number) !== -1;
+    if (number <= Prime.lastKnown) {
+      return Prime.values.contains(number);
     }
 
     const limit = Math.sqrt(number);
 
-    if (limit <= Prime.lastKnown()) {
-      for (const prime of Prime.values) {
+    if (limit <= Prime.lastKnown) {
+      for (const prime of Prime.values.list) {
         if (prime > limit) {
           break;
         }
@@ -92,7 +63,7 @@ export class Prime {
     }
 
     // calculate new prime numbers up to limit
-    while (Prime.findNextUnknown() < limit);
+    while (Prime.findNextUnknown < limit);
 
     // now we should have a sufficient list
     // to determine if input is a prime number
@@ -104,10 +75,10 @@ export class Prime {
       return false;
     }
 
-    const prev = Prime.values[Prime.values.indexOf(number) - 1];
-    const next = Prime.lastKnown() === number
-      ? Prime.findNextUnknown()
-      : Prime.values[Prime.values.indexOf(number) + 1];
+    const prev = Prime.values.list[Prime.values.list.indexOf(number) - 1];
+    const next = Prime.lastKnown === number
+      ? Prime.findNextUnknown
+      : Prime.values.list[Prime.values.list.indexOf(number) + 1];
 
     return Math.pow(number, 2) > prev * next;
   }
@@ -118,10 +89,10 @@ export class Prime {
     }
 
     const square = Math.pow(number, 2);
-    const pos = Prime.values.indexOf(number);
+    const pos = Prime.values.list.indexOf(number);
     let upper = number;
     for (let i = pos - 1; i >= 0; i -= 1) {
-      const lower = Prime.values[i];
+      const lower = Prime.values.list[i];
       upper = Prime.next(upper);
 
       if (square < lower * upper) {
@@ -137,10 +108,10 @@ export class Prime {
       return false;
     }
 
-    const prev = Prime.values[Prime.values.indexOf(number) - 1];
-    const next = Prime.lastKnown() === number
-      ? Prime.findNextUnknown()
-      : Prime.values[Prime.values.indexOf(number) + 1];
+    const prev = Prime.values.list[Prime.values.list.indexOf(number) - 1];
+    const next = Prime.lastKnown === number
+      ? Prime.findNextUnknown
+      : Prime.values.list[Prime.values.list.indexOf(number) + 1];
 
     return number === (prev + next) / 2;
   }
@@ -225,8 +196,8 @@ export class Prime {
     primes.forEach((a) => {
       primes.forEach((b) => {
         arr = arr.filter((n) => n !== a - b && n !== b - a);
-      })
-    })
+      });
+    });
 
     return arr.length === 0;
   }
@@ -240,9 +211,9 @@ export class Prime {
     const outOfLimit = Prime.next(to);
 
     // deliver the list of known primes up to (but excluding) retrieved prime
-    return Prime.values.slice(
+    return Prime.values.list.slice(
       0,
-      Prime.values.indexOf(outOfLimit),
+      Prime.values.list.indexOf(outOfLimit),
     );
   }
 }