diff --git a/spec/param/param_modes.spec.ts b/spec/param/param_modes.spec.ts
index b01100c4958989eab2f3aa0e6c5e052771d29d89..bf3ecb6b04a4ce0570b2fd79ded09d3c7684147a 100644
--- a/spec/param/param_modes.spec.ts
+++ b/spec/param/param_modes.spec.ts
@@ -141,17 +141,12 @@ function createEnv() {
 function checkConsistency(nubToCheck: Nub) {
     expect(nubToCheck.calculatedParam).toBeDefined();
     let calcCount = 0;
-    let varCount = 0;
     for (const p of nubToCheck.parameterIterator) {
         if (p.valueMode === ParamValueMode.CALCUL) {
             calcCount++;
         }
-        if (p.valueMode === ParamValueMode.MINMAX || p.valueMode === ParamValueMode.LISTE) {
-            varCount++;
-        }
     }
     expect(calcCount).toBe(1);
-    expect(varCount).toBeLessThanOrEqual(1);
 
     nubToCheck.CalcSerie();
     expect(nubToCheck.result).toBeDefined();
@@ -232,8 +227,7 @@ describe("cohérence des modes de paramètres : ", () => {
         testModesPermutations(nub2);
     });
 
-    // @TODO reenable after fixing jalhyd#74
-    xit("ouvrages en parallèle : Cloisons", () => {
+    it("ouvrages en parallèle : Cloisons", () => {
         createEnv();
         testModesPermutations(nub3);
     });
diff --git a/spec/param/param_multivar.spec.ts b/spec/param/param_multivar.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..dea5b5bb4695c30cba15526aa5defbf588d092f9
--- /dev/null
+++ b/spec/param/param_multivar.spec.ts
@@ -0,0 +1,441 @@
+import { CreateStructure, ExtensionStrategy, LoiDebit, ParallelStructure, ParallelStructureParams,
+         ParamValueMode, Structure, Session} from "../../src/index";
+import { RectangularStructureParams } from "../../src/structure/structure_cem88d";
+
+let prms1: ParallelStructureParams;
+let nub1: ParallelStructure;
+let struct1: Structure;
+let prmsStruct1: RectangularStructureParams;
+
+let prms2: ParallelStructureParams;
+let nub2: ParallelStructure;
+let struct2: Structure;
+let prmsStruct2: RectangularStructureParams;
+
+function createSingleNubEnv() {
+    prms1 = new ParallelStructureParams(0.5, 102, 101.5);
+    nub1 = new ParallelStructure(prms1);
+    struct1 = CreateStructure(LoiDebit.Cunge80, nub1);
+    prmsStruct1 = struct1.prms as RectangularStructureParams;
+    nub1.addChild(struct1);
+}
+
+function createLinkedNubEnv() {
+    prms1 = new ParallelStructureParams(0.5, 102, 101.5);
+    nub1 = new ParallelStructure(prms1);
+    struct1 = CreateStructure(LoiDebit.Cunge80, nub1);
+    prmsStruct1 = struct1.prms as RectangularStructureParams;
+    nub1.addChild(struct1);
+
+    prms2 = new ParallelStructureParams(0.5, 102, 101.5);
+    nub2 = new ParallelStructure(prms2);
+    struct2 = CreateStructure(LoiDebit.Cunge80, nub2);
+    prmsStruct2 = struct2.prms as RectangularStructureParams;
+    nub2.addChild(struct2);
+
+    Session.getInstance().clear();
+    Session.getInstance().registerNub(nub1);
+    Session.getInstance().registerNub(nub2);
+
+    prms2.Z1.defineReference(nub1, "Z1");
+    prmsStruct2.L.defineReference(struct1, "L");
+}
+
+/**
+ * Generates a random-length list of numbers close
+ * @param refVal reference value
+ * @param minSize minimum size of the list to be returned
+ * @param maxSize maximum size of the list to be returned
+ * @param maxDev maximum deviation, so that generated numbers
+ *               value v verifies (refVal - maxDev) < v < (refVal + maxDev)
+ * @param sort if true, list will be ordered
+ */
+function randomList(
+    refVal: number, minSize: number = 2, maxSize: number = 20, maxDev: number = 3, sort: boolean = true
+): number[] {
+    let list: number[] = [];
+    const size = minSize + Math.floor(Math.random() * ((maxSize - minSize) + 1)); // between minSize and maxSize
+    for (let i = 0; i < size; i++) {
+        let dev = (Math.random() * (2 * maxDev)) - (maxDev + 1); // between -maxDev and maxDev
+        dev = Number(Math.round(Number(dev + "e3")) + "e-3"); // trick to properly round to 3 decimals
+        list.push(refVal + dev);
+    }
+    if (sort) {
+        list = list.sort((a, b) => a - b);
+    }
+    return list;
+}
+
+describe("multiple variated parameters - ", () => {
+
+    describe("on the same Nub, with REPEAT_LAST strategy - ", () => {
+
+        it("test 1 : minmax < list", () => {
+            createSingleNubEnv();
+            prms1.Z1.setValues(100, 102, 0.5); // 5 values
+            prms1.Z1.extensionStrategy = ExtensionStrategy.REPEAT_LAST;
+            prmsStruct1.L.setValues([ 1.89, 1.91, 1.99, 2, 1.7, 2.1, 2.18, 2.23, 2.6 ]); // 9 values
+            prmsStruct1.L.extensionStrategy = ExtensionStrategy.REPEAT_LAST;
+            // check that both parameters are varying
+            expect(prms1.Z1.valueMode).toBe(ParamValueMode.MINMAX);
+            expect(prmsStruct1.L.valueMode).toBe(ParamValueMode.LISTE);
+            // check that calculation works
+            const res = nub1.CalcSerie();
+            expect(res).toBeDefined();
+            expect(res.resultElements.length).toBe(9);
+        });
+
+        it("test 2 : minmax < minmax", () => {
+            createSingleNubEnv();
+            prms1.Z1.setValues(100, 102, 0.5); // 5 values
+            prms1.Z1.extensionStrategy = ExtensionStrategy.REPEAT_LAST;
+            prmsStruct1.L.setValues(1.89, 2.1, 0.03); // 8 values
+            prmsStruct1.L.extensionStrategy = ExtensionStrategy.REPEAT_LAST;
+            // check that both parameters are varying
+            expect(prms1.Z1.valueMode).toBe(ParamValueMode.MINMAX);
+            expect(prmsStruct1.L.valueMode).toBe(ParamValueMode.MINMAX);
+            // check that calculation works
+            const res = nub1.CalcSerie();
+            expect(res).toBeDefined();
+            expect(res.resultElements.length).toBe(8);
+        });
+
+        it("test 3 : list < minmax", () => {
+            createSingleNubEnv();
+            prms1.Z1.setValues(100, 102, 0.1); // 21 values
+            prms1.Z1.extensionStrategy = ExtensionStrategy.REPEAT_LAST;
+            prmsStruct1.L.setValues([ 1.89, 1.91, 1.99, 2, 1.7, 2.1, 2.18, 2.23, 2.6 ]); // 9 values
+            prmsStruct1.L.extensionStrategy = ExtensionStrategy.REPEAT_LAST;
+            // check that both parameters are varying
+            expect(prms1.Z1.valueMode).toBe(ParamValueMode.MINMAX);
+            expect(prmsStruct1.L.valueMode).toBe(ParamValueMode.LISTE);
+            // check that calculation works
+            const res = nub1.CalcSerie();
+            expect(res).toBeDefined();
+            expect(res.resultElements.length).toBe(21);
+        });
+
+        it("test 4 : list < list", () => {
+            createSingleNubEnv();
+            prms1.Z1.setValues([ 100, 101, 102 ]); // 3 values
+            prms1.Z1.extensionStrategy = ExtensionStrategy.REPEAT_LAST;
+            prmsStruct1.L.setValues([ 1.89, 1.91, 1.99, 2, 1.7, 2.1, 2.18, 2.23, 2.6 ]); // 9 values
+            prmsStruct1.L.extensionStrategy = ExtensionStrategy.REPEAT_LAST;
+            // check that both parameters are varying
+            expect(prms1.Z1.valueMode).toBe(ParamValueMode.LISTE);
+            expect(prmsStruct1.L.valueMode).toBe(ParamValueMode.LISTE);
+            // check that calculation works
+            const res = nub1.CalcSerie();
+            expect(res).toBeDefined();
+            expect(res.resultElements.length).toBe(9);
+        });
+
+        it("test 5 : list = list", () => {
+            createSingleNubEnv();
+            prms1.Z1.setValues([ 100, 100.3, 100.7, 101, 101.3, 101.7, 102, 102.3, 115 ]); // 9 values
+            prms1.Z1.extensionStrategy = ExtensionStrategy.REPEAT_LAST;
+            prmsStruct1.L.setValues([ 1.89, 1.91, 1.99, 2, 1.7, 2.1, 2.18, 2.23, 2.6 ]); // 9 values
+            prmsStruct1.L.extensionStrategy = ExtensionStrategy.REPEAT_LAST;
+            // check that both parameters are varying
+            expect(prms1.Z1.valueMode).toBe(ParamValueMode.LISTE);
+            expect(prmsStruct1.L.valueMode).toBe(ParamValueMode.LISTE);
+            // check that calculation works
+            const res = nub1.CalcSerie();
+            expect(res).toBeDefined();
+            expect(res.resultElements.length).toBe(9);
+        });
+
+        it("test 6 : list = minmax", () => {
+            createSingleNubEnv();
+            prms1.Z1.setValues(100, 102, 0.5); // 5 values
+            prms1.Z1.extensionStrategy = ExtensionStrategy.REPEAT_LAST;
+            prmsStruct1.L.setValues(1.89, 2.15, 0.06); // 5 values
+            prmsStruct1.L.extensionStrategy = ExtensionStrategy.REPEAT_LAST;
+            // check that both parameters are varying
+            expect(prms1.Z1.valueMode).toBe(ParamValueMode.MINMAX);
+            expect(prmsStruct1.L.valueMode).toBe(ParamValueMode.MINMAX);
+            // check that calculation works
+            const res = nub1.CalcSerie();
+            expect(res).toBeDefined();
+            expect(res.resultElements.length).toBe(5);
+        });
+
+        it("test 7 : all parameters varying with random lengths", () => {
+            createSingleNubEnv();
+            let longest = 0;
+            let sparedParam = null;
+            // set all parameters to LIST mode with random length (except the first that has to stay in CALC mode)
+            for (const p of nub1.parameterIterator) {
+                if (p.visible) {
+                    if (! sparedParam) {
+                        sparedParam = p;
+                        nub1.calculatedParam = p;
+                    } else {
+                        const rl = randomList(p.singleValue);
+                        longest = Math.max(longest, rl.length);
+                        p.setValues(rl);
+                        p.extensionStrategy = ExtensionStrategy.REPEAT_LAST;
+                    }
+                }
+            }
+            // check that all parameters are varying
+            for (const p of nub1.parameterIterator) {
+                if (p.visible && p !== sparedParam) {
+                    expect(p.hasMultipleValues).toBe(true);
+                }
+            }
+            // check that calculation works and length of result is length of the longest values list
+            const res = nub1.CalcSerie();
+            expect(res).toBeDefined();
+            expect(res.resultElements.length).toBe(longest);
+        });
+    });
+
+    describe("on the same Nub, with RECYCLE strategy - ", () => {
+
+        it("test 1 : minmax < list", () => {
+            createSingleNubEnv();
+            prms1.Z1.setValues(100, 102, 0.5); // 5 values
+            prms1.Z1.extensionStrategy = ExtensionStrategy.RECYCLE;
+            prmsStruct1.L.setValues([ 1.89, 1.91, 1.99, 2, 1.7, 2.1, 2.18, 2.23, 2.6]); // 9 values
+            prmsStruct1.L.extensionStrategy = ExtensionStrategy.RECYCLE;
+            // check that both parameters are varying
+            expect(prms1.Z1.valueMode).toBe(ParamValueMode.MINMAX);
+            expect(prmsStruct1.L.valueMode).toBe(ParamValueMode.LISTE);
+            // check that calculation works
+            const res = nub1.CalcSerie();
+            expect(res).toBeDefined();
+            expect(res.resultElements.length).toBe(9);
+        });
+
+        it("test 2 : minmax < minmax", () => {
+            createSingleNubEnv();
+            prms1.Z1.setValues(100, 102, 0.5); // 5 values
+            prms1.Z1.extensionStrategy = ExtensionStrategy.RECYCLE;
+            prmsStruct1.L.setValues(1.89, 2.1, 0.03); // 8 values
+            prmsStruct1.L.extensionStrategy = ExtensionStrategy.RECYCLE;
+            // check that both parameters are varying
+            expect(prms1.Z1.valueMode).toBe(ParamValueMode.MINMAX);
+            expect(prmsStruct1.L.valueMode).toBe(ParamValueMode.MINMAX);
+            // check that calculation works
+            const res = nub1.CalcSerie();
+            expect(res).toBeDefined();
+            expect(res.resultElements.length).toBe(8);
+        });
+
+        it("test 3 : list < minmax", () => {
+            createSingleNubEnv();
+            prms1.Z1.setValues(100, 102, 0.1); // 21 values
+            prms1.Z1.extensionStrategy = ExtensionStrategy.RECYCLE;
+            prmsStruct1.L.setValues([ 1.89, 1.91, 1.99, 2, 1.7, 2.1, 2.18, 2.23, 2.6 ]); // 9 values
+            prmsStruct1.L.extensionStrategy = ExtensionStrategy.RECYCLE;
+            // check that both parameters are varying
+            expect(prms1.Z1.valueMode).toBe(ParamValueMode.MINMAX);
+            expect(prmsStruct1.L.valueMode).toBe(ParamValueMode.LISTE);
+            // check that calculation works
+            const res = nub1.CalcSerie();
+            expect(res).toBeDefined();
+            expect(res.resultElements.length).toBe(21);
+        });
+
+        it("test 4 : list < list", () => {
+            createSingleNubEnv();
+            prms1.Z1.setValues([ 100, 101, 102 ]); // 3 values
+            prms1.Z1.extensionStrategy = ExtensionStrategy.RECYCLE;
+            prmsStruct1.L.setValues([ 1.89, 1.91, 1.99, 2, 1.7, 2.1, 2.18, 2.23, 2.6 ]); // 9 values
+            prmsStruct1.L.extensionStrategy = ExtensionStrategy.RECYCLE;
+            // check that both parameters are varying
+            expect(prms1.Z1.valueMode).toBe(ParamValueMode.LISTE);
+            expect(prmsStruct1.L.valueMode).toBe(ParamValueMode.LISTE);
+            // check that calculation works
+            const res = nub1.CalcSerie();
+            expect(res).toBeDefined();
+            expect(res.resultElements.length).toBe(9);
+        });
+
+        it("test 5 : list = list", () => {
+            createSingleNubEnv();
+            prms1.Z1.setValues([ 100, 100.3, 100.7, 101, 101.3, 101.7, 102, 102.3, 115 ]); // 9 values
+            prms1.Z1.extensionStrategy = ExtensionStrategy.RECYCLE;
+            prmsStruct1.L.setValues([ 1.89, 1.91, 1.99, 2, 1.7, 2.1, 2.18, 2.23, 2.6 ]); // 9 values
+            prmsStruct1.L.extensionStrategy = ExtensionStrategy.RECYCLE;
+            // check that both parameters are varying
+            expect(prms1.Z1.valueMode).toBe(ParamValueMode.LISTE);
+            expect(prmsStruct1.L.valueMode).toBe(ParamValueMode.LISTE);
+            // check that calculation works
+            const res = nub1.CalcSerie();
+            expect(res).toBeDefined();
+            expect(res.resultElements.length).toBe(9);
+        });
+
+        it("test 6 : list = minmax", () => {
+            createSingleNubEnv();
+            prms1.Z1.setValues(100, 102, 0.5); // 5 values
+            prms1.Z1.extensionStrategy = ExtensionStrategy.RECYCLE;
+            prmsStruct1.L.setValues(1.89, 2.15, 0.06); // 5 values
+            prmsStruct1.L.extensionStrategy = ExtensionStrategy.RECYCLE;
+            // check that both parameters are varying
+            expect(prms1.Z1.valueMode).toBe(ParamValueMode.MINMAX);
+            expect(prmsStruct1.L.valueMode).toBe(ParamValueMode.MINMAX);
+            // check that calculation works
+            const res = nub1.CalcSerie();
+            expect(res).toBeDefined();
+            expect(res.resultElements.length).toBe(5);
+        });
+
+        it("test 7 : all parameters varying with random lengths", () => {
+            createSingleNubEnv();
+            let longest = 0;
+            let sparedParam = null;
+            // set all parameters to LIST mode with random length (except the first that has to stay in CALC mode)
+            for (const p of nub1.parameterIterator) {
+                if (p.visible) {
+                    if (! sparedParam) {
+                        sparedParam = p;
+                        nub1.calculatedParam = p;
+                    } else {
+                        const rl = randomList(p.singleValue);
+                        longest = Math.max(longest, rl.length);
+                        p.setValues(rl);
+                        p.extensionStrategy = ExtensionStrategy.RECYCLE;
+                    }
+                }
+            }
+            // check that all parameters are varying
+            for (const p of nub1.parameterIterator) {
+                if (p.visible && p !== sparedParam) {
+                    expect(p.hasMultipleValues).toBe(true);
+                }
+            }
+            // check that calculation works and length of result is length of the longest values list
+            const res = nub1.CalcSerie();
+            expect(res).toBeDefined();
+            expect(res.resultElements.length).toBe(longest);
+        });
+    });
+
+    describe("on different linked Nubs, with REPEAT_LAST strategy - ", () => {
+
+        it("test 1 : minmax < list", () => {
+            createLinkedNubEnv();
+            prms1.Z1.setValues(100, 102, 0.5); // 5 values
+            prms1.Z1.extensionStrategy = ExtensionStrategy.REPEAT_LAST;
+            prmsStruct1.L.setValues([ 1.89, 1.91, 1.99, 2, 1.7, 2.1, 2.18, 2.23, 2.6 ]); // 9 values
+            prmsStruct1.L.extensionStrategy = ExtensionStrategy.REPEAT_LAST;
+            // check that both parameters are varying
+            expect(prms1.Z1.valueMode).toBe(ParamValueMode.MINMAX);
+            expect(prmsStruct1.L.valueMode).toBe(ParamValueMode.LISTE);
+            // check that calculation works
+            const res = nub2.CalcSerie();
+            expect(res).toBeDefined();
+            expect(res.resultElements.length).toBe(9);
+        });
+
+        it("test 2 : minmax < minmax", () => {
+            createLinkedNubEnv();
+            prms1.Z1.setValues(100, 102, 0.5); // 5 values
+            prms1.Z1.extensionStrategy = ExtensionStrategy.REPEAT_LAST;
+            prmsStruct1.L.setValues(1.89, 2.1, 0.03); // 8 values
+            prmsStruct1.L.extensionStrategy = ExtensionStrategy.REPEAT_LAST;
+            // check that both parameters are varying
+            expect(prms1.Z1.valueMode).toBe(ParamValueMode.MINMAX);
+            expect(prmsStruct1.L.valueMode).toBe(ParamValueMode.MINMAX);
+            // check that calculation works
+            const res = nub2.CalcSerie();
+            expect(res).toBeDefined();
+            expect(res.resultElements.length).toBe(8);
+        });
+
+        it("test 3 : list < minmax", () => {
+            createLinkedNubEnv();
+            prms1.Z1.setValues(100, 102, 0.1); // 21 values
+            prms1.Z1.extensionStrategy = ExtensionStrategy.REPEAT_LAST;
+            prmsStruct1.L.setValues([ 1.89, 1.91, 1.99, 2, 1.7, 2.1, 2.18, 2.23, 2.6 ]); // 9 values
+            prmsStruct1.L.extensionStrategy = ExtensionStrategy.REPEAT_LAST;
+            // check that both parameters are varying
+            expect(prms1.Z1.valueMode).toBe(ParamValueMode.MINMAX);
+            expect(prmsStruct1.L.valueMode).toBe(ParamValueMode.LISTE);
+            // check that calculation works
+            const res = nub2.CalcSerie();
+            expect(res).toBeDefined();
+            expect(res.resultElements.length).toBe(21);
+        });
+
+        it("test 4 : list < list", () => {
+            createLinkedNubEnv();
+            prms1.Z1.setValues([ 100, 101, 102 ]); // 3 values
+            prms1.Z1.extensionStrategy = ExtensionStrategy.REPEAT_LAST;
+            prmsStruct1.L.setValues([ 1.89, 1.91, 1.99, 2, 1.7, 2.1, 2.18, 2.23, 2.6 ]); // 9 values
+            prmsStruct1.L.extensionStrategy = ExtensionStrategy.REPEAT_LAST;
+            // check that both parameters are varying
+            expect(prms1.Z1.valueMode).toBe(ParamValueMode.LISTE);
+            expect(prmsStruct1.L.valueMode).toBe(ParamValueMode.LISTE);
+            // check that calculation works
+            const res = nub2.CalcSerie();
+            expect(res).toBeDefined();
+            expect(res.resultElements.length).toBe(9);
+        });
+
+        it("test 5 : list = list", () => {
+            createLinkedNubEnv();
+            prms1.Z1.setValues([ 100, 100.3, 100.7, 101, 101.3, 101.7, 102, 102.3, 115 ]); // 9 values
+            prms1.Z1.extensionStrategy = ExtensionStrategy.REPEAT_LAST;
+            prmsStruct1.L.setValues([ 1.89, 1.91, 1.99, 2, 1.7, 2.1, 2.18, 2.23, 2.6 ]); // 9 values
+            prmsStruct1.L.extensionStrategy = ExtensionStrategy.REPEAT_LAST;
+            // check that both parameters are varying
+            expect(prms1.Z1.valueMode).toBe(ParamValueMode.LISTE);
+            expect(prmsStruct1.L.valueMode).toBe(ParamValueMode.LISTE);
+            // check that calculation works
+            const res = nub2.CalcSerie();
+            expect(res).toBeDefined();
+            expect(res.resultElements.length).toBe(9);
+        });
+
+        it("test 6 : list = minmax", () => {
+            createLinkedNubEnv();
+            prms1.Z1.setValues(100, 102, 0.5); // 5 values
+            prms1.Z1.extensionStrategy = ExtensionStrategy.REPEAT_LAST;
+            prmsStruct1.L.setValues(1.89, 2.15, 0.06); // 5 values
+            prmsStruct1.L.extensionStrategy = ExtensionStrategy.REPEAT_LAST;
+            // check that both parameters are varying
+            expect(prms1.Z1.valueMode).toBe(ParamValueMode.MINMAX);
+            expect(prmsStruct1.L.valueMode).toBe(ParamValueMode.MINMAX);
+            // check that calculation works
+            const res = nub2.CalcSerie();
+            expect(res).toBeDefined();
+            expect(res.resultElements.length).toBe(5);
+        });
+
+        it("test 7 : all parameters varying with random lengths", () => {
+            createLinkedNubEnv();
+            let longest = 0;
+            let sparedParam = null;
+            // set all parameters to LIST mode with random length (except the first that has to stay in CALC mode)
+            for (const p of nub2.parameterIterator) {
+                if (p.visible) {
+                    if (! sparedParam) {
+                        sparedParam = p;
+                        nub2.calculatedParam = p;
+                    } else {
+                        if (p.valueMode !== ParamValueMode.LINK) {
+                            const rl = randomList(p.singleValue);
+                            longest = Math.max(longest, rl.length);
+                            p.setValues(rl);
+                            p.extensionStrategy = ExtensionStrategy.REPEAT_LAST;
+                        }
+                    }
+                }
+            }
+            // check that all parameters are varying
+            for (const p of nub2.parameterIterator) {
+                if (p.visible && p !== sparedParam && p.valueMode !== ParamValueMode.LINK) {
+                    expect(p.hasMultipleValues).toBe(true);
+                }
+            }
+            // check that calculation works and length of result is length of the longest values list
+            const res = nub2.CalcSerie();
+            expect(res).toBeDefined();
+            expect(res.resultElements.length).toBe(longest);
+        });
+    });
+});
diff --git a/spec/value_ref/value_ref.spec.ts b/spec/value_ref/value_ref.spec.ts
index 937377c6cee52c54652dd4f673e7b7bbc92a5548..d6fbf2a8b0fba94df292cd1896aca35d41e3f200 100644
--- a/spec/value_ref/value_ref.spec.ts
+++ b/spec/value_ref/value_ref.spec.ts
@@ -245,5 +245,27 @@ describe("référence d'un paramètre à un autre : ", () => {
             }
             expect(n).toEqual(input.length);
         });
+
+        it("test 5", () => {
+            // cas de figure :
+            // nub2.A est lié à nub1.A (valeur variée)
+            // lecture de nub2.A
+
+            createEnv();
+
+            const input = [2, 3, 4, 5, 6];
+            prm1.A.setValues(input);
+
+            // valeur esclave bidon, doit être masquée par la valeur maître (cad prm1.A, normalement [2,3,4,5,6])
+            prm2.A.singleValue = 0;
+            prm2.A.defineReference(nub1, "A");
+
+            let ndx = 0;
+            for (const v of prm2.A.paramValues.getValuesIterator()) {
+                expect(v).toEqual(input[ndx]);
+                ndx++;
+            }
+            expect(ndx).toEqual(input.length);
+        });
     });
 });
diff --git a/src/nub.ts b/src/nub.ts
index b0877e6d0db88451380058c86861dcb55a78bf76..43970a54e102804ab30088d383b8e0af628c7806 100644
--- a/src/nub.ts
+++ b/src/nub.ts
@@ -146,8 +146,25 @@ export abstract class Nub extends ComputeNode implements IObservable {
     }
 
     /**
-     * Resets the calculated parameter to the default one if it is in SINGLE
-     * mode, or else to the first calculable parameter other than requirer.
+     * Finds the previous calculated parameter and sets its mode to SINGLE
+     */
+    public unsetCalculatedParam(except: ParamDefinition) {
+        for (const p of this.parameterIterator) {
+            if (
+                p.valueMode === ParamValueMode.CALCUL
+                && p !== except
+            ) {
+                p.setValueMode(ParamValueMode.SINGLE, false);
+            }
+        }
+    }
+
+    /**
+     * Tries to reset the calculated parameter, successively, to :
+     *  - the default one if it is in SINGLE mode
+     *  - the first SINGLE calculable parameter other than requirer
+     *  - the first MINMAX/LISTE calculable parameter other than requirer
+     * 
      * If no default calculated parameter is defined, does nothing.
      */
     public resetDefaultCalculatedParam(requirer?: ParamDefinition) {
@@ -155,15 +172,20 @@ export abstract class Nub extends ComputeNode implements IObservable {
             // if default calculated param is not eligible to CALC mode
             if (
                 requirer === this._defaultCalculatedParam
-                || this._defaultCalculatedParam.valueMode !== ParamValueMode.SINGLE
+                || ! [
+                        ParamValueMode.SINGLE,
+                        ParamValueMode.MINMAX,
+                        ParamValueMode.LISTE
+                    ].includes(this._defaultCalculatedParam.valueMode
+                )
             ) {
                 // first SINGLE calculable parameter if any
-                const newCalculatedParam = this.findFirstSingleParameter(requirer);
+                const newCalculatedParam = this.findFirstCalculableParameter(requirer);
                 if (newCalculatedParam) {
                     this.calculatedParam = newCalculatedParam;
                 } else {
                     // @TODO throws when a new linkable nub is added, and all parameters are already linked !
-                    throw Error("resetDefaultCalculatedParam : could not find any SINGLE parameter");
+                    throw Error("resetDefaultCalculatedParam : could not find any SINGLE/MINMAX/LISTE parameter");
                 }
             } else {
                 // default one
@@ -176,17 +198,17 @@ export abstract class Nub extends ComputeNode implements IObservable {
 
     /**
      * Returns the first visible calculable parameter other than "Pr",
-     * and other than otherThan, that is set to SINGLE mode
-     * (there might be none)
+     * and other than otherThan, that is set to SINGLE, MINMAX or LISTE
+     * mode (there might be none)
      */
-    public findFirstSingleParameter(otherThan?: ParamDefinition) {
+    public findFirstCalculableParameter(otherThan?: ParamDefinition) {
         for (const p of this.parameterIterator) {
             if (
                 [ ParamCalculability.EQUATION, ParamCalculability.DICHO ].includes(p.calculability)
                 && p.symbol !== "Pr"
                 && p.visible
                 && p !== otherThan
-                && p.valueMode === ParamValueMode.SINGLE
+                && [ ParamValueMode.SINGLE, ParamValueMode.MINMAX, ParamValueMode.LISTE ].includes(p.valueMode)
             ) {
                 return p;
             }
@@ -194,45 +216,6 @@ export abstract class Nub extends ComputeNode implements IObservable {
         return undefined;
     }
 
-    /**
-     * Resets all parameters to SINGLE mode, except sourceParam (the one that asked
-     * for the change) and parameters in LINK mode.
-     * If exceptMode is defined, parameters in this mode won't be affected either.
-     *
-     * Used to ensure that
-     *  - exactly one parameter is in CALCUL mode
-     *  - at most one parameter variates (MIMAX / LISTE mode)
-     */
-    public ensureParametersConsistency(sourceParam: ParamDefinition, exceptMode: ParamValueMode|ParamValueMode[] = []) {
-        if (! Array.isArray(exceptMode)) {
-            exceptMode = [ exceptMode ];
-        }
-        // console.log(`nub ${this.uid} (${this.constructor.name})`);
-        // console.log(`(calculated parameter: ${this.calculatedParam.symbol})`);
-        for (const p of this.parameterIterator) {
-            if (
-                p !== sourceParam
-                && ! exceptMode.includes(p.valueMode)
-                && (
-                    // don't touch linked parameters
-                    p.valueMode !== ParamValueMode.LINK
-                    || ( // unless they are linked to multiple values…
-                        p.isReferenceDefined()
-                        && p.hasMultipleValues
-                        // …and we're not skipping multiple values
-                        && ! exceptMode.includes(ParamValueMode.MINMAX)
-                        && ! exceptMode.includes(ParamValueMode.LISTE)
-                    )
-                )
-                && p.valueMode !== ParamValueMode.SINGLE
-            ) {
-                // console.log(">> resetting", p.symbol);
-                // "false" prevents infinite loop when managing CALC params
-                p.setValueMode(ParamValueMode.SINGLE, false);
-            }
-        }
-    }
-
     /**
      * Formule utilisée pour le calcul analytique (solution directe ou méthode de résolution spécifique)
      */
@@ -304,81 +287,81 @@ export abstract class Nub extends ComputeNode implements IObservable {
      * @param sDonnee éventuel symbole / paire symbole-uid du paramètre à calculer
      */
     public CalcSerie(rInit?: number, sDonnee?: any): Result {
-        // instance de ParamValues utilisée pour le paramètre varié (qui peut être un paramètre référencé (importé))
-        let variatedParam: ParamDefinition;
-        let variatedValues: ParamValues;
+        // variated parameters caracteristics
+        const variated: Array<{ param: ParamDefinition, values: ParamValues }> = [];
 
+        // prepare calculation
         this.progress = 0;
         this.triggerChainCalculation();
-
         this.copySingleValuesToSandboxValues();
 
         // check which values are variating, if any
         for (const p of this.parameterIterator) {
-            switch (p.valueMode) {
-                case ParamValueMode.SINGLE:
-                case ParamValueMode.CALCUL:
-                    break;
-
-                case ParamValueMode.LISTE:
-                case ParamValueMode.MINMAX:
-                    variatedParam = this.setVariatedValues(p, variatedParam);
-                    break;
-
-                case ParamValueMode.LINK:
-                    if (
-                        p.isReferenceDefined()
-                        && p.referencedValue.hasMultipleValues()
-                    ) {
-                        variatedParam = this.setVariatedValues(p, variatedParam);
-                    }
-                    break;
-
-                default:
-                    // tslint:disable-next-line:max-line-length
-                    throw new Error(`CalcSerie() : valeur de ParamValueMode ${ParamValueMode[p.valueMode]} non prise en charge`);
+            if (
+                p.valueMode === ParamValueMode.LISTE
+                || p.valueMode === ParamValueMode.MINMAX
+                || (
+                    p.valueMode === ParamValueMode.LINK
+                    && p.isReferenceDefined()
+                    && p.referencedValue.hasMultipleValues()
+                )
+            ) {
+                variated.push({
+                    param: p,
+                    // extract variated values from variated Parameters
+                    // (in LINK mode, proxies to target data)
+                    values: p.paramValues
+                });
             }
         }
 
-        let computedSymbol: any;
-        if (sDonnee) {
-            computedSymbol = sDonnee;
-        } else {
-            if (this.calculatedParam === undefined) {
-                throw new Error(`CalcSerie() : aucun paramètre à calculer`);
-            }
-            computedSymbol = this.calculatedParamDescriptor;
-        }
+        // find calculated parameter
+        const computedSymbol = this.findCalculatedParameter(sDonnee);
 
-        if (rInit === undefined) {
+        if (rInit === undefined && this.calculatedParam) {
             rInit = this.calculatedParam.v;
         }
 
-        if (variatedParam === undefined) {
-            this._result = this.Calc(computedSymbol, rInit); // résultat dans this._result
-            // update progress to 100%
+        if (variated.length === 0) { // no parameter is varying
+            this._result = this.doCalc(computedSymbol, rInit);
             this.progress = 100;
 
-        } else {
-            // extract variated values from variated Parameter
-            // (in LINK mode, proxies to target data)
-            variatedValues = variatedParam.paramValues;
+        } else { // at least one parameter is varying
+            // find longest series
+            let longest = 0;
+            for (let i = 0; i < variated.length; i++) {
+                if (variated[i].values.valuesIterator.count() > variated[longest].values.valuesIterator.count()) {
+                    longest = i;
+                }
+            }
+            const size = variated[longest].values.valuesIterator.count();
 
             // grant the remaining percentage of the progressbar to local calculation
             // (should be 50% if any chain calculation occurred, 100% otherwise)
             let progressStep;
             const remainingProgress = 100 - this.progress;
-            const nbValues = variatedValues.valuesIterator.count();
-            progressStep = remainingProgress / nbValues;
+            progressStep = remainingProgress / size;
 
             const res = new Result(undefined, this);
 
-            variatedValues.initValuesIterator(false);
-            while (variatedValues.hasNext) {
-                const currentIteratorValue = variatedValues.next();
-                variatedParam.v = currentIteratorValue.value; // copy to local sandbox value
+            // (re)init all iterators
+            for (const v of variated) {
+                if (v === variated[longest]) {
+                    v.values.initValuesIterator(false);
+                } else {
+                    v.values.initValuesIterator(false, size);
+                }
+            }
 
-                this.Calc(computedSymbol, rInit);  // résultat dans this._result
+            // iterate over longest series (in fact any series would do)
+            while (variated[longest].values.hasNext) {
+                // get next value for all variating parameters
+                for (const v of variated) {
+                    const currentIteratorValue = v.values.next();
+                    v.param.v = currentIteratorValue.value;
+                }
+                // calculate
+                this.doCalc(computedSymbol, rInit); // résultat dans this._result
                 if (this._result.ok) {
                     res.addResultElement(this._result.resultElement);
                     res.addLog(this._result.log);
@@ -393,8 +376,10 @@ export abstract class Nub extends ComputeNode implements IObservable {
             this.progress = 100;
         }
 
-        const realSymbol = (typeof computedSymbol === "string") ? computedSymbol : computedSymbol.symbol;
-        this._result.name = realSymbol;
+        if (computedSymbol !== undefined) {
+            const realSymbol = (typeof computedSymbol === "string") ? computedSymbol : computedSymbol.symbol;
+            this._result.name = realSymbol;
+        }
 
         this.notifyResultUpdated();
 
@@ -1098,6 +1083,23 @@ export abstract class Nub extends ComputeNode implements IObservable {
         this._observable.notifyObservers(data, sender);
     }
 
+    protected findCalculatedParameter(sDonnee: any): any {
+        let computedSymbol: any;
+        if (sDonnee) {
+            computedSymbol = sDonnee;
+        } else {
+            if (this.calculatedParam === undefined) {
+                throw new Error(`CalcSerie() : aucun paramètre à calculer`);
+            }
+            computedSymbol = this.calculatedParamDescriptor;
+        }
+        return computedSymbol;
+    }
+
+    protected doCalc(computedSymbol?: any, rInit?: number) {
+        return this.Calc(computedSymbol, rInit);
+    }
+
     /**
      * Returns values of parameters and the result of the calculation for the calculated parameter
      * @param sVarCalc Symbol of the calculated param
@@ -1147,18 +1149,6 @@ export abstract class Nub extends ComputeNode implements IObservable {
         return Nub.prototype.Calc.call(this, sVarCalc, rInit);
     }
 
-    /**
-     * Sets the variated values and warns if there were already some
-     */
-    protected setVariatedValues(newValues: ParamDefinition, oldValues: ParamDefinition): ParamDefinition {
-        if (oldValues === undefined) {
-            return newValues;
-        } else {
-            // tslint:disable-next-line:max-line-length
-            throw new Error(`CalcSerie() : Paramètres à varier redondant : ${newValues.name}`);
-        }
-    }
-
     /**
      * Used by GUI to update results display
      */
diff --git a/src/param/param-definition.ts b/src/param/param-definition.ts
index 263440ea7055b9fdf6fa0b76684abee7279a3def..07d16ed6fd6c7cbd09b76acd734271d529fc94e7 100644
--- a/src/param/param-definition.ts
+++ b/src/param/param-definition.ts
@@ -43,6 +43,19 @@ export enum ParamFamily {
     // SPEEDS // vitesses, seulement des résultats
 }
 
+/**
+ * Strategy to apply when multiple parameters are variating, and
+ * values series have to be extended for sizes to match
+ */
+export enum ExtensionStrategy {
+    REPEAT_LAST,    // repeat last value as many times as needed
+    RECYCLE         // repeat the whole series from the beginning
+    // autres propositions :
+    // PAD_LEFT     // pad with zeroes at the beginning ?
+    // PAD_RIGHT    // pad with zeroes at the end ?
+    // INTERPOLATE  // insert regular steps between first and last value
+}
+
 /**
  * Paramètre avec symbole, famille, domaine de définition, calculabilité,
  * pointant éventuellement vers un autre paramètre / résultat
@@ -55,6 +68,9 @@ export class ParamDefinition implements INamedIterableValues, IObservable {
     /** sandbox value used during calculation */
     public v: number;
 
+    /** extension strategy, when multiple parameters vary */
+    private _extensionStrategy: ExtensionStrategy;
+
     /** mode de génération des valeurs : min/max, liste, ... */
     private _valueMode: ParamValueMode;
 
@@ -90,7 +106,7 @@ export class ParamDefinition implements INamedIterableValues, IObservable {
         this._observable = new Observable();
         this._paramValues = new ParamValues();
 
-        // set single value and copy it to currentValue
+        // set single value and copy it to sandbox value
         this._paramValues.singleValue = val;
         this.v = val;
 
@@ -98,6 +114,7 @@ export class ParamDefinition implements INamedIterableValues, IObservable {
         this._family = family;
         this.visible = visible;
         this.valueMode = ParamValueMode.SINGLE;
+        this.extensionStrategy = ExtensionStrategy.REPEAT_LAST;
 
         if (d instanceof ParamDomain) {
             this._domain = d;
@@ -189,6 +206,20 @@ export class ParamDefinition implements INamedIterableValues, IObservable {
         return this._domain.interval;
     }
 
+    public get extensionStrategy(): ExtensionStrategy {
+        return this._extensionStrategy;
+    }
+
+    public set extensionStrategy(strategy: ExtensionStrategy) {
+        this._extensionStrategy = strategy;
+        // synchronise with underlying local ParamValues (for iterator), except for links
+        if ([ ParamValueMode.SINGLE, ParamValueMode.MINMAX, ParamValueMode.LISTE ].includes(this.valueMode)) {
+            if (this._paramValues) {
+                this._paramValues.extensionStrategy = strategy;
+            }
+        }
+    }
+
     public get valueMode(): ParamValueMode {
         return this._valueMode;
     }
@@ -203,8 +234,8 @@ export class ParamDefinition implements INamedIterableValues, IObservable {
     }
 
     /**
-     * Sets the value mode and asks the Nub to ensure all parameters requirements are met
-     * @see Nub::ensureParametersConsistency()
+     * Sets the value mode and asks the Nub to ensure there is only one parameter
+     * in CALC mode
      *
      * If propagateToCalculatedParam is true, any param that goes from CALC mode
      * to any other mode will trigger a reset of the default calculated param at
@@ -221,88 +252,19 @@ export class ParamDefinition implements INamedIterableValues, IObservable {
             return;
         }
 
-        switch (oldMode) {
-            case ParamValueMode.SINGLE:  // ancien état
-                switch (newMode) {
-                    case ParamValueMode.MINMAX:  // nouvel état
-                    case ParamValueMode.LISTE:
-                        this.ensureParametersConsistency(this, ParamValueMode.CALCUL);
-                        break;
-
-                    case ParamValueMode.CALCUL:  // nouvel état
-                        this.ensureParametersConsistency(this, [ ParamValueMode.MINMAX, ParamValueMode.LISTE ]);
-                        break;
-
-                    case ParamValueMode.LINK:  // nouvel état
-                        if (
-                            this.isReferenceDefined()
-                            && this.hasMultipleValues
-                        ) {
-                            this.ensureParametersConsistency(this); // reset existing variable param
-                        }
-                        break;
-                }
-                break;
-
-            case ParamValueMode.LISTE:  // ancien état
-            case ParamValueMode.MINMAX:
-                switch (newMode) {
-                    case ParamValueMode.CALCUL:  // nouvel état
-                        this.ensureParametersConsistency(this);
-                        break;
-
-                    case ParamValueMode.LINK:  // nouvel état
-                        if (
-                            this.isReferenceDefined()
-                            && this.hasMultipleValues
-                        ) {
-                            this.ensureParametersConsistency(this); // reset existing variable param
-                        }
-                        break;
+        if (oldMode === ParamValueMode.CALCUL) {
+            if (propagateToCalculatedParam) {
+                // Set default calculated parameter, only if previous CALC param was
+                // manually set to something else than CALC
+                if (this.parentComputeNode && this.parentComputeNode instanceof Nub) {
+                    this.parentComputeNode.resetDefaultCalculatedParam(this);
                 }
-                break;
-
-            case ParamValueMode.LINK:  // ancien état
-                switch (newMode) {
-                    case ParamValueMode.MINMAX:  // nouvel état
-                    case ParamValueMode.LISTE:
-                        this.ensureParametersConsistency(this, ParamValueMode.CALCUL);
-                        break;
-
-                    case ParamValueMode.CALCUL:  // nouvel état
-                        this.ensureParametersConsistency(this, [ ParamValueMode.MINMAX, ParamValueMode.LISTE ]);
-                        break;
-                }
-                break;
-
-            case ParamValueMode.CALCUL:  // ancien état
-                switch (newMode) {
-                    case ParamValueMode.SINGLE:  // nouvel état
-                        break;
-
-                    case ParamValueMode.MINMAX:  // nouvel état
-                    case ParamValueMode.LISTE:
-                        this.ensureParametersConsistency(this);
-                        break;
-
-                    case ParamValueMode.LINK:  // nouvel état
-                        if (
-                            this.isReferenceDefined()
-                            && this.hasMultipleValues
-                        ) {
-                            this.ensureParametersConsistency(this); // reset existing variable param
-                        }
-                        break;
-                }
-
-                if (propagateToCalculatedParam) {
-                    // Set default calculated parameter, only if previous CALC param was
-                    // manually set to something else than CALC
-                    if (this.parentComputeNode && this.parentComputeNode instanceof Nub) {
-                        this.parentComputeNode.resetDefaultCalculatedParam(this);
-                    }
-                }
-
+            }
+        } else if (newMode === ParamValueMode.CALCUL) {
+            // set old CALC param to SINGLE mode
+            if (this.parentComputeNode && this.parentComputeNode instanceof Nub) {
+                this.parentComputeNode.unsetCalculatedParam(this);
+            }
         }
 
         // set new mode
@@ -403,7 +365,7 @@ export class ParamDefinition implements INamedIterableValues, IObservable {
                 );
                 break;
             case ParamValueMode.LISTE:
-                defined = (this.valueList.length > 0 && this.valueList[0] !== undefined);
+                defined = (this.valueList && this.valueList.length > 0 && this.valueList[0] !== undefined);
                 break;
             case ParamValueMode.CALCUL:
                 if (this.parentNub && this.parentNub.result && this.parentNub.result.nbResultElements > 0) {
@@ -500,6 +462,15 @@ export class ParamDefinition implements INamedIterableValues, IObservable {
         return this.paramValues.singleValue;
     }
 
+    /**
+     * Returns values as a number list, for LISTE and MINMAX modes;
+     * in MINMAX mode, infers the list from min/max/step values
+     */
+    public getInferredValuesList() {
+        this.checkValueMode([ParamValueMode.LISTE, ParamValueMode.MINMAX, ParamValueMode.LINK]);
+        return this.paramValues.getInferredValuesList();
+    }
+
     /**
      * Magic method to define current values into Paramvalues set; defines the mode
      * accordingly by detecting values nature
@@ -1007,6 +978,10 @@ export class ParamDefinition implements INamedIterableValues, IObservable {
         }
     }
 
+    public getExtendedValuesIterator(size: number): INumberIterator {
+        return this._paramValues.getValuesIterator(false, size);
+    }
+
     /**
      * Returns true if there are more than 1 value associated to this parameter;
      * might be its own values (MINMAX / LISTE mode), or targetted values (LINK mode)
@@ -1076,17 +1051,6 @@ export class ParamDefinition implements INamedIterableValues, IObservable {
         return res;
     }
 
-    /**
-     * Asks parent Nub if any to ensure parameters consistency
-     */
-    protected ensureParametersConsistency(sourceParam: ParamDefinition,
-                                          exceptMode: ParamValueMode|ParamValueMode[] = []
-    ) {
-        if (this.parentComputeNode && this.parentComputeNode instanceof Nub) {
-            this.parentComputeNode.ensureParametersConsistency(sourceParam, exceptMode);
-        }
-    }
-
     /**
      * notification envoyée après la modification de la valeur du paramètre
      */
diff --git a/src/param/param-value-iterator.ts b/src/param/param-value-iterator.ts
index c11d0a32257055fb72917fdaa71bc2f6948f480d..dfc309826f2f593b054fd8054885649668ce87f8 100644
--- a/src/param/param-value-iterator.ts
+++ b/src/param/param-value-iterator.ts
@@ -1,5 +1,6 @@
 import { INamedObject, IObjectWithFamily } from "../jalhyd_object";
 import { ArrayReverseIterator } from "../util/iterator";
+import { ExtensionStrategy } from "./param-definition";
 import { ParamValueMode } from "./param-value-mode";
 import { ParamValues } from "./param-values";
 
@@ -56,27 +57,39 @@ export interface INamedIterableValues extends INamedObject, IObjectWithFamily, I
  * itérateur sur les (ou la) valeurs prises par un ParamValues
  */
 export class ParamValueIterator implements INumberIterator {
-    /**
-     * paramètre à itérer
-     */
+
+    /** paramètre à itérer */
     private _param: ParamValues;
 
     /**
-     * true si les valeurs sont fournies de max à min (ParamValueMode.MINMAX)
+     * true si les valeurs sont fournies de max à min (ParamValueMode.MINMAX);
+     * utilisé uniquement par Remous
      */
     private _reverse: boolean;
 
-    /**
-     * valeur courante
-     */
+    /** length to extend the values series to, depending on _param ExtensionStrategy */
+    private _extendTo: number;
+
+    /** valeur courante */
     private _current: number;
 
+    /**
+     * index sur les valeurs définies (et non sur les valeurs étendues): en mode LISTE,
+     * nombre de valeurs actuellement produites; en mode MINMAX, valeur courante (!)
+     */
     private _index: number;
 
-    constructor(prm: ParamValues, reverse: boolean = false) {
+    /**
+     * index sur les valeurs étendues (lorsque plusieurs paramètres varient): en mode MINMAX,
+     * nombre de valeurs actuellement produites, à comparer à this._extendTo; en mode LISTE,
+     * index de la valeur courante à répéter (!)
+     */
+    private _xindex: number;
+
+    constructor(prm: ParamValues, reverse: boolean = false, extendTo: number) {
         prm.check();
         this._param = prm;
-        this.reset(reverse);
+        this.reset(reverse, extendTo);
     }
 
     /**
@@ -91,8 +104,10 @@ export class ParamValueIterator implements INumberIterator {
         return length;
     }
 
-    public reset(reverse: boolean) {
+    public reset(reverse: boolean, extendTo?: number) {
         this._reverse = reverse;
+        this._extendTo = extendTo;
+        this._xindex = 0;
 
         switch (this._param.valueMode) {
             case ParamValueMode.SINGLE:
@@ -116,27 +131,6 @@ export class ParamValueIterator implements INumberIterator {
                 throw new Error(`ParamValueIterator : mode de génération de valeurs ${ParamValueMode[this._param.valueMode]} incorrect`);
         }
     }
-
-    public get hasNext(): boolean {
-        switch (this._param.valueMode) {
-
-            case ParamValueMode.SINGLE:
-                return this._index === 0;
-
-            case ParamValueMode.MINMAX:
-                const end = this._reverse ?
-                    this._index < this._param.min - this._param.step * 1E-7 :
-                    this._index > this._param.max + this._param.step * 1E-7;
-                return !end;
-
-            case ParamValueMode.LISTE:
-                return this._index < this._param.valueList.length;
-
-            default:
-                throw new Error(`ParamValueIterator.hasNext() : erreur interne`);
-        }
-    }
-
     public next(): IteratorResult<number> {
         switch (this._param.valueMode) {
 
@@ -144,6 +138,7 @@ export class ParamValueIterator implements INumberIterator {
                 if (this.hasNext) {
                     this._current = this._param.singleValue;
                     this._index++;
+                    this._xindex ++;
                     return {
                         done: false,
                         value: this._current
@@ -156,40 +151,102 @@ export class ParamValueIterator implements INumberIterator {
                 }
 
             case ParamValueMode.MINMAX:
-                this._current = this._index;
-                if (this.hasNext) {
+                if (this.hasNextWithoutExtension) { // default case
+                    this._current = this._index;
                     if (this._reverse) {
                         this._index -= this._param.step;
                     } else {
                         this._index += this._param.step;
                     }
+                    this._xindex ++; // count values for possible extension
                     return {
                         done: false,
                         value: this._current
                     };
-                } else {
-                    return {
-                        done: true,
-                        value: undefined
-                    };
+                } else { // no more real values
+                    if (this._extendTo && this.hasNext) {
+                        // extend
+                        this._xindex ++;
+                        switch (this._param.extensionStrategy) {
+                            case ExtensionStrategy.REPEAT_LAST:
+                                // repeat last real value (do not change this._current)
+                                return {
+                                    done: false,
+                                    value: this._current
+                                };
+
+                            case ExtensionStrategy.RECYCLE:
+                                // loop over real values
+                                if ( // end reached
+                                    this._reverse ?
+                                    this._index < this._param.min - this._param.step * 1E-7 :
+                                    this._index > this._param.max + this._param.step * 1E-7
+                                ) {
+                                    this._index = this._reverse ? this._param.max : this._param.min;
+                                }
+                                this._current = this._index;
+                                if (this._reverse) {
+                                    this._index -= this._param.step;
+                                } else {
+                                    this._index += this._param.step;
+                                }
+                                return {
+                                    done: false,
+                                    value: this._current
+                                };
+                        }
+                    } else {
+                        return {
+                            done: true,
+                            value: undefined
+                        };
+                    }
                 }
 
             case ParamValueMode.LISTE:
-                if (this.hasNext) {
-                    this._current = this._param.valueList[this._index++];
+                if (this.hasNextWithoutExtension) { // default case
+                    this._current = this._param.valueList[this._index++]; // what about _reverse ?
+                    this._xindex ++; // count values for possible extension
                     return {
                         done: false,
                         value: this._current
                     };
-                } else {
-                    return {
-                        done: true,
-                        value: undefined
-                    };
+                } else { // no more real values
+                    if (this._extendTo && this.hasNext) {
+                        // extend
+                        this._index ++;
+                        switch (this._param.extensionStrategy) {
+                            case ExtensionStrategy.REPEAT_LAST:
+                                // repeat last real value (do not change this._current)
+                                return {
+                                    done: false,
+                                    value: this._current
+                                };
+
+                            case ExtensionStrategy.RECYCLE:
+                                // loop over real values
+                                if (
+                                    this._xindex === undefined
+                                    || this._xindex === this._param.valueList.length
+                                ) {
+                                    this._xindex = 0;
+                                }
+                                this._current = this._param.valueList[this._xindex++];
+                                return {
+                                    done: false,
+                                    value: this._current
+                                };
+                        }
+                    } else {
+                        return {
+                            done: true,
+                            value: undefined
+                        };
+                    }
                 }
 
             default:
-                throw new Error(`ParamValueIterator.next() : erreur interne`);
+                throw new Error(`ParamValueIterator.next() : internal error`);
         }
     }
 
@@ -202,6 +259,47 @@ export class ParamValueIterator implements INumberIterator {
     public [Symbol.iterator](): IterableIterator<number> {
         return this;
     }
+
+    public get hasNext(): boolean {
+        return this.hasNextValue();
+    }
+
+    protected get hasNextWithoutExtension() {
+        return this.hasNextValue(true);
+    }
+
+    /**
+     * Returns true if iterator has at least one more value
+     * @param ignoreExtension if true, will consider only real values (those that were
+     *      set up) and ignore extended values (when multiple parameters are varying)
+     */
+    protected hasNextValue(ignoreExtension: boolean = false): boolean {
+        switch (this._param.valueMode) {
+
+            case ParamValueMode.SINGLE:
+                return this._index === 0;
+
+            case ParamValueMode.MINMAX:
+                if (this._extendTo && !ignoreExtension) {
+                    return this._xindex < this._extendTo;
+                } else {
+                    const end = this._reverse ?
+                        this._index < this._param.min - this._param.step * 1E-7 :
+                        this._index > this._param.max + this._param.step * 1E-7;
+                    return !end;
+                }
+
+            case ParamValueMode.LISTE:
+                if (this._extendTo && !ignoreExtension) {
+                    return this._index < this._extendTo;
+                } else {
+                    return this._index < this._param.valueList.length;
+                }
+
+            default:
+                throw new Error(`ParamValueIterator.hasNext() : internal error`);
+        }
+    }
 }
 
 /**
diff --git a/src/param/param-values.ts b/src/param/param-values.ts
index 9d850c7d16bcf7c042d6e4f298ac74bf15b6b37b..9da01d09f7aa35689daa38ac535fe324b3c1b248 100644
--- a/src/param/param-values.ts
+++ b/src/param/param-values.ts
@@ -1,3 +1,4 @@
+import { ExtensionStrategy } from "./param-definition";
 import { INumberIterator, IterableValues, ParamValueIterator } from "./param-value-iterator";
 import { ParamValueMode } from "./param-value-mode";
 
@@ -8,11 +9,12 @@ import { ParamValueMode } from "./param-value-mode";
  */
 export class ParamValues implements IterableValues {
 
-    /**
-     * usually set by enclosing ParamDefinition
-     */
+    /** usually set by enclosing ParamDefinition */
     public valueMode: ParamValueMode;
 
+    /** usually set by enclosing ParamDefinition */
+    public extensionStrategy: ExtensionStrategy;
+
     /** valeur courante (éventuellement non définie) indépendemment du mode */
     public currentValue: number;
 
@@ -104,14 +106,34 @@ export class ParamValues implements IterableValues {
         }
     }
 
+    /**
+     * Returns values as a number list, for LISTE and MINMAX modes;
+     * in MINMAX mode, infers the list from min/max/step values
+     */
+    public getInferredValuesList() {
+        if (this.valueMode === ParamValueMode.LISTE) {
+            return this.valueList;
+        }
+        if (this.valueMode === ParamValueMode.MINMAX) {
+            const values = [ this.min ];
+            let latestVal = this.min + this.step;
+            while (latestVal <= this.max) {
+                values.push(latestVal);
+                latestVal += this.step;
+            }
+            return values;
+        }
+        throw new Error("ParamValues.getInferredValuesList() : incorrect value mode" + ParamValueMode[this.valueMode]);
+    }
+
     // -- iterator-related methods
 
     /**
      * crée un ParamValueIterator
      * @param reverse true si on veut itérer max->min ou depuis la fin de la liste
      */
-    public getValuesIterator(reverse: boolean = false): INumberIterator {
-        return new ParamValueIterator(this, reverse);
+    public getValuesIterator(reverse: boolean = false, extendTo?: number): INumberIterator {
+        return new ParamValueIterator(this, reverse, extendTo);
     }
 
     // interface IterableValues
@@ -136,11 +158,11 @@ export class ParamValues implements IterableValues {
         }
     }
 
-    public initValuesIterator(reverse: boolean = false): INumberIterator {
+    public initValuesIterator(reverse: boolean = false, extendTo?: number): INumberIterator {
         switch (this.valueMode) {
             case ParamValueMode.LISTE:
             case ParamValueMode.MINMAX:
-                this._iterator = this.getValuesIterator(reverse);
+                this._iterator = this.getValuesIterator(reverse, extendTo);
                 break;
 
             default:
diff --git a/src/section/section_parametree.ts b/src/section/section_parametree.ts
index d09e870807ace50d738993de9f45d0ab78928df2..b88b68ae04ecbf68a3145cf18a9c325ba95195cb 100644
--- a/src/section/section_parametree.ts
+++ b/src/section/section_parametree.ts
@@ -69,67 +69,6 @@ export class SectionParametree extends SectionNub {
         }
     }
 
-    public CalcSerie(rInit?: number, sDonnee?: string): Result {
-
-        this.triggerChainCalculation();
-
-        let variatedParam: ParamDefinition;
-        let variatedValues: ParamValues;
-
-        for (const p of this.parameterIterator) {
-            // checks which values are variating, if any
-            switch (p.valueMode) {
-                case ParamValueMode.SINGLE:
-                case ParamValueMode.CALCUL:
-                    break;
-
-                case ParamValueMode.LISTE:
-                case ParamValueMode.MINMAX:
-                    variatedParam = this.setVariatedValues(p, variatedParam);
-                    break;
-
-                case ParamValueMode.LINK:
-                    if (
-                        p.isReferenceDefined()
-                        && p.referencedValue.hasMultipleValues()
-                    ) {
-                        variatedParam = this.setVariatedValues(p, variatedParam);
-                    }
-                    break;
-
-                default:
-                    // tslint:disable-next-line:max-line-length
-                    throw new Error(`CalcSerie() : valeur de ParamValueMode ${ParamValueMode[p.valueMode]} non prise en charge`);
-            }
-        }
-
-        if (variatedParam === undefined) {
-            this.Calc(); // résultat dans this._result
-        } else {
-            // extract variated values from variated Parameter
-            // (in LINK mode, proxies to target data)
-            variatedValues = variatedParam.paramValues;
-
-            const res = new Result(undefined, this);
-            variatedValues.initValuesIterator(false);
-            while (variatedValues.hasNext) {
-                variatedValues.next();
-
-                this.Calc();  // résultat dans this._result
-                if (this._result.ok) {
-                    res.addResultElement(this._result.resultElement);
-                    res.addLog(this._result.log);
-                }
-                res.globalLog.addLog(this._result.globalLog);
-            }
-            this._result = res;
-
-        }
-
-        this.notifyResultUpdated();
-        return this._result;
-    }
-
     /**
      * Aucune variable à calculer plus que les autres, on stocke toutes les
      * valeurs des variables à calcul dans les résultats complémentaires
@@ -198,6 +137,19 @@ export class SectionParametree extends SectionNub {
         return this._result;
     }
 
+    // calculated param is always "Y"
+    protected findCalculatedParameter(sDonnee: any): any {
+        return undefined;
+    }
+
+    // calculated param is always "Y"
+    protected doCalc(computedSymbol?: any, rInit?: number) {
+        return this.Calc();
+    }
+
+    // does nothing or else tests break !?
+    protected copySingleValuesToSandboxValues() { }
+
     // tslint:disable-next-line:no-empty
     protected setParametersCalculability(): void {}
 
diff --git a/src/structure/structure.ts b/src/structure/structure.ts
index 2547d78fb204f3873e8b380469213b5d218e4ebf..123d0490d8b9cf906af2cc109b43fa505c421cf9 100644
--- a/src/structure/structure.ts
+++ b/src/structure/structure.ts
@@ -106,9 +106,9 @@ export abstract class Structure extends Nub {
      * Forwards to parent, that has vsibility over
      * all the parameters, including the Structure ones
      */
-    public ensureParametersConsistency(sourceParam: ParamDefinition, exceptMode: ParamValueMode|ParamValueMode[] = []) {
+    public unsetCalculatedParam(except: ParamDefinition) {
         if (this.parent) {
-            this.parent.ensureParametersConsistency(sourceParam, exceptMode);
+            this.parent.unsetCalculatedParam(except);
         }
     }
 
@@ -137,9 +137,9 @@ export abstract class Structure extends Nub {
      * Forwards to parent, that has vsibility over
      * all the parameters, including the Structure ones
      */
-    public findFirstSingleParameter(otherThan?: ParamDefinition) {
+    public findFirstCalculableParameter(otherThan?: ParamDefinition) {
         if (this.parent) {
-            return this.parent.findFirstSingleParameter(otherThan);
+            return this.parent.findFirstCalculableParameter(otherThan);
         }
         return undefined;
     }