From 939b365c708218f620814f316a696544da447c7e Mon Sep 17 00:00:00 2001
From: "mathias.chouet" <mathias.chouet@irstea.fr>
Date: Thu, 16 May 2019 11:48:53 +0200
Subject: [PATCH] Fix #90 - PAB : nombre

add function isEqual to compare real numbers
fix integer division / modulo
update jasmine test
---
 spec/pab/pab_nombre.spec.ts | 29 ++++++++++++++++++++++++++++-
 src/base.ts                 | 35 +++++++++++++++++++++++++++++++++++
 src/pab/pab_nombre.ts       | 14 +++++++++++---
 src/util/message.ts         |  5 +++++
 4 files changed, 79 insertions(+), 4 deletions(-)

diff --git a/spec/pab/pab_nombre.spec.ts b/spec/pab/pab_nombre.spec.ts
index fb5d9619..322fb2f7 100644
--- a/spec/pab/pab_nombre.spec.ts
+++ b/spec/pab/pab_nombre.spec.ts
@@ -28,13 +28,40 @@ describe("Class PabNombre: ", () => {
     it ("DHR should be 0.3", () => {
         const prms = new PabNombreParams(
             7.5,      // Chute totale DHT
-            9,    // Nombre de bassins N
+            666,    // Nombre de bassins N
             0.8,    // Chute entre bassins DH
         );
 
         const nub = new PabNombre(prms);
 
         nub.Calc("N", 0);
+        expect(nub.result.vCalc).toBe(9);
         expect(nub.result.getExtraResult("DHR")).toBeCloseTo(0.3);
     });
+
+    it ("DHR should be 0", () => {
+        const prms = new PabNombreParams(
+            3,      // Chute totale DHT
+            666,    // Nombre de bassins N
+            0.2,    // Chute entre bassins DH
+        );
+
+        const nub = new PabNombre(prms);
+
+        nub.Calc("N", 0);
+        expect(nub.result.vCalc).toBe(15);
+        expect(nub.result.getExtraResult("DHR")).toBe(0);
+    });
+
+    it ("non-integer number of basins should throw an error", () => {
+        const prms = new PabNombreParams(
+            3,      // Chute totale DHT
+            4.5,    // Nombre de bassins N
+            0.2,    // Chute entre bassins DH
+        );
+
+        const nub = new PabNombre(prms);
+
+        expect(nub.Calc("DH", 0).log.messages.length).toBeGreaterThan(0);
+    });
 });
diff --git a/src/base.ts b/src/base.ts
index be98cf9a..40c54156 100644
--- a/src/base.ts
+++ b/src/base.ts
@@ -63,3 +63,38 @@ export function isNumeric(s: string): boolean {
     }
     return false;
 }
+
+/**
+ * Retourne true si a et b sont égaux à e près (en gros)
+ */
+export function isEqual(a: number, b: number, e: number = 1E-7): boolean {
+    if (a === 0 && b === 0) {
+        return true;
+    }
+    if (b === 0) { // ne pas diviser par 0
+        [ a, b ] = [ b, a ];
+    }
+    if (a === 0) { // éviter d'avoir un quotient toujours nul
+        return (Math.abs(b) < e);
+    }
+    // cas général
+    return (Math.abs((a / b) - 1) < e);
+}
+
+/**
+ * Division entière de a par b, plus modulo, avec une tolérance e
+ * pour gérer le cas des nombres réels
+ */
+export function floatDivAndMod(a: number, b: number, e: number = 1E-7): { q: number, r: number } {
+    let q = Math.floor(a / b);
+    let r = a % b;
+    // si le modulo est bon à un pouïème, pas de reste
+    if (isEqual(r, b)) {
+        r = 0;
+        // si la division entière n'est pas tombée juste, +1 au quotient
+        if (q !== (a / b)) {
+            q++;
+        }
+    }
+    return { q, r };
+}
diff --git a/src/pab/pab_nombre.ts b/src/pab/pab_nombre.ts
index a853df31..d0fa32b5 100644
--- a/src/pab/pab_nombre.ts
+++ b/src/pab/pab_nombre.ts
@@ -1,8 +1,10 @@
+import { floatDivAndMod } from "../base";
 import { CalculatorType } from "../compute-node";
 import { Nub } from "../nub";
 import { ParamCalculability, ParamDefinition, ParamFamily } from "../param/param-definition";
 import { ParamDomainValue } from "../param/param-domain";
 import { ParamsEquation } from "../param/params-equation";
+import { Message, MessageCode } from "../util/message";
 import { Result } from "../util/result";
 
 export class PabNombreParams extends ParamsEquation {
@@ -58,6 +60,12 @@ export class PabNombre extends Nub {
     }
 
     public Equation(sVarCalc: string): Result {
+        if (sVarCalc !== "N" && ! Number.isInteger(this.prms.N.singleValue)) {
+            const m = new Message(MessageCode.ERROR_PARAMDEF_VALUE_INTEGER);
+            m.extraVar.N = this.prms.N.singleValue;
+            return new Result(m);
+        }
+
         let v: number;
         let DHR = 0;
 
@@ -67,8 +75,9 @@ export class PabNombre extends Nub {
                 break;
 
             case "N":
-                v = Math.floor(this.prms.DHT.v / this.prms.DH.v);
-                DHR = this.prms.DHT.v % this.prms.DH.v;
+                const divAndMod = floatDivAndMod(this.prms.DHT.v, this.prms.DH.v);
+                v = divAndMod.q;
+                DHR = divAndMod.r;
                 break;
 
             case "DH":
@@ -99,5 +108,4 @@ export class PabNombre extends Nub {
             DHR: ParamFamily.HEIGHTS
         };
     }
-
 }
diff --git a/src/util/message.ts b/src/util/message.ts
index cd5b6e39..d5f4cacb 100644
--- a/src/util/message.ts
+++ b/src/util/message.ts
@@ -73,6 +73,11 @@ export enum MessageCode {
      */
     ERROR_PARAMDEF_VALUE_FIXED,
 
+    /**
+     * la valeur d'un ParamDefinition doit être entière
+     */
+    ERROR_PARAMDEF_VALUE_INTEGER,
+
     /**
      * la valeur d'un ParamDefinition doit être > 0
      */
-- 
GitLab