caminos-lib 0.5.2

A modular interconnection network simulator.
Documentation

use std::io::{self,Write,Read,Seek};
use std::collections::{BTreeMap};
use std::convert::TryInto;
use std::path::Path;
use std::fs::File;
//use std::rc::Rc;

use crate::config_parser::{self,ConfigurationValue,Expr};
use crate::{error,source_location};
use crate::error::*;

///Given a list of vectors, `[A1,A2,A3,A4,...]`, `Ai` beging a `Vec<T>` and second vector `b:&Vec<T>=[b1,b2,b3,b4,...]`, each `bi:T`.
///It creates a list of vectors with each combination Ai+bj.
fn vec_product<T:Clone>(a:&[Vec<T>],b:&[T]) -> Vec<Vec<T>>
{
	let mut r=vec![];
	for ae in a.iter()
	{
		for be in b.iter()
		{
			let mut new=ae.clone();
			new.push(be.clone());
			r.push(new);
		}
	}
	r
}

///Expands all the inner ConfigurationValue::Experiments given out a single ConfigurationValue::Experiments
///whose elements are free of them.
pub fn flatten_configuration_value(value:&ConfigurationValue) -> ConfigurationValue
{
	let mut names = BTreeMap::new();//name -> range
	let experiments = flatten_configuration_value_gather_names(value, &mut names);
	//println!("got names {:?}",names);
	expand_named_experiments_range(experiments,&names)
}


fn flatten_configuration_value_gather_names(value:&ConfigurationValue, names:&mut BTreeMap<String,usize>) -> ConfigurationValue
{
	match value
	{
		&ConfigurationValue::Object(ref name, ref list) =>
		{
			let mut r=vec![ vec![] ];
			for &(ref name, ref v) in list
			{
				let fv=flatten_configuration_value_gather_names(v,names);
				if let ConfigurationValue::Experiments(vlist) = fv
				{
					let factor=vlist.iter().map(|x|(name.clone(),x.clone())).collect::<Vec<(String,ConfigurationValue)>>();
					r=vec_product(&r,&factor);
				}
				else
				{
					for x in r.iter_mut()
					{
						x.push((name.clone(),fv.clone()));
					}
				}
			}
			ConfigurationValue::Experiments(r.iter().map(|values|ConfigurationValue::Object(name.clone(),values.clone())).collect())
		},
		&ConfigurationValue::Array(ref list) =>
		{
			let mut r=vec![ vec![] ];
			for v in list
			{
				let fv=flatten_configuration_value_gather_names(v,names);
				if let ConfigurationValue::Experiments(vlist) = fv
				{
					//let factor=vlist.iter().map(|x|x.clone()).collect::<Vec<ConfigurationValue>>();
					//r=vec_product(&r,&factor);
					r=vec_product(&r,&vlist);
				}
				else
				{
					for x in r.iter_mut()
					{
						x.push(fv.clone());
					}
				}
			}
			ConfigurationValue::Experiments(r.iter().map(|values|ConfigurationValue::Array(values.clone())).collect())
		},
		&ConfigurationValue::Experiments(ref experiments) =>
		{
			let mut r:Vec<ConfigurationValue>=vec![];
			for experiment in experiments
			{
				let flat=flatten_configuration_value_gather_names(experiment,names);
				if let ConfigurationValue::Experiments(ref flist) = flat
				{
					r.extend(flist.iter().cloned());
				}
				else
				{
					r.push(flat);
				}
			}
			ConfigurationValue::Experiments(r)
		},
		&ConfigurationValue::NamedExperiments(ref name, ref experiments) =>
		{
			if let Some(&size) = names.get(name)
			{
				if size != experiments.len()
				{
					panic!("{}! has different lengths {} vs {}",name,size,experiments.len());
				}
			}
			else
			{
				names.insert(name.to_string(),experiments.len());
			}
			value.clone()
		},
		&ConfigurationValue::Where(ref v, ref _expr) =>
		{
			flatten_configuration_value_gather_names(v,names)//FIXME, filterby expr
		},
		_ => value.clone(),
	}
}

fn expand_named_experiments_range(experiments:ConfigurationValue, names:&BTreeMap<String,usize>) -> ConfigurationValue
{
	let mut r = experiments;
	for name in names.keys()
	{
		let size=*names.get(name).unwrap();
		let partials = (0..size).map(|index|{
			let mut context : BTreeMap<String,usize> = BTreeMap::new();
			context.insert(name.to_string(),index);
			match particularize_named_experiments_selected(&r,&context)
			{
				ConfigurationValue::Experiments(exps) => exps,
				x => vec![x],
			}
		});
		r=ConfigurationValue::Experiments(partials.flat_map(|t|t.into_iter()).collect());
	}
	r
}

fn particularize_named_experiments_selected(value:&ConfigurationValue, names:&BTreeMap<String,usize>) -> ConfigurationValue
{
	match value
	{
		&ConfigurationValue::Object(ref name, ref list) =>
		{
			let plist = list.iter().map(|(key,x)|(key.to_string(),particularize_named_experiments_selected(x,names))).collect();
			ConfigurationValue::Object(name.to_string(),plist)
		},
		&ConfigurationValue::Array(ref list) =>
		{
			let plist = list.iter().map(|x|particularize_named_experiments_selected(x,names)).collect();
			ConfigurationValue::Array(plist)
		},
		&ConfigurationValue::Experiments(ref list) =>
		{
			let plist = list.iter().map(|x|particularize_named_experiments_selected(x,names)).collect();
			ConfigurationValue::Experiments(plist)
		},
		&ConfigurationValue::NamedExperiments(ref name, ref list) =>
		{
			if let Some(&index) = names.get(name)
			{
				list[index].clone()
			}
			else
			{
				value.clone()
			}
		},
		//&ConfigurationValue::Where(ref v, ref _expr) =>
		//{
		//	flatten_configuration_value_gather_names(v,names)//FIXME, filterby expr
		//},
		_ => value.clone(),
	}
}


///Just returns a `Context{configuration:<configuration>, result:<result>}`.
/// TODO: does something still uses this. Or has all being moved to `OutputEnvironmentEntry::config`.
pub fn combine(experiment_index:usize, configuration:&ConfigurationValue, result:&ConfigurationValue) -> ConfigurationValue
{
	ConfigurationValue::Object(String::from("Context"),vec![
		(String::from("index"),ConfigurationValue::Number(experiment_index as f64)),
		(String::from("configuration"),configuration.clone()),
		(String::from("result"),result.clone()),
	])
}

/**Evaluates an expression given in a context.

For example the expression `=Alpha.beta` will return 42 for the context `Alpha{beta:42}`.

# Available functions

## Comparisons

Arguments `first` and `second`. It evaluates to `ConfigurationValue::{True,False}`.

* eq or equal
* lt

## Arithmetic

* add
* mul

Arguments `first` and `second`. It evaluates to `ConfigurationValue::Number`.

## if

Evaluates to whether its argument `true_expression` or `false_expression` depending on its `condition` argument.

## at

Evaluates to the element at `position` inside the array in `container`.

## AverageBins

Evaluates to an array smaller than the input `data`, as each `width` entries are averaged into a single one.

## FileExpression

Evaluates an `expression` including the file `filename` into the current context under the name `file_data`. For example `FileExpression{filename:"peak.cfg",expression:at{container:file_data,position:index}}.maximum_value` to access into the `peak.cfg` file and evaluating the expression `at{container:file_data,position:index}`. This example assume that `peak.cfg` read into `file_data` is an array and can be accessed by `index`, the entry of the associated execution. The value returned by `FileExpression` is then accessed to its `maximum_value` field.


**/
pub fn evaluate(expr:&Expr, context:&ConfigurationValue, path:&Path) -> Result<ConfigurationValue,Error>
//pub fn evaluate<'a,C:'a,V:Into<BorrowedConfigurationValue<'a,C>>>(expr:&Expr, context:V, path:&Path) -> ConfigurationValue
//	where &'a C:Into<ConfigurationValue>, C:Clone
{
	//let context : BorrowedConfigurationValue<C> = context.into();
	match expr
	{
		&Expr::Equality(ref a,ref b) =>
		{
			let va=evaluate(a,context,path)?;
			let vb=evaluate(b,context,path)?;
			if va==vb
			{
				Ok(ConfigurationValue::True)
			}
			else
			{
				Ok(ConfigurationValue::False)
			}
		},
		&Expr::Literal(ref s) => Ok(ConfigurationValue::Literal(s.clone())),
		&Expr::Number(f) => Ok(ConfigurationValue::Number(f)),
		&Expr::Ident(ref s) => match context
		{
			ConfigurationValue::Object(ref _name, ref attributes) =>
			{
				for &(ref attr_name,ref attr_value) in attributes.iter()
				{
					if attr_name==s
					{
						return Ok(attr_value.clone());
					}
				};
				//panic!("There is not attribute {} in {}",s,context);
				return Err(error!(bad_argument).with_message(format!("There is not attribute {} in {}",s,context)));
			},
			_ => panic!("Cannot evaluate identifier in non-object"),
		},
		&Expr::Member(ref expr, ref attribute) =>
		{
			let value=evaluate(expr,context,path)?;
			match value
			{
				ConfigurationValue::Object(ref _name, ref attributes) =>
				{
					for &(ref attr_name,ref attr_value) in attributes.iter()
					{
						if attr_name==attribute
						{
							return Ok(attr_value.clone());
						}
					};
					//panic!("There is not member {} in {}",attribute,value);
					return Err(error!(bad_argument).with_message(format!("There is no member {attribute} in {value}")));
				},
				//_ => panic!("There is no member {} in {}",attribute,value),
				_=> return Err(error!(bad_argument).with_message(format!("{value} is not an object, so it does not have member {attribute}"))),
			}
		},
		&Expr::Parentheses(ref expr) => evaluate(expr,context,path),
		&Expr::Name(ref expr) =>
		{
			let value=evaluate(expr,context,path)?;
			match value
			{
				ConfigurationValue::Object(ref name, ref _attributes) => Ok(ConfigurationValue::Literal(name.clone())),
				_ => panic!("{} has no name as it is not object",value),
			}
		},
		&Expr::FunctionCall(ref function_name, ref arguments) =>
		{
			match function_name.as_ref()
			{
				"eq" | "equal" =>
				{
					let mut first=None;
					let mut second=None;
					for (key,val) in arguments
					{
						match key.as_ref()
						{
							"first" =>
							{
								first=Some(evaluate(val,context,path)?);
							},
							"second" =>
							{
								second=Some(evaluate(val,context,path)?);
							},
							_ => panic!("unknown argument `{}' for function `{}'",key,function_name),
						}
					}
					let first=first.expect("first argument of lt not given.");
					let second=second.expect("second argument of lt not given.");
					//allow any type
					Ok(if first==second { ConfigurationValue::True } else { ConfigurationValue::False })
				}
				"lt" =>
				{
					let mut first=None;
					let mut second=None;
					for (key,val) in arguments
					{
						match key.as_ref()
						{
							"first" =>
							{
								first=Some(evaluate(val,context,path)?);
							},
							"second" =>
							{
								second=Some(evaluate(val,context,path)?);
							},
							_ => panic!("unknown argument `{}' for function `{}'",key,function_name),
						}
					}
					let first=first.expect("first argument of lt not given.");
					let second=second.expect("second argument of lt not given.");
					let first=match first
					{
						ConfigurationValue::Number(x) => x,
						_ => panic!("first argument of lt evaluated to a non-number ({}:?)",first),
					};
					let second=match second
					{
						ConfigurationValue::Number(x) => x,
						_ => panic!("second argument of lt evaluated to a non-number ({}:?)",second),
					};
					Ok(if first<second { ConfigurationValue::True } else { ConfigurationValue::False })
				}
				"if" =>
				{
					let mut condition=None;
					let mut true_expression=None;
					let mut false_expression=None;
					for (key,val) in arguments
					{
						match key.as_ref()
						{
							"condition" =>
							{
								condition=Some(evaluate(val,context,path)?);
							},
							"true_expression" =>
							{
								true_expression=Some(evaluate(val,context,path)?);
							},
							"false_expression" =>
							{
								false_expression=Some(evaluate(val,context,path)?);
							},
							_ => panic!("unknown argument `{}' for function `{}'",key,function_name),
						}
					}
					let condition=condition.expect("condition argument of if not given.");
					let true_expression=true_expression.expect("true_expression argument of if not given.");
					let false_expression=false_expression.expect("false_expression argument of if not given.");
					let condition = match condition
					{
						ConfigurationValue::True => true,
						ConfigurationValue::False => false,
						_ => panic!("if function condition did not evaluate into a Boolean value."),
					};
					Ok(if condition { true_expression } else { false_expression })
				}
				"add" | "plus" =>
				{
					let mut first=None;
					let mut second=None;
					for (key,val) in arguments
					{
						match key.as_ref()
						{
							"first" =>
							{
								first=Some(evaluate(val,context,path)?);
							},
							"second" =>
							{
								second=Some(evaluate(val,context,path)?);
							},
							_ => panic!("unknown argument `{}' for function `{}'",key,function_name),
						}
					}
					let first=first.expect("first argument of and not given.");
					let second=second.expect("second argument of and not given.");
					let first=match first
					{
						ConfigurationValue::Number(x) => x,
						_ => panic!("first argument of {} evaluated to a non-number ({}:?)",function_name,first),
					};
					let second=match second
					{
						ConfigurationValue::Number(x) => x,
						_ => panic!("second argument of {} evaluated to a non-number ({}:?)",function_name,second),
					};
					Ok(ConfigurationValue::Number(first+second))
				}
				"sub" | "minus" =>
				{
					let mut first=None;
					let mut second=None;
					for (key,val) in arguments
					{
						match key.as_ref()
						{
							"first" =>
							{
								first=Some(evaluate(val,context,path)?);
							},
							"second" =>
							{
								second=Some(evaluate(val,context,path)?);
							},
							_ => panic!("unknown argument `{}' for function `{}'",key,function_name),
						}
					}
					let first=first.expect("first argument of and not given.");
					let second=second.expect("second argument of and not given.");
					let first=match first
					{
						ConfigurationValue::Number(x) => x,
						_ => panic!("first argument of {} evaluated to a non-number ({}:?)",function_name,first),
					};
					let second=match second
					{
						ConfigurationValue::Number(x) => x,
						_ => panic!("second argument of {} evaluated to a non-number ({}:?)",function_name,second),
					};
					Ok(ConfigurationValue::Number(first-second))
				}
				"mul" =>
				{
					let mut first=None;
					let mut second=None;
					for (key,val) in arguments
					{
						match key.as_ref()
						{
							"first" =>
							{
								first=Some(evaluate(val,context,path)?);
							},
							"second" =>
							{
								second=Some(evaluate(val,context,path)?);
							},
							_ => panic!("unknown argument `{}' for function `{}'",key,function_name),
						}
					}
					let first=first.expect("first argument of and not given.");
					let second=second.expect("second argument of and not given.");
					let first=match first
					{
						ConfigurationValue::Number(x) => x,
						_ => panic!("first argument of {} evaluated to a non-number ({}:?)",function_name,first),
					};
					let second=match second
					{
						ConfigurationValue::Number(x) => x,
						_ => panic!("second argument of {} evaluated to a non-number ({}:?)",function_name,second),
					};
					Ok(ConfigurationValue::Number(first*second))
				}
				"div" =>
				{
					let mut first=None;
					let mut second=None;
					for (key,val) in arguments
					{
						match key.as_ref()
						{
							"first" =>
							{
								first=Some(evaluate(val,context,path)?);
							},
							"second" =>
							{
								second=Some(evaluate(val,context,path)?);
							},
							_ => panic!("unknown argument `{}' for function `{}'",key,function_name),
						}
					}
					let first=first.expect("first argument of and not given.");
					let second=second.expect("second argument of and not given.");
					let first=match first
					{
						ConfigurationValue::Number(x) => x,
						_ => panic!("first argument of {} evaluated to a non-number ({}:?)",function_name,first),
					};
					let second=match second
					{
						ConfigurationValue::Number(x) => x,
						_ => panic!("second argument of {} evaluated to a non-number ({}:?)",function_name,second),
					};
					Ok(ConfigurationValue::Number(first/second))
				}
				"log" =>
				{
					let mut arg=None;
					let mut base=None;
					for (key,val) in arguments
					{
						match key.as_ref()
						{
							"arg" =>
							{
								arg=Some(evaluate(val,context,path)?);
							},
							"base" =>
							{
								base=Some(evaluate(val,context,path)?);
							},
							_ => panic!("unknown argument `{}' for function `{}'",key,function_name),
						}
					}
					let arg=arg.expect("arg argument of and not given.");
					let arg=match arg
					{
						ConfigurationValue::Number(x) => x,
						_ => panic!("arg argument of {} evaluated to a non-number ({}:?)",function_name,arg),
					};
					let base=match base
					{
						None => 1f64.exp(),
						Some(ConfigurationValue::Number(x)) => x,
						Some(other) => panic!("base argument of {} evaluated to a non-number ({}:?)",function_name,other),
					};
					Ok(ConfigurationValue::Number(arg.log(base)))
				}
				"pow" =>
				{
					let mut exponent=None;
					let mut base=None;
					for (key,val) in arguments
					{
						match key.as_ref()
						{
							"exponent" =>
							{
								exponent=Some(evaluate(val,context,path)?);
							},
							"base" =>
							{
								base=Some(evaluate(val,context,path)?);
							},
							_ => panic!("unknown argument `{}' for function `{}'",key,function_name),
						}
					}
					let exponent=exponent.expect("exponent argument of and not given.");
					let exponent=match exponent
					{
						ConfigurationValue::Number(x) => x,
						_ => panic!("exponent argument of {} evaluated to a non-number ({}:?)",function_name,exponent),
					};
					let base=match base
					{
						None => 1f64.exp(),
						Some(ConfigurationValue::Number(x)) => x,
						Some(other) => panic!("base argument of {} evaluated to a non-number ({}:?)",function_name,other),
					};
					Ok(ConfigurationValue::Number(base.powf(exponent)))
				}
				"at" =>
				{
					let mut container=None;
					let mut position=None;
					let mut else_value=ConfigurationValue::None;
					for (key,val) in arguments
					{
						match key.as_ref()
						{
							"container" => container=Some(evaluate(val,context,path)?),
							"position" => position=Some(evaluate(val,context,path)?),
							"else" => else_value = evaluate(val,context,path)?,
							_ => panic!("unknown argument `{}' for function `{}'",key,function_name),
						}
					}
					let container=container.expect("container argument of at not given.");
					let position=position.expect("position argument of at not given.");
					let container=match container
					{
						ConfigurationValue::Array(a) => a,
						_ => panic!("conatiner argument of at evaluated to a non-array ({}:?)",container),
					};
					let position=match position
					{
						ConfigurationValue::Number(x) => x as usize,
						_ => panic!("position argument of at evaluated to a non-number ({}:?)",position),
					};
					//container[position].clone()
					if position < container.len() {
						Ok(container[position].clone())
					} else {
						Ok(else_value)
					}
				}
				"AverageBins" =>
				{
					let mut data = None;
					let mut width = None;
					for (key,val) in arguments
					{
						match key.as_ref()
						{
							"data" =>
							{
								data=Some(evaluate(val,context,path)?);
							},
							"width" =>
							{
								width=Some(evaluate(val,context,path)?);
							},
							_ => panic!("unknown argument `{}' for function `{}'",key,function_name),
						}
					}
					let data=data.expect("data argument of at not given.");
					let width=width.expect("width argument of at not given.");
					let data=match data
					{
						ConfigurationValue::Array(a) => a,
						_ => panic!("first argument of AverageBins evaluated to a non-array ({}:?)",data),
					};
					let width=match width
					{
						ConfigurationValue::Number(x) => x as usize,
						_ => panic!("width argument of AverageBins evaluated to a non-number ({}:?)",width),
					};
					//TODO: do we want to include incomplete bins?
					//let n = (data.len()+width-1)/width;
					let n = data.len()/width;
					//let mut result = Vec::with_capacity(n);
					let mut iter = data.into_iter();
					let result =(0..n).map(|_|{
						let mut total = 0f64;
						for _ in 0..width
						{
							total += match iter.next().unwrap()
							{
								ConfigurationValue::Number(x) => x,
								//x => panic!("AverageBins received {:?}",x),
								_ => std::f64::NAN,
							}
						}
						ConfigurationValue::Number(total/width as f64)
					}).collect();
					Ok(ConfigurationValue::Array(result))
				}
				"FileExpression" =>
				{
					let mut filename = None;
					let mut expression = None;
					for (key,val) in arguments
					{
						match key.as_ref()
						{
							"filename" =>
							{
								filename=Some(evaluate(val,context,path)?);
							},
							"expression" =>
							{
								expression = Some(val);
							},
							_ => panic!("unknown argument `{}' for function `{}'",key,function_name),
						}
					}
					let filename=filename.expect("filename argument of at not given.");
					let expression=expression.expect("expression argument of at not given.");
					let filename = match filename
					{
						ConfigurationValue::Literal(s) => s,
						_ => panic!("filename argument of FileExpression evaluated to a non-literal ({}:?)",filename),
					};
					let file_path = path.join(filename);
					let file_data={
						let mut data = ConfigurationValue::None;
						let mut file_contents = String::new();
						let mut cfg_file=File::open(&file_path).expect("data file could not be opened");
						let mut try_raw = true;
						let mut try_binary = false;
						if try_raw
						{
							match cfg_file.read_to_string(&mut file_contents)
							{
								Ok(_) => (),
								Err(_e) => {
									//println!("Got error {} when reading",e);//too noisy
									try_raw = false;
									try_binary = true;
								}
							}
						}
						if try_raw
						{
							let parsed_file=match config_parser::parse(&file_contents)
							{
								Err(x) => panic!("error parsing data file {:?}: {:?}",file_path,x),
								Ok(x) => x,
							};
							data = match parsed_file
							{
								config_parser::Token::Value(value) =>
								{
									value
								},
								_ => panic!("Not a value. Got {:?}",parsed_file),
							}
						}
						if try_binary
						{
							let mut contents = vec![];
							cfg_file.rewind().expect("some problem rewinding data file");
							cfg_file.read_to_end(&mut contents).expect("something went wrong reading binary data");
							data=config_from_binary(&contents,0).expect("something went wrong while deserializing binary data");
						}
						data
					};
					let context = match context{
						ConfigurationValue::Object(name, data) =>
						{
							let mut content = data.clone();
							content.push( (String::from("file_data"), file_data ) );
							ConfigurationValue::Object(name.to_string(),content)
						},
						_ => panic!("wrong context"),
					};
					evaluate( expression, &context, path)
				}
				"map" =>
				{
					let mut container = None;
					let mut binding = None;
					let mut expression = None;
					for (key,val) in arguments
					{
						match key.as_ref()
						{
							"container" =>
							{
								container=Some(evaluate(val,context,path)?);
							},
							"binding" =>
							{
								binding=Some(evaluate(val,context,path)?);
							},
							"expression" =>
							{
								expression = Some(val);
							},
							_ => panic!("unknown argument `{}' for function `{}'",key,function_name),
						}
					}
					let container=container.expect("container argument of at not given.");
					let expression=expression.expect("expression argument of at not given.");
					let binding=match binding
					{
						None => "x".to_string(),
						Some(ConfigurationValue::Literal(s)) => s,
						Some(other) => panic!("{:?} cannot be used as binding variable",other),
					};
					let container=match container
					{
						ConfigurationValue::Array(a) => a,
						ConfigurationValue::None => return Ok(ConfigurationValue::None),
						_ => panic!("first argument of at evaluated to a non-array ({}:?)",container),
					};
					//let container = container.into_iter().map(|item|{
					//	let context = match context{
					//		ConfigurationValue::Object(name, data) =>
					//		{
					//			let mut content = data.clone();
					//			content.push( (binding.clone(), item ) );
					//			ConfigurationValue::Object(name.to_string(),content)
					//		},
					//		_ => panic!("wrong context"),
					//	};
					//	evaluate( expression, &context, path)
					//}).collect();
					let mut context = match context{
						ConfigurationValue::Object(name, data) =>
						{
							let mut content = data.clone();
							content.push( (binding.clone(), ConfigurationValue::None ) );
							ConfigurationValue::Object(name.to_string(),content)
						},
						_ => panic!("wrong context"),
					};
					let container = container.into_iter().map(|item|{
						match &mut context{
							ConfigurationValue::Object(_name, data) =>
							{
								data.last_mut().unwrap().1 = item;
							},
							_ => panic!("wrong context"),
						};
						evaluate( expression, &context, path)
					}).collect::<Result<_,_>>()?;
					Ok(ConfigurationValue::Array(container))
				}
				"slice" =>
				{
					let mut container = None;
					let mut start = None;
					let mut end = None;
					for (key,val) in arguments
					{
						match key.as_ref()
						{
							"container" =>
							{
								container=Some(evaluate(val,context,path)?);
							},
							"start" =>
							{
								start= match evaluate(val,context,path)?
								{
									ConfigurationValue::Number(n) => Some(n as usize),
									_ => panic!("the start argument of slice must be a number"),
								};
							},
							"end" =>
							{
								end= match evaluate(val,context,path)?
								{
									ConfigurationValue::Number(n) => Some(n as usize),
									_ => panic!("the start argument of slice must be a number"),
								};
							},
							_ => panic!("unknown argument `{}' for function `{}'",key,function_name),
						}
					}
					let container=container.expect("container argument of at not given.");
					let container=match container
					{
						ConfigurationValue::Array(a) => a,
						_ => panic!("first argument of at evaluated to a non-array ({}:?)",container),
					};
					let start=start.unwrap_or(0);
					let end=match end
					{
						None => container.len(),
						Some(n) => n.min(container.len()),
					};
					let container = container[start..end].to_vec();
					Ok(ConfigurationValue::Array(container))
				}
				"sort" =>
				{
					let mut container = None;
					let mut expression = None;
					let mut binding = None;
					for (key,val) in arguments
					{
						match key.as_ref()
						{
							"container" =>
							{
								container=Some(evaluate(val,context,path)?);
							},
							"expression" =>
							{
								expression=Some(val);
							},
							"binding" =>
							{
								binding=Some(evaluate(val, context, path)?);
							},
							_ => panic!("unknown argument `{}' for function `{}'",key,function_name),
						}
					}
					let container=container.expect("container argument of at not given.");
					let mut container=match container
					{
						ConfigurationValue::Array(a) => a,
						_ => panic!("first argument of at evaluated to a non-array ({}:?)",container),
					};
					let f:Box< dyn Fn(&ConfigurationValue,&ConfigurationValue)->std::cmp::Ordering > = match expression
					{
						None => Box::new(|a:&ConfigurationValue,b:&ConfigurationValue|a.partial_cmp(b).unwrap()),
						Some(expr) =>
						{
							let binding=match binding
							{
								None => "x".to_string(),
								Some(ConfigurationValue::Literal(s)) => s,
								Some(other) => panic!("{:?} cannot be used as binding variable",other),
							};
							Box::new(move |a,b|{
								let context = match context
								{
									ConfigurationValue::Object(name, data) =>
									{
										let mut content = data.clone();
										content.push( (binding.clone(), a.clone() ) );
										ConfigurationValue::Object(name.to_string(),content)
									},
									_ => panic!("wrong context"),
								};
								let a = evaluate(expr, &context, path).unwrap_or_else(|e|panic!("error {} in sort function",e));
								let context = match context
								{
									ConfigurationValue::Object(name, data) =>
									{
										let mut content = data;
										content.push( (binding.clone(), b.clone() ) );
										ConfigurationValue::Object(name,content)
									},
									_ => panic!("wrong context"),
								};
								let b = evaluate(expr, &context, path).unwrap_or_else(|e|panic!("error {} in sort function",e));
								a.partial_cmp(&b).unwrap()
							})
						},
					};
					//FIXME: It does not seem possibly to propragate errors in the closure through any `sort_by` function.
					//container.sort_by(|a,b|a.partial_cmp(b).unwrap());
					container.sort_by(f);
					Ok(ConfigurationValue::Array(container))
				}
				"last" =>
				{
					let mut container = None;
					for (key,val) in arguments
					{
						match key.as_ref()
						{
							"container" =>
							{
								container=Some(evaluate(val,context,path)?);
							},
							_ => panic!("unknown argument `{}' for function `{}'",key,function_name),
						}
					}
					let container=container.unwrap_or_else(||panic!("container argument of {} not given.",function_name));
					let container=match container
					{
						ConfigurationValue::Array(a) => a,
						_ => panic!("first argument of at evaluated to a non-array ({}:?)",container),
					};
					Ok(container.last().expect("there is not last element in the array").clone())
				}
				"number_or" =>
				// Returns the argument unchanged if it is a number, otherwise return the default value.
				{
					let mut arg = None;
					let mut default = None;
					for (key,val) in arguments
					{
						match key.as_ref()
						{
							"arg" =>
							{
								arg=Some(evaluate(val,context,path)?);
							},
							"default" =>
							{
								default=Some(evaluate(val,context,path));
							},
							_ => panic!("unknown argument `{}' for function `{}'",key,function_name),
						}
					}
					let arg=arg.expect("arg argument of number_or not given.");
					let default=default.expect("default argument of number_or not given.");
					match arg
					{
						ConfigurationValue::Number(n) => Ok(ConfigurationValue::Number(n)),
						_ => default,
					}
				}
				"filter" =>
				{
					let mut container = None;
					let mut expression = None;
					let mut binding = None;
					for (key,val) in arguments
					{
						match key.as_ref()
						{
							"container" =>
							{
								container=Some(evaluate(val,context,path)?);
							},
							"expression" =>
							{
								expression=Some(val);
							},
							"binding" =>
							{
								binding=Some(evaluate(val, context, path)?);
							},
							_ => panic!("unknown argument `{}' for function `{}'",key,function_name),
						}
					}
					let container=container.expect("container argument of filter not given.");
					let expression=expression.expect("expression argument of filter not given.");
					let binding = match binding
					{
						None => String::from("x"),
						Some(ConfigurationValue::Literal(s)) => s,
						Some(b) => panic!("binding argument of filter evaluated to a non-literal ({}:?)",b),
					};
					let container=match container
					{
						ConfigurationValue::Array(a) => a,
						_ => panic!("first argument of at evaluated to a non-array ({:?})",container),
					};
					let mut context = match context{
						ConfigurationValue::Object(name, data) =>
						{
							let mut content = data.clone();
							content.push( (binding.clone(), ConfigurationValue::None ) );
							ConfigurationValue::Object(name.to_string(),content)
						},
						_ => panic!("wrong context"),
					};
					let container = container.into_iter().filter_map(|item|
					{
						match &mut context{
							ConfigurationValue::Object(_name, data) =>
							{
								data.last_mut().unwrap().1 = item.clone();
							},
							_ => panic!("wrong context"),
						};
						let b = evaluate(expression,&context,path);
						match b
						{
							Err(_) => Some(b),
							Ok(ConfigurationValue::True) => Some(Ok(item)),
							Ok(ConfigurationValue::False) => None,
							b => Some(Err(error!(bad_argument)
								.with_message(format!("filter expression evaluated to a non-Boolean ({:?})",b))
							)),
						}
					}).collect::<Result<_,_>>()?;
					Ok(ConfigurationValue::Array(container))
				}
				"try" =>
				{
					let mut expression=None;
					let mut default = None;
					for (key,val) in arguments
					{
						match key.as_ref()
						{
							"expression" =>
							{
								//condition=Some(evaluate(val,context,path)?);
								expression = Some(val);
							},
							"default" =>
							{
								//default=Some(evaluate(val,context,path)?);
								default = Some(val);
							},
							_ => panic!("unknown argument `{}' for function `{}'",key,function_name),
						}
					}
					let expression=expression.expect("expression argument of number_or not given.");
					let value = match evaluate(expression,context,path)
					{
						Ok(value) => value,
						Err(_) => if let Some(d) = default {
								evaluate(d,context,path)?
							} else {
								ConfigurationValue::None
							},
					};
					Ok(value)
				}
				_ => panic!("Unknown function `{}'",function_name),
			}
		}
	}
}

/// Evaluate some expressions inside a ConfigurationValue
pub fn reevaluate(value:&ConfigurationValue, context:&ConfigurationValue, path:&Path) -> Result<ConfigurationValue,Error>
{
	//if let &ConfigurationValue::Expression(ref expr)=value
	//{
	//	evaluate(expr,context,path)
	//}
	//else
	//{
	//	value.clone()
	//}
	match value
	{
		&ConfigurationValue::Expression(ref expr) => evaluate(expr,context,path),
		&ConfigurationValue::Array(ref l) => Ok(ConfigurationValue::Array(
			l.iter()
				.map(|e|reevaluate(e,context,path))
				.collect::<Result<_,_>>()?
		)),
		_ => Ok(value.clone()),
	}
}

///Get a vector of `f32` from a vector of `ConfigurationValue`s, skipping non-numeric values.
pub fn values_to_f32(list:&[ConfigurationValue]) -> Vec<f32>
{
	list.iter().filter_map(|v|match v{
		&ConfigurationValue::Number(f) => Some(f as f32),
		_ => None
	}).collect()
}

///Get a vector of `f32` from a vector of `ConfigurationValue`s, skipping non-numeric values.
///It also counts the number of good, `None`, and other values.
pub fn values_to_f32_with_count(list:&Vec<ConfigurationValue>) -> (Vec<f32>,usize,usize,usize)
{
	let mut values = Vec::with_capacity(list.len());
	let mut good_count=0;
	let mut none_count=0;
	let mut other_count=0;
	for v in list
	{
		match v
		{
			&ConfigurationValue::Number(f) =>
			{
				values.push(f as f32);
				good_count+=1;
			},
			&ConfigurationValue::None => none_count+=1,
			_ => other_count+=1,
		}
	}
	(values,good_count,none_count,other_count)
}


///Convert a ConfigurationValue into a Vec<u8>.
///Intended to create binary files for result files.
pub fn config_to_binary(value:&ConfigurationValue) -> io::Result<Vec<u8>>
{
	//let mut map:BTreeMap<String,usize> = BTreeMap::new();
	//let mut vector:Vec<u8> = Vec::new();
	//config_into_binary(value,&mut vector,&mut map)?;
	//Ok(vector)
	let mut writer = BinaryConfigWriter::default();
	//writer.insert(value)?;
	writer.insert(value).unwrap_or_else(|e|panic!("error={:?} data={:?}",e,writer.vector));
	Ok(writer.take_vector())
}


#[derive(Debug,Default)]
pub struct BinaryConfigWriter
{
	vector:Vec<u8>,
	name_locations:BTreeMap<String,u32>
}

impl BinaryConfigWriter
{
	pub fn new() -> BinaryConfigWriter
	{
		Self::default()
	}
	pub fn take_vector(self) -> Vec<u8>
	{
		self.vector
	}
	///Append the binary version of a ConfigurationValue into a Vec<u8> using a map from names to locations inside the vector.
	///Returns the location at which it has been appended
	//pub fn config_into_binary(value:&ConfigurationValue, vector:&mut Vec<u8>, name_locations:&mut BTreeMap<String,usize>) -> io::Result<usize>
	pub fn insert(&mut self, value:&ConfigurationValue) -> io::Result<u32>
	{
		//Using little endian for everything, to allow moving the binary files between machines.
		//This is, we use to_le_bytes instead to_ne_bytes.
		let location:u32 = {
			//Align to 4 bytes
			const ALIGNMENT: usize = 4;
			let s:usize = self.vector.len();
			let r = s % ALIGNMENT;
			if r == 0 { s } else {
				let new = s + (ALIGNMENT-r);
				self.vector.resize(new, 0u8);
				new
			}.try_into().unwrap()
		};
		match value
		{
			&ConfigurationValue::Literal(ref name) => {
				self.vector.resize((location+2*4).try_into().unwrap(), 0u8);
				let loc:u32 = self.locate(name)?;
				let mut writer = &mut self.vector[location as usize..];
				writer.write_all(&0u32.to_le_bytes())?;
				writer.write_all(&loc.to_le_bytes())?;
				//match self.name_locations.get(name)
				//{
				//	Some(loc) =>{
				//		self.vector.write_all(&loc.to_le_bytes())?;
				//	},
				//	None =>{
				//		let loc = location+4;
				//		self.name_locations.insert(name.to_string(),loc);
				//		self.vector.write_all(&loc.to_ne_bytes())?;
				//		self.vector.write_all(name.as_bytes())?;
				//	},
				//};
			},
			&ConfigurationValue::Number(f) => {
				self.vector.write_all(&1u32.to_le_bytes())?;
				//using f: f64
				self.vector.write_all(&f.to_le_bytes())?;
			},
			&ConfigurationValue::Object(ref name, ref pairs) =>{
				let n:u32 = pairs.len().try_into().unwrap();
				let end = location + 8*n + 3*4;
				self.vector.resize(end as usize, 0u8);
				let loc:u32 = self.locate(name)?;
				//self.vector[location..].write_all(&2u32.to_le_bytes())?;
				let mut writer = &mut self.vector[location as usize..];
				//Write::write_all(&mut self.vector[location..],&2u32.to_le_bytes())?;
				writer.write_all(&2u32.to_le_bytes())?;
				//let mut writer = &mut self.vector[location + 4..];//this allows a drop for the string write before.
				writer.write_all(&loc.to_le_bytes())?;
				//match self.name_locations.get(name)
				//{
				//	Some(loc) =>{
				//		//self.vector[location+1*4..].write_all(&loc.to_le_bytes())?;
				//		writer.write_all(&loc.to_le_bytes())?;
				//	},
				//	None =>{
				//		let loc = end;
				//		self.name_locations.insert(name.to_string(),loc);
				//		writer.write_all(&loc.to_le_bytes())?;
				//		self.vector.write_all(name.as_bytes())?;
				//	},
				//};
				//let mut writer = &mut self.vector[location + 2*4..];//this allows a drop for the string write before.
				writer.write_all(&n.to_le_bytes())?;
				let base:usize = (location +3*4).try_into().unwrap();
				for (index,(key,val)) in pairs.iter().enumerate(){
					//write key
					let loc:u32 = self.locate(key)?;
					let mut writer = &mut self.vector[base + index*2*4 ..];
					writer.write_all(&loc.to_le_bytes())?;
					//match self.name_locations.get(key)
					//{
					//	Some(loc) =>{
					//		let mut writer = &mut self.vector[base + index*2*4 ..];
					//		writer.write_all(&loc.to_le_bytes())?;
					//	},
					//	None =>{
					//		let loc = self.vector.len();
					//		self.name_locations.insert(key.to_string(),loc);
					//		let mut writer = &mut self.vector[base + index*2*4 ..];
					//		writer.write_all(&loc.to_le_bytes())?;
					//		self.vector.write_all(key.as_bytes())?;
					//	},
					//};
					//write value
					//let loc = config_into_binary(val,self.vector,name_locations)?;
					let loc:u32 = self.insert(val)?;
					let mut writer = &mut self.vector[base + index*2*4 +4 ..];
					writer.write_all(&loc.to_le_bytes())?;
				}
			},
			&ConfigurationValue::Array(ref a) => {
				let n:u32 = a.len().try_into().unwrap();
				let end = location + 4*n + 2*4;
				self.vector.resize(end as usize, 0u8);
				let mut writer = &mut self.vector[location as usize..];
				writer.write_all(&3u32.to_le_bytes())?;
				writer.write_all(&n.to_le_bytes())?;
				let base:usize = (location +2*4).try_into().unwrap();
				for (index,val) in a.iter().enumerate(){
					let loc = self.insert(val)?;
					let mut writer = &mut self.vector[base + index*4 ..];
					writer.write_all(&loc.to_le_bytes())?;
				}
			},
			&ConfigurationValue::Experiments(ref list) => {
				let n:u32 = list.len().try_into().unwrap();
				let end = location + 4*n + 2*4;
				self.vector.resize(end as usize, 0u8);
				let mut writer = &mut self.vector[location as usize..];
				writer.write_all(&4u32.to_le_bytes())?;
				writer.write_all(&n.to_le_bytes())?;
				let base:usize = (location +2*4).try_into().unwrap();
				for (index,val) in list.iter().enumerate(){
					let loc = self.insert(val)?;
					let mut writer = &mut self.vector[base  + index*4 ..];
					writer.write_all(&loc.to_le_bytes())?;
				}
			},
			&ConfigurationValue::NamedExperiments(ref name, ref list) => {
				let n:u32 = list.len().try_into().unwrap();
				let end = location + 4*n + 3*4;
				self.vector.resize(end as usize, 0u8);
				let loc = self.locate(name)?;
				let mut writer = &mut self.vector[location as usize ..];
				writer.write_all(&5u32.to_le_bytes())?;
				writer.write_all(&loc.to_le_bytes())?;
				writer.write_all(&n.to_le_bytes())?;
				let base:usize = (location +3*4).try_into().unwrap();
				for (index,val) in list.iter().enumerate(){
					let loc = self.insert(val)?;
					let mut writer = &mut self.vector[base + index*4 ..];
					writer.write_all(&loc.to_le_bytes())?;
				}
			},
			&ConfigurationValue::True => self.vector.write_all(&6u32.to_le_bytes())?,
			&ConfigurationValue::False => self.vector.write_all(&7u32.to_le_bytes())?,
			&ConfigurationValue::Where(ref _id, ref _expr) => {
				//TODO: This is not yet supported
				//its id=8 is reserved
				self.vector.write_all(&8u32.to_le_bytes())?;
			},
			&ConfigurationValue::Expression(ref _expr) => {
				//TODO: This is not yet supported
				//its id=9 is reserved
				self.vector.write_all(&9u32.to_le_bytes())?;
			},
			&ConfigurationValue::None => self.vector.write_all(&10u32.to_le_bytes())?,
		}
		Ok(location)
	}
	///Get a location with the name given. Insert it in the map and vector if necessary.
	fn locate(&mut self, name:&str) -> io::Result<u32>
	{
		Ok(match self.name_locations.get(name)
		{
			Some(loc) =>{
				*loc
			},
			None =>{
				let loc:u32 = self.vector.len().try_into().unwrap();
				self.name_locations.insert(name.to_string(),loc);
				self.vector.write_all(&(name.len() as u32).to_le_bytes())?;
				self.vector.write_all(name.as_bytes())?;
				loc
			},
		})
	}
}

///Read the value from the input at the given offset.
#[allow(clippy::identity_op)]
pub fn config_from_binary(data:&[u8], offset:usize) -> Result<ConfigurationValue,std::string::FromUtf8Error>
{
	let magic = u32::from_le_bytes(data[offset..offset+4].try_into().unwrap());
	//println!(">>config_from_binary data.len={} offset={} magic={}",data.len(),offset,magic);
	match magic{
		0 => {
			let loc:usize = u32::from_le_bytes(data[offset+4..offset+8].try_into().unwrap()).try_into().unwrap();
			let size:usize = u32::from_le_bytes(data[loc..loc+4].try_into().unwrap()).try_into().unwrap();
			Ok(ConfigurationValue::Literal(String::from_utf8(data[loc+4..loc+4+size].to_vec())?))
		},
		1 => {
			let f= f64::from_le_bytes(data[offset+4..offset+4+8].try_into().unwrap());
			Ok(ConfigurationValue::Number(f))
		},
		2 => {
			let loc:usize = u32::from_le_bytes(data[offset+4..offset+2*4].try_into().unwrap()).try_into().unwrap();
			let n:usize = u32::from_le_bytes(data[offset+2*4..offset+3*4].try_into().unwrap()).try_into().unwrap();
			let size:usize = u32::from_le_bytes(data[loc..loc+4].try_into().unwrap()).try_into().unwrap();
			let name = String::from_utf8(data[loc+4..loc+4+size].to_vec())?;
			let mut pairs = Vec::with_capacity(n);
			for index in 0..n
			{
				let item_offset = offset+3*4 +index*2*4;
				let loc:usize = u32::from_le_bytes(data[item_offset..item_offset+4].try_into().unwrap()).try_into().unwrap();
				let size:usize = u32::from_le_bytes(data[loc..loc+4].try_into().unwrap()).try_into().unwrap();
				let key = String::from_utf8(data[loc+4..loc+4+size].to_vec())?;
				let loc:usize = u32::from_le_bytes(data[item_offset+4..item_offset+2*4].try_into().unwrap()).try_into().unwrap();
				let val = config_from_binary(data,loc)?;
				pairs.push( (key,val) );
			}
			Ok(ConfigurationValue::Object(name,pairs))
		},
		3 => {
			let n:usize = u32::from_le_bytes(data[offset+1*4..offset+2*4].try_into().unwrap()).try_into().unwrap();
			let mut a = Vec::with_capacity(n);
			for index in 0..n
			{
				let item_offset = offset+2*4 +index*4;
				let loc:usize = u32::from_le_bytes(data[item_offset..item_offset+4].try_into().unwrap()).try_into().unwrap();
				let val = config_from_binary(data,loc)?;
				a.push( val );
			}
			Ok(ConfigurationValue::Array(a))
		},
		4 => {
			let n:usize = u32::from_le_bytes(data[offset+1*4..offset+2*4].try_into().unwrap()).try_into().unwrap();
			let mut list = Vec::with_capacity(n);
			for index in 0..n
			{
				let item_offset = offset+2*4 +index*4;
				let loc:usize = u32::from_le_bytes(data[item_offset..item_offset+4].try_into().unwrap()).try_into().unwrap();
				let val = config_from_binary(data,loc)?;
				list.push( val );
			}
			Ok(ConfigurationValue::Experiments(list))
		},
		5 => {
			let loc:usize = u32::from_le_bytes(data[offset+4..offset+2*4].try_into().unwrap()).try_into().unwrap();
			let n:usize = u32::from_le_bytes(data[offset+2*4..offset+3*4].try_into().unwrap()).try_into().unwrap();
			let size:usize = u32::from_le_bytes(data[loc..loc+4].try_into().unwrap()).try_into().unwrap();
			let name = String::from_utf8(data[loc+4..loc+4+size].to_vec())?;
			let mut list = Vec::with_capacity(n);
			for index in 0..n
			{
				let item_offset = offset+3*4 +index*4;
				let loc:usize = u32::from_le_bytes(data[item_offset..item_offset+4].try_into().unwrap()).try_into().unwrap();
				let val = config_from_binary(data,loc)?;
				list.push( val );
			}
			Ok(ConfigurationValue::NamedExperiments(name,list))
		},
		6 => Ok(ConfigurationValue::True),
		7 => Ok(ConfigurationValue::False),
		8 => panic!("binary format of where clauses is not yet supported"),
		9 => panic!("binary format of expressions is not yet supported"),
		10 => Ok(ConfigurationValue::None),
		_ => panic!("Do not know what to do with magic={}",magic),
	}
}


///Rewrites the value in-place.
///If `edition` is `term=new_value` where `term` can be interpreted as a left-value then replace its content with `new_value`.
///returns `true` is something in `value` has been changed.
pub fn rewrite_eq(value:&mut ConfigurationValue, edition:&Expr, path:&Path) -> bool
{
	match edition
	{
		Expr::Equality(left,right) =>
		{
			//let new_value = evaluate(right,value,path);
			let new_value = match evaluate(right,value,path)
			{
				Ok(x) => x,
				Err(_e) => return false,
			};
			//rewrite_pair(value,left,new_value)
			if let Some(ptr) = config_mut_into(value,left)
			{
				*ptr = new_value;
				true
			} else {
				false
			}
		}
		_ => false,
	}
}

///Rewrites the value in-place.
///If `path_expr` can be interpreted as a left-value then replace its content with `new_value`.
///returns `true` is something in `value` has been changed.
pub fn rewrite_pair(value:&mut ConfigurationValue, path_expr:&Expr, new_value:&Expr, path:&Path) -> bool
{
	let new_value = match evaluate(new_value,value,path)
	{
		Ok(x) => x,
		Err(_e) => return false,
	};
	if let Some(ptr) = config_mut_into(value,path_expr)
	{
		*ptr = new_value;
		true
	} else {
		false
	}
}

///Rewrites the value in-place.
///If `path_expr` can be interpreted as a left-value then replace its content with `new_value`.
///returns `true` is something in `value` has been changed.
pub fn rewrite_pair_value(value:&mut ConfigurationValue, path_expr:&Expr, new_value:ConfigurationValue) -> bool
{
	if let Some(ptr) = config_mut_into(value,path_expr)
	{
		*ptr = new_value;
		true
	} else {
		false
	}
}

///Tries to access to a given path inside a ConfigurationValue
///Returns `None` if the path is not found.
pub fn config_mut_into<'a>(value:&'a mut ConfigurationValue, expr_path:&Expr) -> Option<&'a mut ConfigurationValue>
{
	match expr_path
	{
		Expr::Ident(ref name) =>
		{
			match value
			{
				ConfigurationValue::Object(ref _object_name,ref mut arr) =>
				{
					for (key,val) in arr.iter_mut()
					{
						if key==name
						{
							return Some(val);
						}
					}
					None
				}
				_ => None,
			}
		}
		Expr::Member(ref parent, ref field_name) =>
		{
			match config_mut_into(value,parent)
			{
				Some(into_parent) => config_mut_into(into_parent,&Expr::Ident(field_name.clone())),
				None => None,
			}
		}
		_ =>
		{
			None
		}
	}
}

/// Less strict than PartialEq
/// Ignores the fields `legend_name`, and `launch_configurations`.
pub fn config_relaxed_cmp(a:&ConfigurationValue, b:&ConfigurationValue) -> bool
{
	use ConfigurationValue::*;
	let ignore = |key| key == "legend_name" || key == "launch_configurations";
	match (a,b)
	{
		(Literal(sa),Literal(sb)) => sa==sb,
		(Number(xa),Number(xb)) => xa==xb,
		(Object(na,xa),Object(nb,xb)) =>
		{
			//na==nb && xa==xb,
			if na != nb { return false; }
			//do we want to enforce order of the fields?
			for ( (ka,va),(kb,vb) ) in
				xa.iter().filter(|(key,_)| !ignore(key) ).zip(
				xb.iter().filter(|(key,_)| !ignore(key)  ) )
			{
				if ka != kb { return false; }
				if !config_relaxed_cmp(va,vb) { return false; }
			}
			return true;
		}
		(Array(xa),Array(xb)) =>
		{
			//xa==xb
			for (va,vb) in
				xa.iter().zip(
				xb.iter() )
			{
				if !config_relaxed_cmp(va,vb) { return false; }
			}
			return true;
		}
		(Experiments(xa),Experiments(xb)) =>
		{
			//xa==xb
			for (va,vb) in
				xa.iter().zip(
				xb.iter() )
			{
				if !config_relaxed_cmp(va,vb) { return false; }
			}
			return true;
		}
		(NamedExperiments(na,xa),NamedExperiments(nb,xb)) =>
		{
			//na==nb && xa==xb,
			if na != nb { return false; }
			for (va,vb) in
				xa.iter().zip(
				xb.iter() )
			{
				if !config_relaxed_cmp(va,vb) { return false; }
			}
			return true;
		}
		(True,True) => true,
		(False,False) => true,
		(Where(xa,ea),Where(xb,eb)) => xa==xb && ea==eb,
		(Expression(xa),Expression(xb)) => xa==xb,
		(None,None) => true,
		_ => false,
	}
}


/// match arms agains the keys of an object
/// first argument, `$cv:expr`, is the ConfigurationValue expected to be the object
/// second argument, `$name:literal`, is the name the Object should have.
/// third argument, `$valueid:ident`, is the variable name capturing the value in the object's elements
///    and can be used in the arms
/// the remaining arguments are the arms of the match.
#[macro_export]
macro_rules! match_object{
	//($cv:expr, $name:literal, $valueid:ident, $($key:literal => $arm:tt)* ) => {{
	($cv:expr, $name:literal, $valueid:ident, $($arm:tt)* ) => {{
		//Error::$kind( source_location!(), $($args),* )
		if let &ConfigurationValue::Object(ref cv_name, ref cv_pairs) = $cv
		{
			if cv_name!= $name
			{
				panic!("A {} must be created from a `{}` object not `{}`",$name,$name,cv_name);
			}
			for &(ref name,ref $valueid) in cv_pairs
			{
				//match name.as_ref()
				match AsRef::<str>::as_ref(&name)
				{
					//"pattern" => pattern=Some(new_pattern(PatternBuilderArgument{cv:value,..arg})),
					$( $arm )*
					"legend_name" => (),
					//_ => panic!("Nothing to do with field {} in {}",name,$name),
					_ => return Err(error!(ill_formed_configuration,$cv.clone()).with_message(format!("Nothing to do with field {} in {}",name,$name))),
				}
			}
		}
		else
		{
			//panic!("Trying to create a {} from a non-Object",$name);
			return Err(error!(ill_formed_configuration,$cv.clone()).with_message(format!("Trying to create a {} from a non-Object",$name)));
		}
	}};
}
///Like `match_object!` but panicking on errors.
#[macro_export]
macro_rules! match_object_panic{
	($cv:expr, $name:literal, $valueid:ident ) => {{
		match_object_panic!($cv,$name,$valueid,)
	}};
	($cv:expr, $name:literal, $valueid:ident, $($arm:tt)* ) => {{
		if let &ConfigurationValue::Object(ref cv_name, ref cv_pairs) = $cv
		{
			if cv_name!= $name
			{
				panic!("A {} must be created from a `{}` object not `{}`",$name,$name,cv_name);
			}
			for &(ref name,ref $valueid) in cv_pairs
			{
				match AsRef::<str>::as_ref(&name)
				{
					$( $arm )*
					"legend_name" => (),
					_ => panic!("Nothing to do with field {} in {}",name,$name),
				}
			}
		}
		else
		{
			panic!("Trying to create a {} from a non-Object",$name);
		}
	}};
}

impl ConfigurationValue
{
	pub fn as_bool(&self) -> Result<bool,Error>
	{
		match self
		{
			&ConfigurationValue::True => Ok(true),
			&ConfigurationValue::False => Ok(false),
			_ => Err(error!(ill_formed_configuration, self.clone() )),
		}
	}
	pub fn as_str(&self) -> Result<&str,Error>
	{
		match self
		{
			&ConfigurationValue::Literal(ref s) => Ok(s),
			_ => Err(error!(ill_formed_configuration, self.clone() )),
		}
	}
	pub fn as_f64(&self) -> Result<f64,Error>
	{
		match self
		{
			&ConfigurationValue::Number(x) => Ok(x),
			_ => Err(error!(ill_formed_configuration, self.clone() )),
		}
	}
	pub fn as_array(&self) -> Result<&Vec<ConfigurationValue>,Error>
	{
		match self
		{
			&ConfigurationValue::Array(ref x) => Ok(x),
			_ => Err(error!(ill_formed_configuration, self.clone() )),
		}
	}
	pub fn as_expr(&self) -> Result<&Expr,Error>
	{
		match self
		{
			&ConfigurationValue::Expression(ref e) => Ok(e),
			_ => Err(error!(ill_formed_configuration, self.clone() )),
		}
	}
	/// Build a generic IllFormedConfiguration error from this ConfigurationValue.
	pub fn ill(&self,message:&str) -> Error
	{
		error!(ill_formed_configuration,self.clone()).with_message(message.to_string())
	}
	/// Convert this value into some string without newlines or commas.
	/// If not possible just return `"error".to_string()`.
	/// XXX: we could make a `or_else` method receiving a `Fn()->String`.
	pub fn to_csv_field(&self) -> String
	{
		use ConfigurationValue::*;
		match self
		{
			Literal(s) => s.to_string(),
			Number(x) => format!("{x}"),
			Object(name,attrs) => if attrs.is_empty() { name } else { "error" }.to_string(),
			True => "true".to_string(),
			False => "false".to_string(),
			None => "None".to_string(),
			_ => "error".to_string(),
		}
	}
	//pub fn borrow(&self) -> BorrowedConfigurationValue<ConfigurationValue>
	//{
	//	BorrowedConfigurationValue::from(self)
	//}
	pub fn rename(&mut self,new_name: String)
	{
		match self
		{
			&mut ConfigurationValue::Literal ( ref mut name ) => *name = new_name,
			&mut ConfigurationValue::Object( ref mut name, _ ) => *name = new_name,
			&mut ConfigurationValue::NamedExperiments( ref mut name,_) => *name = new_name,
			_ => (),
		}
	}
}

//Perhaps the best would be to implement a trait
// trait AsConfigurationValue
// fn expand() -> ExpandedConfigurationValue<Self::Base>
// impl AsConfigurationValue for ConfigurationValue
// impl AsConfigurationValue for &ConfigurationValue
// impl AsConfigurationValue for IndirectObject{name:String,content:Vec<&str,C>} where C:AsConfigurationValue

//#[derive(Debug)]
//enum BorrowedConfigurationValue<'a,C>
//{
//Literal(&'a str),
//Number(f64),
//Object(&'a str,&'a [(String,C)]),
//Array(&'a [C]),
//Experiments(&'a [C]),
//NamedExperiments(&'a str,&'a [C]),
//True,
//False,
//Where(Rc<C>,&'a Expr),
//Expression(&'a Expr),
//None,
//}
//
//impl<'a> From<&'a ConfigurationValue> for BorrowedConfigurationValue<'a,ConfigurationValue>
//{
//	fn from(cv:&'a ConfigurationValue) -> BorrowedConfigurationValue<'a,ConfigurationValue>
//	{
//		match cv
//		{
//			ConfigurationValue::Literal(s) => BorrowedConfigurationValue::Literal(s),
//			ConfigurationValue::Number(x) => BorrowedConfigurationValue::Number(*x),
//			ConfigurationValue::Object(s,a) => BorrowedConfigurationValue::Object(s,a),
//			ConfigurationValue::Array(a) => BorrowedConfigurationValue::Array(a),
//			ConfigurationValue::Experiments(a) => BorrowedConfigurationValue::Experiments(a),
//			ConfigurationValue::NamedExperiments(s,a) => BorrowedConfigurationValue::NamedExperiments(s,a),
//			ConfigurationValue::True => BorrowedConfigurationValue::True,
//			ConfigurationValue::False => BorrowedConfigurationValue::False,
//			ConfigurationValue::Where(c,e) => BorrowedConfigurationValue::Where( Rc::clone(c),e),
//			ConfigurationValue::Expression(e) => BorrowedConfigurationValue::Expression(e),
//			ConfigurationValue::None => BorrowedConfigurationValue::None,
//		}
//	}
//}
//
//impl<'a> From<&mut'a ConfigurationValue> for BorrowedConfigurationValue<'a,ConfigurationValue>
//{
//	fn from(cv:&mut'a ConfigurationValue) -> BorrowedConfigurationValue<'a,ConfigurationValue>
//	{
//		BorrowedConfigurationValue::from(&*cv)
//	}
//}
//
//impl<'a> From<&'a ConfigurationValue> for ConfigurationValue
//{
//	fn from(cv:&'a ConfigurationValue) -> ConfigurationValue
//	{
//		cv.clone()
//	}
//}
//
//impl<'a,C> From<BorrowedConfigurationValue<'a,C>> for ConfigurationValue
//	where &'a C:Into<ConfigurationValue>
//{
//	fn from(cv:BorrowedConfigurationValue<'a,C>) -> ConfigurationValue
//	{
//		match cv
//		{
//			BorrowedConfigurationValue::Literal(s) => ConfigurationValue::Literal(s.to_string()),
//			BorrowedConfigurationValue::Number(x) => ConfigurationValue::Number(x),
//			BorrowedConfigurationValue::Object(s,a) => ConfigurationValue::Object(s.to_string(),a
//				.iter().map(|(x,y)|(x.to_string(),y.into())).collect()),
//			BorrowedConfigurationValue::Array(a) => ConfigurationValue::Array(a
//				.iter().map(|x|x.into()).collect()),
//			BorrowedConfigurationValue::Experiments(a) => ConfigurationValue::Experiments(a
//				.iter().map(|x|x.into()).collect()),
//			BorrowedConfigurationValue::NamedExperiments(s,a) => ConfigurationValue::NamedExperiments(s.to_string(),a
//				.iter().map(|x|x.into()).collect()),
//			BorrowedConfigurationValue::True => ConfigurationValue::True,
//			BorrowedConfigurationValue::False => ConfigurationValue::False,
//			BorrowedConfigurationValue::Where(c,e) => ConfigurationValue::Where( Rc::new((&*c).into()),e.clone()),
//			BorrowedConfigurationValue::Expression(e) => ConfigurationValue::Expression(e.clone()),
//			BorrowedConfigurationValue::None => ConfigurationValue::None,
//		}
//	}
//}

//impl<'a,S,C,VSC,VC> Borrow<BorrowedConfigurationValue<'a,S,C,VSC,VC>> for ConfigurationValue
//	where String:Borrow<S>, ConfigurationValue:Borrow<C>,
//		Vec<(String,ConfigurationValue)>:Borrow<VSC>, Vec<ConfigurationValue>:Borrow<VC>,
//		Self:'a
//{
//	fn borrow(&self) -> &BorrowedConfigurationValue<'a,S,C,VSC,VC>
//	{
//		match self
//		{
//			&ConfigurationValue::Literal(s) => &BorrowedConfigurationValue::Literal(s.borrow()), 
//			_=> &BorrowedConfigurationValue::None,
//		}
//	}
//}
//
//impl<'a,S,C,VSC,VC> ToOwned for BorrowedConfigurationValue<'a,S,C,VSC,VC>
//	where String:Borrow<S>, ConfigurationValue:Borrow<C>,
//		Vec<(String,ConfigurationValue)>:Borrow<VSC>, Vec<ConfigurationValue>:Borrow<VC>,
//{
//	type Owned = ConfigurationValue;
//	fn to_owned(&self) -> ConfigurationValue
//	{
//		todo!()
//	}
//}


#[cfg(test)]
mod tests {
	use super::*;
	#[test]
	fn config_functions()
	{
		use std::path::PathBuf;
		let context = ConfigurationValue::None;
		let path = PathBuf::from(".");
		let v1 = Expr::Number(1.0);
		let v2 = Expr::Number(2.0);
		match evaluate(&Expr::FunctionCall("add".to_string(),vec![("first".to_string(),v1),("second".to_string(),v2)]),&context,&path)
		{
			Ok( ConfigurationValue::Number(x) ) => assert_eq!(x,3.0),
			_ => assert!(false),
		}
	}
}