text: Elm.Native = Elm.Native || {};
  Elm.Native.Utils = {};
  Elm.Native.Utils.make = function(localRuntime) {
  	localRuntime.Native = localRuntime.Native || {};
  	localRuntime.Native.Utils = localRuntime.Native.Utils || {};
  	if (localRuntime.Native.Utils.values)
  	{
  		return localRuntime.Native.Utils.values;
  	}
  
  
  	// COMPARISONS
  
  	function eq(l, r)
  	{
  		var stack = [{'x': l, 'y': r}];
  		while (stack.length > 0)
  		{
  			var front = stack.pop();
  			var x = front.x;
  			var y = front.y;
  			if (x === y)
  			{
  				continue;
  			}
  			if (typeof x === 'object')
  			{
  				var c = 0;
  				for (var i in x)
  				{
  					++c;
  					if (i in y)
  					{
  						if (i !== 'ctor')
  						{
  							stack.push({ 'x': x[i], 'y': y[i] });
  						}
  					}
  					else
  					{
  						return false;
  					}
  				}
  				if ('ctor' in x)
  				{
  					stack.push({'x': x.ctor, 'y': y.ctor});
  				}
  				if (c !== Object.keys(y).length)
  				{
  					return false;
  				}
  			}
  			else if (typeof x === 'function')
  			{
  				throw new Error('Equality error: general function equality is ' +
  								'undecidable, and therefore, unsupported');
  			}
  			else
  			{
  				return false;
  			}
  		}
  		return true;
  	}
  
  	// code in Generate/JavaScript.hs depends on the particular
  	// integer values assigned to LT, EQ, and GT
  	var LT = -1, EQ = 0, GT = 1, ord = ['LT', 'EQ', 'GT'];
  
  	function compare(x, y)
  	{
  		return {
  			ctor: ord[cmp(x, y) + 1]
  		};
  	}
  
  	function cmp(x, y) {
  		var ord;
  		if (typeof x !== 'object')
  		{
  			return x === y ? EQ : x < y ? LT : GT;
  		}
  		else if (x.isChar)
  		{
  			var a = x.toString();
  			var b = y.toString();
  			return a === b
  				? EQ
  				: a < b
  					? LT
  					: GT;
  		}
  		else if (x.ctor === '::' || x.ctor === '[]')
  		{
  			while (true)
  			{
  				if (x.ctor === '[]' && y.ctor === '[]')
  				{
  					return EQ;
  				}
  				if (x.ctor !== y.ctor)
  				{
  					return x.ctor === '[]' ? LT : GT;
  				}
  				ord = cmp(x._0, y._0);
  				if (ord !== EQ)
  				{
  					return ord;
  				}
  				x = x._1;
  				y = y._1;
  			}
  		}
  		else if (x.ctor.slice(0, 6) === '_Tuple')
  		{
  			var n = x.ctor.slice(6) - 0;
  			var err = 'cannot compare tuples with more than 6 elements.';
  			if (n === 0) return EQ;
  			if (n >= 1) { ord = cmp(x._0, y._0); if (ord !== EQ) return ord;
  			if (n >= 2) { ord = cmp(x._1, y._1); if (ord !== EQ) return ord;
  			if (n >= 3) { ord = cmp(x._2, y._2); if (ord !== EQ) return ord;
  			if (n >= 4) { ord = cmp(x._3, y._3); if (ord !== EQ) return ord;
  			if (n >= 5) { ord = cmp(x._4, y._4); if (ord !== EQ) return ord;
  			if (n >= 6) { ord = cmp(x._5, y._5); if (ord !== EQ) return ord;
  			if (n >= 7) throw new Error('Comparison error: ' + err); } } } } } }
  			return EQ;
  		}
  		else
  		{
  			throw new Error('Comparison error: comparison is only defined on ints, ' +
  							'floats, times, chars, strings, lists of comparable values, ' +
  							'and tuples of comparable values.');
  		}
  	}
  
  
  	// TUPLES
  
  	var Tuple0 = {
  		ctor: '_Tuple0'
  	};
  
  	function Tuple2(x, y)
  	{
  		return {
  			ctor: '_Tuple2',
  			_0: x,
  			_1: y
  		};
  	}
  
  
  	// LITERALS
  
  	function chr(c)
  	{
  		var x = new String(c);
  		x.isChar = true;
  		return x;
  	}
  
  	function txt(str)
  	{
  		var t = new String(str);
  		t.text = true;
  		return t;
  	}
  
  
  	// GUID
  
  	var count = 0;
  	function guid(_)
  	{
  		return count++;
  	}
  
  
  	// RECORDS
  
  	function update(oldRecord, updatedFields)
  	{
  		var newRecord = {};
  		for (var key in oldRecord)
  		{
  			var value = (key in updatedFields) ? updatedFields[key] : oldRecord[key];
  			newRecord[key] = value;
  		}
  		return newRecord;
  	}
  
  
  	// MOUSE COORDINATES
  
  	function getXY(e)
  	{
  		var posx = 0;
  		var posy = 0;
  		if (e.pageX || e.pageY)
  		{
  			posx = e.pageX;
  			posy = e.pageY;
  		}
  		else if (e.clientX || e.clientY)
  		{
  			posx = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
  			posy = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
  		}
  
  		if (localRuntime.isEmbed())
  		{
  			var rect = localRuntime.node.getBoundingClientRect();
  			var relx = rect.left + document.body.scrollLeft + document.documentElement.scrollLeft;
  			var rely = rect.top + document.body.scrollTop + document.documentElement.scrollTop;
  			// TODO: figure out if there is a way to avoid rounding here
  			posx = posx - Math.round(relx) - localRuntime.node.clientLeft;
  			posy = posy - Math.round(rely) - localRuntime.node.clientTop;
  		}
  		return Tuple2(posx, posy);
  	}
  
  
  	//// LIST STUFF ////
  
  	var Nil = { ctor: '[]' };
  
  	function Cons(hd, tl)
  	{
  		return {
  			ctor: '::',
  			_0: hd,
  			_1: tl
  		};
  	}
  
  	function list(arr)
  	{
  		var out = Nil;
  		for (var i = arr.length; i--; )
  		{
  			out = Cons(arr[i], out);
  		}
  		return out;
  	}
  
  	function range(lo, hi)
  	{
  		var list = Nil;
  		if (lo <= hi)
  		{
  			do
  			{
  				list = Cons(hi, list);
  			}
  			while (hi-- > lo);
  		}
  		return list;
  	}
  
  	function append(xs, ys)
  	{
  		// append Strings
  		if (typeof xs === 'string')
  		{
  			return xs + ys;
  		}
  
  		// append Text
  		if (xs.ctor.slice(0, 5) === 'Text:')
  		{
  			return {
  				ctor: 'Text:Append',
  				_0: xs,
  				_1: ys
  			};
  		}
  
  
  		// append Lists
  		if (xs.ctor === '[]')
  		{
  			return ys;
  		}
  		var root = Cons(xs._0, Nil);
  		var curr = root;
  		xs = xs._1;
  		while (xs.ctor !== '[]')
  		{
  			curr._1 = Cons(xs._0, Nil);
  			xs = xs._1;
  			curr = curr._1;
  		}
  		curr._1 = ys;
  		return root;
  	}
  
  
  	// CRASHES
  
  	function crash(moduleName, region)
  	{
  		return function(message) {
  			throw new Error(
  				'Ran into a `Debug.crash` in module `' + moduleName + '` ' + regionToString(region) + '\n'
  				+ 'The message provided by the code author is:\n\n    '
  				+ message
  			);
  		};
  	}
  
  	function crashCase(moduleName, region, value)
  	{
  		return function(message) {
  			throw new Error(
  				'Ran into a `Debug.crash` in module `' + moduleName + '`\n\n'
  				+ 'This was caused by the `case` expression ' + regionToString(region) + '.\n'
  				+ 'One of the branches ended with a crash and the following value got through:\n\n    ' + toString(value) + '\n\n'
  				+ 'The message provided by the code author is:\n\n    '
  				+ message
  			);
  		};
  	}
  
  	function regionToString(region)
  	{
  		if (region.start.line == region.end.line)
  		{
  			return 'on line ' + region.start.line;
  		}
  		return 'between lines ' + region.start.line + ' and ' + region.end.line;
  	}
  
  
  	// BAD PORTS
  
  	function badPort(expected, received)
  	{
  		throw new Error(
  			'Runtime error when sending values through a port.\n\n'
  			+ 'Expecting ' + expected + ' but was given ' + formatValue(received)
  		);
  	}
  
  	function formatValue(value)
  	{
  		// Explicity format undefined values as "undefined"
  		// because JSON.stringify(undefined) unhelpfully returns ""
  		return (value === undefined) ? "undefined" : JSON.stringify(value);
  	}
  
  
  	// TO STRING
  
  	var _Array;
  	var Dict;
  	var List;
  
  	var toString = function(v)
  	{
  		var type = typeof v;
  		if (type === 'function')
  		{
  			var name = v.func ? v.func.name : v.name;
  			return '<function' + (name === '' ? '' : ': ') + name + '>';
  		}
  		else if (type === 'boolean')
  		{
  			return v ? 'True' : 'False';
  		}
  		else if (type === 'number')
  		{
  			return v + '';
  		}
  		else if ((v instanceof String) && v.isChar)
  		{
  			return '\'' + addSlashes(v, true) + '\'';
  		}
  		else if (type === 'string')
  		{
  			return '"' + addSlashes(v, false) + '"';
  		}
  		else if (type === 'object' && 'ctor' in v)
  		{
  			if (v.ctor.substring(0, 6) === '_Tuple')
  			{
  				var output = [];
  				for (var k in v)
  				{
  					if (k === 'ctor') continue;
  					output.push(toString(v[k]));
  				}
  				return '(' + output.join(',') + ')';
  			}
  			else if (v.ctor === '_Array')
  			{
  				if (!_Array)
  				{
  					_Array = Elm.Array.make(localRuntime);
  				}
  				var list = _Array.toList(v);
  				return 'Array.fromList ' + toString(list);
  			}
  			else if (v.ctor === '::')
  			{
  				var output = '[' + toString(v._0);
  				v = v._1;
  				while (v.ctor === '::')
  				{
  					output += ',' + toString(v._0);
  					v = v._1;
  				}
  				return output + ']';
  			}
  			else if (v.ctor === '[]')
  			{
  				return '"[]';
  			}
  			else if (v.ctor === 'RBNode_elm_builtin' || v.ctor === 'RBEmpty_elm_builtin' || v.ctor === 'Set_elm_builtin')
  			{
  				if (!Dict)
  				{
  					Dict = Elm.Dict.make(localRuntime);
  				}
  				var list;
  				var name;
  				if (v.ctor === 'Set_elm_builtin')
  				{
  					if (!List)
  					{
  						List = Elm.List.make(localRuntime);
  					}
  					name = 'Set';
  					list = A2(List.map, function(x) {return x._0; }, Dict.toList(v._0));
  				}
  				else
  				{
  					name = 'Dict';
  					list = Dict.toList(v);
  				}
  				return name + '.fromList ' + toString(list);
  			}
  			else if (v.ctor.slice(0, 5) === 'Text:')
  			{
  				return '<text>';
  			}
  			else
  			{
  				var output = '';
  				for (var i in v)
  				{
  					if (i === 'ctor') continue;
  					var str = toString(v[i]);
  					var parenless = str[0] === '{' || str[0] === '<' || str.indexOf(' ') < 0;
  					output += ' ' + (parenless ? str : '(' + str + ')');
  				}
  				return v.ctor + output;
  			}
  		}
  		else if (type === 'object' && 'notify' in v && 'id' in v)
  		{
  			return '<Signal>';
  		}
  		else if (type === 'object' && probablyPublic(v))
  		{
  			var output = [];
  			for (var k in v)
  			{
  				output.push(k + ' = ' + toString(v[k]));
  			}
  			if (output.length === 0)
  			{
  				return '{}';
  			}
  			return '{ ' + output.join(', ') + ' }';
  		}
  		return '<internal structure>';
  	};
  
  	function addSlashes(str, isChar)
  	{
  		var s = str.replace(/\\/g, '\\\\')
  				  .replace(/\n/g, '\\n')
  				  .replace(/\t/g, '\\t')
  				  .replace(/\r/g, '\\r')
  				  .replace(/\v/g, '\\v')
  				  .replace(/\0/g, '\\0');
  		if (isChar)
  		{
  			return s.replace(/\'/g, '\\\'');
  		}
  		else
  		{
  			return s.replace(/\"/g, '\\"');
  		}
  	}
  
  	function probablyPublic(v)
  	{
  		var keys = Object.keys(v);
  		var len = keys.length;
  		if (len === 3
  			&& 'props' in v
  			&& 'element' in v)
  		{
  			return false;
  		}
  		else if (len === 5
  			&& 'horizontal' in v
  			&& 'vertical' in v
  			&& 'x' in v
  			&& 'y' in v)
  		{
  			return false;
  		}
  		else if (len === 7
  			&& 'theta' in v
  			&& 'scale' in v
  			&& 'x' in v
  			&& 'y' in v
  			&& 'alpha' in v
  			&& 'form' in v)
  		{
  			return false;
  		}
  		return true;
  	}
  
  
  	return localRuntime.Native.Utils.values = {
  		eq: eq,
  		cmp: cmp,
  		compare: F2(compare),
  		Tuple0: Tuple0,
  		Tuple2: Tuple2,
  		chr: chr,
  		txt: txt,
  		update: update,
  		guid: guid,
  		getXY: getXY,
  
  		Nil: Nil,
  		Cons: Cons,
  		list: list,
  		range: range,
  		append: F2(append),
  
  		crash: crash,
  		crashCase: crashCase,
  		badPort: badPort,
  
  		toString: toString
  	};
  };
  