@bgord/tools 0.14.3 → 0.15.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/dist/age.vo.d.ts +10 -6
  2. package/dist/age.vo.js +22 -14
  3. package/dist/api-key.vo.d.ts +3 -0
  4. package/dist/api-key.vo.js +7 -1
  5. package/dist/clock-format.service.d.ts +9 -0
  6. package/dist/clock-format.service.js +10 -0
  7. package/dist/clock.vo.d.ts +7 -14
  8. package/dist/clock.vo.js +25 -34
  9. package/dist/height.vo.d.ts +7 -7
  10. package/dist/height.vo.js +6 -6
  11. package/dist/hour-format.service.d.ts +10 -0
  12. package/dist/hour-format.service.js +19 -0
  13. package/dist/hour.vo.d.ts +8 -17
  14. package/dist/hour.vo.js +22 -39
  15. package/dist/iban-mask.service.js +6 -3
  16. package/dist/iban.vo.d.ts +6 -4
  17. package/dist/iban.vo.js +7 -5
  18. package/dist/image.vo.d.ts +8 -2
  19. package/dist/image.vo.js +14 -2
  20. package/dist/index.d.ts +2 -2
  21. package/dist/index.js +2 -2
  22. package/dist/language.vo.d.ts +4 -1
  23. package/dist/language.vo.js +5 -3
  24. package/dist/mean.service.d.ts +4 -2
  25. package/dist/mean.service.js +9 -5
  26. package/dist/min-max-scaler.service.d.ts +22 -14
  27. package/dist/min-max-scaler.service.js +16 -15
  28. package/dist/minute.vo.d.ts +5 -6
  29. package/dist/minute.vo.js +14 -14
  30. package/dist/money.vo.d.ts +13 -2
  31. package/dist/money.vo.js +18 -13
  32. package/dist/outlier-detector.service.d.ts +2 -1
  33. package/dist/outlier-detector.service.js +5 -3
  34. package/dist/percentage.service.d.ts +3 -2
  35. package/dist/percentage.service.js +3 -2
  36. package/dist/population-standard-deviation.service.d.ts +3 -2
  37. package/dist/population-standard-deviation.service.js +5 -4
  38. package/dist/random.service.d.ts +6 -0
  39. package/dist/random.service.js +12 -6
  40. package/dist/rounding.adapter.d.ts +16 -0
  41. package/dist/{rounding.service.js → rounding.adapter.js} +8 -7
  42. package/dist/rounding.port.d.ts +3 -0
  43. package/dist/rounding.port.js +1 -0
  44. package/dist/simple-linear-regression.service.d.ts +11 -4
  45. package/dist/simple-linear-regression.service.js +39 -30
  46. package/dist/size.vo.js +1 -1
  47. package/dist/stopwatch.service.d.ts +1 -1
  48. package/dist/stopwatch.service.js +3 -4
  49. package/dist/sum.service.d.ts +2 -1
  50. package/dist/sum.service.js +11 -0
  51. package/dist/time.service.js +1 -1
  52. package/dist/timestamp.vo.d.ts +3 -0
  53. package/dist/timestamp.vo.js +7 -1
  54. package/dist/timezone.vo.d.ts +3 -0
  55. package/dist/timezone.vo.js +4 -3
  56. package/dist/tsconfig.tsbuildinfo +1 -1
  57. package/dist/visually-unambiguous-characters-generator.service.js +0 -1
  58. package/dist/weight.vo.d.ts +4 -4
  59. package/dist/weight.vo.js +1 -1
  60. package/dist/z-score.service.d.ts +3 -2
  61. package/dist/z-score.service.js +3 -2
  62. package/package.json +2 -2
  63. package/readme.md +4 -2
  64. package/src/age.vo.ts +24 -14
  65. package/src/api-key.vo.ts +9 -1
  66. package/src/clock-format.service.ts +15 -0
  67. package/src/clock.vo.ts +24 -43
  68. package/src/height.vo.ts +12 -11
  69. package/src/hour-format.service.ts +21 -0
  70. package/src/hour.vo.ts +24 -47
  71. package/src/iban-mask.service.ts +8 -3
  72. package/src/iban.vo.ts +10 -8
  73. package/src/image.vo.ts +19 -4
  74. package/src/index.ts +2 -2
  75. package/src/language.vo.ts +7 -3
  76. package/src/mean.service.ts +13 -5
  77. package/src/min-max-scaler.service.ts +39 -24
  78. package/src/minute.vo.ts +18 -15
  79. package/src/money.vo.ts +26 -23
  80. package/src/outlier-detector.service.ts +6 -4
  81. package/src/percentage.service.ts +6 -7
  82. package/src/population-standard-deviation.service.ts +8 -5
  83. package/src/random.service.ts +16 -8
  84. package/src/relative-date.vo.ts +0 -1
  85. package/src/rounding.adapter.ts +33 -0
  86. package/src/rounding.port.ts +3 -0
  87. package/src/simple-linear-regression.service.ts +41 -31
  88. package/src/size.vo.ts +1 -1
  89. package/src/stopwatch.service.ts +4 -6
  90. package/src/sum.service.ts +15 -1
  91. package/src/time.service.ts +1 -1
  92. package/src/timestamp.vo.ts +9 -1
  93. package/src/timezone.vo.ts +15 -15
  94. package/src/visually-unambiguous-characters-generator.service.ts +0 -1
  95. package/src/weight.vo.ts +5 -4
  96. package/src/z-score.service.ts +6 -3
  97. package/dist/dates-of-the-week.vo.d.ts +0 -9
  98. package/dist/dates-of-the-week.vo.js +0 -10
  99. package/dist/rounding.service.d.ts +0 -17
  100. package/src/dates-of-the-week.vo.ts +0 -9
  101. package/src/rounding.service.ts +0 -31
@@ -1,4 +1,4 @@
1
- import { type RoundingStrategy } from "./rounding.service";
1
+ import type { RoundingPort } from "./rounding.port";
2
2
  type MinMaxScalerValueType = number;
3
3
  type MinMaxScalerConfigType = {
4
4
  min: MinMaxScalerValueType;
@@ -7,27 +7,35 @@ type MinMaxScalerConfigType = {
7
7
  lower: MinMaxScalerValueType;
8
8
  upper: MinMaxScalerValueType;
9
9
  };
10
- rounding?: RoundingStrategy;
10
+ rounding?: RoundingPort;
11
+ };
12
+ export declare const MinMaxInvalidMinMaxError: "minmax.invalid.minmax";
13
+ export declare const MinMaxInvalidBoundError: "minmax.invalid.bound";
14
+ export declare const MinMaxValueOutOfRangeError: "minmax.value.out.of.range";
15
+ export declare const MinMaxScaledOutOfBoundsError: "minmax.scaled.out.of.bounds";
16
+ export declare const MinMaxEmptyArrayError: "minmax.empty.array";
17
+ type ScaleResult = {
18
+ original: MinMaxScalerValueType;
19
+ scaled: MinMaxScalerValueType;
20
+ isMin: boolean;
21
+ isMax: boolean;
22
+ };
23
+ type DescaleResult = {
24
+ original: MinMaxScalerValueType;
25
+ scaled: MinMaxScalerValueType;
26
+ isLowerBound: boolean;
27
+ isUpperBound: boolean;
11
28
  };
12
29
  export declare class MinMaxScaler {
30
+ private static readonly DEFAULT_ROUNDING;
13
31
  private readonly min;
14
32
  private readonly max;
15
33
  private readonly lower;
16
34
  private readonly upper;
17
35
  private readonly rounding;
18
36
  constructor(config: MinMaxScalerConfigType);
19
- scale(value: MinMaxScalerValueType): {
20
- original: number;
21
- scaled: number;
22
- isMin: boolean;
23
- isMax: boolean;
24
- };
25
- descale(scaled: MinMaxScalerValueType): {
26
- original: number;
27
- scaled: number;
28
- isLowerBound: boolean;
29
- isUpperBound: boolean;
30
- };
37
+ scale(value: MinMaxScalerValueType): ScaleResult;
38
+ descale(scaled: MinMaxScalerValueType): DescaleResult;
31
39
  static getMinMax(values: MinMaxScalerValueType[]): {
32
40
  min: number;
33
41
  max: number;
@@ -1,19 +1,24 @@
1
- import { RoundToDecimal } from "./rounding.service";
1
+ import { RoundToDecimal } from "./rounding.adapter";
2
+ export const MinMaxInvalidMinMaxError = "minmax.invalid.minmax";
3
+ export const MinMaxInvalidBoundError = "minmax.invalid.bound";
4
+ export const MinMaxValueOutOfRangeError = "minmax.value.out.of.range";
5
+ export const MinMaxScaledOutOfBoundsError = "minmax.scaled.out.of.bounds";
6
+ export const MinMaxEmptyArrayError = "minmax.empty.array";
2
7
  export class MinMaxScaler {
8
+ static DEFAULT_ROUNDING = new RoundToDecimal(2);
3
9
  min;
4
10
  max;
5
11
  lower;
6
12
  upper;
7
13
  rounding;
8
14
  constructor(config) {
9
- const rounding = config.rounding ?? new RoundToDecimal(2);
10
15
  const lower = config.bound?.lower ?? 0;
11
16
  const upper = config.bound?.upper ?? 1;
12
17
  if (config.max - config.min < 0)
13
- throw new Error("Invalid MinMaxScaler min-max config");
18
+ throw new Error(MinMaxInvalidMinMaxError);
14
19
  if (upper - lower <= 0)
15
- throw new Error("Invalid MinMaxScaler bound config");
16
- this.rounding = rounding;
20
+ throw new Error(MinMaxInvalidBoundError);
21
+ this.rounding = config.rounding ?? MinMaxScaler.DEFAULT_ROUNDING;
17
22
  this.min = config.min;
18
23
  this.max = config.max;
19
24
  this.lower = lower;
@@ -22,14 +27,10 @@ export class MinMaxScaler {
22
27
  scale(value) {
23
28
  const { min, max, lower, upper } = this;
24
29
  if (value < min || value > max)
25
- throw new Error("Value out of min/max range");
26
- if (min === max)
27
- return {
28
- original: value,
29
- scaled: (lower + upper) / 2,
30
- isMin: value === min,
31
- isMax: value === max,
32
- };
30
+ throw new Error(MinMaxValueOutOfRangeError);
31
+ if (min === max) {
32
+ return { original: value, scaled: (lower + upper) / 2, isMin: value === min, isMax: value === max };
33
+ }
33
34
  const result = ((value - min) / (max - min)) * (upper - lower) + lower;
34
35
  return {
35
36
  original: value,
@@ -41,7 +42,7 @@ export class MinMaxScaler {
41
42
  descale(scaled) {
42
43
  const { min, max, lower, upper } = this;
43
44
  if (scaled < lower || scaled > upper)
44
- throw new Error("Scaled value out of bounds");
45
+ throw new Error(MinMaxScaledOutOfBoundsError);
45
46
  const result = ((scaled - lower) / (upper - lower)) * (max - min) + min;
46
47
  return {
47
48
  original: this.rounding.round(result),
@@ -52,7 +53,7 @@ export class MinMaxScaler {
52
53
  }
53
54
  static getMinMax(values) {
54
55
  if (values.length === 0)
55
- throw new Error("An empty array supplied");
56
+ throw new Error(MinMaxEmptyArrayError);
56
57
  return { min: Math.min(...values), max: Math.max(...values) };
57
58
  }
58
59
  }
@@ -1,16 +1,15 @@
1
1
  import type { TimestampType } from "./timestamp.vo";
2
+ export declare const MinuteValueError: "invalid.minute";
2
3
  export declare class Minute {
3
4
  private readonly value;
4
5
  static readonly ZERO: Minute;
5
6
  static readonly MAX: Minute;
6
7
  constructor(candidate: number);
7
- static fromUtcTimestamp(timestamp: TimestampType): Minute;
8
- get(): {
9
- raw: number;
10
- formatted: string;
11
- };
8
+ static fromEpochMs(timestamp: TimestampType): Minute;
9
+ get(): number;
10
+ toString(): string;
12
11
  equals(another: Minute): boolean;
13
12
  isAfter(another: Minute): boolean;
14
13
  isBefore(another: Minute): boolean;
15
- static list(): Minute[];
14
+ static list(): readonly Minute[];
16
15
  }
package/dist/minute.vo.js CHANGED
@@ -1,33 +1,33 @@
1
+ export const MinuteValueError = "invalid.minute";
1
2
  export class Minute {
2
3
  value;
3
4
  static ZERO = new Minute(0);
4
5
  static MAX = new Minute(59);
5
6
  constructor(candidate) {
6
- if (!Number.isInteger(candidate))
7
- throw new Error("Invalid minute");
8
- if (candidate < 0)
9
- throw new Error("Invalid minute");
10
- if (candidate >= 60)
11
- throw new Error("Invalid minute");
7
+ if (!Number.isInteger(candidate) || candidate < 0 || candidate >= 60) {
8
+ throw new Error(MinuteValueError);
9
+ }
12
10
  this.value = candidate;
13
11
  }
14
- static fromUtcTimestamp(timestamp) {
15
- const minutes = new Date(timestamp).getUTCMinutes();
16
- return new Minute(minutes);
12
+ static fromEpochMs(timestamp) {
13
+ return new Minute(new Date(timestamp).getUTCMinutes());
17
14
  }
18
15
  get() {
19
- return { raw: this.value, formatted: this.value.toString().padStart(2, "0") };
16
+ return this.value;
17
+ }
18
+ toString() {
19
+ return this.value.toString().padStart(2, "0");
20
20
  }
21
21
  equals(another) {
22
- return this.value === another.get().raw;
22
+ return this.value === another.value;
23
23
  }
24
24
  isAfter(another) {
25
- return this.value > another.get().raw;
25
+ return this.value > another.value;
26
26
  }
27
27
  isBefore(another) {
28
- return this.value < another.get().raw;
28
+ return this.value < another.value;
29
29
  }
30
30
  static list() {
31
- return Array.from({ length: 60 }).map((_, index) => new Minute(index));
31
+ return Array.from({ length: 60 }, (_, index) => new Minute(index));
32
32
  }
33
33
  }
@@ -1,5 +1,15 @@
1
1
  import { z } from "zod/v4";
2
- import { type RoundingStrategy } from "./rounding.service";
2
+ import type { RoundingPort } from "./rounding.port";
3
+ export declare const MoneyAmountInvalidError: {
4
+ readonly error: "money.amount.invalid";
5
+ };
6
+ export declare const MoneyMultiplicationFactorInvalidError: {
7
+ readonly error: "money.multiplication-factor.invalid";
8
+ };
9
+ export declare const MoneyDivisionFactorInvalidError: {
10
+ readonly error: "money.division-factor.invalid";
11
+ };
12
+ export declare const MoneySubtractLessThanZeroError: "money.subtract.less.than.zero";
3
13
  export declare const MoneyAmount: z.core.$ZodBranded<z.ZodNumber, "MoneyAmount">;
4
14
  export type MoneyAmountType = z.infer<typeof MoneyAmount>;
5
15
  export declare const MoneyMultiplicationFactor: z.core.$ZodBranded<z.ZodNumber, "MoneyMultiplicationFactor">;
@@ -8,9 +18,10 @@ export declare const MoneyDivisionFactor: z.core.$ZodBranded<z.ZodNumber, "Money
8
18
  export type MoneyDivisionFactorType = z.infer<typeof MoneyDivisionFactor>;
9
19
  export declare class Money {
10
20
  private static readonly ZERO;
21
+ private static readonly DEFAULT_ROUNDING;
11
22
  private readonly amount;
12
23
  private readonly rounding;
13
- constructor(value?: number, rounding?: RoundingStrategy);
24
+ constructor(value?: number, rounding?: RoundingPort);
14
25
  getAmount(): MoneyAmountType;
15
26
  add(money: Money): Money;
16
27
  multiply(factor: MoneyMultiplicationFactorType): Money;
package/dist/money.vo.js CHANGED
@@ -1,26 +1,32 @@
1
1
  import { z } from "zod/v4";
2
- import { RoundToNearest } from "./rounding.service";
2
+ import { RoundToNearest } from "./rounding.adapter";
3
+ export const MoneyAmountInvalidError = { error: "money.amount.invalid" };
4
+ export const MoneyMultiplicationFactorInvalidError = {
5
+ error: "money.multiplication-factor.invalid",
6
+ };
7
+ export const MoneyDivisionFactorInvalidError = { error: "money.division-factor.invalid" };
8
+ export const MoneySubtractLessThanZeroError = "money.subtract.less.than.zero";
3
9
  export const MoneyAmount = z
4
- .number()
5
- .int({ message: "money.amount.invalid" })
6
- .min(0, { message: "money.amount.invalid" })
10
+ .number(MoneyAmountInvalidError)
11
+ .int(MoneyAmountInvalidError)
12
+ .min(0, MoneyAmountInvalidError)
7
13
  .brand("MoneyAmount");
8
14
  export const MoneyMultiplicationFactor = z
9
- .number()
10
- .min(0, { message: "money.multiplication-factor.invalid" })
15
+ .number(MoneyMultiplicationFactorInvalidError)
16
+ .min(0, MoneyMultiplicationFactorInvalidError)
11
17
  .brand("MoneyMultiplicationFactor");
12
18
  export const MoneyDivisionFactor = z
13
- .number()
14
- .min(0, { message: "money.division-factor.invalid" })
15
- .refine((value) => value !== 0, { message: "money.division-factor.invalid" })
19
+ .number(MoneyDivisionFactorInvalidError)
20
+ .gt(0, MoneyDivisionFactorInvalidError)
16
21
  .brand("MoneyDivisionFactor");
17
22
  export class Money {
18
23
  static ZERO = 0;
24
+ static DEFAULT_ROUNDING = new RoundToNearest();
19
25
  amount;
20
26
  rounding;
21
27
  constructor(value = Money.ZERO, rounding) {
22
28
  this.amount = MoneyAmount.parse(value);
23
- this.rounding = rounding ?? new RoundToNearest();
29
+ this.rounding = rounding ?? Money.DEFAULT_ROUNDING;
24
30
  }
25
31
  getAmount() {
26
32
  return this.amount;
@@ -36,7 +42,7 @@ export class Money {
36
42
  subtract(money) {
37
43
  const result = this.rounding.round(this.amount - money.getAmount());
38
44
  if (result < Money.ZERO)
39
- throw new Error("Less than zero");
45
+ throw new Error(MoneySubtractLessThanZeroError);
40
46
  return new Money(MoneyAmount.parse(result), this.rounding);
41
47
  }
42
48
  divide(factor) {
@@ -56,8 +62,7 @@ export class Money {
56
62
  return this.amount === Money.ZERO;
57
63
  }
58
64
  format() {
59
- const result = this.amount / 100;
60
- const whole = Math.floor(result);
65
+ const whole = Math.floor(this.amount / 100);
61
66
  const fraction = this.amount % 100;
62
67
  const fractionFormatted = fraction.toString().padStart(2, "0");
63
68
  return `${whole}.${fractionFormatted}`;
@@ -1,6 +1,7 @@
1
+ export declare const OutlierDetectorMinValuesError: "outlier.detector.min.values";
1
2
  export declare class OutlierDetector {
2
3
  private readonly zScore;
3
4
  private readonly threshold;
4
5
  constructor(values: number[], threshold: number);
5
- check(value: number): boolean;
6
+ isInlier(value: number): boolean;
6
7
  }
@@ -1,14 +1,16 @@
1
1
  import { ZScore } from "./z-score.service";
2
+ export const OutlierDetectorMinValuesError = "outlier.detector.min.values";
2
3
  export class OutlierDetector {
3
4
  zScore;
4
5
  threshold;
5
6
  constructor(values, threshold) {
6
7
  if (values.length < 2)
7
- throw new Error("At least two values are needed");
8
+ throw new Error(OutlierDetectorMinValuesError);
8
9
  this.zScore = new ZScore(values);
9
10
  this.threshold = Math.abs(threshold);
10
11
  }
11
- check(value) {
12
- return this.zScore.calculate(value) <= this.threshold;
12
+ isInlier(value) {
13
+ const score = this.zScore.calculate(value);
14
+ return Math.abs(score) <= this.threshold;
13
15
  }
14
16
  }
@@ -1,4 +1,5 @@
1
- import { type RoundingStrategy } from "./rounding.service";
1
+ import type { RoundingPort } from "./rounding.port";
2
+ export declare const PercentageInvalidDenominatorError: "percentage.invalid.denominator";
2
3
  export declare class Percentage {
3
- static of(numerator: number, denominator: number, rounding?: RoundingStrategy): number;
4
+ static of(numerator: number, denominator: number, rounding?: RoundingPort): number;
4
5
  }
@@ -1,8 +1,9 @@
1
- import { RoundToNearest } from "./rounding.service";
1
+ import { RoundToNearest } from "./rounding.adapter";
2
+ export const PercentageInvalidDenominatorError = "percentage.invalid.denominator";
2
3
  export class Percentage {
3
4
  static of(numerator, denominator, rounding = new RoundToNearest()) {
4
5
  if (denominator === 0)
5
- throw new Error("Invalid denominator");
6
+ throw new Error(PercentageInvalidDenominatorError);
6
7
  if (numerator === 0)
7
8
  return 0;
8
9
  return rounding.round((numerator / denominator) * 100);
@@ -1,4 +1,5 @@
1
- import { type RoundingStrategy } from "./rounding.service";
1
+ import type { RoundingPort } from "./rounding.port";
2
+ export declare const PopulationStandardDeviationMinValuesError: "population.standard.deviation.min.values";
2
3
  export declare class PopulationStandardDeviation {
3
- static calculate(values: number[], rounding?: RoundingStrategy): number;
4
+ static calculate(values: number[], rounding?: RoundingPort): number;
4
5
  }
@@ -1,15 +1,16 @@
1
1
  import { Mean } from "./mean.service";
2
- import { RoundToDecimal } from "./rounding.service";
2
+ import { RoundToDecimal } from "./rounding.adapter";
3
3
  import { Sum } from "./sum.service";
4
+ export const PopulationStandardDeviationMinValuesError = "population.standard.deviation.min.values";
4
5
  export class PopulationStandardDeviation {
5
6
  static calculate(values, rounding = new RoundToDecimal(2)) {
6
7
  if (values.length < 2)
7
- throw new Error("At least two values are needed");
8
+ throw new Error(PopulationStandardDeviationMinValuesError);
8
9
  const mean = Mean.calculate(values);
9
- const n = values.length;
10
+ const count = values.length;
10
11
  const squaredDifferences = values.map((value) => (value - mean) ** 2);
11
12
  const sumOfSquaredDifferences = Sum.of(squaredDifferences);
12
- const variance = sumOfSquaredDifferences / n;
13
+ const variance = sumOfSquaredDifferences / count;
13
14
  return rounding.round(Math.sqrt(variance));
14
15
  }
15
16
  }
@@ -2,7 +2,13 @@ type RandomGenerateConfigType = {
2
2
  min: number;
3
3
  max: number;
4
4
  };
5
+ export declare const RandomMinNotIntegerError: "random.min.not.integer";
6
+ export declare const RandomMaxNotIntegerError: "random.max.not.integer";
7
+ export declare const RandomMinEqualsMaxError: "random.min.equals.max";
8
+ export declare const RandomMinGreaterThanMaxError: "random.min.greater.than.max";
5
9
  export declare class Random {
10
+ private static readonly DEFAULT_MIN;
11
+ private static readonly DEFAULT_MAX;
6
12
  static generate(config?: RandomGenerateConfigType): number;
7
13
  }
8
14
  export {};
@@ -1,15 +1,21 @@
1
+ export const RandomMinNotIntegerError = "random.min.not.integer";
2
+ export const RandomMaxNotIntegerError = "random.max.not.integer";
3
+ export const RandomMinEqualsMaxError = "random.min.equals.max";
4
+ export const RandomMinGreaterThanMaxError = "random.min.greater.than.max";
1
5
  export class Random {
6
+ static DEFAULT_MIN = 0;
7
+ static DEFAULT_MAX = 1;
2
8
  static generate(config) {
3
- const min = config?.min ?? 0;
4
- const max = config?.max ?? 1;
9
+ const min = config ? config.min : Random.DEFAULT_MIN;
10
+ const max = config ? config.max : Random.DEFAULT_MAX;
5
11
  if (!Number.isInteger(min))
6
- throw new Error("Minimum value is not an integer");
12
+ throw new Error(RandomMinNotIntegerError);
7
13
  if (!Number.isInteger(max))
8
- throw new Error("Maximum value is not an integer");
14
+ throw new Error(RandomMaxNotIntegerError);
9
15
  if (min === max)
10
- throw new Error("Minimum and maximum values cannot be equal");
16
+ throw new Error(RandomMinEqualsMaxError);
11
17
  if (min > max)
12
- throw new Error("Minimum value cannot be greater than maximum value");
18
+ throw new Error(RandomMinGreaterThanMaxError);
13
19
  return Math.floor(Math.random() * (max - min + 1)) + min;
14
20
  }
15
21
  }
@@ -0,0 +1,16 @@
1
+ import type { RoundingPort } from "./rounding.port";
2
+ export declare class RoundToNearest implements RoundingPort {
3
+ round(value: number): number;
4
+ }
5
+ export declare class RoundUp implements RoundingPort {
6
+ round(value: number): number;
7
+ }
8
+ export declare class RoundDown implements RoundingPort {
9
+ round(value: number): number;
10
+ }
11
+ export declare const RoundingDecimalsError: "invalid.rounding.decimals";
12
+ export declare class RoundToDecimal implements RoundingPort {
13
+ private readonly decimals;
14
+ constructor(decimals: number);
15
+ round(value: number): number;
16
+ }
@@ -1,25 +1,26 @@
1
- export class RoundingStrategy {
2
- }
3
- export class RoundToNearest extends RoundingStrategy {
1
+ export class RoundToNearest {
4
2
  round(value) {
5
3
  return Math.round(value);
6
4
  }
7
5
  }
8
- export class RoundUp extends RoundingStrategy {
6
+ export class RoundUp {
9
7
  round(value) {
10
8
  return Math.ceil(value);
11
9
  }
12
10
  }
13
- export class RoundDown extends RoundingStrategy {
11
+ export class RoundDown {
14
12
  round(value) {
15
13
  return Math.floor(value);
16
14
  }
17
15
  }
18
- export class RoundToDecimal extends RoundingStrategy {
16
+ export const RoundingDecimalsError = "invalid.rounding.decimals";
17
+ export class RoundToDecimal {
19
18
  decimals;
20
19
  constructor(decimals) {
21
- super();
22
20
  this.decimals = decimals;
21
+ if (!Number.isInteger(decimals) || decimals < 0 || decimals > 100) {
22
+ throw new Error(RoundingDecimalsError);
23
+ }
23
24
  }
24
25
  round(value) {
25
26
  return Number.parseFloat(value.toFixed(this.decimals));
@@ -0,0 +1,3 @@
1
+ export interface RoundingPort {
2
+ round(value: number): number;
3
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -1,4 +1,4 @@
1
- import { type RoundingStrategy } from "./rounding.service";
1
+ import type { RoundingPort } from "./rounding.port";
2
2
  export type SLRPairType = {
3
3
  x: number;
4
4
  y: number;
@@ -8,11 +8,18 @@ export type SLRParamsType = {
8
8
  b: number;
9
9
  };
10
10
  export type SLRPredictionType = number;
11
+ export declare const SLRMinPairsError: "slr.min.pairs";
12
+ export declare const SLRSumXTooBigError: "slr.sum.x.too.big";
13
+ export declare const SLRSumYTooBigError: "slr.sum.y.too.big";
14
+ export declare const SLRSumXSquaredTooBigError: "slr.sum.x.squared.too.big";
15
+ export declare const SLRSumXTimesYTooBigError: "slr.sum.x.times.y.too.big";
16
+ export declare const SLRModelCreationError: "slr.model.creation";
11
17
  export declare class SimpleLinearRegression {
18
+ private static readonly DEFAULT_ROUNDING;
12
19
  private readonly params;
13
20
  private readonly rounding;
14
- constructor(params: SLRParamsType, rounding?: RoundingStrategy);
15
- static fromPairs(pairs: SLRPairType[], rounding?: RoundingStrategy): SimpleLinearRegression;
16
- predict(x: SLRPairType["x"], strategy?: RoundingStrategy): SLRPredictionType;
21
+ constructor(params: SLRParamsType, rounding?: RoundingPort);
22
+ static fromPairs(pairs: SLRPairType[], rounding?: RoundingPort): SimpleLinearRegression;
23
+ predict(x: SLRPairType["x"], rounding?: RoundingPort): SLRPredictionType;
17
24
  inspect(): SimpleLinearRegression["params"];
18
25
  }
@@ -1,43 +1,52 @@
1
- import { RoundToNearest } from "./rounding.service";
2
- import { Sum } from "./sum.service";
1
+ import { RoundToNearest } from "./rounding.adapter";
2
+ export const SLRMinPairsError = "slr.min.pairs";
3
+ export const SLRSumXTooBigError = "slr.sum.x.too.big";
4
+ export const SLRSumYTooBigError = "slr.sum.y.too.big";
5
+ export const SLRSumXSquaredTooBigError = "slr.sum.x.squared.too.big";
6
+ export const SLRSumXTimesYTooBigError = "slr.sum.x.times.y.too.big";
7
+ export const SLRModelCreationError = "slr.model.creation";
3
8
  export class SimpleLinearRegression {
9
+ static DEFAULT_ROUNDING = new RoundToNearest();
4
10
  params;
5
- rounding = new RoundToNearest();
11
+ rounding;
6
12
  constructor(params, rounding) {
7
13
  this.params = params;
8
- this.rounding = rounding ?? this.rounding;
14
+ this.rounding = rounding ?? SimpleLinearRegression.DEFAULT_ROUNDING;
9
15
  }
10
16
  static fromPairs(pairs, rounding) {
11
- const n = pairs.length;
12
- if (n < 2)
13
- throw new Error("At least two pairs needed");
14
- const x = pairs.map((pair) => pair.x);
15
- const y = pairs.map((pair) => pair.y);
16
- const xx = x.map((x) => x ** 2);
17
- const xy = pairs.map((pair) => pair.x * pair.y);
18
- const sX = Sum.of(x);
19
- if (sX >= Number.MAX_SAFE_INTEGER)
20
- throw new Error("Sum of x values is too big");
21
- const sY = Sum.of(y);
22
- if (sY >= Number.MAX_SAFE_INTEGER)
23
- throw new Error("Sum of y values is too big");
24
- const sSX = Sum.of(xx);
25
- if (sSX >= Number.MAX_SAFE_INTEGER)
26
- throw new Error("Sum of x squared values is too big");
27
- const sXY = Sum.of(xy);
28
- if (sXY >= Number.MAX_SAFE_INTEGER)
29
- throw new Error("Sum of x times y values is too big");
30
- const bDenominator = sSX - sX ** 2 / n;
17
+ const count = pairs.length;
18
+ if (count < 2)
19
+ throw new Error(SLRMinPairsError);
20
+ let sumX = 0;
21
+ let sumY = 0;
22
+ let sumXX = 0;
23
+ let sumXY = 0;
24
+ for (let index = 0; index < count; index++) {
25
+ const pair = pairs[index];
26
+ sumX += pair.x;
27
+ sumY += pair.y;
28
+ sumXX += pair.x * pair.x;
29
+ sumXY += pair.x * pair.y;
30
+ }
31
+ if (Math.abs(sumX) >= Number.MAX_SAFE_INTEGER)
32
+ throw new Error(SLRSumXTooBigError);
33
+ if (Math.abs(sumY) >= Number.MAX_SAFE_INTEGER)
34
+ throw new Error(SLRSumYTooBigError);
35
+ if (Math.abs(sumXY) >= Number.MAX_SAFE_INTEGER)
36
+ throw new Error(SLRSumXTimesYTooBigError);
37
+ if (Math.abs(sumXX) >= Number.MAX_SAFE_INTEGER)
38
+ throw new Error(SLRSumXSquaredTooBigError);
39
+ const bDenominator = sumXX - sumX ** 2 / count;
31
40
  if (bDenominator === 0)
32
- throw new Error("Unable to create the model");
33
- const b = (sXY - (sX * sY) / n) / bDenominator;
34
- const a = (sY - b * sX) / n;
41
+ throw new Error(SLRModelCreationError);
42
+ const b = (sumXY - (sumX * sumY) / count) / bDenominator;
43
+ const a = (sumY - b * sumX) / count;
35
44
  return new SimpleLinearRegression({ a, b }, rounding);
36
45
  }
37
- predict(x, strategy) {
38
- const rounding = strategy ?? this.rounding;
46
+ predict(x, rounding) {
47
+ const chosen = rounding ?? this.rounding;
39
48
  const prediction = this.params.b * x + this.params.a;
40
- return rounding.round(prediction);
49
+ return chosen.round(prediction);
41
50
  }
42
51
  inspect() {
43
52
  return this.params;
package/dist/size.vo.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { z } from "zod/v4";
2
- import { RoundToDecimal } from "./rounding.service";
2
+ import { RoundToDecimal } from "./rounding.adapter";
3
3
  export var SizeUnit;
4
4
  (function (SizeUnit) {
5
5
  SizeUnit["b"] = "b";
@@ -1,11 +1,11 @@
1
1
  import { type TimestampType } from "./timestamp.vo";
2
+ export declare const StopwatchStateError: "stopwatch.already.stopped";
2
3
  export type StopwatchResultType = {
3
4
  durationMs: TimestampType;
4
5
  };
5
6
  export declare class Stopwatch {
6
7
  private readonly startMs;
7
8
  private state;
8
- private stopMs;
9
9
  constructor(startMs: TimestampType);
10
10
  stop(): StopwatchResultType;
11
11
  }
@@ -4,18 +4,17 @@ var StopwatchState;
4
4
  StopwatchState["started"] = "started";
5
5
  StopwatchState["stopped"] = "stopped";
6
6
  })(StopwatchState || (StopwatchState = {}));
7
+ export const StopwatchStateError = "stopwatch.already.stopped";
7
8
  export class Stopwatch {
8
9
  startMs;
9
10
  state = StopwatchState.started;
10
- stopMs;
11
11
  constructor(startMs) {
12
12
  this.startMs = startMs;
13
13
  }
14
14
  stop() {
15
15
  if (this.state === StopwatchState.stopped)
16
- throw new Error("Stopwatch is already stopped");
16
+ throw new Error(StopwatchStateError);
17
17
  this.state = StopwatchState.stopped;
18
- this.stopMs = Timestamp.parse(Date.now());
19
- return { durationMs: Timestamp.parse(this.stopMs - this.startMs) };
18
+ return { durationMs: Timestamp.parse(Date.now() - this.startMs) };
20
19
  }
21
20
  }
@@ -1,3 +1,4 @@
1
1
  export declare class Sum {
2
- static of(values: number[]): number;
2
+ static of(values: readonly number[]): number;
3
+ static precise(values: readonly number[]): number;
3
4
  }