diff --git a/index.js b/index.js new file mode 100644 index 0000000..e69de29 diff --git a/lib/b2c6/currency/Currency.js b/lib/b2c6/currency/Currency.js new file mode 100644 index 0000000..d9b60b4 --- /dev/null +++ b/lib/b2c6/currency/Currency.js @@ -0,0 +1,86 @@ +import { AbstractArithmetizable } from "../mathematics/AbstractArithmetizable"; + +export class Currency extends AbstractArithmetizable { + static defaultCurrencyIsoName = "CLP"; + static defaultDecimals = 2; + + #isoName; + #isoDecimals; + #storer; + #numberStorerFabric; + + + /** + * constructor + * + * @param {Object} inits options for currency instance + */ + constructor(inits) { + super(); + inits = { + ...{ + isoName: Currency.defaultCurrencyIsoName, + decimals: Currency.defaultDecimals, + ammount: 0, + numberStorerFabric: null + }, + ...inits + }; + + this.#isoName = inits.isoName; + this.#isoDecimals = inits.isoDecimals; + this.#numberStorerFabric = inits.numberStorerFabric; + + this.#storer = new this.#numberStorerFabric(inits.ammount); + } + + get isoDecimals() { + return this.#isoDecimals + } + + get isoName() { + return this.#isoName + } + + get ammount() { + return this.#storer.toString(); + } + + add(value) { + const pojo = this.toPojo(); + pojo.ammount = this.#storer.add(value).toString(); + return new Currency(pojo); + } + + substract() { + const pojo = this.toPojo(); + pojo.ammount = this.#storer.substract(value).toString(); + return new Currency(pojo); + } + + multiply() { + const pojo = this.toPojo(); + pojo.ammount = this.#storer.multiply(value).toString(); + return new Currency(pojo); + } + + divide() { + const pojo = this.toPojo(); + pojo.ammount = this.#storer.divide(value).toString(); + return new Currency(pojo); + } + + toString() { + return this.#storer.toString(); + } + + + toPojo() { + return { + isoName: this.#isoName, + decimals: this.#isoDecimals, + ammount: this.toString(), + numberStorerFabric: this.#numberStorerFabric + }; + } +} \ No newline at end of file diff --git a/lib/b2c6/errors/Errors.js b/lib/b2c6/errors/Errors.js new file mode 100644 index 0000000..a3ca623 --- /dev/null +++ b/lib/b2c6/errors/Errors.js @@ -0,0 +1,32 @@ +class InstanciatingAbstractClassError extends Error { + constructor(message = "You are trying to instantiate an abstract class, asshole!") { + super(message); + this.name = "InstanciatingAbstractClassError"; + } +} + +class UndefinedMethod extends Error { + constructor (message = "You are trying to invoke an undefined method, asshole") { + super(message); + this.name = 'UndefinedMethod'; + } +} + +/** + * InvokingAbstractClassMethod + * + * Exception throwed when invoking a method from an abstract class + */ + class InvokingAbstractClassMethod extends UndefinedMethod { + constructor (message = "You are trying to invoke a method of an abstract class, asshole! (May be you have not implement this method in the concrete class yet? moron!)") { + super(message); + this.name = 'InvokingAbstractClassMethod'; + } +} + + +module.exports = { + InstanciatingAbstractClassError, + UndefinedMethod, + InvokingAbstractClassMethod +}; \ No newline at end of file diff --git a/lib/b2c6/mathematics/AbstractArithmetizable.js b/lib/b2c6/mathematics/AbstractArithmetizable.js new file mode 100644 index 0000000..f210b9c --- /dev/null +++ b/lib/b2c6/mathematics/AbstractArithmetizable.js @@ -0,0 +1,25 @@ +import { InstanciatingAbstractClassError, InvokingAbstractClassMethod } from "../errors/Errors"; + +export class AbstractArithmetizable { + constructor() { + if (this.constructor === AbstractArithmetizable) { + throw new InstanciatingAbstractClassError(); + } + } + + add() { + throw new InvokingAbstractClassMethod("AbstractArithmetizable::add"); + } + + substract() { + throw new InvokingAbstractClassMethod("AbstractArithmetizable::substract"); + } + + multiply() { + throw new InvokingAbstractClassMethod("AbstractArithmetizable::multiply"); + } + + divide() { + throw new InvokingAbstractClassMethod("AbstractArithmetizable::denominator"); + } +} diff --git a/lib/b2c6/mathematics/DecimalChanta.js b/lib/b2c6/mathematics/DecimalChanta.js new file mode 100644 index 0000000..48e7955 --- /dev/null +++ b/lib/b2c6/mathematics/DecimalChanta.js @@ -0,0 +1,74 @@ +import { AbstractArithmetizable } from "./AbstractArithmetizable"; + +const DEFAULT_PRECISION = 32; +const ROUND_TO_NEAREST = true; + +export class DecimalChanta extends AbstractArithmetizable { + static precision = DEFAULT_PRECISION; + static round = ROUND_TO_NEAREST; + static poweredFactor = BigInt("1" + "0".repeat(DecimalChanta.precision)); + + + static createFromBigInt(bigInt) { + const pojo = { _daBigValue: bigInt }; + return Object.assign( Object.create(DecimalChanta.prototype), pojo); + } + + static roundedDivision(numerator, denominator) { + const correction = (DecimalChanta.round ? numerator * 2n / denominator % 2n : 0n); + return DecimalChanta.createFromBigInt(numerator / denominator + correction); + } + + + + constructor(value) { + super(); + if (value instanceof DecimalChanta) { + return value; + } + + let [ integerPart, decimalPart ] = String(value).split(".").map( (e, i) => { + if (typeof e != "undefined" && !!e) { + return e; + } + }); + + decimalPart = decimalPart ?? "0"; + + this._daBigValue = BigInt(integerPart + decimalPart.padEnd(DecimalChanta.precision, "0") + .slice(0, DecimalChanta.precision)) + + BigInt(DecimalChanta.round && decimalPart[DecimalChanta.precision] >= "5"); + } + + add(value) { + return DecimalChanta.createFromBigInt(this._daBigValue + new DecimalChanta(value)._daBigValue); + } + + substract(value) { + return DecimalChanta.createFromBigInt(this._daBigValue - new DecimalChanta(value)._daBigValue); + } + + multiply(value) { + return DecimalChanta.roundedDivision(this._daBigValue * new DecimalChanta(value)._daBigValue, DecimalChanta.poweredFactor); + } + + divide(value) { + return DecimalChanta.roundedDivision(this._daBigValue * DecimalChanta.poweredFactor, new DecimalChanta(value)._daBigValue); + } + + + toString() { + const s = this._daBigValue.toString().padStart(DecimalChanta.precision + 1, "0"); + return s.slice(0, -DecimalChanta.precision) + "." + s.slice(-DecimalChanta.precision) + .replace(/\.?0+$/, ""); + } +} + +const proxy = new Proxy( + DecimalChanta, + { + set: function(obj, prop) { + DecimalChanta.poweredFactor = BigInt("1" + "0".repeat(DecimalChanta.precision)) + } + } +); \ No newline at end of file diff --git a/lib/b2c6/storers/.gitignore b/lib/b2c6/storers/.gitignore new file mode 100644 index 0000000..b16c2ba --- /dev/null +++ b/lib/b2c6/storers/.gitignore @@ -0,0 +1,5 @@ +node_modules +dist +.vscode +.git +package-lock.json diff --git a/lib/b2c6/storers/AbstractStorer.js b/lib/b2c6/storers/AbstractStorer.js new file mode 100644 index 0000000..da96385 --- /dev/null +++ b/lib/b2c6/storers/AbstractStorer.js @@ -0,0 +1,38 @@ +import { InstanciatingAbstractClassError, InvokingAbstractClassMethod } from "../errors/Errors"; + +/** + * AbstracStorer + * + * Provides an abstract interface to implement custom storers. + * + * An storer is a component responsible of handle the arithmetical + * operation over an object. + * + */ +export class AbstractStorer { + constructor() { + if (this.constructor === AbstractStorer) { + throw new InstanciatingAbstractClassError(); + } + } + + add() { + throw new InvokingAbstractClassMethod("AbstractStorer::add"); + } + + substract() { + throw new InvokingAbstractClassMethod("AbstractStorer::substract"); + } + + multiply() { + throw new InvokingAbstractClassMethod("AbstractStorer::multiply"); + } + + divide() { + throw new InvokingAbstractClassMethod("AbstractStorer::denominator"); + } + + toString() { + throw new InvokingAbstractClassMethod("AbstractStorer::toString"); + } +} diff --git a/lib/b2c6/storers/BigStorer.js b/lib/b2c6/storers/BigStorer.js new file mode 100644 index 0000000..b3b06ef --- /dev/null +++ b/lib/b2c6/storers/BigStorer.js @@ -0,0 +1,51 @@ +import { AbstractStorer } from './AbstractStorer'; +import Big from 'big.js'; + + +/** + * BigStorer + * + * NumberStorer para la librería Big.js + * @link https://github.com/MikeMcl/big.js/ + */ +export class BigStorer extends AbstractStorer { + + static #fromDecimalChanta(decimal) { + return new Big(decimal.toString()); + } + + #ammount; + + constructor(rawAmmount) { + super(); + + Big.PE = 32; + Big.NE = -32; + this.#ammount = new Big(rawAmmount); + } + + get ammount() { + return this.#ammount; + } + + add(value) { + return this.#ammount.plus(value); + } + + substract(value) { + return this.#ammount.minus(value); + } + + multiply(value) { + return this.#ammount.times(value); + } + + divide(value) { + return this.#ammount.div(value); + } + + toString() { + return this.ammount.toString(); + } + +} \ No newline at end of file diff --git a/lib/b2c6/storers/DecimalChantaStorer.js b/lib/b2c6/storers/DecimalChantaStorer.js new file mode 100644 index 0000000..03f8bb8 --- /dev/null +++ b/lib/b2c6/storers/DecimalChantaStorer.js @@ -0,0 +1,49 @@ +import { AbstractStorer } from "./AbstractStorer"; +import { DecimalChanta } from "../mathematics/DecimalChanta"; + +/** + * DecimalChantaStorer + * + * Implements a DecimaChanta based storer. + * + * Encapsulates DecimalChanta services. + * + */ +export class DecimalChantaStorer extends AbstractStorer { + + #ammount; + + /** + * constructor + * + * @param {Mixed} rawAmmount The value to be stored as a DecimalChanta value + */ + constructor(rawAmmount) { + super(); + this.#ammount = new DecimalChanta(rawAmmount); + } + + get ammount() { + return this.#ammount; + } + + add(value) { + return this.#ammount.add(value); + } + + substract(value) { + return this.#ammount.substract(value); + } + + multiply(value) { + return this.#ammount.multiply(value); + } + + divide() { + return this.#ammount.divide(value); + } + + toString() { + return this.ammount.toString(); + } +} diff --git a/tests/currency/currency.test.js b/tests/currency/currency.test.js new file mode 100644 index 0000000..2f3444b --- /dev/null +++ b/tests/currency/currency.test.js @@ -0,0 +1,39 @@ +import { Currency } from "../../lib/b2c6/currency/Currency"; +import { DecimalChantaStorer } from "../../lib/b2c6/storers/DecimalChantaStorer"; +import { BigStorer } from "../../lib/b2c6/storers/BigStorer"; + +const defaultStorerStrategy = BigStorer; + +test('Testing Instantiating of Currency', () => { + let chileanPeso = new Currency({ + isoName: "CLP", + ammount: 575, + numberStorerFabric: defaultStorerStrategy + }); + console.log(`chilean peso: ${chileanPeso.toString()}`); + return; +}); + + +test('Testing Currency addition', () => { + const chileanPesoPrice = new Currency({ + isoName: "CLP", + ammount: 1575, + numberStorerFabric: defaultStorerStrategy + }); + + const otherChileanPesoPrice = new Currency({ + isoName: "CLP", + ammount: 425, + numberStorerFabric: defaultStorerStrategy + }); + + const ammount = 1229; + + const currencyAddition = chileanPesoPrice.add(otherChileanPesoPrice); + const currencyIntegerAddition = chileanPesoPrice.add(ammount); + + console.log(`currencyAddition: ${currencyAddition.toString()}`); + console.log(`currencyIntegerAddition: ${currencyIntegerAddition.toString()}`); + return; +}); diff --git a/tests/mathematics/AbstractArithmetizable.test.js b/tests/mathematics/AbstractArithmetizable.test.js new file mode 100644 index 0000000..02e39a0 --- /dev/null +++ b/tests/mathematics/AbstractArithmetizable.test.js @@ -0,0 +1,14 @@ +import { AbstractArithmetizable } from "../../lib/b2c6/mathematics/AbstractArithmetizable"; +import { InstanciatingAbstractClassError } from "../../lib/b2c6/errors/Errors"; + +test('Testing no instantiating of AbstractArithmetizable', () => { + try { + const abstractInstance = new AbstractArithmetizable(); + } catch (e) { + if (e instanceof InstanciatingAbstractClassError) { + console.info("%cSuccess! Coudnt instanciate AbstractArithmetizable", "color:blue;"); + return; + } + throw e; + } +}); \ No newline at end of file diff --git a/tests/mathematics/DecimalChanta.test.js b/tests/mathematics/DecimalChanta.test.js new file mode 100644 index 0000000..f5dd1de --- /dev/null +++ b/tests/mathematics/DecimalChanta.test.js @@ -0,0 +1,66 @@ +import { DecimalChanta } from "../../lib/b2c6/mathematics/DecimalChanta"; + +test('Testing Instantiating of DecimalChanta', () => { + let a = new DecimalChanta(11.5); + console.log(`decimal chanta: ${a.toString()}`); + + a = new DecimalChanta("11.5346141710991074111232578901231234387777733339904343464574908070735623435479797897857434567625943"); + console.log(`decimal chanta: ${a.toString()}`); + return; +}); + + +test('Testing adding of DecimalChanta', () => { + const a = new DecimalChanta(11.5); + const b = new DecimalChanta(1.18); + const c = a.add(b); + + console.log(`a + b = c`); + console.log(`${a.toString()} + ${b.toString()} = ${c.toString()}`); +}); + +test('Testing substracting of DecimalChanta', () => { + const a = new DecimalChanta(11.5); + const b = new DecimalChanta(1.18); + const c = a.substract(b); + + console.log(`a - b = c`); + console.log(`${a.toString()} - ${b.toString()} = ${c.toString()}`); +}); + +test('Testing multiplying of DecimalChanta', () => { + const a = new DecimalChanta(11.5); + const b = new DecimalChanta(1.18); + const c = a.multiply(b); + + console.log(`a * b = c`); + console.log(`${a.toString()} * ${b.toString()} = ${c.toString()}`); +}); + +test('Testing division of DecimalChanta', () => { + const a = new DecimalChanta(11.5); + const b = new DecimalChanta(1.18); + const c = a.divide(b); + + console.log(`a / b = c`); + console.log(`${a.toString()} / ${b.toString()} = ${c.toString()}`); +}); + + + +test('Testing result of arithmetic operations in DecimalChanta', () => { + const a = new DecimalChanta(11.5); + const b = new DecimalChanta(1.18); + const c = a.divide(b); + const aAgain = c.multiply(b); + + console.log(`a / b = c`); + console.log(`${a.toString()} / ${b.toString()} = ${c.toString()}`); + console.log(`a = ${a.toString()} = aAgain = ${aAgain.toString()}`); + + expect(a.toString()).toBe(aAgain.toString()); + + // const expected = "9.745762711864406779661016949152542372881355932203389830508474576"; + const expected = "9.74576271186440677966101694915254"; + expect(c.toString()).toBe(expected); +}); diff --git a/tests/storers/DecimalChantaStorer.test.js b/tests/storers/DecimalChantaStorer.test.js new file mode 100644 index 0000000..ab426f2 --- /dev/null +++ b/tests/storers/DecimalChantaStorer.test.js @@ -0,0 +1,12 @@ +import { DecimalChantaStorer } from "../../lib/b2c6/storers/DecimalChantaStorer"; + +test('Testing Instantiating of DecimalChantaStorer', () => { + let a = new DecimalChantaStorer(11.5); + + console.log(`decimal chanta storer: ${a.toString()}`); + + a = new DecimalChantaStorer("11.5346141710991074111232578901231234387777733339904343464574908070735623435479797897857434567625943"); + console.log(`decimal chanta storer: ${a.toString()}`); + return; + }); +