diff --git a/spec/iterator/named_iterable_value.spec.ts b/spec/iterator/named_iterable_value.spec.ts index e8c3ac56bafbad846a7375060e585d6366203f5f..42926eb2d7d5ac0cf1122c61a0c724cce037dd16 100644 --- a/spec/iterator/named_iterable_value.spec.ts +++ b/spec/iterator/named_iterable_value.spec.ts @@ -1,4 +1,4 @@ -import { BaseParam, ExtraResults, ParamDomainValue, Result, ResultElement } from "../../src"; +import { ExtraResults, ParamDefinition, ParamDomainValue, Result, ResultElement } from "../../src"; /** * IMPORTANT ! @@ -8,8 +8,8 @@ import { BaseParam, ExtraResults, ParamDomainValue, Result, ResultElement } from */ // import { describe, expect, it, xdescribe, xit } from "../mock_jasmine"; -function testBaseParamValues(vals: number[]): BaseParam { - const p: BaseParam = new BaseParam("aa", ParamDomainValue.ANY); +function testParamDefinitionValues(vals: number[]): ParamDefinition { + const p: ParamDefinition = new ParamDefinition("aa", ParamDomainValue.ANY); p.paramValues.setValues(vals); @@ -58,28 +58,28 @@ function testExtraResultsValues(vals: number[]): ExtraResults { } describe("INamedIterableValues : ", () => { - describe("BaseParam : ", () => { + describe("ParamDefinition : ", () => { it("test 1", () => { const name = "aa"; - const p: BaseParam = new BaseParam(name, ParamDomainValue.ANY); + const p: ParamDefinition = new ParamDefinition(name, ParamDomainValue.ANY); expect(p.name).toEqual(name); }); it("test 2", () => { const vals: number[] = []; - const p = testBaseParamValues(vals); + const p = testParamDefinitionValues(vals); expect(p.hasMultipleValues).toBeFalsy(); }); it("test 3", () => { const vals: number[] = [0]; - const p = testBaseParamValues(vals); + const p = testParamDefinitionValues(vals); expect(p.hasMultipleValues).toBeFalsy(); }); it("test 4", () => { const vals: number[] = [0, 1]; - const p = testBaseParamValues(vals); + const p = testParamDefinitionValues(vals); expect(p.hasMultipleValues).toBeTruthy(); }); }); diff --git a/src/dichotomie.ts b/src/dichotomie.ts index 43a29c63b06331eb24da29326bf3979b2647aa47..df166f00733f8f20406d5c868dffeb4ea1099b18 100644 --- a/src/dichotomie.ts +++ b/src/dichotomie.ts @@ -5,7 +5,6 @@ import { ParamDefinition } from "./param/param-definition"; import { ParamDomain, ParamDomainValue } from "./param/param-domain"; import { Interval } from "./util/interval"; import { Message, MessageCode } from "./util/message"; -import { Pair } from "./util/pair"; import { Result } from "./util/result"; class SearchInterval extends Interval { @@ -13,7 +12,7 @@ class SearchInterval extends Interval { private _dicho: Dichotomie; - private _targets: Pair; + private _targets: Interval; constructor(d: Dichotomie, min: number, max: number, s: number) { super(min, max); @@ -43,7 +42,8 @@ class SearchInterval extends Interval { get targets() { if (this._targets === undefined) { - this._targets = new Pair(undefined, undefined); + // @TODO just set _targets to undefined / null ? + this._targets = new Interval(undefined, undefined); } return this._targets; } @@ -53,6 +53,7 @@ class SearchInterval extends Interval { */ get targetLower() { this.updateTargets(); + // @TODO val1 is not guaranteed to be the lower bound; use .min ? return this.targets.val1; } @@ -61,17 +62,18 @@ class SearchInterval extends Interval { */ get targetUpper() { this.updateTargets(); + // @TODO val2 is not guaranteed to be the upper bound; use .max ? return this.targets.val2; } public next() { if (this._step > 0) { - this._val1 = this._val2; - this._val2 += this._step; + this.val1 = this.val2; + this.val2 += this._step; this.targets.setValues(this.targets.val2, undefined); } else { - this._val2 = this._val1; - this._val1 += this._step; + this.val2 = this.val1; + this.val1 += this._step; this.targets.setValues(undefined, this.targets.val1); } } @@ -88,12 +90,12 @@ class SearchInterval extends Interval { private updateTargets() { let t1 = this.targets.val1; if (t1 === undefined) { - t1 = this._dicho.CalculX(this._val1).vCalc; + t1 = this._dicho.CalculX(this.val1).vCalc; } let t2 = this.targets.val2; if (t2 === undefined) { - t2 = this._dicho.CalculX(this._val2).vCalc; + t2 = this._dicho.CalculX(this.val2).vCalc; } this.targets.setValues(t1, t2); } @@ -229,7 +231,7 @@ export class Dichotomie extends Debug { private isIncreasingFunction(x: number, dom: Interval): boolean { const epsilon = 1e-8; const bounds = new Interval(x - epsilon, x + epsilon); - bounds.setBounds(bounds.intersect(dom)); // au cas où l'on sorte du domaine de la variable de la fonction + bounds.setInterval(bounds.intersect(dom)); // au cas où l'on sorte du domaine de la variable de la fonction const y1 = this.CalculX(bounds.min).vCalc; const y2 = this.CalculX(bounds.max).vCalc; @@ -252,7 +254,7 @@ export class Dichotomie extends Debug { if (inters.length === 0) { break; } - intSearch.setBounds(inters); + intSearch.setInterval(inters); if (intSearch.hasTargetValue(rTarget)) { ok = true; break; @@ -293,7 +295,7 @@ export class Dichotomie extends Debug { // initialisation de l'intervalle de recherche let intSearch: SearchInterval = new SearchInterval(this, rInit - step / 2, rInit + step / 2, step); // au cas où l'on sorte du domaine de la variable de la fonction - intSearch.setBounds(intSearch.intersect(intMax)); + intSearch.setInterval(intSearch.intersect(intMax)); // sens de variation de la fonction const inc = this.isIncreasingFunction(rInit, intMax); @@ -318,7 +320,7 @@ export class Dichotomie extends Debug { const oldStepSign = intSearch.step > 0 ? 1 : -1; intSearch = new SearchInterval(this, rInit + step / 2, rInit + step, step * -oldStepSign); // au cas où l'on sorte du domaine de la variable de la fonction - intSearch.setBounds(intSearch.intersect(intMax)); + intSearch.setInterval(intSearch.intersect(intMax)); a = this.searchTarget(rTarget, intSearch, intMax); if (a.ok) { diff --git a/src/index.ts b/src/index.ts index e26d05fe25b35b7b218d3827775896e39ddc687c..193d48e71bfb343cd4ae153680d4590dc170c5ec 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,4 @@ export * from "./base"; -export * from "./param/param-base"; export * from "./param/param-definition"; export * from "./param/param-domain"; export * from "./param/params-equation"; @@ -23,7 +22,6 @@ export * from "./util/message"; export * from "./util/log"; export * from "./util/result"; export * from "./util/resultelement"; -export * from "./util/pair"; export * from "./util/interval"; export * from "./util/observer"; export * from "./util/iterator"; diff --git a/src/param/param-base.ts b/src/param/param-base.ts deleted file mode 100644 index d847fd33dfda79fe787648be5e17d56a54208850..0000000000000000000000000000000000000000 --- a/src/param/param-base.ts +++ /dev/null @@ -1,420 +0,0 @@ -import { Interval } from "../util/interval"; -import { Message, MessageCode } from "../util/message"; - -import { IJalhydObject, JalhydObject } from "../jalhyd_object"; -import { IObservable, Observable, Observer } from "../util/observer"; -import { Result } from "../util/result"; -import { INubReference, IReferencedNub } from "../value_ref/object_ref"; -import { ParamDomain, ParamDomainValue } from "./param-domain"; -import { INamedIterableValues, INumberIterator } from "./param-value-iterator"; -import { ParamValueMode } from "./param-value-mode"; -import { ParamValues } from "./param-values"; - -/** - * paramètre avec symbole et domaine de définition - */ -// tslint:disable-next-line:max-classes-per-file -export class BaseParam extends JalhydObject implements INubReference, INamedIterableValues, IObservable { - /** - * symbole - */ - private _symbol: string; - - /** - * domaine de définition - */ - private _domain: ParamDomain; - - /** - * valeur(s) prise(s) - */ - private _paramValues: ParamValues; - - /** - * implémentation par délégation de IObservable - */ - private _observable: Observable; - - constructor(symb: string, d: ParamDomain | ParamDomainValue, val?: number) { - super(); - this._symbol = symb; - - this._observable = new Observable(); - - this._paramValues = new ParamValues(); - this._paramValues.setSingleValue(val); - - if (d instanceof ParamDomain) { - this._domain = d; - } else { - this._domain = new ParamDomain(d as ParamDomainValue); - } - - this.checkValue(val); - } - - get symbol(): string { - return this._symbol; - } - - public getDomain(): ParamDomain { - return this._domain; - } - - public get interval(): Interval { - return this._domain.interval; - } - - public get paramValues(): ParamValues { - if (this.isReferenceDefined) { - return this.referencedParamValues; - } - return this._paramValues; - } - - /** - * gestion de la valeur - */ - - public get isDefined(): boolean { - return this._paramValues.isDefined; - } - - public getValue(): number { - if (!this._paramValues.isDefined) { - const e = new Message(MessageCode.ERROR_PARAMDEF_VALUE_UNDEFINED); - e.extraVar.symbol = this.symbol; - throw e; - } - - return this._paramValues.currentValue; - } - - public get currentValue(): number { - return this.getValue(); - } - - public setValue(val: number, sender?: any) { - this.checkValue(val); - this._paramValues.currentValue = val; - this.notifyValueModified(sender); - } - - public get uncheckedValue(): number { - return this._paramValues.uncheckedValue; - } - - public checkValue(v: number) { - const sDomain = ParamDomainValue[this._domain.domain]; - - switch (this._domain.domain) { - case ParamDomainValue.ANY: - break; - - case ParamDomainValue.POS: - if (v <= 0) { - const f = new Message(MessageCode.ERROR_PARAMDEF_VALUE_POS); - f.extraVar.symbol = this.symbol; - f.extraVar.value = v; - throw f; - } - break; - - case ParamDomainValue.POS_NULL: - if (v < 0) { - const f = new Message(MessageCode.ERROR_PARAMDEF_VALUE_POSNULL); - f.extraVar.symbol = this.symbol; - f.extraVar.value = v; - throw f; - } - break; - - case ParamDomainValue.NOT_NULL: - if (v === 0) { - const f = new Message(MessageCode.ERROR_PARAMDEF_VALUE_NULL); - f.extraVar.symbol = this.symbol; - throw f; - } - break; - - case ParamDomainValue.INTERVAL: - const min = this._domain.minValue; - const max = this._domain.maxValue; - if (v < min || v > max) { - const f = new Message(MessageCode.ERROR_PARAMDEF_VALUE_INTERVAL); - f.extraVar.symbol = this.symbol; - f.extraVar.value = v; - f.extraVar.minValue = min; - f.extraVar.maxValue = max; - throw f; - } - break; - - default: - const e = new Message(MessageCode.ERROR_PARAMDOMAIN_INVALID); - e.extraVar.symbol = this.symbol; - e.extraVar.domain = sDomain; - throw e; - } - } - - public checkMin(min: number): boolean { - return this.isMinMaxDomainValid(min) && (min < this._paramValues.max); - } - - public checkMax(max: number): boolean { - return this.isMinMaxDomainValid(max) && (this._paramValues.min < max); - } - - public checkStep(step: number): boolean { - return this.isMinMaxValid && this._paramValues.stepRefValue.intervalHasValue(step); - } - - get isValueValid(): boolean { - try { - const v = this.getValue(); - this.checkValue(v); - return true; - } catch (e) { - return false; - } - } - - get isMinMaxValid(): boolean { - return this.checkMinMax(this._paramValues.min, this._paramValues.max); - } - - public get isRangeValid(): boolean { - switch (this._paramValues.valueMode) { - case ParamValueMode.LISTE: - return this.isListValid; - - case ParamValueMode.MINMAX: - return this.checkStep(this._paramValues.step); - } - - // tslint:disable-next-line:max-line-length - throw new Error(`"BaseParam.isRangeValid() : valeur ${ParamValueMode[this._paramValues.valueMode]} de ParamValueMode non prise en compte`); - } - - public get isValid() { - switch (this._paramValues.valueMode) { - case ParamValueMode.SINGLE: - return this.isValueValid; - - case ParamValueMode.MINMAX: - case ParamValueMode.LISTE: - return this.isRangeValid; - - case ParamValueMode.CALCUL: - return true; - - case ParamValueMode.LINK: - if (!this.isReferenceDefined) { - return false; - } - - try { - for (const v of this.valuesIterator) { - this.checkValue(v); - } - return true; - } catch (e) { - return false; - } - } - - throw new Error( - // tslint:disable-next-line:max-line-length - `BaseParam.isValid() : valeur de ParamValueMode '${ParamValueMode[this._paramValues.valueMode]}' non prise en charge` - ); - } - - public get valueMode() { - return this._paramValues.valueMode; - } - - public set valueMode(m: ParamValueMode) { - this._paramValues.valueMode = m; - } - - // interface INubReference - - public defineReference(target: IReferencedNub, desc: string) { - const oldDef = this._paramValues.referenceDefinition; - const oldTarget = this._paramValues.referencedNub; - try { - this._paramValues.defineReference(target, desc); - this.checkReferenceCircularity(this, []); - } catch (e) { - this._paramValues.defineReference(oldTarget, oldDef); - throw e; - } - } - - public undefineReference() { - this._paramValues.undefineReference(); - } - - get referenceDefinition(): string { - return this._paramValues.referenceDefinition; - } - - get referencedNub(): IReferencedNub { - return this._paramValues.referencedNub; - } - - get isReferenceDefined(): boolean { - return this._paramValues.isReferenceDefined; - } - - get referencedParamValues(): ParamValues { - return this._paramValues.referencedParamValues; - } - - get referencedResult(): Result { - return this._paramValues.referencedResult; - } - - get referencedExtraResult(): any { - return this._paramValues.referencedExtraResult; - } - - get referencedValuesIterator(): INumberIterator { - return this._paramValues.referencedValuesIterator; - } - - get referencedObject(): INamedIterableValues { - return this._paramValues.referencedObject; - } - - // interface INamedIterableValues - - public get valuesIterator(): INumberIterator { - if (this.isReferenceDefined) { - return this.referencedValuesIterator; - } - return this._paramValues.valuesIterator; - } - - public get hasMultipleValues(): boolean { - return this._paramValues.hasMultipleValues; - } - - public get name(): string { - return this._symbol; - } - - public initValuesIterator(reverse: boolean = false): INumberIterator { - return this._paramValues.initValuesIterator(reverse); - } - - public get hasNext(): boolean { - return this._paramValues.hasNext; - } - - public next(): IteratorResult<number> { - return this._paramValues.next(); - } - - public [Symbol.iterator](): IterableIterator<number> { - return this._paramValues; - } - - // interface IObservable - - /** - * ajoute un observateur à la liste - */ - public addObserver(o: Observer) { - this._observable.addObserver(o); - } - - /** - * supprime un observateur de la liste - */ - public removeObserver(o: Observer) { - this._observable.removeObserver(o); - } - - /** - * notifie un événement aux observateurs - */ - public notifyObservers(data: any, sender?: any) { - this._observable.notifyObservers(data, sender); - } - - /** - * notification envoyée après la modification de la valeur du paramètre - */ - private notifyValueModified(sender: any) { - this.notifyObservers( - { - action: "baseparamAfterValue", - }, sender - ); - } - - /** - * vérifie si un min/max est valide par rapport au domaine de définition - */ - private isMinMaxDomainValid(v: number): boolean { - if (v === undefined) { - return false; - } - - if (this._paramValues.valueMode === ParamValueMode.MINMAX) { - try { - this.checkValue(v); - } catch (e) { - return false; - } - } - - return true; - } - - private checkMinMax(min: number, max: number): boolean { - return this.isMinMaxDomainValid(min) && this.isMinMaxDomainValid(max) && (min < max); - } - - private get isListValid(): boolean { - if (this._paramValues.valueList === undefined) { - return false; - } - - for (const v of this._paramValues.valueList) { - try { - this.checkValue(v); - } catch (e) { - return false; - } - } - return true; - } - - /** - * vérifie l'absence de référence circulaire - * @param seenUids liste des uids déjà vérifiés - * @param o objet à tester (son uid est il déjà dans la liste ?) - */ - private checkReferenceCircularity(o: any, seenUids: number[]) { - if ("uid" in o) { - // if (o.uid in seenUids) - if (seenUids.indexOf(o.uid) !== -1) { - throw new Error(`références circulaires détectées (uids : ${seenUids})`); - } - - seenUids.push(o.uid); - - if ("referencedObject" in o) { - const curr = o as INubReference; - const next = curr.referencedObject; - if (next !== undefined) { - this.checkReferenceCircularity(next as IJalhydObject, seenUids); - } - } - } - } - -} diff --git a/src/param/param-definition.ts b/src/param/param-definition.ts index 43997761ffc4edf10e310ad740f1379b5ce47b0a..d38c0be764ae7780508980140596116da8b9d852 100644 --- a/src/param/param-definition.ts +++ b/src/param/param-definition.ts @@ -1,54 +1,106 @@ +import { Interval } from "../util/interval"; import { Message, MessageCode } from "../util/message"; + +import { IJalhydObject, JalhydObject } from "../jalhyd_object"; +import { IObservable, Observable, Observer } from "../util/observer"; import { Result } from "../util/result"; -import { BaseParam } from "./param-base"; +import { INubReference, IReferencedNub } from "../value_ref/object_ref"; import { ParamDomain, ParamDomainValue } from "./param-domain"; +import { INamedIterableValues, INumberIterator } from "./param-value-iterator"; import { ParamValueMode } from "./param-value-mode"; +import { ParamValues } from "./param-values"; /** - * calculabilité du paramètre + * Calculabilité du paramètre */ export enum ParamCalculability { - /** - * paramètre fixé (immuable, cad non modifiable après création) - */ + /** paramètre fixé (immuable, cad non modifiable après création) */ NONE, - - /** - * paramètre libre (modifiable) - */ + /** paramètre libre (modifiable) */ FREE, - - /** - * paramètre calculable analytiquement, par méthode de Newton, ... - */ + /** paramètre calculable analytiquement, par méthode de Newton, ... */ EQUATION, - - /** - * paramètre calculable par dichotomie - */ + /** paramètre calculable par dichotomie */ DICHO } /** - * définition d'un paramètre d'un noeud de calcul + * Paramètre avec symbole, domaine de définition, calculabilité */ -// tslint:disable-next-line:max-classes-per-file -export class ParamDefinition extends BaseParam { - /** - * calculabilité - */ +export class ParamDefinition extends JalhydObject implements INubReference, INamedIterableValues, IObservable { + + /** symbole */ + private _symbol: string; + + /** domaine de définition */ + private _domain: ParamDomain; + + /** calculabilité */ private _calc: ParamCalculability; - constructor(s: string, d: ParamDomain | ParamDomainValue, val?: number) { - super(s, d, val); + /** valeur(s) prise(s) */ + private _paramValues: ParamValues; + + /** implémentation par délégation de IObservable */ + private _observable: Observable; + + constructor(symb: string, d: ParamDomain | ParamDomainValue, val?: number) { + super(); + this._symbol = symb; + this._observable = new Observable(); + this._paramValues = new ParamValues(); + this._paramValues.setSingleValue(val); this._calc = ParamCalculability.FREE; + + if (d instanceof ParamDomain) { + this._domain = d; + } else { + this._domain = new ParamDomain(d as ParamDomainValue); + } + + this.checkValue(val); + } + + get symbol(): string { + return this._symbol; + } + + public getDomain(): ParamDomain { + return this._domain; + } + + public get interval(): Interval { + return this._domain.interval; + } + + public get paramValues(): ParamValues { + if (this.isReferenceDefined) { + return this.referencedParamValues; + } + return this._paramValues; } + get calculability(): ParamCalculability { + if (this._calc === undefined) { + // throw "value of parameter '" + this._symbol + "' calculability is not defined"; + const e = new Message(MessageCode.ERROR_PARAMDEF_CALC_UNDEFINED); + e.extraVar.symbol = this.symbol; + throw e; + } + + return this._calc; + } + + set calculability(c: ParamCalculability) { + this._calc = c; + } + + /** returns value or referenced value */ get v(): number { if (this.isReferenceDefined) { const ro = this.referencedObject; - if (ro instanceof BaseParam) { + if (ro instanceof ParamDefinition) { switch (this.referencedParamValues.valueMode) { case ParamValueMode.CALCUL: const r = this.referencedResult; @@ -67,42 +119,296 @@ export class ParamDefinition extends BaseParam { return this.referencedExtraResult; } - return super.getValue(); + return this.getValue(); } + /** set value, with calculability control */ set v(val: number) { if (this.calculability === ParamCalculability.NONE) { const e = new Message(MessageCode.ERROR_PARAMDEF_VALUE_FIXED); e.extraVar.symbol = this.symbol; throw e; } - super.setValue(val); + this.setValue(val); } - /* - * méthodes de calculabilité - */ - /** - * variable calculable par l'équation ? + * gestion de la valeur */ - public isAnalytical(): boolean { - return this.calculability === ParamCalculability.EQUATION; + + public get isDefined(): boolean { + return this._paramValues.isDefined; } - get calculability(): ParamCalculability { - if (this._calc === undefined) { - // throw "value of parameter '" + this._symbol + "' calculability is not defined"; - const e = new Message(MessageCode.ERROR_PARAMDEF_CALC_UNDEFINED); + public getValue(): number { + if (!this._paramValues.isDefined) { + const e = new Message(MessageCode.ERROR_PARAMDEF_VALUE_UNDEFINED); e.extraVar.symbol = this.symbol; throw e; } - return this._calc; + return this._paramValues.currentValue; } - set calculability(c: ParamCalculability) { - this._calc = c; + public get currentValue(): number { + return this.getValue(); + } + + public setValue(val: number, sender?: any) { + this.checkValue(val); + this._paramValues.currentValue = val; + this.notifyValueModified(sender); + } + + public get uncheckedValue(): number { + return this._paramValues.uncheckedValue; + } + + public checkValue(v: number) { + const sDomain = ParamDomainValue[this._domain.domain]; + + switch (this._domain.domain) { + case ParamDomainValue.ANY: + break; + + case ParamDomainValue.POS: + if (v <= 0) { + const f = new Message(MessageCode.ERROR_PARAMDEF_VALUE_POS); + f.extraVar.symbol = this.symbol; + f.extraVar.value = v; + throw f; + } + break; + + case ParamDomainValue.POS_NULL: + if (v < 0) { + const f = new Message(MessageCode.ERROR_PARAMDEF_VALUE_POSNULL); + f.extraVar.symbol = this.symbol; + f.extraVar.value = v; + throw f; + } + break; + + case ParamDomainValue.NOT_NULL: + if (v === 0) { + const f = new Message(MessageCode.ERROR_PARAMDEF_VALUE_NULL); + f.extraVar.symbol = this.symbol; + throw f; + } + break; + + case ParamDomainValue.INTERVAL: + const min = this._domain.minValue; + const max = this._domain.maxValue; + if (v < min || v > max) { + const f = new Message(MessageCode.ERROR_PARAMDEF_VALUE_INTERVAL); + f.extraVar.symbol = this.symbol; + f.extraVar.value = v; + f.extraVar.minValue = min; + f.extraVar.maxValue = max; + throw f; + } + break; + + default: + const e = new Message(MessageCode.ERROR_PARAMDOMAIN_INVALID); + e.extraVar.symbol = this.symbol; + e.extraVar.domain = sDomain; + throw e; + } + } + + public checkMin(min: number): boolean { + return this.isMinMaxDomainValid(min) && (min < this._paramValues.max); + } + + public checkMax(max: number): boolean { + return this.isMinMaxDomainValid(max) && (this._paramValues.min < max); + } + + public checkStep(step: number): boolean { + return this.isMinMaxValid && this._paramValues.stepRefValue.intervalHasValue(step); + } + + get isValueValid(): boolean { + try { + const v = this.getValue(); + this.checkValue(v); + return true; + } catch (e) { + return false; + } + } + + get isMinMaxValid(): boolean { + return this.checkMinMax(this._paramValues.min, this._paramValues.max); + } + + public get isRangeValid(): boolean { + switch (this._paramValues.valueMode) { + case ParamValueMode.LISTE: + return this.isListValid; + + case ParamValueMode.MINMAX: + return this.checkStep(this._paramValues.step); + } + + // tslint:disable-next-line:max-line-length + throw new Error(`"ParamDefinition.isRangeValid() : valeur ${ParamValueMode[this._paramValues.valueMode]} de ParamValueMode non prise en compte`); + } + + public get isValid() { + switch (this._paramValues.valueMode) { + case ParamValueMode.SINGLE: + return this.isValueValid; + + case ParamValueMode.MINMAX: + case ParamValueMode.LISTE: + return this.isRangeValid; + + case ParamValueMode.CALCUL: + return true; + + case ParamValueMode.LINK: + if (!this.isReferenceDefined) { + return false; + } + + try { + for (const v of this.valuesIterator) { + this.checkValue(v); + } + return true; + } catch (e) { + return false; + } + } + + throw new Error( + // tslint:disable-next-line:max-line-length + `ParamDefinition.isValid() : valeur de ParamValueMode '${ParamValueMode[this._paramValues.valueMode]}' non prise en charge` + ); + } + + public get valueMode() { + return this._paramValues.valueMode; + } + + public set valueMode(m: ParamValueMode) { + this._paramValues.valueMode = m; + } + + // interface INubReference + + public defineReference(target: IReferencedNub, desc: string) { + const oldDef = this._paramValues.referenceDefinition; + const oldTarget = this._paramValues.referencedNub; + try { + this._paramValues.defineReference(target, desc); + this.checkReferenceCircularity(this, []); + } catch (e) { + this._paramValues.defineReference(oldTarget, oldDef); + throw e; + } + } + + public undefineReference() { + this._paramValues.undefineReference(); + } + + get referenceDefinition(): string { + return this._paramValues.referenceDefinition; + } + + get referencedNub(): IReferencedNub { + return this._paramValues.referencedNub; + } + + get isReferenceDefined(): boolean { + return this._paramValues.isReferenceDefined; + } + + get referencedParamValues(): ParamValues { + return this._paramValues.referencedParamValues; + } + + get referencedResult(): Result { + return this._paramValues.referencedResult; + } + + get referencedExtraResult(): any { + return this._paramValues.referencedExtraResult; + } + + get referencedValuesIterator(): INumberIterator { + return this._paramValues.referencedValuesIterator; + } + + get referencedObject(): INamedIterableValues { + return this._paramValues.referencedObject; + } + + // interface INamedIterableValues + + public get valuesIterator(): INumberIterator { + if (this.isReferenceDefined) { + return this.referencedValuesIterator; + } + return this._paramValues.valuesIterator; + } + + public get hasMultipleValues(): boolean { + return this._paramValues.hasMultipleValues; + } + + public get name(): string { + return this._symbol; + } + + public initValuesIterator(reverse: boolean = false): INumberIterator { + return this._paramValues.initValuesIterator(reverse); + } + + public get hasNext(): boolean { + return this._paramValues.hasNext; + } + + public next(): IteratorResult<number> { + return this._paramValues.next(); + } + + public [Symbol.iterator](): IterableIterator<number> { + return this._paramValues; + } + + // interface IObservable + + /** + * ajoute un observateur à la liste + */ + public addObserver(o: Observer) { + this._observable.addObserver(o); + } + + /** + * supprime un observateur de la liste + */ + public removeObserver(o: Observer) { + this._observable.removeObserver(o); + } + + /** + * notifie un événement aux observateurs + */ + public notifyObservers(data: any, sender?: any) { + this._observable.notifyObservers(data, sender); + } + + /** + * variable calculable par l'équation ? + */ + public isAnalytical(): boolean { + return this.calculability === ParamCalculability.EQUATION; } public clone(): ParamDefinition { @@ -110,4 +416,76 @@ export class ParamDefinition extends BaseParam { res._calc = this._calc; return res; } + + /** + * notification envoyée après la modification de la valeur du paramètre + */ + private notifyValueModified(sender: any) { + this.notifyObservers( + { action: "paramdefinitionAfterValue" }, sender + ); + } + + /** + * vérifie si un min/max est valide par rapport au domaine de définition + */ + private isMinMaxDomainValid(v: number): boolean { + if (v === undefined) { + return false; + } + + if (this._paramValues.valueMode === ParamValueMode.MINMAX) { + try { + this.checkValue(v); + } catch (e) { + return false; + } + } + + return true; + } + + private checkMinMax(min: number, max: number): boolean { + return this.isMinMaxDomainValid(min) && this.isMinMaxDomainValid(max) && (min < max); + } + + private get isListValid(): boolean { + if (this._paramValues.valueList === undefined) { + return false; + } + + for (const v of this._paramValues.valueList) { + try { + this.checkValue(v); + } catch (e) { + return false; + } + } + return true; + } + + /** + * vérifie l'absence de référence circulaire + * @param seenUids liste des uids déjà vérifiés + * @param o objet à tester (son uid est il déjà dans la liste ?) + */ + private checkReferenceCircularity(o: any, seenUids: number[]) { + if ("uid" in o) { + // if (o.uid in seenUids) + if (seenUids.indexOf(o.uid) !== -1) { + throw new Error(`références circulaires détectées (uids : ${seenUids})`); + } + + seenUids.push(o.uid); + + if ("referencedObject" in o) { + const curr = o as INubReference; + const next = curr.referencedObject; + if (next !== undefined) { + this.checkReferenceCircularity(next as IJalhydObject, seenUids); + } + } + } + } + } diff --git a/src/param/param-values.ts b/src/param/param-values.ts index 7fe5049d89ef9d750eddc9c6f9ea186837b16f47..0b285d74a16a9cedccd1c5c424060b5d059435ff 100644 --- a/src/param/param-values.ts +++ b/src/param/param-values.ts @@ -1,6 +1,5 @@ -import { Result } from ".."; +import { Interval, Result } from ".."; import { DefinedNumber } from "../util/definedvalue"; -import { Pair } from "../util/pair"; import { INubReference, IReferencedNub, NubReference } from "../value_ref/object_ref"; import { INamedIterableValues, INumberIterator, IterableValues, ParamValueIterator } from "./param-value-iterator"; import { ParamValueMode } from "./param-value-mode"; @@ -202,9 +201,9 @@ export class ParamValues implements INubReference, IterableValues { this._valueMode = ParamValueMode.MINMAX; } - public get stepRefValue(): Pair { + public get stepRefValue(): Interval { this.checkValueMode(ParamValueMode.MINMAX); - return new Pair(1e-9, this._maxValue - this._minValue); + return new Interval(1e-9, this._maxValue - this._minValue); } public get step() { diff --git a/src/structure/parallel_structure.ts b/src/structure/parallel_structure.ts index ce52378ad39882307008205a533d666d82e0c8e2..697c782fe37e54373872eed9f6e1d3e0c205ec2d 100644 --- a/src/structure/parallel_structure.ts +++ b/src/structure/parallel_structure.ts @@ -4,7 +4,7 @@ import { Result } from "../util/result"; import { ParallelStructureParams } from "./parallel_structure_params"; import { Structure } from "./structure"; -import { BaseParam } from "../param/param-base"; +import { ParamDefinition } from "../param/param-definition"; import { INamedIterableValues } from "../param/param-value-iterator"; import { ParamValues } from "../param/param-values"; import { IParamDefinitionIterator, ParamsEquation, ParamsEquationArrayIterator } from "../param/params-equation"; @@ -247,7 +247,7 @@ export class ParallelStructure extends Nub { public getReferencedParamValues(desc: string): ParamValues { try { const ro = this.getReferencedObject(desc); - if (ro instanceof BaseParam) { + if (ro instanceof ParamDefinition) { return ro.paramValues; } return undefined; diff --git a/src/util/interval.ts b/src/util/interval.ts index 8b2a78a1622f4d4dbe0a2626bcfc7940bf69aa2a..8e83f33aac0c33676932de80b474e7fe183258cf 100644 --- a/src/util/interval.ts +++ b/src/util/interval.ts @@ -1,12 +1,49 @@ import { Message, MessageCode } from "./message"; -import { Pair } from "./pair"; /** - * couple de valeurs ordonnées + * Couple de valeurs ordonnées */ -export class Interval extends Pair { - constructor(bound1: number, bound2: number) { - super(bound1, bound2); +export class Interval { + + constructor(public val1: number, public val2: number) { } + + /** @TODO utile ? */ + public setValues(v1: number, v2: number) { + this.val1 = v1; + this.val2 = v2; + } + + /** "constructeur" par copie */ + public setInterval(i: Interval) { + this.setValues(i.val1, i.val2); + } + + get min() { + return Math.min(this.val1, this.val2); + } + + get max() { + return Math.max(this.val1, this.val2); + } + + get length(): number { + return this.max - this.min; + } + + public intervalHasValue(v: number) { + return this.min <= v && v <= this.max; + } + + public intersect(i: Interval): Interval { + const min = Math.max(this.min, i.min); + const max = Math.min(this.max, i.max); + + let intersection = null; + if (min <= max) { + intersection = new Interval(min, max); + } // else no intersection + + return intersection; } public checkValue(v: number) { @@ -14,7 +51,6 @@ export class Interval extends Pair { const e = new Message(MessageCode.ERROR_INTERVAL_UNDEF); throw e; } - if (!this.intervalHasValue(v)) { const e = new Message(MessageCode.ERROR_INTERVAL_OUTSIDE); e.extraVar.value = v; @@ -23,17 +59,7 @@ export class Interval extends Pair { } } - get length(): number { - return this.max - this.min; - } - - public intersect(i: Interval): Interval { - const min = Math.max(this.min, i.min); - const max = Math.min(this.max, i.max); - return new Interval(min, max); - } - - public setBounds(i: Pair) { - this.setPair(i); + public toString(): string { + return "[" + this.min + "," + this.max + "]"; } } diff --git a/src/util/pair.ts b/src/util/pair.ts deleted file mode 100644 index 1ea1b6b35ab3affcfe0987a2db1825d5e9cb76ea..0000000000000000000000000000000000000000 --- a/src/util/pair.ts +++ /dev/null @@ -1,49 +0,0 @@ -/** - * couple de valeurs non ordonnées - */ -export class Pair { - protected _val1: number; - protected _val2: number; - - constructor(v1: number, v2: number) { - this.setValues(v1, v2); - } - - get val1() { - return this._val1; - } - - get val2() { - return this._val2; - } - - public setValues(v1: number, v2: number) { - this._val1 = v1; - this._val2 = v2; - } - - public setPair(p: Pair) { - this.setValues(p._val1, p._val2); - } - - public undefine() { - this._val1 = undefined; - this._val2 = undefined; - } - - get min() { - return Math.min(this._val1, this._val2); - } - - get max() { - return Math.max(this._val1, this._val2); - } - - public intervalHasValue(v: number) { - return this.min <= v && v <= this.max; - } - - public toString(): string { - return "[" + this.min + "," + this.max + "]"; - } -}