Index: TXM/trunk/bundles/org.txm.specificities.rcp/src/org/txm/specificities/rcp/editors/SpecificitiesEditor.java
===================================================================
--- TXM/trunk/bundles/org.txm.specificities.rcp/src/org/txm/specificities/rcp/editors/SpecificitiesEditor.java	(revision 3996)
+++ TXM/trunk/bundles/org.txm.specificities.rcp/src/org/txm/specificities/rcp/editors/SpecificitiesEditor.java	(revision 3997)
@@ -43,8 +43,10 @@
 import org.eclipse.swt.widgets.Spinner;
 import org.eclipse.swt.widgets.Table;
 import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.swt.widgets.Text;
 import org.txm.Toolbox;
 import org.txm.core.messages.TXMCoreMessages;
+import org.txm.core.preferences.TBXPreferences;
 import org.txm.core.preferences.TXMPreferences;
 import org.txm.core.results.Parameter;
 import org.txm.rcp.editors.TXMEditor;
@@ -121,6 +123,11 @@
 	@Parameter(key = TXMPreferences.V_MAX)
 	protected Spinner vMaxSpinner;
 	
+	/**
+	 * Maximum number of lines filtering.
+	 */
+	@Parameter(key = SpecificitiesPreferences.CONTRAST_SCRIPT)
+	protected Text modeText;
 	
 	@Override
 	public void _createPartControl() {
@@ -151,6 +158,11 @@
 			this.maxScoreFilter.setMinimum(0);
 			this.maxScoreFilter.setMaximum(100000);
 			
+			// change statistical computing
+			if (TBXPreferences.getInstance().getBoolean(TBXPreferences.EXPERT_USER)) {
+//				modeText = new Text(extendedParametersArea, SWT.BORDER); // not yet ready
+			}
+			
 			// Result area
 			GLComposite resultArea = this.getResultArea();
 			
Index: TXM/trunk/bundles/org.txm.specificities.core/src/org/txm/specificities/core/functions/Specificities.java
===================================================================
--- TXM/trunk/bundles/org.txm.specificities.core/src/org/txm/specificities/core/functions/Specificities.java	(revision 3996)
+++ TXM/trunk/bundles/org.txm.specificities.core/src/org/txm/specificities/core/functions/Specificities.java	(revision 3997)
@@ -42,7 +42,6 @@
 import org.txm.core.messages.TXMCoreMessages;
 import org.txm.core.preferences.TXMPreferences;
 import org.txm.core.results.Parameter;
-import org.txm.core.results.TXMParameters;
 import org.txm.core.results.TXMResult;
 import org.txm.index.core.functions.PartitionIndex;
 import org.txm.lexicaltable.core.functions.LexicalTable;
@@ -61,6 +60,7 @@
 import org.txm.statsengine.r.core.RWorkspace;
 import org.txm.statsengine.r.core.exceptions.RWorkspaceException;
 import org.txm.utils.TXMProgressMonitor;
+import org.txm.utils.io.IOUtils;
 import org.txm.utils.logger.Log;
 
 /**
@@ -74,46 +74,46 @@
  * 
  */
 public class Specificities extends TXMResult implements RResult {
-	
+
 	public static int MAXSPECIF = 1000;
-	
+
 	/** The indices. */
 	private double[][] indices;
-	
+
 	/** The rowindex. */
 	private int[] rowindex = null;
-	
+
 	/** The colindex. */
 	private int[] colindex = null;
-	
+
 	/** The frequencies. */
 	private int[][] frequencies = null;
-	
+
 	/** The colnames. */
 	private List<String> colnames = null;
-	
+
 	/** The rownames. */
 	private List<String> rownames = null;
-	
+
 	/** The symbol. */
 	private String symbol;
-	
+
 	/** The writer. */
 	@Deprecated
 	private BufferedWriter writer;
-	
+
 	/**
 	 * The table. contains **all** the corpus data necessary to compute the
 	 * Specificities
 	 */
 	private LexicalTable lexicalTable;
-	
+
 	/**
 	 * Maximum score.
 	 */
 	@Parameter(key = SpecificitiesPreferences.MAX_SCORE)
 	protected int pMaxScore;
-	
+
 	/**
 	 * Minimum score.
 	 */
@@ -121,6 +121,12 @@
 	protected int pMinScore;
 	
 	/**
+	 * Minimum score.
+	 */
+	@Parameter(key = SpecificitiesPreferences.CONTRAST_SCRIPT)
+	protected String pContrastScript;
+
+	/**
 	 * Creates a not computed Specificities.
 	 * 
 	 * @param parent
@@ -128,7 +134,7 @@
 	public Specificities(LexicalTable parent) {
 		super(parent);
 	}
-	
+
 	/**
 	 * Creates a not computed Specificities.
 	 * 
@@ -137,7 +143,7 @@
 	public Specificities(String parametersNodePath) {
 		this(parametersNodePath, null);
 	}
-	
+
 	/**
 	 * Creates a not computed Specificities.
 	 *
@@ -146,39 +152,57 @@
 	public Specificities(String parametersNodePath, LexicalTable parent) {
 		super(parametersNodePath, parent);
 	}
-	
+
 	@Override
 	public boolean loadParameters() {
 		this.lexicalTable = (LexicalTable) this.parent;
 		return true;
 	}
-	
+
 	@Override
 	public boolean saveParameters() {
 		// nothing to do
 		return true;
 	}
-	
+
 	@Override
 	protected boolean _compute(TXMProgressMonitor monitor) throws Exception {
-		
+
 		this.frequencies = null;
-		
+
 		// delete the specificities selection chart children since they can not be valid anymore if the unit property has changed
 		// FIXME: doesn't work
-//		if (this.lexicalTable.hasParameterChanged(TBXPreferences.UNIT_PROPERTY)) {
-//			this.deleteChildren(SpecificitiesSelection.class);
-//		}
-		
+		//		if (this.lexicalTable.hasParameterChanged(TBXPreferences.UNIT_PROPERTY)) {
+		//			this.deleteChildren(SpecificitiesSelection.class);
+		//		}
+
 		ILexicalTable data = this.lexicalTable.getData();
-		SpecificitiesR rSpecificities = new SpecificitiesR(data);
-		double[][] specIndex = rSpecificities.getScores();
-		
-		init(rSpecificities.getSymbol(), specIndex);
-		
+		double[][] specIndex = null;
+		if ("specif".equals(pContrastScript)) {
+			
+			SpecificitiesR rSpecificities = new SpecificitiesR(data);
+			specIndex = rSpecificities.getScores();
+			init(rSpecificities.getSymbol(), specIndex);
+		} else if ("relative".equals(pContrastScript)) {
+			
+			String script = "apply("+this.lexicalTable.getRSymbol()+", 2, function(x) x/sum(x)*100)";
+			specIndex = RWorkspace.getRWorkspaceInstance().eval(script).asDoubleMatrix();
+			init(lexicalTable.getRSymbol()+"Rel", specIndex);
+		} else if (pContrastScript.contains("SYMBOL")) {
+			
+			String script = pContrastScript.replace("SYMBOL", this.lexicalTable.getRSymbol());
+			specIndex = RWorkspace.getRWorkspaceInstance().eval(script).asDoubleMatrix();
+			init(lexicalTable.getRSymbol()+"Rel", specIndex);
+		} else if (pContrastScript.toUpperCase().endsWith(".R") && new File(pContrastScript).exists()) {
+			
+			String script = IOUtils.getText(new File(pContrastScript)).replace("SYMBOL", this.lexicalTable.getRSymbol());
+			specIndex = RWorkspace.getRWorkspaceInstance().eval(script).asDoubleMatrix();
+			init(lexicalTable.getRSymbol()+"Rel", specIndex);
+		}
+
 		return true;
 	}
-	
+
 	/**
 	 * Instantiates a new specificities result.
 	 *
@@ -187,7 +211,7 @@
 	 * @throws Exception the stat exception
 	 */
 	protected void init(String symbol, double[][] specIndex) throws Exception {
-		
+
 		if (this.symbol != null && this.symbol.equals(symbol)) {
 			try {
 				RWorkspace.getRWorkspaceInstance().removeVariableFromWorkspace(this.symbol);
@@ -197,12 +221,12 @@
 			}
 		}
 		this.symbol = symbol;
-		
+
 		if (this.lexicalTable == null) {
 			throw new IllegalArgumentException(SpecificitiesCoreMessages.theLexicalTableCannotBeNull);
 		}
 		// this.name = name;
-		
+
 		if (specIndex == null || specIndex.length == 0) {
 			throw new IllegalArgumentException(SpecificitiesCoreMessages.noSpecificitiesIndexArray);
 		}
@@ -211,20 +235,20 @@
 			throw new IllegalArgumentException(
 					SpecificitiesCoreMessages.theSpecificityIndexArrayDoesNotProperlyRepresentAMatrix);
 		}
-		
+
 		this.indices = specIndex;
 		this.colnames = Arrays.asList(lexicalTable.getColNames().asStringsArray());
 		this.rownames = Arrays.asList(lexicalTable.getRowNames().asStringsArray());
-		
+
 		// filter by max&min
 		int MAX = MAXSPECIF;
 		int MIN = -MAXSPECIF;
-		
+
 		if (pMaxScore > 0) {
 			MAX = pMaxScore;
 			MIN = -pMaxScore;
 		}
-		
+
 		// fixing max and min score values
 		for (int i = 0; i < indices.length; i++) {
 			for (int j = 0; j < indices[i].length; j++) {
@@ -236,14 +260,14 @@
 				}
 			}
 		}
-		
+
 		if (this.rownames != null && this.rownames.size() != 0) {
 			if (this.rownames.size() != specIndex.length) {
 				throw new IllegalArgumentException(
 						SpecificitiesCoreMessages.bind(SpecificitiesCoreMessages.numberOfRowsWantedP0AndFoundP1Mismatch,
 								this.rownames.size(), specIndex.length));
 			}
-			
+
 			rowindex = ArrayIndex.getIndex(this.lexicalTable.getRowNames().asStringsArray(),
 					this.rownames.toArray(new String[] {}));
 			for (int i : rowindex) {
@@ -269,7 +293,7 @@
 		// }
 		// }
 	}
-	
+
 	/**
 	 * Utility method to compute the specificity index of a word in a subcorpus.
 	 * <br>
@@ -297,7 +321,7 @@
 		}
 		return 0.0d;
 	}
-	
+
 	/**
 	 * Sets the unit property.
 	 * 
@@ -306,7 +330,7 @@
 	public void setUnitProperty(WordProperty unitProperty) {
 		this.getLexicalTable().setUnitProperty(unitProperty);
 	}
-	
+
 	/**
 	 * Gets the type focus.
 	 *
@@ -315,7 +339,7 @@
 	public List<String> getTypeFocus() {
 		return rownames;
 	}
-	
+
 	/**
 	 * Gets the part focus.
 	 *
@@ -324,7 +348,7 @@
 	public List<String> getPartFocus() {
 		return colnames;
 	}
-	
+
 	/**
 	 * Gets the number of columns of the lexical table.
 	 *
@@ -346,7 +370,7 @@
 			return getColumnsNames().length;
 		}
 	}
-	
+
 	/**
 	 * Gets the sum of all the columns frequencies.
 	 *
@@ -361,7 +385,7 @@
 			return 0;
 		}
 	}
-	
+
 	/**
 	 * Gets the sums of each columns frequencies.
 	 *
@@ -370,10 +394,10 @@
 	 * @throws CqiClientException
 	 */
 	public int[] getColumnsFrequenciesSums() throws StatException, CqiClientException {
-		
+
 		if (
-		// this.lexicalTable != null &&
-		this.lexicalTable.hasBeenComputedOnce()) {
+				// this.lexicalTable != null &&
+				this.lexicalTable.hasBeenComputedOnce()) {
 			Vector colsSizes = lexicalTable.getColMarginsVector();
 			if (colindex != null) {
 				colsSizes = colsSizes.get(colindex);
@@ -411,12 +435,12 @@
 		// }
 		// }
 	}
-	
+
 	@Override
 	public LexicalTable getParent() {
 		return (LexicalTable) this.parent;
 	}
-	
+
 	/**
 	 * Gets the specificities index.
 	 * 
@@ -425,7 +449,7 @@
 	public double[][] getSpecificitesIndices() {
 		return indices;
 	}
-	
+
 	/**
 	 * Name of the type for which specificities are computed.
 	 *
@@ -435,7 +459,7 @@
 	public String[] getRowNames() throws Exception {
 		return lexicalTable.getRowNames().asStringsArray();
 	}
-	
+
 	/**
 	 * Gets the names of the lexical table columns.
 	 *
@@ -445,7 +469,7 @@
 	public String[] getColumnsNames() throws StatException {
 		return lexicalTable.getColNames().asStringsArray();
 	}
-	
+
 	/**
 	 * Gets the frequencies of all columns.
 	 *
@@ -459,7 +483,7 @@
 			// if (lexicalTable != null) {
 			// System.out.println("FROM TABLE");
 			frequencies = RWorkspace.getRWorkspaceInstance().evalToInt2D(lexicalTable.getData().getSymbol());
-			
+
 			// }
 			// else {// if table == null : subcorpus specif
 			// //System.out.println("FROM LEXICON");
@@ -487,7 +511,7 @@
 		}
 		return frequencies;
 	}
-	
+
 	/**
 	 * Gets the lexical table.
 	 *
@@ -496,7 +520,7 @@
 	public LexicalTable getLexicalTable() {
 		return lexicalTable;
 	}
-	
+
 	/**
 	 * The frequency in the whole corpus.
 	 *
@@ -511,10 +535,10 @@
 		// }
 		return formFrequencies.asIntArray();
 	}
-	
+
 	@Override
 	public boolean _toTxt(File outfile, String encoding, String colseparator, String txtseparator) throws Exception {
-		
+
 		// NK: Declared as class attribute to perform a clean if the operation is
 		// interrupted
 		// OutputStreamWriter writer;
@@ -529,7 +553,7 @@
 			org.txm.utils.logger.Log.printStackTrace(e1);
 			return false;
 		}
-		
+
 		// int[] T = getPartSize();
 		acquireSemaphore();
 		try {
@@ -568,10 +592,10 @@
 		finally {
 			releaseSemaphore();
 		}
-		
+
 		return true;
 	}
-	
+
 	/**
 	 * release the R semaphore
 	 */
@@ -579,7 +603,7 @@
 		super.resetComputingState();
 		releaseSemaphore();
 	}
-	
+
 	@Override
 	public void clean() {
 		try {
@@ -591,7 +615,7 @@
 			// TODO Auto-generated catch block
 			e.printStackTrace();
 		}
-		
+
 		try {
 			if (this.writer != null) {
 				this.writer.flush();
@@ -602,12 +626,12 @@
 			org.txm.utils.logger.Log.printStackTrace(e);
 		}
 	}
-	
+
 	@Override
 	public String getRSymbol() {
 		return this.symbol;
 	}
-	
+
 	// /**
 	// * Gets the sorted part indexes.
 	// *
@@ -643,13 +667,13 @@
 	// }
 	// return new int[0];
 	// }
-	
+
 	@Override
 	public String getName() {
 		// FIXME: SJ: to define
 		return this.getParent().getParent().getName() + TXMPreferences.PARENT_NAME_SEPARATOR + this.getSimpleName();
 	}
-	
+
 	@Override
 	public String getSimpleName() {
 		// FIXME: SJ: to define
@@ -676,12 +700,12 @@
 			return this.getEmptyName();
 		}
 	}
-	
+
 	@Override
 	public String getDetails() {
 		return NLS.bind(this.getName() + " (max score={0})", this.pMaxScore); //$NON-NLS-1$
 	}
-	
+
 	@Override
 	public String getComputingStartMessage() {
 		// from lexical table
@@ -705,44 +729,44 @@
 			}
 		}
 	}
-	
+
 	@Override
 	public boolean canCompute() {
-		
+
 		if (this.lexicalTable == null) {
 			Log.severe("Specificities.canCompute(): can not compute without a lexical table.");
 			return false;
 		}
-		
+
 		// if (this.lexicalTable.getNColumns() < 2) {
 		// Log.severe(SpecificitiesCoreMessages.ComputeError_NEED_AT_LEAST_2_PARTS);
 		// return false;
 		// }
-		
+
 		return this.lexicalTable.getProperty() != null;
 	}
-	
+
 	/**
 	 * @param maxScoreFilter the pMaxScore to set
 	 */
 	public void setMaxScoreFilter(int maxScoreFilter) {
 		this.pMaxScore = maxScoreFilter;
 	}
-	
+
 	/**
 	 * @return the maxScore
 	 */
 	public int getMaxScore() {
 		return pMaxScore;
 	}
-	
+
 	/**
 	 * @return the unitProperty
 	 */
 	public Property getUnitProperty() {
 		return this.getLexicalTable().getProperty();
 	}
-	
+
 	@Override
 	public String getResultType() {
 		return SpecificitiesCoreMessages.RESULT_TYPE;
Index: TXM/trunk/bundles/org.txm.specificities.core/src/org/txm/specificities/core/preferences/SpecificitiesPreferences.java
===================================================================
--- TXM/trunk/bundles/org.txm.specificities.core/src/org/txm/specificities/core/preferences/SpecificitiesPreferences.java	(revision 3996)
+++ TXM/trunk/bundles/org.txm.specificities.core/src/org/txm/specificities/core/preferences/SpecificitiesPreferences.java	(revision 3997)
@@ -23,6 +23,7 @@
 	 * Display format.
 	 */
 	public static final String FORMAT = "format"; //$NON-NLS-1$
+	public static final String CONTRAST_SCRIPT = "contrast_mode"; //$NON-NLS-1$
 	
 	// charts
 	public static final String CHART_GROUP_BY_LINES = "chart_group_by_lines"; //$NON-NLS-1$
@@ -50,6 +51,7 @@
 		Preferences preferences = this.getDefaultPreferencesNode();
 		
 		preferences.put(FORMAT, "%,.1f"); //$NON-NLS-1$
+		preferences.put(CONTRAST_SCRIPT, "specif");
 		preferences.putInt(MAX_SCORE, 1000);
 		
 		preferences.putBoolean(CHART_GROUP_BY_LINES, false);
