diff --git a/CHANGELOG.fr.md b/CHANGELOG.fr.md
index 278c318ef1b2f09cd89a43944617fd23938aa4e1..1990aedb4ac0b590adc550595fef211462f2d62e 100644
--- a/CHANGELOG.fr.md
+++ b/CHANGELOG.fr.md
@@ -14,6 +14,11 @@ et ce projet adhère au [versionnage sémantique](https://semver.org/spec/v2.0.0
 
 ## [Non-publié/Non-Stabilisé] (par [1000i100])
 
+## [Version 3.5.2] - 2022-12-16 (par [1000i100])
+### Corrections
+- Dictionary échappe correctement les caractères spéciaux
+- Dictionary splitGet permet d'avoir séparément idSec et pwd sans ambiguïté liée aux caractères échappés.
+
 ## [Version 3.5.1] - 2022-12-09 (par [1000i100])
 ### Corrections
 - Dictionary applique systématiquement les variantes passées en option.
@@ -125,8 +130,10 @@ et ce projet adhère au [versionnage sémantique](https://semver.org/spec/v2.0.0
 - intégration des librairies de crypto nécessaires
 - calcul de la clef publique correspondant à chaque combinaison de secrets saisie, et comparaison à la clef publique de référence.
 
-[Non-publié/Non-Stabilisé]: https://git.duniter.org/libs/g1lib.js/-/compare/v3.5.0...main
+[Non-publié/Non-Stabilisé]: https://git.duniter.org/libs/g1lib.js/-/compare/v3.5.2...main
 
+[Version 3.5.2]: https://git.duniter.org/libs/g1lib.js/-/compare/v3.5.1...v3.5.2
+[Version 3.5.1]: https://git.duniter.org/libs/g1lib.js/-/compare/v3.5.0...v3.5.1
 [Version 3.5.0]: https://git.duniter.org/libs/g1lib.js/-/compare/v3.4.2...v3.5.0
 [Version 3.4.2]: https://git.duniter.org/libs/g1lib.js/-/compare/v3.4.1...v3.4.2
 [Version 3.4.1]: https://git.duniter.org/libs/g1lib.js/-/compare/v3.4.0...v3.4.1
diff --git a/npm/package.json b/npm/package.json
index 24424ff00161b726409793584818aaa8ee9f6452..a15559e0717f660e3d54c1532b1b1e9fbe56d19b 100644
--- a/npm/package.json
+++ b/npm/package.json
@@ -1,6 +1,6 @@
 {
   "name": "g1lib",
-  "version": "3.5.1",
+  "version": "3.5.2",
   "description": "An ubiquitous static javascript toolbox lib for Ǧ1 / Duniter ecosystem with reliability in mind.",
   "main": "nodejs/all.mjs",
 	"browser": "browser/all.mjs",
diff --git a/src/dictionary-escaper.mjs b/src/dictionary-escaper.mjs
new file mode 100644
index 0000000000000000000000000000000000000000..233d9445b0a350a5eed882d09f6243196a376bda
--- /dev/null
+++ b/src/dictionary-escaper.mjs
@@ -0,0 +1,34 @@
+const specialMap = {
+	'(': String.fromCharCode(0x01),
+	')': String.fromCharCode(0x02),
+	'|': String.fromCharCode(0x03),
+	'{': String.fromCharCode(0x04),
+	'}': String.fromCharCode(0x05),
+	',': String.fromCharCode(0x06),
+	'[': String.fromCharCode(0x07),
+	']': String.fromCharCode(0x08),
+	'-': String.fromCharCode(0x09),
+	'<': String.fromCharCode(0x0a),
+	'>': String.fromCharCode(0x0b),
+	':': String.fromCharCode(0x0c),
+	'=': String.fromCharCode(0x0d),
+	'@': String.fromCharCode(0x0e)
+};
+const revertSpecial = swapKeyValue(specialMap);
+function swapKeyValue(object) {
+	const result = {};
+	for (const key in object) {
+		result[object[key]] = key;
+	}
+
+	return result;
+}
+export function escape2utfSpecial(str) {
+	return str.replace(/\\(.)/g, (a, chr) => specialMap[chr] ? specialMap[chr] : chr);
+}
+export function utfSpecial2unEscaped(str) {
+	return str.split('').map(chr => revertSpecial[chr] ? revertSpecial[chr] : chr).join('');
+}
+export function utfSpecial2escaped(str) {
+	return str.split('').map(chr => revertSpecial[chr] ? `\\${revertSpecial[chr]}` : chr).join('');
+}
diff --git a/src/dictionary-escaper.test.mjs b/src/dictionary-escaper.test.mjs
new file mode 100644
index 0000000000000000000000000000000000000000..cf308d501581d0b7f8c554fd776bf2b10e9c3638
--- /dev/null
+++ b/src/dictionary-escaper.test.mjs
@@ -0,0 +1,17 @@
+import test from "ava";
+import * as app from "./dictionary-escaper.mjs";
+import {escape2utfSpecial} from "./dictionary-escaper.mjs";
+
+test('unescape special characters & re-escape them generate identical string', t => {
+	const cases = [
+		'plop:\\:ici',
+		'[\\]*]',
+	]
+	cases.forEach(c=>t.is(app.utfSpecial2escaped(app.escape2utfSpecial(c)), c));
+});
+test('unescape special characters & reconvert without escaping remove escaping', t => {
+	t.is(app.utfSpecial2unEscaped(app.escape2utfSpecial('plop:\\:ici')), 'plop::ici');
+});
+test('unescape usual characters & re-escape them generate usual string', t => {
+	t.is(app.utfSpecial2escaped(app.escape2utfSpecial('\\a')), 'a');
+});
diff --git a/src/dictionary-parser.mjs b/src/dictionary-parser.mjs
index 7e04e64c51c9d587f34d38a937b0864f96384369..538f73bc67174335751534e7651f0ba8cd460c17 100644
--- a/src/dictionary-parser.mjs
+++ b/src/dictionary-parser.mjs
@@ -1,5 +1,6 @@
 import latinize from '../node_modules/latinize-to-ascii/latinize.mjs';
 import * as tree from './dictionary-tree.mjs';
+import {escape2utfSpecial, utfSpecial2escaped} from "./dictionary-escaper.mjs";
 
 export function parse(dictionaryString,idSecPwd=true,accent=0,lowerCase=0,leet=0) {
 	resetCache();
@@ -11,7 +12,7 @@ export function parse(dictionaryString,idSecPwd=true,accent=0,lowerCase=0,leet=0
 		.replace(/§void§\|/g,'')
 		.replace(/\|§void§/g,'')
 		.replace(/§void§/g,'');
-	if(monoLined.includes('§infiniteRecursion§')) throw new Error(`Unable to parse : ${utfSpecial2unEscaped(flattenIt(monoLined))}`);
+	if(monoLined.includes('§infiniteRecursion§')) throw new Error(`Unable to parse : ${utfSpecial2escaped(flattenIt(monoLined))}`);
 	if(!idSecPwd) return parseEnd(monoLined,allLines,accent,lowerCase,leet);
 	const parts = tree.splitAround('@@',tree.buildTreeStruct(monoLined));
 	const flatParts = [];
@@ -22,7 +23,7 @@ export function parse(dictionaryString,idSecPwd=true,accent=0,lowerCase=0,leet=0
 }
 function parseEnd(str,allLines,accent,lowerCase,leet) {
 	let theString = syncRefHandler(str,allLines)
-	if(theString.match(/=[^>]+>/)) throw new Error(`Unable to parse : ${utfSpecial2unEscaped(flattenIt(theString))}`);
+	if(theString.match(/=[^>]+>/)) throw new Error(`Unable to parse : ${utfSpecial2escaped(flattenIt(theString))}`);
 	theString = qtyHandler(theString);
 	if(accent===1) theString = zeroAccent(theString);
 	if(accent===2) theString = optionalAccent(theString);
@@ -34,7 +35,7 @@ function parseEnd(str,allLines,accent,lowerCase,leet) {
 	if(leet===2) theString = allLeetSpeak(theString);
 	if(leet===3) theString = everyLeetSpeak(theString);
 	//TODO: bigLeet Variants
-	return utfSpecial2unEscaped(flattenIt(theString));
+	return utfSpecial2escaped(flattenIt(theString));
 }
 function zeroVariant(str,func){
 	const zero = func(str);
@@ -164,37 +165,6 @@ function cache(key, func, ...args) {
 	/* Compute and cache */ if (typeof cache.cached[key] === 'undefined') cache.cached[key] = func(...args);
 	/* Answer from cache */ return cache.cached[key];
 }
-const specialMap = {
-	'(': String.fromCharCode(0x01),
-	')': String.fromCharCode(0x02),
-	'|': String.fromCharCode(0x03),
-	'{': String.fromCharCode(0x04),
-	'}': String.fromCharCode(0x05),
-	',': String.fromCharCode(0x06),
-	'[': String.fromCharCode(0x07),
-	']': String.fromCharCode(0x08),
-	'-': String.fromCharCode(0x09),
-	'<': String.fromCharCode(0x0a),
-	'>': String.fromCharCode(0x0b),
-	':': String.fromCharCode(0x0c),
-	'=': String.fromCharCode(0x0d),
-	'@': String.fromCharCode(0x0e)
-};
-const revertSpecial = swapKeyValue(specialMap);
-function swapKeyValue(object) {
-	const result = {};
-	for (const key in object) {
-		result[object[key]] = key;
-	}
-
-	return result;
-}
-function escape2utfSpecial(str) {
-	return str.replace(/\\(.)/g, (a, chr) => specialMap[chr] ? specialMap[chr] : chr);
-}
-function utfSpecial2unEscaped(str) {
-	return str.split('').map(chr => revertSpecial[chr] ? revertSpecial[chr] : chr).join('');
-}
 function bracketsHandler(theString) {
 	// Handle []
 	const lower = 'abcdefghijklmnopqrstuvwxyz';
diff --git a/src/dictionary-parser.test.mjs b/src/dictionary-parser.test.mjs
index b96bc42643743d560d94604109999a44c553fa83..6e8fffe0e7558bdff2f98caa96e922c9ad8d27b0 100644
--- a/src/dictionary-parser.test.mjs
+++ b/src/dictionary-parser.test.mjs
@@ -44,15 +44,12 @@ test('parse handle nested (s|t(ri|ng)){qty}', t => {
 });
 
 test('parse handle plop:\\:', t => {
-	t.is(app.parse('plop:\\:ici',false), 'plop::ici');
-	t.is(app.parse('plop\\::ici',false), 'plop::ici');
+	t.is(app.parse('plop:\\:ici',false), 'plop:\\:ici');
+	t.is(app.parse('plop\\::ici',false), 'plop\\::ici');
 	t.is(app.parse('plop::ici',false), '');
 });
 test('parse handle [\\]*]', t => {
-	t.is(app.parse('[\\]*]',false), '(]|*)');
-});
-test('parse handle escaping common chr \\a', t => {
-	t.is(app.parse('\\a',false), 'a');
+	t.is(app.parse('[\\]*]',false), '(\\]|*)');
 });
 
 test('parse handle =ref>', t => {
diff --git a/src/dictionary-tree.mjs b/src/dictionary-tree.mjs
index b219137a5ed8c9f986be338bc66836aabdf8558e..b9da3c96b6668b664f9be66c5f94ab32b283f4df 100644
--- a/src/dictionary-tree.mjs
+++ b/src/dictionary-tree.mjs
@@ -1,8 +1,10 @@
+import {escape2utfSpecial, utfSpecial2escaped, utfSpecial2unEscaped} from "./dictionary-escaper.mjs";
+
 export function buildTreeStruct(monoLineString) {
-	const stringAsArray = monoLineString.split('');
+	const stringAsArray = escape2utfSpecial(monoLineString).split('');
 	const rawTree = leftParser(stringAsArray);
 	const outOfScope = stringAsArray.length;
-	if (outOfScope) throw new Error(`fail to build tree from : "${monoLineString}" parsed: ${JSON.stringify(rawTree)} unparsed/failed: ${stringAsArray.join('')}`);
+	if (outOfScope) throw new Error(`fail to build tree from : "${utfSpecial2escaped(monoLineString)}" parsed: ${JSON.stringify(rawTree)} unparsed/failed: ${utfSpecial2escaped(stringAsArray.join(''))}`);
 	let lastTree, tree = rawTree;
 	do {
 		lastTree = JSON.stringify(tree);
@@ -119,7 +121,11 @@ function trivialDedup(tree) {
 }
 export function splitAround(pattern,treeStruct){
 	function recSplitter(treeStruct){
-		if (isString(treeStruct)) return treeStruct.str.includes(pattern) ? {matching:treeStruct.str} : {notMatching:treeStruct.str};
+		if (isString(treeStruct)) {
+			if(!treeStruct.str.includes(pattern)) return {notMatching:treeStruct.str};
+			if(treeStruct.str.split(pattern).length>2) throw new Error('Error: @@ can only appear once in expression (idSec at left, password à right)');
+			return {matching:treeStruct.str};
+		}
 		if (isStep(treeStruct)) {
 			let isMatch = 0;
 			let isAlt = false;
@@ -162,7 +168,7 @@ export function splitAround(pattern,treeStruct){
 	return res;
 }
 export function serialize(treeStruct) {
-	if (isString(treeStruct)) return treeStruct.str;
+	if (isString(treeStruct)) return utfSpecial2escaped(treeStruct.str);
 	if (isStep(treeStruct)) return treeStruct.step.map(serialize).join('');
 	if (isAlt(treeStruct)) return `(${treeStruct.alt.map(serialize).join('|')})`;
 	throw new Error(`Error: how to serialize ${JSON.stringify(treeStruct)} RAW: ${treeStruct}`);
@@ -206,6 +212,10 @@ export function altCount(treeStruct) {
 }
 
 export function getAlternative(altIndex, tree) {
+	const refAltIndex = {index: altIndex};
+	return utfSpecial2unEscaped(_getAlternative(refAltIndex, tree));
+}
+export function getRawAlternative(altIndex, tree) {
 	const refAltIndex = {index: altIndex};
 	return _getAlternative(refAltIndex, tree);
 }
diff --git a/src/dictionary-tree.test.mjs b/src/dictionary-tree.test.mjs
index c71dc7e03810b6f223a7f82e705e00af90234e6f..48e0cdcf731379f8440ccf1783716ef6e107faed 100644
--- a/src/dictionary-tree.test.mjs
+++ b/src/dictionary-tree.test.mjs
@@ -13,11 +13,12 @@ test('(b|b) trivial dedup', t => t.is(buildTreeThenSerialize('(|b||b|)'), '(|b)'
 test('a(b|c) mix fix and alt', t => t.is(buildTreeThenSerialize('a(b|c)'), 'a(b|c)'));
 test('a(b) flat merge when no alt', t => t.is(buildTreeThenSerialize('a(b)'), 'ab'));
 test('(a(b|c)|(d|e)) flat merge when unneeded depth', t => t.is(buildTreeThenSerialize('(a(b|c)|(d|e))'), '(a(b|c)|d|e)'));
-test('build complexe tree with (|) pattern', t => t.is(buildTreeThenSerialize('(a(|b@@@@c|d|)|(e|f)|g|h@@@@i)'), '(a(|b@@@@c|d)|e|f|g|h@@@@i)'));
+test('build complexe tree with (|) pattern', t => t.is(buildTreeThenSerialize('(a(|b@@c|d|)|(e|f)|g|h@@i)'), '(a(|b@@c|d)|e|f|g|h@@i)'));
 
 test('serialize incorrect tree throw', t => t.throws(() => app.serialize({plop: ['a']})));
 
-test('splitAround incorrect tree throw', t => t.throws(() => app.splitAround('@@',{plop: ['a']})));
+test('splitAround throw when to many split', t => t.throws(() => app.splitAround('@@',app.buildTreeStruct('z@@a(b|c@@d)'))));
+test('splitAround throw with @@@@', t => t.throws(() => app.splitAround('@@',app.buildTreeStruct('@@@@'))));
 test('splitAround return notMatching case', t => t.deepEqual(app.splitAround('@',app.buildTreeStruct('a(b|c)')),{notMatching:'a(b|c)'}));
 test('splitAround return matching case', t => t.deepEqual(app.splitAround('@',app.buildTreeStruct('a@b')),{matching:'a@b'}));
 test('splitAround return both matching case and not matching one', t => t.deepEqual(app.splitAround('@',app.buildTreeStruct('a@b|c')),{matching:'a@b',notMatching:'c'}));
@@ -25,7 +26,7 @@ test('splitAround return both matching case and not matching one', t => t.deepEq
 test('mono altCount', t => t.is(app.altCount(app.buildTreeStruct('ipsum')), 1));
 test('simple altCount', t => t.is(app.altCount(app.buildTreeStruct('(lore|ipsu)m')), 2));
 test('multi altCount', t => t.is(app.altCount(app.buildTreeStruct('(a|b|c)(d|e|f)g(h|i|j|k)')), 36));
-test('multi level tree altCount', t => t.is(app.altCount(app.buildTreeStruct('a(b(c|d)|e(f|g|h)ij(k|l)|@@@@m)')), 9));
+test('multi level tree altCount', t => t.is(app.altCount(app.buildTreeStruct('a(b(c|d)|e(f|g|h)ij(k|l)|@@m)')), 9));
 
 const exampleTree = () => app.buildTreeStruct('a(b(c|d)|e(f|g(h|i)|j)kl(m|n(o|p)|q(r|s)|t)|(u|v)w)');
 // console.log(JSON.stringify(exampleTree()));
@@ -61,3 +62,12 @@ test('getAlternative 20', t => t.is(app.getAlternative(20, exampleTree()), 'aejk
 test('getAlternative 26', t => t.is(app.getAlternative(26, exampleTree()), 'auw'));
 test('getAlternative 27', t => t.is(app.getAlternative(27, exampleTree()), 'avw'));
 test('getAlternative 28 or more throw', t => t.throws(() => app.getAlternative(28, exampleTree())));
+
+test('escaped special characters are reconverted with getAlternative but not with getRawAlternative', t => {
+	const tree = app.buildTreeStruct('a\\(b(c|d)@@e');
+	t.is(app.getAlternative(0,tree), 'a(bc@@e');
+	t.is(app.getRawAlternative(0,tree), `a${String.fromCharCode(0x01)}bc@@e`);
+	const treeWhereRawIsUseful = app.buildTreeStruct('a\\@\\@b(c|d)@@e');
+	t.is(app.getAlternative(0,treeWhereRawIsUseful), 'a@@bc@@e');
+	t.is(app.getRawAlternative(0,treeWhereRawIsUseful), `a${String.fromCharCode(0x0e)+String.fromCharCode(0x0e)}bc@@e`);
+});
diff --git a/src/dictionary.mjs b/src/dictionary.mjs
index 6c7be142ea7f8ca16dea33135dff7f5496a47d46..1aa138be80eb92840d2dbe88140c7b1cc489162e 100644
--- a/src/dictionary.mjs
+++ b/src/dictionary.mjs
@@ -1,5 +1,6 @@
-import {buildTreeStruct, getAlternative, serialize} from "./dictionary-tree.mjs";
+import {buildTreeStruct, getRawAlternative, serialize} from "./dictionary-tree.mjs";
 import {parse} from "./dictionary-parser.mjs";
+import {utfSpecial2unEscaped} from "./dictionary-escaper.mjs";
 
 export class Dictionary {
 	constructor(dictionaryString, options= {}) {
@@ -17,11 +18,13 @@ export class Dictionary {
 		this.tried = 0;
 		this.cache = [];
 		this.duplicatedCount = ()=> Object.keys(this.cache).length? this.tried - Object.keys(this.cache).length : 0;
-		this.dryGet = index => getAlternative(index,this.tree);
-		this.get = index => {
+		this.dryGet = index => utfSpecial2unEscaped(this.rawDryGet(index));
+		this.rawDryGet = index => getRawAlternative(index,this.tree);
+		this.get = index => utfSpecial2unEscaped(this.rawGet(index));
+		this.rawGet = index => {
 			if(typeof this.startTime === "undefined") this.startTime = Date.now();
 			this.tried++;
-			const alt = getAlternative(index,this.tree);
+			const alt = getRawAlternative(index,this.tree);
 			if(this.config.cache && this.length < this.config.cacheMax) {
 				if(typeof this.cache[alt] === "undefined") this.cache[alt] = [];
 				this.cache[alt].push(index);
@@ -29,6 +32,18 @@ export class Dictionary {
 			}
 			return alt;
 		}
+		function _split(str){
+			const parts = str.split('@@');
+			const idSec = utfSpecial2unEscaped(parts[0]);
+			const pwd = utfSpecial2unEscaped(parts[parts.length - 1]);
+			const res = [idSec,pwd];
+			res.idSec = idSec;
+			res.pwd = pwd;
+			res.pass = pwd;
+			return res;
+		}
+		this.splitGet = index => _split(this.rawGet(index));
+		this.splitDryGet = index => _split(this.rawDryGet(index));
 		this.timeSpent = ()=>(Date.now()-this.startTime)/1000;
 		this.duplicatedFound = ()=>{
 			const duplicated = [];
@@ -37,6 +52,7 @@ export class Dictionary {
 			return sortedDuplicate;
 		}
 		this.dryRunDedup = ()=>dryRunDedup(this);
+		this.serialize = ()=>serialize(this.tree);
 	}
 }
 function adjustVariant(self){
diff --git a/src/dictionary.test.mjs b/src/dictionary.test.mjs
index d87278834cf0582937bce11a46546682bcca1e7b..027da3867300d6e5b4feca1caf2c9c4af3480fdf 100644
--- a/src/dictionary.test.mjs
+++ b/src/dictionary.test.mjs
@@ -1,6 +1,9 @@
 import test from 'ava';
 import * as app from './dictionary.mjs';
 
+function sleep(ms){
+	return new Promise((resolve)=>setTimeout(resolve,ms));
+}
 test('get dictionary length', t => {
 	const dictionaryString = '(a|b|c)d(e|f|g)';
 	const dico = new app.Dictionary(dictionaryString,{idSecPwd:false});
@@ -11,14 +14,52 @@ test('get dictionary iteration', t => {
 	const dico = new app.Dictionary(dictionaryString);
 	t.is(dico.get(5), "ade@@bdg");
 });
-test('get past iteration count in this dictionary', t => {
-	const dictionaryString = '(a|b|c)d(e|f|g)';
-	const dico = new app.Dictionary(dictionaryString);
+test('get iteration are tracked', t => {
+	const dico = new app.Dictionary('(a|b|c)d(e|f|g)');
 	t.is(dico.tried, 0);
 	dico.get(1);
 	dico.get(2);
 	t.is(dico.tried, 2);
 });
+test('dryGet iteration are not tracked', t => {
+	const dico = new app.Dictionary('(a|b|c)d(e|f|g)');
+	t.is(dico.tried, 0);
+	dico.dryGet(1);
+	dico.dryGet(2);
+	t.is(dico.tried, 0);
+});
+test('_\\@\\@_@@_@\\@_ can be ambiguous with get, dryGet or not with rawGet, rawDryGet, splitGet', t => {
+	const dico = new app.Dictionary('(\\@\\@|_@@\\)@\\@)', {cache:false});
+	t.is(dico.length, 2);
+	t.is(dico.dryGet(0), '_@@)@@');
+	t.is(dico.dryGet(1), '@@@@@@');
+	t.is(dico.get(1), '@@@@@@');
+	const escAro = String.fromCharCode(0x0e);
+	t.is(dico.rawGet(0), `_@@${String.fromCharCode(0x02)}@${escAro}`);
+	t.is(dico.rawDryGet(1), `${escAro+escAro}@@${escAro+escAro}`);
+	t.is(dico.splitGet(0)[0], '_');
+	t.is(dico.splitGet(0)[1], ')@@');
+	t.is(dico.splitGet(0).idSec, '_');
+	t.is(dico.splitGet(0).pwd, ')@@');
+	t.is(dico.splitGet(0).pass, ')@@');
+	t.is(dico.splitDryGet(0)[0], '_');
+	t.is(dico.splitDryGet(0)[1], ')@@');
+});
+
+test('get is time tracked', async t => {
+	const dico = new app.Dictionary('(a|b|c)d(e|f|g)');
+	dico.get(1);
+	await sleep(5);
+	dico.get(2);
+	t.true(dico.timeSpent()>=0.005);
+});
+test('estimateDuration && estimateRemaining', t => {
+	const dico = new app.Dictionary('(a|b|c)d(e|f|g)');
+	dico.get(1);
+	dico.get(2);
+	t.is(dico.estimateDuration(), 81);
+	t.is(dico.estimateRemaining(), 79);
+});
 test('get duplicated found count (from dictionary)', t => {
 	const dictionaryString = '(a|b|c)d(e|f|g)';
 	const dico = new app.Dictionary(dictionaryString);
@@ -107,3 +148,10 @@ test("huge alt number apply variants if set", t => {
 	t.is((new app.Dictionary(`[0-9]{${2+x}}@@[A-Z]`, {lowerCase:0})).length,2_600 * 10**x);
 	t.is((new app.Dictionary(`[0-9]{${2+x}}@@[A-Z]`, {lowerCase:1})).length,5_200 * 10**x);
 });
+test('escaped special characters still escaped when re-serialized', t => {
+	const dictionaryString = '(a|b|c\\)c)d(e|f|g)';
+	const dico = new app.Dictionary(dictionaryString, {idSecPwd:false});
+	const serialized = dico.serialize();
+	t.is(serialized, dictionaryString);
+	t.is(dico.get(6), 'c)cde');
+});